@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
145 lines (113 loc) • 4.32 kB
JavaScript
import {
DoubleSide,
HalfFloatType,
Mesh,
MeshNormalMaterial,
NearestFilter,
RGBAFormat,
Scene,
Vector2,
WebGLMultisampleRenderTarget
} from "three";
import { StandardFrameBuffers } from "../../../StandardFrameBuffers.js";
import { three_setSceneAutoUpdate } from "../../../three/three_setSceneAutoUpdate.js";
import { ThreeBypassRenderer } from "../../utils/ThreeBypassRenderer.js";
import { FrameBuffer } from "../FrameBuffer.js";
export class NormalFrameBuffer extends FrameBuffer {
constructor(id) {
super(id);
this.dependencies.push(StandardFrameBuffers.ColorAndDepth);
}
initialize(renderer) {
super.initialize(renderer);
// enable floating texture extension
renderer.extensions.get('EXT_color_buffer_float');
const size = new Vector2();
renderer.getSize(size);
const target = new WebGLMultisampleRenderTarget(size.x, size.y, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat, // note, three.js required RGBA render texture, even though RGB would do here
type: HalfFloatType,
depthBuffer: true,
stencilBuffer: false,
generateMipmaps: false
});
target.texture.internalFormat = 'RGBA16F';
this.renderTarget = target;
this.__material_static = new MeshNormalMaterial({ depthWrite: true });
this.__material_skinned = new MeshNormalMaterial({ depthWrite: true });
//SET material to double-sided, to support double-sided materials. This is an overkill, and will hurt performance a little. A full-blown material cache might be a better solution in the long run
this.__material_static.side = DoubleSide;
this.__material_skinned.side = DoubleSide;
const scene = new Scene();
/**
* @type {Scene}
* @private
*/
this.__scene = scene;
three_setSceneAutoUpdate(scene, false);
scene.matrixAutoUpdate = false;
scene.matrixWorldNeedsUpdate = false;
}
/**
*
* @param {THREE.Mesh|{customNormalMaterial:THREE.Material}} mesh
* @return {MeshNormalMaterial}
* @private
*/
__bindMaterial(mesh) {
let target_material;
if (mesh.customNormalMaterial !== undefined) {
target_material = mesh.customNormalMaterial;
} else {
/**
*
* @type {Material}
*/
const source_material = mesh.material;
// TODO take normal map into account
if (mesh.isSkinnedMesh) {
target_material = this.__material_skinned;
} else {
target_material = this.__material_static;
}
if (source_material !== undefined) {
// assign normal map and color map
if (source_material.alphaTest > 0) {
target_material.map = source_material.map;
} else {
target_material.map = null;
}
target_material.normalMap = source_material.normalMap;
}
}
return target_material;
}
/**
*
* @see {@link OutlineRenderer} for details on this technique
* @param renderer
* @param camera
* @param scene
* @param dependencies
*/
render(renderer, camera, scene, dependencies) {
this.__renderer = renderer;
// read current state
const _state_rt = renderer.getRenderTarget();
renderer.setRenderTarget(this.renderTarget);
renderer.clear(true, true, false);
ThreeBypassRenderer.INSTANCE.build_scene({
scene: this.__scene,
input: scene.children,
input_size: scene.children.length,
object_filter: object3D => object3D.visible,
material_extractor: this.__bindMaterial,
material_extractor_context: this
});
renderer.render(this.__scene, camera);
// restore state
renderer.setRenderTarget(_state_rt);
}
}