@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
175 lines (142 loc) • 4.93 kB
JavaScript
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);
}
}