Skip to content

Commit

Permalink
Merge branch 'main' into fix-esm-identity
Browse files Browse the repository at this point in the history
  • Loading branch information
marklundin authored Jan 7, 2025
2 parents dac0695 + 0526a94 commit 1b64566
Show file tree
Hide file tree
Showing 15 changed files with 449 additions and 190 deletions.
258 changes: 197 additions & 61 deletions src/framework/components/button/component.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/framework/components/button/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { ButtonComponentData } from './data.js';
const _schema = [
'enabled',
'active',
{ name: 'imageEntity', type: 'entity' },
{ name: 'hitPadding', type: 'vec4' },
'transitionMode',
{ name: 'hoverTint', type: 'rgba' },
Expand Down Expand Up @@ -52,6 +51,7 @@ class ButtonComponentSystem extends ComponentSystem {
}

initializeComponentData(component, data, properties) {
component.imageEntity = data.imageEntity;
super.initializeComponentData(component, data, _schema);
}

Expand Down
5 changes: 5 additions & 0 deletions src/framework/components/element/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class ElementComponentSystem extends ComponentSystem {

this.defaultImageMaterials = [];

this.on('add', this.onAddComponent, this);
this.on('beforeremove', this.onRemoveComponent, this);
}

Expand Down Expand Up @@ -267,6 +268,10 @@ class ElementComponentSystem extends ComponentSystem {
}
}

onAddComponent(entity, component) {
entity.fire('element:add');
}

