Skip to content

Commit

Permalink
feat(octree): Use octree to Filter the scene tree (#275)
Browse files Browse the repository at this point in the history
Use octree to Filter the scene tree
sort transparent nodes.
Merge transparent tree and opaque to one tree.
  • Loading branch information
hellmor committed Aug 9, 2023
1 parent 09266a6 commit f30a2ae
Show file tree
Hide file tree
Showing 20 changed files with 948 additions and 103 deletions.
1 change: 0 additions & 1 deletion samples/base/Sample_BoundingBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class Sample_BoundingBox {
await Engine3D.init({ renderLoop: () => { this.loop() } });
GUIHelp.init();
let param = createSceneParam();
param.camera.distance = 1;
param.camera.near = 0.01;
param.camera.far = 100;
param.camera.distance = 2;
Expand Down
115 changes: 115 additions & 0 deletions samples/octree/Sample_OctTreeBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { GUIHelp } from '@orillusion/debug/GUIHelp';
import { BoundingBox, BoxGeometry, Color, Engine3D, LitMaterial, MeshRenderer, Object3D, Object3DUtil, PointerEvent3D, Time, Vector3, View3D, } from '@orillusion/core';
import { createExampleScene, createSceneParam } from '@samples/utils/ExampleScene';
import { OctreeEntity } from '../../src/core/tree/octree/OctreeEntity';
import { Octree } from '../../src/core/tree/octree/Octree';

// A sample to use octTree
export class Sample_OctTreeBox {
view: View3D;
entities: OctreeEntity[] = [];
tree: Octree;
red = new Color(1, 0, 0, 1);
gree = new Color(0, 1, 0, 1);
yellow = new Color(1, 1, 0, 1)
blue = new Color(0, 0, 1, 1)
white = new Color(1, 1, 1, 1)

movingBox: BoundingBox;

async run() {
// init engine
await Engine3D.init({ renderLoop: () => { this.loop() } });
GUIHelp.init();
let param = createSceneParam();
param.camera.distance = 400;
param.camera.near = 0.1;
param.camera.far = 10000;
let exampleScene = createExampleScene(param);
Engine3D.startRenderViews([exampleScene.view]);
Engine3D.getRenderJob(exampleScene.view);

this.view = exampleScene.view;

let box: BoundingBox = new BoundingBox();
box.setFromMinMax(new Vector3(-100, -100, -100), new Vector3(100, 100, 100));
this.tree = new Octree(box);

this.entities = this.initBoxList();
let center = new Vector3(3, -3, -100);
let size = new Vector3(200, 137, 134);
this.movingBox = new BoundingBox().setFromCenterAndSize(center, size);

let updateBox = () => {
this.movingBox.setFromCenterAndSize(this.movingBox.center, this.movingBox.size);
}

GUIHelp.addFolder('Center');
GUIHelp.add(this.movingBox.center, 'x', -100, 100, 1).onChange(() => { updateBox(); });
GUIHelp.add(this.movingBox.center, 'y', -100, 100, 1).onChange(() => { updateBox(); });
GUIHelp.add(this.movingBox.center, 'z', -100, 100, 1).onChange(() => { updateBox(); });
GUIHelp.open();
GUIHelp.endFolder();
GUIHelp.addFolder('Size');
GUIHelp.add(this.movingBox.size, 'x', 1, 200, 1).onChange(() => { updateBox(); });
GUIHelp.add(this.movingBox.size, 'y', 1, 200, 1).onChange(() => { updateBox(); });
GUIHelp.add(this.movingBox.size, 'z', 1, 200, 1).onChange(() => { updateBox(); });
GUIHelp.open();
GUIHelp.endFolder();
}

_material: LitMaterial;

private initBoxList(): OctreeEntity[] {
this._material = new LitMaterial();

let object3D: Object3D;
let entities: OctreeEntity[] = [];
let geometry = new BoxGeometry();
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 100; j++) {
for (let k = 0; k < 4; k++) {
object3D = new Object3D();
let renderer = object3D.addComponent(MeshRenderer);
renderer.geometry = geometry;
renderer.material = this._material;
object3D.localPosition.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
object3D.localPosition.multiplyScalar(190);
object3D.localPosition = object3D.localPosition;
object3D.localScale = new Vector3(1 + Math.random(), 1 + Math.random(), 1 + Math.random());
object3D.name = 'name' + i;

let entity: OctreeEntity = new OctreeEntity(renderer);
entities.push(entity);
this.tree.tryInsertEntity(entity);
this.view.scene.addChild(object3D);
}
}
}
return entities;
}

private queryResult: OctreeEntity[] = [];
private octreeTest() {
this.queryResult.length = 0;

let now: number = Date.now();
this.tree.boxCasts(this.movingBox, this.queryResult);
let time: number = Date.now() - now;
console.log('time: ' + time + ' count: ' + this.queryResult.length);

let retBoolean = {};
for (let item of this.queryResult) {
retBoolean[item.uuid] = true;
}
for (let item of this.entities) {
item.renderer.enable = retBoolean[item.uuid];
}
this.view.graphic3D.drawBoundingBox('pick', this.movingBox, this.gree);
}

loop() {
this.entities && this.octreeTest();
}

}
117 changes: 117 additions & 0 deletions samples/octree/Sample_OctTreeFrustum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { GUIHelp } from '@orillusion/debug/GUIHelp';
import { BoundingBox, BoxGeometry, Camera3D, Color, Engine3D, Frustum, LitMaterial, MeshRenderer, Object3D, Object3DUtil, PointerEvent3D, Time, Vector3, View3D, } from '@orillusion/core';
import { createExampleScene, createSceneParam } from '@samples/utils/ExampleScene';
import { OctreeEntity } from '../../src/core/tree/octree/OctreeEntity';
import { Octree } from '../../src/core/tree/octree/Octree';
import { GUIUtil } from '@samples/utils/GUIUtil';
import { Stats } from '@orillusion/stats';

