UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

175 lines (142 loc) 4.93 kB
import { ClampToEdgeWrapping, DataTexture, FloatType, NearestFilter, RedFormat, RGBAFormat, UnsignedByteType, WebGLRenderTarget } from "three"; import { array_copy } from "../../../../../../core/collection/array/array_copy.js"; import { makeQueryShader } from "./QueryShader.js"; import { renderScreenSpace } from "../../../utils/renderScreenSpace.js"; import { assert } from "../../../../../../core/assert.js"; import { max2 } from "../../../../../../core/math/max2.js"; /** * Query element contains following data: * [AABB( x0,y0,z0, x1,y1,z1 ) :: Float32], [MATRIX4( ... x16 ) :: Float32] * @type {number} * @readonly */ const QUERY_ELEMENT_SIZE = 22; export class BatchOcclusionQuery { constructor() { const input_texture = new DataTexture(new Float32Array(1), 1, 1); input_texture.format = RedFormat; input_texture.type = FloatType; input_texture.internalFormat = 'R32F'; input_texture.generateMipmaps = false; input_texture.wrapT = ClampToEdgeWrapping; input_texture.wrapS = ClampToEdgeWrapping; input_texture.minFilter = NearestFilter; input_texture.magFilter = NearestFilter; this.__input_data_texture = input_texture; this.__output_data_texture = new WebGLRenderTarget(1, 1, { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat, type: UnsignedByteType, // internalFormat: 'R8', depthBuffer: false, stencilBuffer: false, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, generateMipmaps: false }); this.__size = -1; /** * * @type {ShaderMaterial} * @private */ this.__material = makeQueryShader(); this.__material.defines.ITEM_SIZE = QUERY_ELEMENT_SIZE; } /** * * @return {THREE.Texture} */ getResultTexture() { return this.__output_data_texture.texture; } /** * * @return {number} */ getSize() { return this.__size; } /** * * @param {number} count */ setSize(count) { if (this.__size === count) { return; } this.__size = count; /** * How many query elements are packed per texture row * @type {number} * @readonly */ const TEXTURE_COLUMN_COUNT = max2( Math.ceil(Math.sqrt(count)), 4 ); const input_texture = this.__input_data_texture; input_texture.dispose(); const row_count = Math.ceil(count / TEXTURE_COLUMN_COUNT); const input_image = input_texture.image; input_image.width = TEXTURE_COLUMN_COUNT * QUERY_ELEMENT_SIZE; input_image.height = row_count; input_image.data = new Float32Array(input_image.width * input_image.height); this.__output_data_texture.setSize(TEXTURE_COLUMN_COUNT, row_count); } /** * * @param {number} index * @param {number[]|Float32Array} aabb bounding box of the queries region * @param {number[]} transform 4x4 transform matrix */ setElement(index, aabb, transform) { assert.lessThan(index, this.__size, 'index overflow'); assert.isNonNegativeInteger(index, 'index'); // write query element const address = index * QUERY_ELEMENT_SIZE; const input_texture = this.__input_data_texture; /** * * @type {Uint8ClampedArray} */ const data = input_texture.image.data; array_copy(aabb, 0, data, address, 6); array_copy(transform, 0, data, address + 6, 16); input_texture.needsUpdate = true; } /** * * @param {THREE.WebGLRenderer} renderer * @param {Uint8Array} destination */ readResultTexture(renderer, destination) { const rt = this.__output_data_texture; renderer.readRenderTargetPixels(rt, 0, 0, rt.width, rt.height, destination); } /** * * @param {THREE.WebGLRenderer} renderer * @param {HierarchicalZBuffer} hiz_buffer */ execute(renderer, hiz_buffer) { const uniforms = this.__material.uniforms; uniforms.utDepth.value = hiz_buffer.getTexture(); uniforms.utInput.value = this.__input_data_texture; uniforms.uDepthMipLimits.value.set(hiz_buffer.mip_limits.x, hiz_buffer.mip_limits.y); const __rt = renderer.getRenderTarget(); renderer.setRenderTarget(this.__output_data_texture); renderScreenSpace(renderer, this.__material); renderer.setRenderTarget(__rt); } }