@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
291 lines (290 loc) • 11.3 kB
JavaScript
"use strict";
import {
Vector2,
Scene,
BufferGeometry,
BufferAttribute,
Mesh,
WebGLRenderTarget,
ClampToEdgeWrapping,
NearestFilter,
RGBAFormat,
HalfFloatType,
Vector3,
OrthographicCamera,
Sphere
} from "three";
import {
adjacencyTexture,
distancesTexture,
positionTexture,
viscositySpringTexture,
createTexturesFromAllocation
} from "./ClothAttributeToTexture";
export function clothMaterialCopyConfigRef(src, target) {
target.tSize.value.copy(src.tSize.value);
}
function textureContainerToRef(src, ref) {
if (src.texture) {
ref.value = src.texture;
}
}
export class ClothFBOController {
constructor(mainController) {
this.mainController = mainController;
this.tSize = new Vector2();
this.fboScene = new Scene();
this.fboCamera = new OrthographicCamera();
this.RESOLUTION = new Vector2();
this.originalRT = { texture: null };
this.viscositySpringT = { texture: null };
this.previousRT = new Array(2);
this.targetRT = new Array(2);
this.positionRT = new Array(2);
this.adjacentsRT = new Array(2);
this.distancesRT = new Array(2);
this._initialized = false;
this.RESOLUTION.copy(this.mainController.geometryInit.resolution);
this.tSize.set(this.RESOLUTION.x, this.RESOLUTION.y);
const geometry = new BufferGeometry();
const positions = new Float32Array([-1, -1, 3, -1, -1, 3]);
geometry.setAttribute("position", new BufferAttribute(positions, 2));
geometry.boundingSphere = new Sphere(new Vector3(0, 0, 0), 5);
this.fboMesh = new Mesh(geometry, this.mainController.materials.copyShader);
this.fboMesh.frustumCulled = false;
this.fboScene.add(this.fboMesh);
this.fboScene.updateMatrixWorld = function() {
};
this.normalsRT = createRenderTarget(this.RESOLUTION);
}
init(renderer) {
if (this._initialized) {
return;
}
this._initialized = true;
this.renderer = renderer;
const originalRenderTarget = renderer.getRenderTarget();
this.createPositionTexture();
this.createViscositySpringTexture();
this.createTexturesFromAllocation();
for (let i = 0; i < 2; i++) {
this.createAdjacentsTexture(i);
this.createDistancesTexture(i);
this.positionRT[i] = createRenderTarget(this.RESOLUTION);
this.previousRT[i] = createRenderTarget(this.RESOLUTION);
this.targetRT[i] = createRenderTarget(this.RESOLUTION);
this.copyTexture(this.originalRT, this.positionRT[i], !i, renderer);
this.copyTexture(this.originalRT, this.previousRT[i], !i, renderer);
}
renderer.setRenderTarget(originalRenderTarget);
}
copyTexture(input, output, order, renderer) {
const copyShader = this.mainController.materials.copyShader;
this.fboMesh.material = copyShader;
copyShader.uniforms.order.value = order ? 1 : -1;
copyShader.uniforms.tSize.value = this.tSize;
copyShader.uniforms.texture.value = input.texture;
renderer.setRenderTarget(output);
renderer.render(this.fboScene, this.fboCamera);
}
//
//
// create textures
//
//
createPositionTexture() {
this.originalRT = {
texture: positionTexture(
this.mainController.geometryInit.geometry,
// this.mainController.geometryInit.vertices,
this.RESOLUTION
)
};
}
createViscositySpringTexture() {
const texture = viscositySpringTexture(this.mainController.geometryInit.geometry, this.RESOLUTION);
if (texture) {
this.viscositySpringT = {
texture
};
}
}
createTexturesFromAllocation() {
const allocation = this.mainController.textureAllocationsController();
if (!allocation) {
return;
}
const texturesByName = createTexturesFromAllocation(
this.mainController.geometryInit.geometry,
this.RESOLUTION,
allocation
);
this.mainController.assignReadonlyTextures(this.mainController.materials.integrateShader, texturesByName);
}
createAdjacentsTexture(k) {
this.adjacentsRT[k] = {
texture: adjacencyTexture(
this.mainController.geometryInit.geometry,
this.RESOLUTION,
this.mainController.geometryInit.adjacency,
k
)
};
}
createDistancesTexture(k) {
this.distancesRT[k] = {
texture: distancesTexture(
this.mainController.geometryInit.geometry,
// this.mainController.geometryInit.vertices,
this.RESOLUTION,
this.mainController.geometryInit.adjacency,
k
)
};
}
//
//
// update
//
//
update(config) {
const renderer = this.renderer;
if (!(renderer && this._initialized)) {
return;
}
const originalRenderTarget = renderer.getRenderTarget();
this.integrate(renderer);
const selectedVertexIndex = this.mainController.selectedVertexIndex();
const steps = this.mainController.stepsCount;
for (let i = 0; i < steps; i++) {
if (selectedVertexIndex >= 0 && i < steps - 5) {
this.mouseOffset(renderer);
}
this.solveConstraints(renderer, i == steps - 1 ? 1 : 0);
}
this.computeVertexNormals(renderer);
renderer.setRenderTarget(originalRenderTarget);
if (config) {
this._updateTextureRefs(config);
}
}
_updateTextureRefs(config) {
config.tSize.value = this.mainController.fbo.tSize;
config.tPosition0.value = this.positionRT[0].texture;
config.tPosition1.value = this.positionRT[1].texture;
config.tNormal.value = this.normalsRT.texture;
textureContainerToRef(this.originalRT, config.tOriginalRT);
textureContainerToRef(this.viscositySpringT, config.tViscositySpringT);
config.tPreviousRT0.value = this.previousRT[0].texture;
config.tPreviousRT1.value = this.previousRT[1].texture;
config.tTargetRT0.value = this.targetRT[0].texture;
config.tTargetRT1.value = this.targetRT[1].texture;
config.tNormalsRT.value = this.normalsRT.texture;
config.tPositionRT0.value = this.positionRT[0].texture;
config.tPositionRT1.value = this.positionRT[1].texture;
textureContainerToRef(this.adjacentsRT[0], config.tAdjacentsRT0);
textureContainerToRef(this.adjacentsRT[1], config.tAdjacentsRT1);
textureContainerToRef(this.distancesRT[0], config.tDistanceRT0);
textureContainerToRef(this.distancesRT[1], config.tDistanceRT1);
config.integrationMat.value = this.mainController.materials.integrateShader;
}
integrate(renderer) {
const integrateShader = this.mainController.materials.integrateShader;
this.fboMesh.material = integrateShader;
integrateShader.uniforms.viscosity.value = this.mainController.viscosity;
integrateShader.uniforms.spring.value = this.mainController.spring;
integrateShader.uniforms.tSize.value.copy(this.tSize);
integrateShader.uniforms.tOriginal.value = this.originalRT.texture;
integrateShader.uniforms.tPrevious0.value = this.previousRT[0].texture;
integrateShader.uniforms.tPrevious1.value = this.previousRT[1].texture;
integrateShader.uniforms.tPosition0.value = this.positionRT[0].texture;
integrateShader.uniforms.tPosition1.value = this.positionRT[1].texture;
integrateShader.uniforms.tViscositySpring.value = this.viscositySpringT.texture;
integrateShader.uniforms.order.value = 1;
renderer.setRenderTarget(this.targetRT[0]);
renderer.render(this.fboScene, this.fboCamera);
integrateShader.uniforms.order.value = -1;
renderer.setRenderTarget(this.targetRT[1]);
renderer.render(this.fboScene, this.fboCamera);
let tmp = this.previousRT[0];
this.previousRT[0] = this.positionRT[0];
this.positionRT[0] = this.targetRT[0];
this.targetRT[0] = tmp;
tmp = this.previousRT[1];
this.previousRT[1] = this.positionRT[1];
this.positionRT[1] = this.targetRT[1];
this.targetRT[1] = tmp;
}
solveConstraints(renderer, secondaryMotionMult) {
const constraintsShader = this.mainController.materials.constraintsShader;
this.fboMesh.material = constraintsShader;
constraintsShader.uniforms.constraintInfluence.value = this.mainController.constraintInfluence;
constraintsShader.uniforms.tSize.value.copy(this.tSize);
constraintsShader.uniforms.tPosition0.value = this.positionRT[0].texture;
constraintsShader.uniforms.tPosition1.value = this.positionRT[1].texture;
constraintsShader.uniforms.tAdjacentsA.value = this.adjacentsRT[0].texture;
constraintsShader.uniforms.tAdjacentsB.value = this.adjacentsRT[1].texture;
constraintsShader.uniforms.tDistancesA.value = this.distancesRT[0].texture;
constraintsShader.uniforms.tDistancesB.value = this.distancesRT[1].texture;
constraintsShader.uniforms.secondaryMotionMult.value = secondaryMotionMult;
constraintsShader.uniforms.order.value = 1;
renderer.setRenderTarget(this.targetRT[0]);
renderer.render(this.fboScene, this.fboCamera);
constraintsShader.uniforms.order.value = -1;
renderer.setRenderTarget(this.targetRT[1]);
renderer.render(this.fboScene, this.fboCamera);
let tmp = this.positionRT[0];
this.positionRT[0] = this.targetRT[0];
this.targetRT[0] = tmp;
tmp = this.positionRT[1];
this.positionRT[1] = this.targetRT[1];
this.targetRT[1] = tmp;
}
mouseOffset(renderer) {
const mouseShader = this.mainController.materials.mouseShader;
this.fboMesh.material = mouseShader;
mouseShader.uniforms.tSize.value.copy(this.tSize);
mouseShader.uniforms.vertex.value = this.mainController.selectedVertexIndex();
this.mainController.constraintPosition(mouseShader.uniforms.coordinates.value);
mouseShader.uniforms.tOriginal.value = this.originalRT.texture;
mouseShader.uniforms.tPosition0.value = this.positionRT[0].texture;
mouseShader.uniforms.tPosition1.value = this.positionRT[1].texture;
mouseShader.uniforms.order.value = 1;
renderer.setRenderTarget(this.targetRT[0]);
renderer.render(this.fboScene, this.fboCamera);
mouseShader.uniforms.order.value = -1;
renderer.setRenderTarget(this.targetRT[1]);
renderer.render(this.fboScene, this.fboCamera);
let tmp = this.positionRT[0];
this.positionRT[0] = this.targetRT[0];
this.targetRT[0] = tmp;
tmp = this.positionRT[1];
this.positionRT[1] = this.targetRT[1];
this.targetRT[1] = tmp;
}
computeVertexNormals(renderer) {
const normalsShader = this.mainController.materials.normalsShader;
this.fboMesh.material = normalsShader;
normalsShader.uniforms.tSize.value.copy(this.tSize);
normalsShader.uniforms.tPosition0.value = this.positionRT[0].texture;
normalsShader.uniforms.tPosition1.value = this.positionRT[1].texture;
normalsShader.uniforms.tAdjacentsA.value = this.adjacentsRT[0].texture;
normalsShader.uniforms.tAdjacentsB.value = this.adjacentsRT[1].texture;
renderer.setRenderTarget(this.normalsRT);
renderer.render(this.fboScene, this.fboCamera);
}
}
function createRenderTarget(resolution) {
return new WebGLRenderTarget(resolution.x, resolution.y, {
wrapS: ClampToEdgeWrapping,
wrapT: ClampToEdgeWrapping,
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
type: HalfFloatType,
// depthTest: false,
// depthWrite: false,
depthBuffer: false,
stencilBuffer: false
});
}