@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
310 lines (309 loc) • 11.9 kB
JavaScript
;
import { NodeContext } from "./../../../poly/NodeContext";
import { TypeAssert } from "./../../../poly/Assert";
import { RAYMARCHING_UNIFORMS } from "./../../gl/gl/raymarching/uniforms";
import { NodeParamsConfig, ParamConfig } from "../../utils/params/ParamsConfig";
import { PrimitiveMatNode } from "../_Base";
import { Material } from "three";
import { isBooleanTrue } from "../../../../core/Type";
import { CustomMaterialRayMarchingParamConfig } from "./customMaterials/CustomMaterialRayMarching";
import { ThreeToGl } from "../../../../core/ThreeToGl";
var RayMarchingDebugMode = /* @__PURE__ */ ((RayMarchingDebugMode2) => {
RayMarchingDebugMode2["STEPS_COUNT"] = "Steps Count";
RayMarchingDebugMode2["DEPTH"] = "Depth";
return RayMarchingDebugMode2;
})(RayMarchingDebugMode || {});
const RAYMARCHING_DEBUG_MODES = ["Steps Count" /* STEPS_COUNT */, "Depth" /* DEPTH */];
const DEBUG_STEPS_COUNT = RAYMARCHING_DEBUG_MODES.indexOf("Steps Count" /* STEPS_COUNT */);
function generateCubeUVSize(parameters) {
const imageHeight = parameters.envMapCubeUVHeight;
if (imageHeight === null)
return null;
const maxMip = Math.log2(imageHeight) - 2;
const texelHeight = 1 / imageHeight;
const texelWidth = 1 / (3 * Math.max(Math.pow(2, maxMip), 7 * 16));
return { texelWidth, texelHeight, maxMip };
}
function setDefines(shaderMaterial, props) {
shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"] = props ? 1 : 0;
shaderMaterial.defines["CUBEUV_TEXEL_WIDTH"] = props ? props.texelWidth : ThreeToGl.float(0.1);
shaderMaterial.defines["CUBEUV_TEXEL_HEIGHT"] = props ? props.texelHeight : ThreeToGl.float(0.1);
shaderMaterial.defines["CUBEUV_MAX_MIP"] = props ? ThreeToGl.float(props.maxMip) : ThreeToGl.float(1);
shaderMaterial.defines["ROTATE_ENV_MAP_Y"] = props ? props.tEnvMapRotate : 0;
}
export function RayMarchingMainParamConfig(Base) {
return class Mixin extends Base {
constructor() {
super(...arguments);
/** @param maximum number of steps the raymarcher will run */
this.maxSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.MAX_STEPS.value, {
range: [1, 128],
rangeLocked: [true, false]
});
/** @param maximum distance the raymarcher will step through */
this.maxDist = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.MAX_DIST.value, {
range: [1, 100],
rangeLocked: [true, false]
});
/** @param when the ray reaches this distance from a surface it will stop marching. You can lower this value to increase the precision of the raymarcher */
this.surfDist = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.SURF_DIST.value, {
range: [0, 0.1],
rangeLocked: [true, false],
step: 1e-7
});
/** @param precision for normals computation */
this.normalsBias = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.NORMALS_BIAS.value, {
range: [0, 0.1],
rangeLocked: [true, false],
step: 1e-7
});
/** @param precision for shadows computation */
this.shadowBias = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.SHADOW_BIAS.value, {
range: [-0.1, 0.1],
rangeLocked: [false, false],
step: 1e-7
});
}
};
}
export function RayMarchingEnvMapParamConfig(Base) {
return class Mixin extends Base {
constructor() {
super(...arguments);
/** @param toggle if you want to use an environment map */
this.useEnvMap = ParamConfig.BOOLEAN(0, {
separatorBefore: true
// ...BooleanParamOptions(TextureEnvMapController),
});
/** @param specify the environment map COP node */
this.envMap = ParamConfig.NODE_PATH("", {
visibleIf: { useEnvMap: 1 },
nodeSelection: { context: NodeContext.COP }
});
/** @param environment intensity */
this.envMapIntensity = ParamConfig.FLOAT(1, {
visibleIf: { useEnvMap: 1 },
cook: false,
callback: (node) => RayMarchingController.updateUniformEnvMapIntensity(node)
});
/** @param environment roughness */
this.envMapRoughness = ParamConfig.FLOAT(1, {
visibleIf: { useEnvMap: 1 },
cook: false,
callback: (node) => RayMarchingController.updateUniformEnvMapRoughness(node)
});
/** @param allow env map rotation */
this.tEnvMapRotate = ParamConfig.BOOLEAN(0, {
visibleIf: { useEnvMap: 1 }
});
/** @param env map rotation */
this.envMapRotation = ParamConfig.FLOAT(0, {
range: [-Math.PI, Math.PI],
rangeLocked: [false, false],
step: 1e-4,
visibleIf: { useEnvMap: 1, tEnvMapRotate: 1 },
cook: false,
callback: (node) => RayMarchingController.updateUniformEnvMapRotate(node)
});
}
};
}
export function RayMarchingDebugParamConfig(Base) {
return class Mixin extends Base {
constructor() {
super(...arguments);
/** @param debug mode */
this.debug = ParamConfig.BOOLEAN(0, {
separatorBefore: true
});
/** @param outputs color showing the number of steps required to solve the raymarching */
this.debugMode = ParamConfig.INTEGER(DEBUG_STEPS_COUNT, {
menu: { entries: RAYMARCHING_DEBUG_MODES.map((name, value) => ({ name, value })) },
visibleIf: { debug: true }
});
/** @param min steps count */
this.debugMinSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.debugMinSteps.value, {
range: [0, 128],
rangeLocked: [true, false],
step: 1
// visibleIf: {debug: true, debugMode: DEBUG_STEPS_COUNT},
});
/** @param max steps count */
this.debugMaxSteps = ParamConfig.INTEGER(RAYMARCHING_UNIFORMS.debugMaxSteps.value, {
range: [0, 128],
rangeLocked: [true, false],
step: 1
// visibleIf: {debug: true, debugMode: DEBUG_STEPS_COUNT},
});
/** @param min depth */
this.debugMinDepth = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.debugMinDepth.value, {
range: [0, 128],
rangeLocked: [true, false],
step: 1
// visibleIf: {debug: true, debugMode: DEBUG_DEPTH},
});
/** @param max depth */
this.debugMaxDepth = ParamConfig.FLOAT(RAYMARCHING_UNIFORMS.debugMaxDepth.value, {
range: [0, 128],
rangeLocked: [true, false],
step: 1
// visibleIf: {debug: true, debugMode: DEBUG_DEPTH},
});
}
};
}
class RayMarchingMaterial extends Material {
}
class RayMarchingParamsConfig extends CustomMaterialRayMarchingParamConfig(
RayMarchingDebugParamConfig(RayMarchingEnvMapParamConfig(RayMarchingMainParamConfig(NodeParamsConfig)))
) {
}
class RayMarchingMatNode extends PrimitiveMatNode {
}
export class RayMarchingController {
constructor(node) {
this.node = node;
}
async updateUniformsFromParams(shaderMaterial) {
const uniforms = shaderMaterial.uniforms;
if (!uniforms) {
return;
}
const pv = this.node.pv;
uniforms.MAX_STEPS.value = pv.maxSteps;
uniforms.MAX_DIST.value = pv.maxDist;
uniforms.SURF_DIST.value = pv.surfDist;
uniforms.NORMALS_BIAS.value = pv.normalsBias;
uniforms.SHADOW_BIAS.value = pv.shadowBias;
uniforms.shadowDepthMin.value = pv.shadowDepthMin;
uniforms.shadowDepthMax.value = pv.shadowDepthMax;
uniforms.shadowDistanceMin.value = pv.shadowDistanceMin;
uniforms.shadowDistanceMax.value = pv.shadowDistanceMax;
this._updateUniforms(shaderMaterial);
this._updateDebug(shaderMaterial, uniforms);
await this._updateEnvMap(shaderMaterial, uniforms);
}
_updateDebug(shaderMaterial, uniforms) {
const pv = this.node.pv;
if (isBooleanTrue(pv.debug)) {
let updateDebugMode2 = function(uniforms2) {
const debugMode = RAYMARCHING_DEBUG_MODES[pv.debugMode];
switch (debugMode) {
case "Steps Count" /* STEPS_COUNT */: {
uniforms2.debugMinSteps.value = pv.debugMinSteps;
uniforms2.debugMaxSteps.value = pv.debugMaxSteps;
shaderMaterial.defines["DEBUG_STEPS_COUNT"] = 1;
delete shaderMaterial.defines["DEBUG_DEPTH"];
shaderMaterial.needsUpdate = true;
return;
}
case "Depth" /* DEPTH */: {
uniforms2.debugMinDepth.value = pv.debugMinDepth;
uniforms2.debugMaxDepth.value = pv.debugMaxDepth;
shaderMaterial.defines["DEBUG_DEPTH"] = 1;
delete shaderMaterial.defines["DEBUG_STEPS_COUNT"];
shaderMaterial.needsUpdate = true;
return;
}
}
TypeAssert.unreachable(debugMode);
};
var updateDebugMode = updateDebugMode2;
updateDebugMode2(uniforms);
} else {
if (shaderMaterial.defines["DEBUG_STEPS_COUNT"] != null) {
delete shaderMaterial.defines["DEBUG_STEPS_COUNT"];
shaderMaterial.needsUpdate = true;
}
if (shaderMaterial.defines["DEBUG_DEPTH"] != null) {
delete shaderMaterial.defines["DEBUG_DEPTH"];
shaderMaterial.needsUpdate = true;
}
}
}
async _updateEnvMap(shaderMaterial, uniforms) {
const pv = this.node.pv;
setDefines(shaderMaterial, null);
const currentDefine = shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"];
const _fetchTexture = async () => {
const pathParam = this.node.p.envMap;
if (pathParam.isDirty()) {
await pathParam.compute();
}
const textureNode = pathParam.value.nodeWithContext(NodeContext.COP);
if (textureNode) {
const container = await textureNode.compute();
const texture = container.texture();
return texture;
}
};
const _applyTexture = (texture) => {
uniforms["envMap"].value = texture;
const props = generateCubeUVSize({ envMapCubeUVHeight: texture.image.height });
setDefines(shaderMaterial, props ? { ...props, tEnvMapRotate: pv.tEnvMapRotate } : null);
};
const _removeTexture = () => {
uniforms["envMap"].value = null;
setDefines(shaderMaterial, null);
};
const _updateNeedsUpdateIfRequired = () => {
if (currentDefine != shaderMaterial.defines["ENVMAP_TYPE_CUBE_UV"]) {
shaderMaterial.needsUpdate = true;
}
};
if (isBooleanTrue(pv.useEnvMap)) {
const texture = await _fetchTexture();
if (texture) {
_applyTexture(texture);
} else {
_removeTexture();
}
} else {
_removeTexture();
}
_updateNeedsUpdateIfRequired();
}
/**
*
* uniforms
*
*/
_updateUniforms(shaderMaterial) {
RayMarchingController._updateUniforms(this.node, shaderMaterial);
}
static _updateUniforms(node, shaderMaterial) {
this._updateUniformEnvMapIntensity(node, shaderMaterial);
this._updateUniformEnvMapRoughness(node, shaderMaterial);
this._updateUniformEnvMapRotate(node, shaderMaterial);
}
static async updateUniformEnvMapIntensity(node) {
this._updateUniformEnvMapIntensity(node, await node.material());
}
static _updateUniformEnvMapIntensity(node, shaderMaterial) {
const uniforms = shaderMaterial.uniforms;
if (!uniforms) {
return;
}
uniforms["envMapIntensity"].value = node.pv.envMapIntensity;
}
static async updateUniformEnvMapRoughness(node) {
this._updateUniformEnvMapRoughness(node, await node.material());
}
static _updateUniformEnvMapRoughness(node, shaderMaterial) {
const uniforms = shaderMaterial.uniforms;
if (!uniforms) {
return;
}
uniforms["roughness"].value = node.pv.envMapRoughness;
}
static async updateUniformEnvMapRotate(node) {
this._updateUniformEnvMapRotate(node, await node.material());
}
static _updateUniformEnvMapRotate(node, shaderMaterial) {
const uniforms = shaderMaterial.uniforms;
if (!uniforms) {
return;
}
uniforms["envMapRotationY"].value = node.pv.envMapRotation;
}
}