// A sample to use octTree
export class Sample_OctTreeFrustum {
view: View3D;
entities: OctreeEntity[] = [];
tree: Octree;
red = new Color(1, 0, 0, 1);
gree = new Color(0, 1, 0, 1);
yellow = new Color(1, 1, 0, 1)
blue = new Color(0, 0, 1, 1)
white = new Color(1, 1, 1, 1)

camera: Camera3D;
frustumBound: BoundingBox;
async run() {

Engine3D.setting.occlusionQuery.octree = { width: 1000, height: 1000, depth: 1000, x: 0, y: 0, z: 0 }

// init engine
await Engine3D.init({ renderLoop: () => { this.loop() } });
GUIHelp.init();
let param = createSceneParam();
param.camera.distance = 400;
param.camera.near = 0.1;
param.camera.far = 10000;
let exampleScene = createExampleScene(param);
Engine3D.startRenderViews([exampleScene.view]);
Engine3D.getRenderJob(exampleScene.view);

this.view = exampleScene.view;
this.view.scene.addComponent(Stats);

let box: BoundingBox = new BoundingBox();
box.setFromCenterAndSize(new Vector3(), new Vector3(1000, 1000, 1000));
this.tree = new Octree(box);

this.entities = this.initBoxList();
this.camera = new Object3D().addComponent(Camera3D);
this.camera.perspective(60, Engine3D.aspect, 1, 1000);
this.view.scene.addChild(this.camera.object3D);

this.frustumBound = new BoundingBox();

GUIUtil.renderTransform(this.camera.transform);
}

_material: LitMaterial;

private initBoxList(): OctreeEntity[] {
this._material = new LitMaterial();

let object3D: Object3D;
let entities: OctreeEntity[] = [];
let geometry = new BoxGeometry();
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 100; j++) {
for (let k = 0; k < 5; k++) {
object3D = new Object3D();
let renderer = object3D.addComponent(MeshRenderer);
renderer.geometry = geometry;
renderer.material = this._material;
object3D.localPosition.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
object3D.localPosition.multiplyScalar(480);
object3D.localPosition = object3D.localPosition;
object3D.localScale = new Vector3(1 + Math.random(), 1 + Math.random(), 1 + Math.random());
object3D.name = 'name' + i;

let entity: OctreeEntity = new OctreeEntity(renderer);
entities.push(entity);
this.tree.tryInsertEntity(entity);
this.view.scene.addChild(object3D);
}
}
}
return entities;
}


private queryResult: OctreeEntity[] = [];
private octreeTest() {
let range = this.camera.frustum.genBox(this.camera.pvMatrixInv);
this.frustumBound.setFromMinMax(new Vector3(range.minX, range.minY, range.minZ), new Vector3(range.maxX, range.maxY, range.maxZ));
this.view.graphic3D.drawCameraFrustum(this.camera, this.gree);
this.view.graphic3D.drawBoundingBox('box', this.frustumBound, this.red);

let frustum = this.camera.frustum;

//__________exec octree query
let now = performance.now();
this.queryResult.length = 0;
// this.tree.boxCasts(this.frustumBound, this.queryResult);
this.tree.frustumCasts(frustum, this.queryResult);

console.log('exec octree: ', performance.now() - now, 'count', this.queryResult.length);
//end——————————————

let retBoolean = {};
for (let item of this.queryResult) {
retBoolean[item.uuid] = true;
}
for (let item of this.entities) {
item.renderer.enable = retBoolean[item.uuid];
}
}

loop() {
this.entities && this.octreeTest();
}

}
123 changes: 123 additions & 0 deletions samples/octree/Sample_OctTreeRay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { GUIHelp } from '@orillusion/debug/GUIHelp';
import { BoundingBox, BoxGeometry, Color, Engine3D, LitMaterial, MeshRenderer, Object3D, Object3DUtil, PointerEvent3D, Time, Vector3, View3D, } from '@orillusion/core';
import { createExampleScene, createSceneParam } from '@samples/utils/ExampleScene';
import { OctreeEntity } from '../../src/core/tree/octree/OctreeEntity';
import { Octree } from '../../src/core/tree/octree/Octree';

