Skip to content

Commit

Permalink
Merge pull request #4538 from mozilla/hdr-lightmaps
Browse files Browse the repository at this point in the history
HDR Lightmaps & Environment Settings System
  • Loading branch information
netpro2k authored Sep 2, 2021
2 parents 4d5fd3c + 6b2bcee commit d16a21b
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 92 deletions.
8 changes: 8 additions & 0 deletions src/assets/stylesheets/hub.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ a-entity {
vertical-align: -0.125em;
height: 1em;
}

// Dat GUI
.dg select {
color: black;
}
.dg input {
line-height: normal;
}
5 changes: 0 additions & 5 deletions src/components/camera-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,6 @@ AFRAME.registerComponent("camera-tool", {
const width = 0.28;
const geometry = new THREE.PlaneBufferGeometry(width, width / this.camera.aspect);

const environmentMapComponent = this.el.sceneEl.components["environment-map"];
if (environmentMapComponent) {
environmentMapComponent.applyEnvironmentMap(this.el.object3D);
}

this.screen = new THREE.Mesh(geometry, material);
this.screen.rotation.set(0, Math.PI, 0);
this.screen.position.set(0, 0, -0.042);
Expand Down
25 changes: 0 additions & 25 deletions src/components/environment-map.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { forEachMaterial } from "../utils/material-utils";
import cubeMapPosX from "../assets/images/cubemap/posx.jpg";
import cubeMapNegX from "../assets/images/cubemap/negx.jpg";
import cubeMapPosY from "../assets/images/cubemap/posy.jpg";
Expand All @@ -14,27 +13,3 @@ export async function createDefaultEnvironmentMap() {
texture.format = THREE.RGBFormat;
return texture;
}

AFRAME.registerComponent("environment-map", {
init() {
this.environmentMap = null;

this.updateEnvironmentMap = this.updateEnvironmentMap.bind(this);
},

updateEnvironmentMap(environmentMap) {
this.environmentMap = environmentMap;
this.applyEnvironmentMap(this.el.object3D);
},

applyEnvironmentMap(object3D) {
object3D.traverse(object => {
forEachMaterial(object, material => {
if (material.isMeshStandardMaterial) {
material.envMap = this.environmentMap;
material.needsUpdate = true;
}
});
});
}
});
99 changes: 92 additions & 7 deletions src/components/gltf-model-plus.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MeshBVH, acceleratedRaycast } from "three-mesh-bvh";
import { disposeNode, cloneObject3D } from "../utils/three-utils";
import HubsTextureLoader from "../loaders/HubsTextureLoader";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

THREE.Mesh.prototype.raycast = acceleratedRaycast;

Expand Down Expand Up @@ -398,6 +399,21 @@ class GLTFHubsPlugin {
node.extras.gltfIndex = i;
}
}

function hookDef(defType, hookName) {
return Promise.all(
parser.json[defType].map((_def, idx) => {
return Promise.all(
parser._invokeAll(function(ext) {
return ext[hookName] && ext[hookName](idx);
})
);
})
);
}

// TODO decide if thse should get put into the GLTF loader itself
return Promise.all([hookDef("scenes", "extendScene"), hookDef("nodes", "extendNode")]);
}

afterRoot(gltf) {
Expand Down Expand Up @@ -431,6 +447,49 @@ class GLTFHubsPlugin {
}
}

class GLTFHubsComponentsExtension {
constructor(parser) {
this.parser = parser;
this.name = "MOZ_hubs_components";
}

_markDefs() {
// TODO hack to keep hubs component data in userData. Remove once we handle all component stuff in a plugin
delete this.parser.extensions.MOZ_hubs_components;
}

extendScene(sceneIdx) {
const ext = this.parser.json.scenes[sceneIdx]?.extensions?.MOZ_hubs_components;
if (ext) return this.resolveComponentLinks(ext);
}

extendNode(nodeIdx) {
const ext = this.parser.json.nodes[nodeIdx]?.extensions?.MOZ_hubs_components;
if (ext) return this.resolveComponentLinks(ext);
}

resolveComponentLinks(ext) {
const deps = [];

for (const componentName in ext) {
const props = ext[componentName];
for (const propName in props) {
const value = props[propName];
const type = value?.__mhc_link_type;
if (type && value.index !== undefined) {
deps.push(
this.parser.getDependency(type, value.index).then(loadedDep => {
props[propName] = loadedDep;
})
);
}
}
}

return Promise.all(deps);
}
}

