Add getBounds() to ScenegraphNode (#1733)

This commit is contained in:
Xiaoji Chen 2023-03-23 11:06:06 -07:00 committed by GitHub
parent 9e235e1ca8
commit 7eb9f2eaad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 6 deletions

View File

@ -46,6 +46,10 @@ var node = new Model(gl, props);
Note that setting orientation props does not actually update the object's matrix. `update()` must be called.
### getBounds() : [min: number[], max: number[]] | null
Calculate the bounding box of the node.
### update() - DEPRECATED
Update the model matrix. Useful to update changes to the `position`, `rotation` or `scale` properties.

View File

@ -125,7 +125,7 @@ export class GLTFInstantiator {
? gltfPrimitive.indices.count
: this.getVertexCount(gltfPrimitive.attributes);
return createGLTFModel(
const model = createGLTFModel(
this.device,
{
id: gltfPrimitive.name || `${gltfMesh.name || gltfMesh.id}-primitive-${i}`,
@ -136,6 +136,9 @@ export class GLTFInstantiator {
...this.options
}
);
model.bounds = [gltfPrimitive.attributes.POSITION.min, gltfPrimitive.attributes.POSITION.max];
return model;
}
getVertexCount(attributes: any) {

View File

@ -1,4 +1,4 @@
import {Matrix4} from '@math.gl/core';
import {Matrix4, Vector3} from '@math.gl/core';
import {log} from '@luma.gl/api';
import {ScenegraphNode, ScenegraphNodeProps} from './scenegraph-node';
@ -23,6 +23,40 @@ export class GroupNode extends ScenegraphNode {
this.children = children;
}
override getBounds(): [number[], number[]] | null {
const result: [number[], number[]] = [[Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity]];
this.traverse((node, {worldMatrix}) => {
const bounds = node.getBounds();
if (!bounds) {
return;
}
const [min, max] = bounds;
const center = new Vector3(min).add(max).divide([2, 2, 2]);
worldMatrix.transformAsPoint(center, center);
const halfSize = new Vector3(max).subtract(min).divide([2, 2, 2]);
worldMatrix.transformAsVector(halfSize, halfSize);
for (let v = 0; v < 8; v++) {
// Test all 8 corners of the box
const position = new Vector3(
v & 0b001 ? -1 : 1,
v & 0b010 ? -1 : 1,
v & 0b100 ? -1 : 1
).multiply(halfSize).add(center);
for (let i = 0; i < 3; i++) {
result[0][i] = Math.min(result![0][i], position[i]);
result[1][i] = Math.max(result![1][i], position[i]);
}
}
});
if (!Number.isFinite(result[0][0])) {
return null;
}
return result;
}
override destroy(): void {
this.children.forEach((child) => child.destroy());
this.removeAll();
@ -55,7 +89,7 @@ export class GroupNode extends ScenegraphNode {
return this;
}
traverse(visitor, {worldMatrix = new Matrix4()} = {}) {
traverse(visitor: (node: ScenegraphNode, context: {worldMatrix: Matrix4}) => void, {worldMatrix = new Matrix4()} = {}) {
const modelMatrix = new Matrix4(worldMatrix).multiplyRight(this.matrix);
for (const child of this.children) {

View File

@ -8,6 +8,7 @@ export type ModelNodeProps = ScenegraphNodeProps & ModelProps & {
export class ModelNode extends ScenegraphNode {
readonly model: Model;
bounds: [number[], number[]] | null = null;
AfterRender = null;
managedResources: any[];
@ -38,6 +39,10 @@ export class ModelNode extends ScenegraphNode {
return this;
}
override getBounds(): [number[], number[]] | null {
return this.bounds;
}
override destroy(): void {
if (this.model) {
this.model.delete();

View File

@ -31,6 +31,10 @@ export class ScenegraphNode {
this._setScenegraphNodeProps(props);
}
getBounds(): [number[], number[]] | null {
return null;
}
destroy(): void {}
/** @deprecated use .destroy() */

View File

@ -1,8 +1,12 @@
// luma.gl, MIT license
import test from 'tape-promise/tape';
import {GroupNode, ScenegraphNode} from '@luma.gl/experimental';
import {GroupNode, ScenegraphNode, ModelNode} from '@luma.gl/experimental';
import {Matrix4} from '@math.gl/core';
import {fixture} from 'test/setup';
import {DUMMY_VS, DUMMY_FS} from './model-node.spec';
const {gl} = fixture;
test('GroupNode#construction', (t) => {
const grandChild = new ScenegraphNode();
@ -102,3 +106,34 @@ test('GroupNode#traverse', (t) => {
t.end();
});
test('GroupNode#getBounds', (t) => {
const matrix = new Matrix4().translate([0, 0, 1]).scale(2);
const childSNode = new ModelNode(gl, {id: 'childSNode', vs: DUMMY_VS, fs: DUMMY_FS});
const grandChildSNode = new ModelNode(gl, {id: 'grandChildSNode', vs: DUMMY_VS, fs: DUMMY_FS});
const child1 = new GroupNode({id: 'child-1', matrix, children: [grandChildSNode]});
const groupNode = new GroupNode({id: 'parent', matrix, children: [child1, childSNode]});
t.deepEqual(groupNode.getBounds(), null, 'child bounds are not defined');
childSNode.bounds = [
[0, 0, 0],
[1, 1, 1]
];
grandChildSNode.bounds = [
[-1, -1, -1],
[0, 0, 0]
];
t.deepEqual(
groupNode.getBounds(),
[
[-4, -4, -1],
[2, 2, 3]
],
'bounds calculated'
);
t.end();
});

View File

@ -6,11 +6,11 @@ import {makeSpy} from '@probe.gl/test-utils';
import {Model} from '@luma.gl/webgl-legacy';
import {ModelNode} from '@luma.gl/experimental';
const DUMMY_VS = `
export const DUMMY_VS = `
void main() { gl_Position = vec4(1.0); }
`;
const DUMMY_FS = `
export const DUMMY_FS = `
precision highp float;
void main() { gl_FragColor = vec4(1.0); }
`;