// A sample to use octTree
export class Sample_OctTreeRay {
view: View3D;
entities: OctreeEntity[] = [];
tree: Octree;
red = new Color(1, 0, 0, 1);
gree = new Color(0, 1, 0, 1);
yellow = new Color(1, 1, 0, 1)
blue = new Color(0, 0, 1, 1)
white = new Color(1, 1, 1, 1)

async run() {
Engine3D.setting.occlusionQuery.octree = { width: 400, height: 400, depth: 400, x: 0, y: 0, z: 0 }
// init engine
await Engine3D.init({ renderLoop: () => { this.loop() } });
GUIHelp.init();
let param = createSceneParam();
param.camera.distance = 400;
param.camera.near = 0.1;
param.camera.far = 10000;
let exampleScene = createExampleScene(param);
Engine3D.startRenderViews([exampleScene.view]);
Engine3D.getRenderJob(exampleScene.view);

this.view = exampleScene.view;

let box: BoundingBox = new BoundingBox();
box.setFromCenterAndSize(new Vector3(), new Vector3(400, 400, 400));
this.tree = new Octree(box);

this.entities = this.initBoxList();
}

_material: LitMaterial;

private initBoxList(): OctreeEntity[] {
this._material = new LitMaterial();

let object3D: Object3D;
let entities: OctreeEntity[] = [];
let geometry = new BoxGeometry();
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 100; j++) {
for (let k = 0; k < 2; k++) {
object3D = new Object3D();
let renderer = object3D.addComponent(MeshRenderer);
renderer.geometry = geometry;
renderer.material = this._material;
object3D.localPosition.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
object3D.localPosition.multiplyScalar(190);
object3D.localPosition = object3D.localPosition;
object3D.localScale = new Vector3(1 + Math.random(), 1 + Math.random(), 1 + Math.random());
object3D.name = 'name' + i;

let entity: OctreeEntity = new OctreeEntity(renderer);
entities.push(entity);
this.tree.tryInsertEntity(entity);
this.view.scene.addChild(object3D);
}
}
}
return entities;
}


private queryResult: OctreeEntity[] = [];
private octreeTest() {
let ray = this.view.camera.screenPointToRay(Engine3D.inputSystem.mouseX, Engine3D.inputSystem.mouseY);
this.queryResult.length = 0;
let now: number = Date.now();
this.tree.rayCasts(ray, this.queryResult);
let time: number = Date.now() - now;
console.log('time: ' + time + ' count: ', this.queryResult.length);
this.view.graphic3D.ClearAll();

let retBoolean = {};
let boundList = {};
for (let item of this.queryResult) {
if (ray.intersectBox(item.renderer.object3D.bound)) {
retBoolean[item.uuid] = true;
boundList[item.owner.uuid] = item.owner;
}
}
for (let item of this.entities) {
item.renderer.enable = !retBoolean[item.uuid];
}

//show box
for (let key in boundList) {
let tree = boundList[key];
this.view.graphic3D.drawBoundingBox(key, tree.box, this.gree);
}
}

loop() {
this.entities && this.updateBoxTransform();
}

private counter: number = 0;
private updateBoxTransform() {
this.octreeTest();
this.counter += Time.delta;
if (this.counter > 4000) {
this.counter = 0;
let obj: Object3D;
for (const entity of this.entities) {
obj = entity.renderer.object3D;
if (Math.random() < 0.1) {
obj.localPosition.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
obj.localPosition.multiplyScalar(190);
obj.localPosition = obj.localPosition;
entity.update(this.tree);
}
}
}
}
}
6 changes: 3 additions & 3 deletions samples/utils/GUIUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ export class GUIUtil {
public static renderTransform(transform: Transform, open: boolean = true, name?: string) {
name ||= 'Transform';
GUIHelp.addFolder(name);
GUIHelp.add(transform, 'x', -10.0, 10.0, 0.01);
GUIHelp.add(transform, 'y', -10.0, 10.0, 0.01);
GUIHelp.add(transform, 'z', -10.0, 10.0, 0.01);
GUIHelp.add(transform, 'x', -100.0, 100.0, 0.01);
GUIHelp.add(transform, 'y', -100.0, 100.0, 0.01);
GUIHelp.add(transform, 'z', -100.0, 100.0, 0.01);
GUIHelp.add(transform, 'rotationX', 0.0, 360.0, 0.01);
GUIHelp.add(transform, 'rotationY', 0.0, 360.0, 0.01);
GUIHelp.add(transform, 'rotationZ', 0.0, 360.0, 0.01);
Expand Down
8 changes: 4 additions & 4 deletions src/components/Transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ export class Transform extends ComponentBase {
this._parent = value;
let hasRoot = value ? value.scene3D : null;
if (!hasRoot) {
this.object3D.components.forEach((c) => {
for (let c of this.object3D.components.values()) {
c[`__stop`]();
});
}
} else {
this._scene3d = hasRoot;
this.object3D.components.forEach((c) => {
for (let c of this.object3D.components.values()) {
ComponentCollect.appendWaitStart(this.object3D, c);
});
}
}

for (let child of this.object3D.entityChildren) {
Expand Down

0 comments on commit f30a2ae

Please sign in to comment.