class GLTFHubsLightMapExtension {
constructor(parser) {
this.parser = parser;
Expand Down Expand Up @@ -501,6 +560,36 @@ class GLTFHubsTextureBasisExtension {
}
}

class GLTFMozTextureRGBE {
constructor(parser, loader) {
this.parser = parser;
this.loader = loader;
this.name = "MOZ_texture_rgbe";
}

loadTexture(textureIndex) {
const parser = this.parser;
const json = parser.json;
const textureDef = json.textures[textureIndex];

if (!textureDef.extensions || !textureDef.extensions[this.name]) {
return null;
}

const extensionDef = textureDef.extensions[this.name];
const source = json.images[extensionDef.source];
return parser.loadTextureImage(textureIndex, source, this.loader).then(t => {
// TODO pretty severe artifacting when using mipmaps, disable for now
if (t.minFilter == THREE.NearestMipmapNearestFilter || t.minFilter == THREE.NearestMipmapLinearFilter) {
t.minFilter = THREE.NearestFilter;
} else if (t.minFilter == THREE.LinearMipmapNearestFilter || t.minFilter == THREE.LinearMipmapLinearFilter) {
t.minFilter = THREE.LinearFilter;
}
return t;
});
}
}

export async function loadGLTF(src, contentType, onProgress, jsonPreprocessor) {
let gltfUrl = src;
let fileMap;
Expand All @@ -514,9 +603,11 @@ export async function loadGLTF(src, contentType, onProgress, jsonPreprocessor) {
loadingManager.setURLModifier(getCustomGLTFParserURLResolver(gltfUrl));
const gltfLoader = new THREE.GLTFLoader(loadingManager);
gltfLoader
.register(parser => new GLTFHubsComponentsExtension(parser))
.register(parser => new GLTFHubsPlugin(parser, jsonPreprocessor))
.register(parser => new GLTFHubsLightMapExtension(parser))
.register(parser => new GLTFHubsTextureBasisExtension(parser));
.register(parser => new GLTFHubsTextureBasisExtension(parser))
.register(parser => new GLTFMozTextureRGBE(parser, new RGBELoader().setDataType(THREE.HalfFloatType)));

// TODO some models are loaded before the renderer exists. This is likely things like the camera tool and loading cube.
// They don't currently use KTX textures but if they did this would be an issue. Fixing this is hard but is part of
Expand Down Expand Up @@ -701,12 +792,6 @@ AFRAME.registerComponent("gltf-model-plus", {
if (el) rewires.push(() => (o.el = el));
});

const environmentMapComponent = this.el.sceneEl.components["environment-map"];

if (environmentMapComponent) {
environmentMapComponent.applyEnvironmentMap(object3DToSet);
}

if (lastSrc) {
gltfCache.release(lastSrc);
}
Expand Down
10 changes: 0 additions & 10 deletions src/components/media-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { waitForDOMContentLoaded } from "../utils/async-utils";

import { SHAPE } from "three-ammo/constants";

let loadingObjectEnvMap;
let loadingObject;

waitForDOMContentLoaded().then(() => {
Expand Down Expand Up @@ -192,15 +191,6 @@ AFRAME.registerComponent("media-loader", {
this.updateScale(true, false);

if (useFancyLoader) {
const environmentMapComponent = this.el.sceneEl.components["environment-map"];
if (environmentMapComponent) {
const currentEnivronmentMap = environmentMapComponent.environmentMap;
if (loadingObjectEnvMap !== currentEnivronmentMap) {
environmentMapComponent.applyEnvironmentMap(mesh);
loadingObjectEnvMap = currentEnivronmentMap;
}
}

this.loaderMixer = new THREE.AnimationMixer(mesh);

this.loadingClip = this.loaderMixer.clipAction(mesh.animations[0]);
Expand Down
1 change: 0 additions & 1 deletion src/components/scene-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import "./floaty-object";
import "./super-spawner";
import "./water";
import "./simple-water";
import "./environment-map";
import "./trigger-volume";
import "./video-pause-state";
import "./particle-emitter";
Expand Down
29 changes: 6 additions & 23 deletions src/components/skybox.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
// in webpack production mode.
require("three/examples/js/lights/LightProbeGenerator");

import qsTruthy from "../utils/qs_truthy";
const isBotMode = qsTruthy("bot");

const {
AmbientLight,
BackSide,
Expand Down Expand Up @@ -430,12 +427,6 @@ AFRAME.registerComponent("skybox", {
init() {
this.sky = new Sky();
this.el.setObject3D("mesh", this.sky);

this.updateEnvironmentMap = this.updateEnvironmentMap.bind(this);
// HACK: Render environment map on next frame to avoid bug where the render target texture is black.
// This is likely due to the custom elements attached callback being synchronous on Chrome but not Firefox.
// Added timeout due to additional case where texture is black in Firefox.
requestAnimationFrame(() => setTimeout(this.updateEnvironmentMap));
},

update(oldData) {
Expand Down Expand Up @@ -478,26 +469,18 @@ AFRAME.registerComponent("skybox", {
this.sky.matrixNeedsUpdate = true;
}

this.updateEnvironmentMap();
},

updateEnvironmentMap() {
const environmentMapComponent = this.el.sceneEl.components["environment-map"];
const renderer = this.el.sceneEl.renderer;

const quality = window.APP.store.materialQualitySetting;

if (environmentMapComponent && !isBotMode && quality === "high") {
const envMap = this.sky.generateEnvironmentMap(renderer);
environmentMapComponent.updateEnvironmentMap(envMap);
} else if (quality === "medium") {
// TODO Remove or rework medium quality mode
if (window.APP.store.materialQualitySetting === "medium") {
// This extra ambient light is here to normalize lighting with the MeshStandardMaterial.
// Without it, objects are significantly darker in brighter environments.
// It's kept to a low value to not wash out objects in very dark environments.
// This is a hack, but the results are much better than they are without it.
this.el.setObject3D("ambient-light", new AmbientLight(0xffffff, 0.3));
this.el.setObject3D("light-probe", this.sky.generateLightProbe(renderer));
this.el.setObject3D("light-probe", this.sky.generateLightProbe(this.el.sceneEl.renderer));
}

// TODO if we care about dynamic skybox changes we should also update the enviornment map here
//
},

remove() {
Expand Down
5 changes: 0 additions & 5 deletions src/components/tools/networked-drawing.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ AFRAME.registerComponent("networked-drawing", {

this.el.setObject3D("mesh", this.drawing);

const environmentMapComponent = this.el.sceneEl.components["environment-map"];
if (environmentMapComponent) {
environmentMapComponent.applyEnvironmentMap(this.drawing);
}

this.prevIdx = Object.assign({}, this.sharedBuffer.idx);
this.idx = Object.assign({}, this.sharedBuffer.idx);
this.vertexCount = 0; //number of vertices added for current line (used for line deletion).
Expand Down
6 changes: 0 additions & 6 deletions src/components/tools/pen-laser.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ AFRAME.registerComponent("pen-laser", {
this.laserTip.scale.setScalar(0.01);
this.laserTip.matrixNeedsUpdate = true;

const environmentMapComponent = this.el.sceneEl.components["environment-map"];
if (environmentMapComponent) {
environmentMapComponent.applyEnvironmentMap(this.laser);
environmentMapComponent.applyEnvironmentMap(this.laserTip);
}

//prevents the line from being a raycast target for the cursor
this.laser.raycast = function() {};

Expand Down
5 changes: 0 additions & 5 deletions src/components/tools/pen.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ AFRAME.registerComponent("pen", {

this.el.setObject3D("mesh", this.penTip);

const environmentMapComponent = this.el.sceneEl.components["environment-map"];
if (environmentMapComponent) {
environmentMapComponent.applyEnvironmentMap(this.el.parentEl.object3D);
}

this.penLaserAttributesUpdated = false;
this.penLaserAttributes = {
color: "#FF0033",
Expand Down
40 changes: 37 additions & 3 deletions src/gltf-component-mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,12 @@ AFRAME.GLTFModelPlus.registerComponent(
enterComponentMapping = getSanitizedComponentMapping(enterComponent, enterProperty, publicComponents);
leaveComponentMapping = getSanitizedComponentMapping(leaveComponent, leaveProperty, publicComponents);

targetEntity = indexToEntityMap[target];
// indexToEntityMap should be considered depredcated. These references are now resovled by the GLTFHubsComponentExtension
if (typeof target === "number") {
targetEntity = indexToEntityMap[target];
} else {
targetEntity = target?.el;
}

if (!targetEntity) {
throw new Error(`Couldn't find target entity with index: ${target}.`);
Expand Down Expand Up @@ -490,7 +495,12 @@ AFRAME.GLTFModelPlus.registerComponent(

let srcEl;
if (srcNode !== undefined) {
srcEl = indexToEntityMap[srcNode];
// indexToEntityMap should be considered depredcated. These references are now resovled by the GLTFHubsComponentExtension
if (typeof srcNode === "number") {
srcEl = indexToEntityMap[srcNode];
} else {
srcEl = srcNode?.el;
}
if (!srcEl) {
console.warn(
`Error inflating gltf component "video-texture-srcEl": Couldn't find srcEl entity with index ${srcNode}`
Expand Down Expand Up @@ -518,7 +528,12 @@ AFRAME.GLTFModelPlus.registerComponent(

let srcEl;
if (srcNode !== undefined) {
srcEl = indexToEntityMap[srcNode];
// indexToEntityMap should be considered depredcated. These references are now resovled by the GLTFHubsComponentExtension
if (typeof srcNode === "number") {
srcEl = indexToEntityMap[srcNode];
} else {
srcEl = srcNode?.el;
}
if (!srcEl) {
console.warn(
`Error inflating gltf component ${componentName}: Couldn't find srcEl entity with index ${srcNode}`
Expand Down Expand Up @@ -578,3 +593,22 @@ AFRAME.GLTFModelPlus.registerComponent("audio-params", "audio-params", (el, comp
AFRAME.GLTFModelPlus.registerComponent("audio-zone", "audio-zone", (el, componentName, componentData) => {
el.setAttribute(componentName, { ...componentData });
});

AFRAME.GLTFModelPlus.registerComponent(
"environment-settings",
"environment-settings",
(el, componentName, componentData) => {
if (componentData.envMapTexture) {
// assume equirect for now
componentData.envMapTexture.mapping = THREE.EquirectangularReflectionMapping;
// TODO do we always want to flip for enviornmetn map?
componentData.envMapTexture.flipY = true;
}

// TODO a bit silly to be storing this as an aframe component. Use a glboal store of some sort
el.setAttribute(componentName, {
...componentData,
backgroundColor: new THREE.Color(componentData.backgroundColor)
});
}
);
1 change: 0 additions & 1 deletion src/hub.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
freeze-controller
personal-space-bubble="debug: false;"
rotate-selected-object
environment-map
light="defaultLightsEnabled: false"
>
<a-assets>
Expand Down
Loading

0 comments on commit d16a21b

Please sign in to comment.