onRemoveComponent(entity, component) {
component.onRemove();
}
Expand Down
1 change: 1 addition & 0 deletions src/framework/components/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class ComponentSystem extends EventHandler {
const record = this.store[entity.getGuid()];
const component = entity.c[id];

component.fire('beforeremove');
this.fire('beforeremove', entity, component);

delete this.store[entity.getGuid()];
Expand Down
5 changes: 5 additions & 0 deletions src/framework/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,11 @@ function resolveDuplicatedEntityReferenceProperties(oldSubtreeRoot, oldEntity, n
newEntity.render.resolveDuplicatedEntityReferenceProperties(components.render, duplicatedIdsMap);
}

// Handle entity button attributes
if (components.button) {
newEntity.button.resolveDuplicatedEntityReferenceProperties(components.button, duplicatedIdsMap);
}

// Handle entity anim attributes
if (components.anim) {
newEntity.anim.resolveDuplicatedEntityReferenceProperties(components.anim, duplicatedIdsMap);
Expand Down
26 changes: 22 additions & 4 deletions src/framework/lightmapper/lightmap-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class LightmapFilters {
this.pixelOffset = new Float32Array(2);

// denoise is optional and gets created only when needed
this.shaderDenoise = null;
this.shaderDenoise = [];
this.sigmas = null;
this.constantSigmas = null;
this.kernel = null;
Expand All @@ -35,10 +35,13 @@ class LightmapFilters {
this.constantPixelOffset.setValue(this.pixelOffset);
}

prepareDenoise(filterRange, filterSmoothness) {
prepareDenoise(filterRange, filterSmoothness, bakeHDR) {

if (!this.shaderDenoise) {
this.shaderDenoise = createShaderFromCode(this.device, shaderChunks.fullscreenQuadVS, shaderChunksLightmapper.bilateralDeNoisePS, 'lmBilateralDeNoise');
const index = bakeHDR ? 0 : 1;
if (!this.shaderDenoise[index]) {
const name = `lmBilateralDeNoise-${bakeHDR ? 'hdr' : 'rgbm'}`;
const define = bakeHDR ? '#define HDR\n' : '';
this.shaderDenoise[index] = createShaderFromCode(this.device, shaderChunks.fullscreenQuadVS, define + shaderChunksLightmapper.bilateralDeNoisePS, name);
this.sigmas = new Float32Array(2);
this.constantSigmas = this.device.scope.resolve('sigmas');
this.constantKernel = this.device.scope.resolve('kernel[0]');
Expand All @@ -52,6 +55,21 @@ class LightmapFilters {
this.evaluateDenoiseUniforms(filterRange, filterSmoothness);
}

getDenoise(bakeHDR) {
const index = bakeHDR ? 0 : 1;
return this.shaderDenoise[index];
}

getDilate(device, bakeHDR) {
const index = bakeHDR ? 0 : 1;
if (!this.shaderDilate[index]) {
const name = `lmDilate-${bakeHDR ? 'hdr' : 'rgbm'}`;
const define = bakeHDR ? '#define HDR\n' : '';
this.shaderDilate[index] = createShaderFromCode(device, shaderChunks.fullscreenQuadVS, define + shaderChunksLightmapper.dilatePS, name);
}
return this.shaderDilate[index];
}

evaluateDenoiseUniforms(filterRange, filterSmoothness) {

function normpdf(x, sigma) {
Expand Down
14 changes: 9 additions & 5 deletions src/framework/lightmapper/lightmapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class Lightmapper {

initBake(device) {

this.bakeHDR = this.scene.lightmapPixelFormat !== PIXELFORMAT_RGBA8;

// only initialize one time
if (!this._initCalled) {
this._initCalled = true;
Expand Down Expand Up @@ -251,7 +253,7 @@ class Lightmapper {
} else {
material.ambient = new Color(0, 0, 0); // don't bake ambient
}
material.chunks.basePS = shaderChunks.basePS + (scene.lightmapPixelFormat === PIXELFORMAT_RGBA8 ? '\n#define LIGHTMAP_RGBM\n' : '');
material.chunks.basePS = shaderChunks.basePS + (this.bakeHDR ? '' : '\n#define LIGHTMAP_RGBM\n');
material.chunks.endPS = bakeLmEndChunk;
material.lightMap = this.blackTex;
} else {
Expand Down Expand Up @@ -299,7 +301,7 @@ class Lightmapper {
height: size,
format: this.scene.lightmapPixelFormat,
mipmaps: false,
type: this.scene.lightmapPixelFormat === PIXELFORMAT_RGBA8 ? TEXTURETYPE_RGBM : TEXTURETYPE_DEFAULT,
type: this.bakeHDR ? TEXTURETYPE_DEFAULT : TEXTURETYPE_RGBM,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST,
addressU: ADDRESS_CLAMP_TO_EDGE,
Expand Down Expand Up @@ -876,12 +878,14 @@ class Lightmapper {
postprocessTextures(device, bakeNodes, passCount) {

const numDilates2x = 1; // 1 or 2 dilates (depending on filter being enabled)
const dilateShader = this.lightmapFilters.shaderDilate;
const dilateShader = this.lightmapFilters.getDilate(device, this.bakeHDR);
let denoiseShader;

// bilateral denoise filter - runs as a first pass, before dilate
const filterLightmap = this.scene.lightmapFilterEnabled;
if (filterLightmap) {
this.lightmapFilters.prepareDenoise(this.scene.lightmapFilterRange, this.scene.lightmapFilterSmoothness);
this.lightmapFilters.prepareDenoise(this.scene.lightmapFilterRange, this.scene.lightmapFilterSmoothness, this.bakeHDR);
denoiseShader = this.lightmapFilters.getDenoise(this.bakeHDR);
}

device.setBlendState(BlendState.NOBLEND);
Expand All @@ -908,7 +912,7 @@ class Lightmapper {

this.lightmapFilters.setSourceTexture(lightmap);
const bilateralFilterEnabled = filterLightmap && pass === 0 && i === 0;
drawQuadWithShader(device, tempRT, bilateralFilterEnabled ? this.lightmapFilters.shaderDenoise : dilateShader);
drawQuadWithShader(device, tempRT, bilateralFilterEnabled ? denoiseShader : dilateShader);

this.lightmapFilters.setSourceTexture(tempTex);
drawQuadWithShader(device, nodeRT, dilateShader);
Expand Down
10 changes: 9 additions & 1 deletion src/framework/parsers/gsplat-resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,22 @@ class GSplatResource {
*/
splat = null;

/**
* @type {string[] | null}
* @ignore
*/
comments = null;

/**
* @param {GraphicsDevice} device - The graphics device.
* @param {GSplatData} splatData - The splat data.
* @param {string[]} comments - The PLY file header comments
* @ignore
*/
constructor(device, splatData) {
constructor(device, splatData, comments) {
this.device = device;
this.splatData = splatData;
this.comments = comments;
}

destroy() {
Expand Down
75 changes: 43 additions & 32 deletions src/framework/parsers/ply.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,16 @@ class StreamBuf {
// string containing the ply format
const parseHeader = (lines) => {
const elements = [];
const comments = [];
let format;

for (let i = 1; i < lines.length; ++i) {
const words = lines[i].split(' ');

switch (words[0]) {
case 'comment':
comments.push(words.slice(1).join(' '));
break;
case 'format':
format = words[1];
break;
Expand Down Expand Up @@ -196,7 +200,7 @@ const parseHeader = (lines) => {
}
}

return { elements, format };
return { elements, format, comments };
};

// return true if the array of elements references a compressed ply file
Expand Down Expand Up @@ -239,7 +243,7 @@ const isFloatPly = (elements) => {
};

// read the data of a compressed ply file
const readCompressedPly = async (streamBuf, elements, littleEndian) => {
const readCompressedPly = async (streamBuf, elements) => {
const result = new GSplatCompressedData();

const numChunks = elements[0].count;
Expand Down Expand Up @@ -294,7 +298,7 @@ const readCompressedPly = async (streamBuf, elements, littleEndian) => {
};

// read the data of a floating point ply file
const readFloatPly = async (streamBuf, elements, littleEndian) => {
const readFloatPly = async (streamBuf, elements) => {
// calculate the size of an input element record
const element = elements[0];
const properties = element.properties;
Expand Down Expand Up @@ -336,7 +340,7 @@ const readFloatPly = async (streamBuf, elements, littleEndian) => {
return new GSplatData(elements);
};

const readGeneralPly = async (streamBuf, elements, littleEndian) => {
const readGeneralPly = async (streamBuf, elements) => {
// read and deinterleave the data
for (let i = 0; i < elements.length; ++i) {
const element = elements[i];
Expand Down Expand Up @@ -391,7 +395,7 @@ const readGeneralPly = async (streamBuf, elements, littleEndian) => {
*
* @param {ReadableStreamDefaultReader<Uint8Array>} reader - The reader.
* @param {Function|null} propertyFilter - Function to filter properties with.
* @returns {Promise<GSplatData | GSplatCompressedData>} The ply file data.
* @returns {Promise<{ data: GSplatData | GSplatCompressedData, comments: string[] }>} The ply file data.
*/
const readPly = async (reader, propertyFilter = null) => {
/**
Expand Down Expand Up @@ -464,14 +468,13 @@ const readPly = async (reader, propertyFilter = null) => {
// decode buffer header text and split into lines and remove comments
const lines = new TextDecoder('ascii')
.decode(streamBuf.data.subarray(0, headerLength))
.split('\n')
.filter(line => !line.startsWith('comment '));
.split('\n');

// decode header and build element and property list
const { elements, format } = parseHeader(lines);
const { elements, format, comments } = parseHeader(lines);

// check format is supported
if (format !== 'binary_little_endian' && format !== 'binary_big_endian') {
if (format !== 'binary_little_endian') {
throw new Error('Unsupported ply format');
}

Expand All @@ -480,29 +483,36 @@ const readPly = async (reader, propertyFilter = null) => {
streamBuf.head = headerLength + endHeaderBytes.length;
streamBuf.compact();

// load compressed PLY with fast path
if (isCompressedPly(elements)) {
return await readCompressedPly(streamBuf, elements, format === 'binary_little_endian');
}
const readData = async () => {
// load compressed PLY with fast path
if (isCompressedPly(elements)) {
return await readCompressedPly(streamBuf, elements);
}

// allocate element storage
elements.forEach((e) => {
e.properties.forEach((p) => {
const storageType = dataTypeMap.get(p.type);
if (storageType) {
const storage = (!propertyFilter || propertyFilter(p.name)) ? new storageType(e.count) : null;
p.storage = storage;
}
// allocate element storage
elements.forEach((e) => {
e.properties.forEach((p) => {
const storageType = dataTypeMap.get(p.type);
if (storageType) {
const storage = (!propertyFilter || propertyFilter(p.name)) ? new storageType(e.count) : null;
p.storage = storage;
}
});
});
});

// load float32 PLY with fast path
if (isFloatPly(elements)) {
return await readFloatPly(streamBuf, elements, format === 'binary_little_endian');
}
// load float32 PLY with fast path
if (isFloatPly(elements)) {
return await readFloatPly(streamBuf, elements);
}

// fallback, general case
return await readGeneralPly(streamBuf, elements, format === 'binary_little_endian');
// fallback, general case
return await readGeneralPly(streamBuf, elements);
};

return {
data: await readData(),
comments
};
};

// by default load everything
Expand Down Expand Up @@ -543,19 +553,20 @@ class PlyParser {
if (!response || !response.body) {
callback('Error loading resource', null);
} else {
const gsplatData = await readPly(response.body.getReader(), asset.data.elementFilter ?? defaultElementFilter);
const { data, comments } = await readPly(response.body.getReader(), asset.data.elementFilter ?? defaultElementFilter);

// reorder data
if (!gsplatData.isCompressed) {
if (!data.isCompressed) {
if (asset.data.reorder ?? true) {
gsplatData.reorderData();
data.reorderData();
}
}

// construct the resource
const resource = new GSplatResource(
this.device,
gsplatData.isCompressed && asset.data.decompress ? gsplatData.decompress() : gsplatData
data.isCompressed && asset.data.decompress ? data.decompress() : data,
comments
);

callback(null, resource);
Expand Down
21 changes: 15 additions & 6 deletions src/scene/gsplat/gsplat-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,22 @@ class GSplatData {
return false;
}

get hasSHData() {
for (let i = 0; i < 45; ++i) {
if (!this.getProp(`f_rest_${i}`)) {
return false;
// return the number of spherical harmonic bands present. value will be between 0 and 3 inclusive.
get shBands() {
const numProps = () => {
for (let i = 0; i < 45; ++i) {
if (!this.getProp(`f_rest_${i}`)) {
return i;
}
}
}
return true;
return 45;
};
const sizes = {
9: 1,
24: 2,
45: 3
};
return sizes[numProps()] ?? 0;
}

calcMortonOrder() {
Expand Down
Loading

0 comments on commit 1b64566

Please sign in to comment.