-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GLTFSerializer : Ext mesh gpu instancing (#12495)
* add Ext_mesh_gpu_instancing * finalize Ext_mesh_gpu_instancing * cleaning * change signature of _buildAccessor the buffer might be always a Float32Array because all the values are coming from matrix decompose * remove quaternion quantization option
- Loading branch information
1 parent
49a6cee
commit 5a8db04
Showing
6 changed files
with
234 additions
and
4 deletions.
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
packages/dev/serializers/src/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import type { IBufferView, IAccessor, INode, IEXTMeshGpuInstancing } from "babylonjs-gltf2interface"; | ||
import { AccessorType, AccessorComponentType } from "babylonjs-gltf2interface"; | ||
import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension"; | ||
import { _Exporter, _BinaryWriter } from "../glTFExporter"; | ||
import type { Nullable } from "core/types"; | ||
import type { Node } from "core/node"; | ||
import { Mesh } from "core/Meshes"; | ||
import { TmpVectors, Quaternion, Vector3 } from "core/Maths/math.vector"; | ||
import { VertexBuffer } from "core/Buffers/buffer"; | ||
|
||
const NAME = "EXT_mesh_gpu_instancing"; | ||
|
||
/** | ||
* [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_mesh_gpu_instancing/README.md) | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
export class EXT_mesh_gpu_instancing implements IGLTFExporterExtensionV2 { | ||
/** Name of this extension */ | ||
public readonly name = NAME; | ||
|
||
/** Defines whether this extension is enabled */ | ||
public enabled = true; | ||
|
||
/** Defines whether this extension is required */ | ||
public required = false; | ||
|
||
private _exporter: _Exporter; | ||
|
||
private _wasUsed = false; | ||
|
||
constructor(exporter: _Exporter) { | ||
this._exporter = exporter; | ||
} | ||
|
||
public dispose() {} | ||
|
||
/** @hidden */ | ||
public get wasUsed() { | ||
return this._wasUsed; | ||
} | ||
|
||
public postExportNodeAsync?( | ||
context: string, | ||
node: Nullable<INode>, | ||
babylonNode: Node, | ||
nodeMap?: { [key: number]: number }, | ||
binaryWriter?: _BinaryWriter | ||
): Promise<Nullable<INode>> { | ||
return new Promise((resolve) => { | ||
if (node && babylonNode instanceof Mesh) { | ||
if (babylonNode.hasThinInstances && binaryWriter) { | ||
this._wasUsed = true; | ||
|
||
const noTranslation = Vector3.Zero(); | ||
const noRotation = Quaternion.Identity(); | ||
const noScale = Vector3.One(); | ||
|
||
// retreive all the instance world matrix | ||
const matrix = babylonNode.thinInstanceGetWorldMatrices(); | ||
|
||
const iwt = TmpVectors.Vector3[2]; | ||
const iwr = TmpVectors.Quaternion[1]; | ||
const iws = TmpVectors.Vector3[3]; | ||
|
||
let hasAnyInstanceWorldTranslation = false; | ||
let hasAnyInstanceWorldRotation = false; | ||
let hasAnyInstanceWorldScale = false; | ||
|
||
// prepare temp buffers | ||
const translationBuffer = new Float32Array(babylonNode.thinInstanceCount * 3); | ||
const rotationBuffer = new Float32Array(babylonNode.thinInstanceCount * 4); | ||
const scaleBuffer = new Float32Array(babylonNode.thinInstanceCount * 3); | ||
|
||
let i = 0; | ||
for (const m of matrix) { | ||
m.decompose(iws, iwr, iwt); | ||
|
||
// fill the temp buffer | ||
translationBuffer.set(iwt.asArray(), i * 3); | ||
rotationBuffer.set(iwr.normalize().asArray(), i * 4); // ensure the quaternion is normalized | ||
scaleBuffer.set(iws.asArray(), i * 3); | ||
|
||
// this is where we decide if there is any transformation | ||
hasAnyInstanceWorldTranslation = hasAnyInstanceWorldTranslation || !iwt.equalsWithEpsilon(noTranslation); | ||
hasAnyInstanceWorldRotation = hasAnyInstanceWorldRotation || !iwr.equalsWithEpsilon(noRotation); | ||
hasAnyInstanceWorldScale = hasAnyInstanceWorldScale || !iws.equalsWithEpsilon(noScale); | ||
|
||
i++; | ||
} | ||
|
||
const extension: IEXTMeshGpuInstancing = { | ||
attributes: {}, | ||
}; | ||
|
||
// do we need to write TRANSLATION ? | ||
if (hasAnyInstanceWorldTranslation) { | ||
extension.attributes["TRANSLATION"] = this._buildAccessor( | ||
translationBuffer, | ||
AccessorType.VEC3, | ||
babylonNode.thinInstanceCount, | ||
binaryWriter, | ||
AccessorComponentType.FLOAT | ||
); | ||
} | ||
// do we need to write ROTATION ? | ||
if (hasAnyInstanceWorldRotation) { | ||
const componentType = AccessorComponentType.FLOAT; // we decided to stay on FLOAT for now see https://github.com/BabylonJS/Babylon.js/pull/12495 | ||
extension.attributes["ROTATION"] = this._buildAccessor(rotationBuffer, AccessorType.VEC4, babylonNode.thinInstanceCount, binaryWriter, componentType); | ||
} | ||
// do we need to write SCALE ? | ||
if (hasAnyInstanceWorldScale) { | ||
extension.attributes["SCALE"] = this._buildAccessor( | ||
scaleBuffer, | ||
AccessorType.VEC3, | ||
babylonNode.thinInstanceCount, | ||
binaryWriter, | ||
AccessorComponentType.FLOAT | ||
); | ||
} | ||
|
||
/* eslint-enable @typescript-eslint/naming-convention*/ | ||
node.extensions = node.extensions || {}; | ||
node.extensions[NAME] = extension; | ||
} | ||
} | ||
resolve(node); | ||
}); | ||
} | ||
|
||
private _buildAccessor(buffer: Float32Array, type: AccessorType, count: number, binaryWriter: _BinaryWriter, componentType: AccessorComponentType): number { | ||
// write the buffer | ||
const bufferOffset = binaryWriter.getByteOffset(); | ||
switch (componentType) { | ||
case AccessorComponentType.FLOAT: { | ||
for (let i = 0; i != buffer.length; i++) { | ||
binaryWriter.setFloat32(buffer[i]); | ||
} | ||
break; | ||
} | ||
case AccessorComponentType.BYTE: { | ||
for (let i = 0; i != buffer.length; i++) { | ||
binaryWriter.setByte(buffer[i] * 127); | ||
} | ||
break; | ||
} | ||
case AccessorComponentType.SHORT: { | ||
for (let i = 0; i != buffer.length; i++) { | ||
binaryWriter.setInt16(buffer[i] * 32767); | ||
} | ||
|
||
break; | ||
} | ||
} | ||
// build the buffer view | ||
const bv: IBufferView = { buffer: 0, byteOffset: bufferOffset, byteLength: buffer.length * VertexBuffer.GetTypeByteLength(componentType) }; | ||
const bufferViewIndex = this._exporter._bufferViews.length; | ||
this._exporter._bufferViews.push(bv); | ||
|
||
// finally build the accessor | ||
const accessorIndex = this._exporter._accessors.length; | ||
const accessor: IAccessor = { | ||
bufferView: bufferViewIndex, | ||
componentType: componentType, | ||
count: count, | ||
type: type, | ||
normalized: componentType == AccessorComponentType.BYTE || componentType == AccessorComponentType.SHORT, | ||
}; | ||
this._exporter._accessors.push(accessor); | ||
return accessorIndex; | ||
} | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
_Exporter.RegisterExtension(NAME, (exporter) => new EXT_mesh_gpu_instancing(exporter)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+167 KB
...ols/tests/test/visualization/ReferenceImages/glTFSerializerKhrGpuInstancing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters