@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
183 lines (180 loc) • 8.1 kB
JavaScript
import { NEEDLE_progressive } from "@needle-tools/gltf-progressive";
import { MeshPhysicalMaterial, ShaderMaterial, Vector4 } from "three";
import { getParam } from "../engine/engine_utils.js";
const debug = getParam("debuglightmaps");
// this component is automatically added by the Renderer if the object has lightmap uvs AND we have a lightmap
// for multimaterial objects GLTF exports a "Group" with the renderer component
// and every child mesh is a material from unity
export class RendererLightmap {
get lightmap() {
return this.lightmapTexture;
}
set lightmap(tex) {
if (tex !== this.lightmapTexture) {
this.lightmapTexture = tex;
this.applyLightmap();
if (this.lightmapTexture) {
NEEDLE_progressive.assignTextureLOD(this.lightmapTexture, 0).then(res => {
if (res?.isTexture)
this.lightmapTexture = res;
});
}
}
}
lightmapIndex = -1;
lightmapScaleOffset = new Vector4(1, 1, 0, 0);
context;
gameObject;
lightmapTexture = null;
lightmapScaleOffsetUniform = { value: new Vector4(1, 1, 0, 0) };
lightmapUniform = { value: null };
constructor(gameObject, context) {
this.gameObject = gameObject;
this.context = context;
}
init(lightmapIndex, lightmapScaleOffset, lightmapTexture) {
console.assert(this.gameObject !== undefined && this.gameObject !== null, "Missing gameobject", this);
this.lightmapIndex = lightmapIndex;
if (this.lightmapIndex < 0)
return;
this.lightmapScaleOffset = lightmapScaleOffset;
this.lightmapTexture = lightmapTexture;
NEEDLE_progressive.assignTextureLOD(lightmapTexture, 0).then(res => {
if (res?.isTexture)
this.lightmapTexture = res;
});
if (debug == "show") {
console.log("Lightmap:", this.gameObject.name, lightmapIndex, "\nScaleOffset:", lightmapScaleOffset, "\nTexture:", lightmapTexture);
this.setLightmapDebugMaterial();
}
else if (debug)
console.log("Use debuglightmaps=show to render lightmaps only in the scene.");
this.applyLightmap();
}
updateLightmapUniforms(material) {
const uniforms = material["uniforms"];
if (uniforms && uniforms.lightmap) {
this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
}
}
/**
* Apply the lightmap to the object. This will clone the material and set the lightmap texture and scale/offset
*/
applyLightmap() {
if (this.gameObject.type === "Object3D") {
if (debug)
console.warn("Can not add lightmap. Is this object missing a renderer?", this.gameObject.name);
return;
}
if (this.gameObject.type === "Group") {
if (this.gameObject["Needle:Multimaterial-LightmapWarning"] === undefined) {
this.gameObject["Needle:Multimaterial-LightmapWarning"] = true;
console.warn("Lightmap on multimaterial object is not supported yet... please open a feature request on https://github.com/needle-tools/needle-engine-support if your project requires it");
}
return;
}
console.assert(this.gameObject.type === "Mesh", "Lightmap only works on meshes", this);
const mesh = this.gameObject;
if (!mesh.geometry.getAttribute("uv1"))
mesh.geometry.setAttribute("uv1", mesh.geometry.getAttribute("uv"));
if (Array.isArray(this.gameObject.material)) {
const mats = this.gameObject.material;
for (let i = 0; i < mats.length; i++) {
mats[i] = this.ensureLightmapMaterial(mats[i]);
}
}
else {
this.gameObject.material = this.ensureLightmapMaterial(this.gameObject.material);
}
if (this.lightmapIndex >= 0 && this.lightmapTexture) {
// always on channel 1 for now. We could optimize this by passing the correct lightmap index along
this.lightmapTexture.channel = 1;
const mat = this.gameObject.material;
if (Array.isArray(mat)) {
for (const entry of mat) {
this.assignLightmapTexture(entry);
}
}
else if (mat) {
this.assignLightmapTexture(mat);
}
}
}
ensureLightmapMaterial(material) {
if (!material.userData)
material.userData = {};
// if (material instanceof MeshPhysicalMaterial) {
// return material;
// }
// check if the material version has changed and only then clone the material
if (material["NEEDLE:lightmap-material-version"] != material.version) {
if (material["NEEDLE:lightmap-material-version"] == undefined) {
if (debug)
console.warn("Cloning material for lightmap " + material.name);
const mat = material.clone();
material = mat;
material.onBeforeCompile = this.onBeforeCompile;
}
else {
// we need to clone the material
}
}
return material;
}
assignLightmapTexture(material) {
if (!material)
return;
if (material instanceof MeshPhysicalMaterial && material.transmission > 0) {
return;
}
const hasChanged = material.lightMap !== this.lightmapTexture || material["NEEDLE:lightmap-material-version"] !== material.version;
if (!hasChanged) {
return;
}
if (debug)
console.log("Assigning lightmap", material.name, material.version);
// assign the lightmap
material.lightMap = this.lightmapTexture;
// store the version of the material
material["NEEDLE:lightmap-material-version"] = material.version;
}
onBeforeCompile = (shader, _) => {
if (debug)
console.log("Lightmaps, before compile\n", shader);
this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
this.lightmapUniform.value = this.lightmapTexture;
shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
};
setLightmapDebugMaterial() {
// debug lightmaps
this.gameObject["material"] = new ShaderMaterial({
vertexShader: `
varying vec2 vUv1;
void main()
{
vUv1 = uv1;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: `
uniform sampler2D lightMap;
uniform float lightMapIntensity;
uniform vec4 lightmapScaleOffset;
varying vec2 vUv1;
// took from threejs 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4
vec4 conv_sRGBToLinear( in vec4 value ) {
return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );
}
void main() {
vec2 lUv = vUv1.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
vec4 lightMapTexel = texture2D( lightMap, lUv);
gl_FragColor = lightMapTexel;
gl_FragColor.a = 1.;
}
`,
defines: { USE_LIGHTMAP: '' }
});
}
}
//# sourceMappingURL=RendererLightmap.js.map