-
Notifications
You must be signed in to change notification settings - Fork 334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance Enhancements for Web Viewer #1891
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,9 +87,9 @@ function fromMatrix(matrix, dimension) | |
* @param {mx.Uniforms} uniforms | ||
* @param {THREE.textureLoader} textureLoader | ||
*/ | ||
function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, flipY) | ||
function toThreeUniform(viewer, type, value, name, uniforms, textureLoader, searchPath, flipY) | ||
{ | ||
let outValue; | ||
let outValue = null; | ||
switch (type) | ||
{ | ||
case 'float': | ||
|
@@ -117,18 +117,65 @@ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, | |
case 'filename': | ||
if (value) | ||
{ | ||
let fullPath = searchPath + IMAGE_PATH_SEPARATOR + value; | ||
const texture = textureLoader.load(fullPath); | ||
// Set address & filtering mode | ||
if (texture) | ||
setTextureParameters(texture, name, uniforms, flipY); | ||
outValue = texture; | ||
// Cache / reuse texture to avoid reload overhead. | ||
// Note: that data blobs and embedded data textures are not cached as they are transient data. | ||
let checkCache = true; | ||
let texturePath = searchPath + IMAGE_PATH_SEPARATOR + value; | ||
if (value.startsWith('blob:')) | ||
{ | ||
texturePath = value; | ||
console.log('Load blob URL:', texturePath); | ||
checkCache = false; | ||
} | ||
else if (value.startsWith('http')) | ||
{ | ||
texturePath = value; | ||
console.log('Load HTTP URL:', texturePath); | ||
} | ||
else if (value.startsWith('data:')) | ||
{ | ||
texturePath = value; | ||
checkCache = false; | ||
console.log('Load data URL:', texturePath); | ||
} | ||
const cachedTexture = checkCache && THREE.Cache.get(texturePath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do proper cache checking. |
||
if (cachedTexture) | ||
{ | ||
// Get texture from cache | ||
outValue = cachedTexture; | ||
console.log('Use cached texture: ', texturePath, outValue); | ||
} | ||
else | ||
{ | ||
outValue = textureLoader.load( | ||
texturePath, | ||
function (texture) { | ||
console.log('Load new texture: ' + texturePath, texture); | ||
outValue = texture; | ||
|
||
// Add texture to ThreeJS cache | ||
if (checkCache) | ||
THREE.Cache.add(texturePath, texture); | ||
|
||
viewer.scheduleUpdate(); | ||
}, | ||
undefined, | ||
function (error) { | ||
console.error('Error loading texture: ', error); | ||
}); | ||
|
||
// Set address & filtering mode | ||
if (outValue) | ||
setTextureParameters(outValue, name, uniforms, flipY); | ||
} | ||
} | ||
break; | ||
case 'samplerCube': | ||
case 'string': | ||
default: | ||
break; | ||
default: | ||
console.log('Value type not supported: ' + type); | ||
outValue = null; | ||
} | ||
|
||
return outValue; | ||
|
@@ -283,7 +330,7 @@ export function registerLights(mx, lights, genContext) | |
* @param {mx.shaderStage} shaderStage | ||
* @param {THREE.TextureLoader} textureLoader | ||
*/ | ||
export function getUniformValues(shaderStage, textureLoader, searchPath, flipY) | ||
export function getUniformValues(viewer, shaderStage, textureLoader, searchPath, flipY) | ||
{ | ||
let threeUniforms = {}; | ||
|
||
|
@@ -297,7 +344,8 @@ export function getUniformValues(shaderStage, textureLoader, searchPath, flipY) | |
const variable = uniforms.get(i); | ||
const value = variable.getValue()?.getData(); | ||
const name = variable.getVariable(); | ||
threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms, | ||
//console.log('fill uniform, name:', name, ', value:', value); | ||
threeUniforms[name] = new THREE.Uniform(toThreeUniform(viewer, variable.getType().getName(), value, name, uniforms, | ||
textureLoader, searchPath, flipY)); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,7 @@ function init() | |
orbitControls.addEventListener('change', () => | ||
{ | ||
viewer.getScene().setUpdateTransforms(); | ||
viewer.scheduleUpdate(); | ||
}) | ||
|
||
// Add hotkey 'f' to capture the current frame and save an image file. | ||
|
@@ -91,6 +92,7 @@ function init() | |
if (event.key === 'f') | ||
{ | ||
captureRequested = true; | ||
viewer.scheduleUpdate(); | ||
} | ||
}); | ||
|
||
|
@@ -129,6 +131,7 @@ function init() | |
|
||
}).then(() => | ||
{ | ||
viewer.scheduleUpdate(); | ||
animate(); | ||
}).catch(err => | ||
{ | ||
|
@@ -146,6 +149,7 @@ function init() | |
viewer.getMaterial().loadMaterials(viewer, materialFilename); | ||
viewer.getEditor().updateProperties(0.9); | ||
viewer.getScene().setUpdateTransforms(); | ||
viewer.scheduleUpdate(); | ||
}); | ||
|
||
setSceneLoadingCallback(file => | ||
|
@@ -154,6 +158,7 @@ function init() | |
console.log('Drop geometry to:', glbFileName); | ||
scene.setGeometryURL(glbFileName); | ||
scene.loadGeometry(viewer, orbitControls); | ||
viewer.scheduleUpdate(); | ||
}); | ||
|
||
// enable three.js Cache so that dropped files can reference each other | ||
|
@@ -165,10 +170,12 @@ function onWindowResize() | |
viewer.getScene().updateCamera(); | ||
viewer.getScene().setUpdateTransforms(); | ||
renderer.setSize(window.innerWidth, window.innerHeight); | ||
console.log('resize refresh....'); | ||
viewer.scheduleUpdate(); | ||
} | ||
|
||
function animate() | ||
{ | ||
{ | ||
requestAnimationFrame(animate); | ||
|
||
if (turntableEnabled) | ||
|
@@ -179,8 +186,19 @@ function animate() | |
viewer.getScene().setUpdateTransforms(); | ||
} | ||
|
||
renderer.render(viewer.getScene().getScene(), viewer.getScene().getCamera()); | ||
viewer.getScene().updateTransforms(); | ||
// Only re-render when an update request was made | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-render gated by requirement to refresh. All the |
||
if (viewer.needUpdate() || turntableEnabled) | ||
{ | ||
renderer.render(viewer.getScene().getScene(), viewer.getScene().getCamera()); | ||
|
||
if (viewer.getScene().getUpdateTransforms()) | ||
{ | ||
viewer.getScene().updateTransforms(); | ||
viewer.getScene().setUpdateTransforms(false); | ||
} | ||
|
||
viewer.finishUpdate(); | ||
} | ||
|
||
if (captureRequested) | ||
{ | ||
|
@@ -197,9 +215,11 @@ function handleKeyEvents(event) | |
if (event.keyCode == V_KEY) | ||
{ | ||
viewer.getScene().toggleBackgroundTexture(); | ||
viewer.scheduleUpdate(); | ||
} | ||
else if (event.keyCode == P_KEY) | ||
{ | ||
turntableEnabled = !turntableEnabled; | ||
viewer.scheduleUpdate(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -221,9 +221,14 @@ export class Scene | |
orbitControls.update(); | ||
} | ||
|
||
setUpdateTransforms() | ||
setUpdateTransforms(val=true) | ||
{ | ||
this.#_updateTransforms = true; | ||
this.#_updateTransforms = val; | ||
} | ||
|
||
getUpdateTransforms() | ||
{ | ||
return this.#_updateTransforms; | ||
} | ||
|
||
updateTransforms() | ||
|
@@ -235,7 +240,7 @@ export class Scene | |
{ | ||
return; | ||
} | ||
this.#_updateTransforms = false; | ||
this.setUpdateTransforms(false); | ||
|
||
const scene = this.getScene(); | ||
const camera = this.getCamera(); | ||
|
@@ -622,7 +627,12 @@ export class Material | |
|
||
// Load material | ||
if (mtlxMaterial) | ||
await mx.readFromXmlString(doc, mtlxMaterial, searchPath); | ||
try { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small fix. Would "crash" sometimes on invalid files. |
||
await mx.readFromXmlString(doc, mtlxMaterial, searchPath); | ||
} | ||
catch (error) { | ||
console.log('Error loading material file: ', error); | ||
} | ||
else | ||
Material.createFallbackMaterial(doc); | ||
|
||
|
@@ -847,12 +857,14 @@ export class Material | |
assigned += viewer.getScene().updateMaterial(matassign); | ||
matassign.setGeometry(temp); | ||
assignedSolo = true; | ||
viewer.scheduleUpdate(); | ||
break | ||
} | ||
} | ||
else | ||
{ | ||
assigned += viewer.getScene().updateMaterial(matassign); | ||
viewer.scheduleUpdate(); | ||
} | ||
} | ||
} | ||
|
@@ -861,6 +873,7 @@ export class Material | |
this._defaultMaterial = new MaterialAssign(this._materials[0].getMaterial(), ALL_GEOMETRY_SPECIFIER); | ||
this._defaultMaterial.setShader(this._materials[0].getShader()); | ||
viewer.getScene().updateMaterial(this._defaultMaterial); | ||
viewer.scheduleUpdate(); | ||
} | ||
|
||
if (assigned > 0) | ||
|
@@ -916,8 +929,8 @@ export class Material | |
let theScene = viewer.getScene(); | ||
let flipV = theScene.getFlipGeometryV(); | ||
let uniforms = { | ||
...getUniformValues(shader.getStage('vertex'), textureLoader, searchPath, flipV), | ||
...getUniformValues(shader.getStage('pixel'), textureLoader, searchPath, flipV), | ||
...getUniformValues(viewer, shader.getStage('vertex'), textureLoader, searchPath, flipV), | ||
...getUniformValues(viewer, shader.getStage('pixel'), textureLoader, searchPath, flipV), | ||
} | ||
|
||
Object.assign(uniforms, { | ||
|
@@ -953,6 +966,7 @@ export class Material | |
if (logDetailedTime) | ||
console.log("- Per material generate time: ", performance.now() - startGenerateMat, "ms"); | ||
|
||
viewer.scheduleUpdate(); | ||
return newMaterial; | ||
} | ||
|
||
|
@@ -1009,6 +1023,7 @@ export class Material | |
} | ||
viewer.getMaterial().updateMaterialAssignments(viewer, this._soloMaterial); | ||
viewer.getScene().setUpdateTransforms(); | ||
viewer.scheduleUpdate(); | ||
} | ||
|
||
// | ||
|
@@ -1231,6 +1246,10 @@ export class Material | |
} | ||
const w = currentFolder.add(material.uniforms[name], 'value', minValue, maxValue, step).name(path); | ||
w.domElement.classList.add('peditoritem'); | ||
w.onChange(function (value) | ||
{ | ||
viewer.scheduleUpdate(); | ||
}); | ||
} | ||
break; | ||
|
||
|
@@ -1294,6 +1313,10 @@ export class Material | |
{ | ||
let w = currentFolder.add(material.uniforms[name], 'value', minValue, maxValue, step).name(path); | ||
w.domElement.classList.add('peditoritem'); | ||
w.onChange(function (value) | ||
{ | ||
viewer.scheduleUpdate(); | ||
}); | ||
} | ||
else | ||
{ | ||
|
@@ -1319,6 +1342,7 @@ export class Material | |
{ | ||
material.uniforms[name].value = value; | ||
} | ||
viewer.scheduleUpdate(); | ||
} | ||
const defaultOption = enumList[value]; // Set the default selected option | ||
const dropdownController = currentFolder.add(enumeration, defaultOption, enumeration).name(path); | ||
|
@@ -1334,6 +1358,11 @@ export class Material | |
{ | ||
let w = currentFolder.add(material.uniforms[name], 'value').name(path); | ||
w.domElement.classList.add('peditoritem'); | ||
w.onChange(function (value) | ||
{ | ||
viewer.scheduleUpdate(); | ||
}); | ||
|
||
} | ||
break; | ||
|
||
|
@@ -1377,6 +1406,10 @@ export class Material | |
let w = vecFolder.add(material.uniforms[name].value, | ||
key, minValue[key], maxValue[key], step[key]).name(keyString[key]); | ||
w.domElement.classList.add('peditoritem'); | ||
w.onChange(function (value) | ||
{ | ||
viewer.scheduleUpdate(); | ||
}); | ||
}) | ||
} | ||
break; | ||
|
@@ -1398,6 +1431,7 @@ export class Material | |
{ | ||
const color3 = new THREE.Color(value); | ||
material.uniforms[name].value.set(color3.toArray()); | ||
viewer.scheduleUpdate(); | ||
}); | ||
w.domElement.classList.add('peditoritem'); | ||
} | ||
|
@@ -1421,7 +1455,11 @@ export class Material | |
let item = currentFolder.add(dummy, 'thevalue'); | ||
item.name(path); | ||
item.disable(true); | ||
item.domElement.classList.add('peditoritem'); | ||
let w = item.domElement.classList.add('peditoritem'); | ||
w.onChange(function (value) | ||
{ | ||
viewer.scheduleUpdate(); | ||
}); | ||
} | ||
break; | ||
default: | ||
|
@@ -1465,6 +1503,30 @@ export class Viewer | |
|
||
this.fileLoader = new THREE.FileLoader(); | ||
this.hdrLoader = new RGBELoader(); | ||
|
||
this.updates = 0 | ||
} | ||
|
||
scheduleUpdate() | ||
{ | ||
this.updates++; | ||
//console.log('Schedule update: ', this.updates) | ||
} | ||
|
||
finishUpdate() | ||
{ | ||
if (this.updates > 0) | ||
{ | ||
this.updates--; | ||
//console.log('Finish update: ', this.updates) | ||
} | ||
} | ||
|
||
needUpdate() | ||
{ | ||
//if (this.updates > 0) | ||
// console.log('Need update: ', this.updates > 0) | ||
return this.updates > 0; | ||
} | ||
|
||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blobs, embedded data and http references are not relative to a search path. This fixes it so users can use these texture URI specifications in their files.