Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/agargaro/instanced-mesh
Simplified and enhanced InstancedMesh with frustum culling, fast raycasting (using BVH), sorting, visibility management and more.
https://github.com/agargaro/instanced-mesh
bvh frustum-culling instances performance three-js threejs visibility
Last synced: 6 days ago
JSON representation
Simplified and enhanced InstancedMesh with frustum culling, fast raycasting (using BVH), sorting, visibility management and more.
- Host: GitHub
- URL: https://github.com/agargaro/instanced-mesh
- Owner: agargaro
- License: mit
- Created: 2024-06-27T13:15:14.000Z (3 months ago)
- Default Branch: master
- Last Pushed: 2024-09-24T19:52:40.000Z (9 days ago)
- Last Synced: 2024-09-24T22:02:44.235Z (9 days ago)
- Topics: bvh, frustum-culling, instances, performance, three-js, threejs, visibility
- Language: TypeScript
- Homepage:
- Size: 27.6 MB
- Stars: 107
- Watchers: 0
- Forks: 7
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Three.ez - InstancedMesh2
Simplify your three.js application development with three.ez!
[![npm](https://img.shields.io/npm/v/@three.ez/instanced-mesh)](https://www.npmjs.com/package/@three.ez/instanced-mesh)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=agargaro_instanced-mesh&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=agargaro_instanced-mesh)
[![DeepScan grade](https://deepscan.io/api/teams/21196/projects/27990/branches/896898/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=21196&pid=27990&bid=896898)
[![Stars](https://badgen.net/github/stars/three-ez/instanced-mesh)](https://github.com/three-ez/instanced-mesh)
[![BundlePhobia](https://badgen.net/bundlephobia/min/@three.ez/instanced-mesh)](https://bundlephobia.com/package/@three.ez/instanced-mesh)
[![Discord](https://img.shields.io/discord/1150091562227859457)](https://discord.gg/MVTwrdX3JM)`InstancedMesh2` is an alternative version of `InstancedMesh` that offers advantages:
- *frustum culling for each instance*
- *sorting*
- *visibility for each instance*
- *each instance can have an object similar to `Object3D` to simplify its use*
- *spatial indexing [(*BVH*)](https://en.wikipedia.org/wiki/Bounding_volume_hierarchy) for fast raycasting and frustum culling*```ts
import { InstancedMesh2 } from '@three.ez/instanced-mesh';const myInstancedMesh = new InstancedMesh2(renderer, count, geometry, material);
myInstancedMesh.updateInstances((obj, index) => {
obj.position.z = index;
obj.rotateY(Math.PI);
});myInstancedMesh.computeBVH();
```This library has two dependencies:
- `three.js r159+`
- [`bvh.js`](https://github.com/agargaro/BVH.js)## Live Examples
These examples use `vite`, and some mobile devices may run out of memory.
- [1kk static trees](https://stackblitz.com/edit/three-ezinstancedmesh2-1kk-static-trees?file=src%2Fmain.ts&embed=1&hideDevTools=1&view=preview)
- [Instances array dynamic](https://stackblitz.com/edit/three-ezinstancedmesh2-instances-array-dynamic?file=src%2Fmain.ts&embed=1&hideDevTools=1&view=preview)
- [Sorting](https://stackblitz.com/edit/three-ezinstancedmesh2-sorting?file=src%2Fmain.ts&embed=1&hideDevTools=1&view=preview)
- [Custom material](https://stackblitz.com/edit/three-ezinstancedmesh2-custom-material?file=src%2Fmain.ts&embed=1&hideDevTools=1&view=preview)
- [Dynamic BVH (no vite)](https://stackblitz.com/edit/three-ezinstancedmesh2-dynamic-bvh?file=index.ts&embed=1&hideDevTools=1&view=preview)
- [Fast raycasting](https://stackblitz.com/edit/three-ezinstancedmesh2-fast-raycasting?file=src%2Fmain.ts&embed=1&hideDevTools=1&view=preview)More examples will be added soon...
## Frustum Culling
Avoiding rendering objects outside the camera frustum can drastically improve performance (especially for complex geometries).
***Frustum culling by default is performed by iterating all instances***, [but it is possible to speed up this process by creating a spatial indexing data structure **(BVH)**](#spatial-indexing-data-structure-dynamic-bvh).
By default `perObjectFrustumCulled` is **true**.## Sorting
Sorting should be used to decrease overdraw and render transparent objects.
By default `sortObjects` is **false**.```ts
import { createRadixSort } from '@three.ez/instanced-mesh';myInstancedMesh.sortObjects = true;
myInstancedMesh.customSort = createRadixSort(myInstancedMesh);
```## Visibility
Set the visibility status of each instance like this:
```ts
myInstancedMesh.setVisibilityAt(false, 0);
myInstancedMesh.instances[0].visible = false; // if instances array is created
```## Instances Array
It is possible to create an array of ***InstancedEntity (Object3D-like)*** in order to easily change the visibility, apply transformations and add custom data to each instance, ***using more memory***.
```ts
myInstancedMesh.createInstances((obj, index) => {
obj.position.random();
});myInstancedMesh.instances[0].visible = false;
myInstancedMesh.instances[1].userData = {};
myInstancedMesh.instances[2].position.random();
myInstancedMesh.instances[2].quaternion.random();
myInstancedMesh.instances[2].scale.random();
myInstancedMesh.instances[2].updateMatrix(); // necessary after transformationsmyInstancedMesh.instances[3].rotateX(Math.PI);
myInstancedMesh.instances[3].updateMatrix(); // necessary after transformations
```## Spatial Indexing Data Structure (Dynamic BVH)
To speed up raycasting and frustum culling, a spatial indexing data structure can be created to contain the boundingBoxes of all instances.
This works very well if the instances are mostly static (updating a BVH can be expensive) and scattered in world space.```ts
// call this function after all instances have been valued
myInstancedMesh.computeBVH({ margin: 0, highPrecision: false });
```If all instances are static set the margin to 0.
***Setting a margin makes BVH updating faster***, but may make raycasting and frustum culling slightly slower.## Raycasting tips
If you are not using a BVH, you can set the `raycastOnlyFrustum` property to **true** to avoid iterating over all instances.
It's also highly recommended to use [three-mesh-bvh](https://github.com/gkjohnson/three-mesh-bvh) to create a geometry BVH.
## API
InstancedMesh2
```ts
export type Entity = InstancedEntity & T;
export type UpdateEntityCallback = (obj: Entity, index: number) => void;export interface BVHParams {
margin?: number;
highPrecision?: boolean;
}export declare class InstancedMesh2 extends Mesh {
type: 'InstancedMesh2';
isInstancedMesh2: true;
instances: Entity[];
instanceIndex: GLInstancedBufferAttribute;
matricesTexture: DataTexture;
colorsTexture: DataTexture;
morphTexture: DataTexture;
boundingBox: Box3;
boundingSphere: Sphere;
instancesCount: number;
bvh: InstancedMeshBVH;
perObjectFrustumCulled: boolean;
sortObjects: boolean;
customSort: any;
raycastOnlyFrustum: boolean;
visibilityArray: boolean[];
customDepthMaterial: MeshDepthMaterial;
customDistanceMaterial: MeshDistanceMaterial;
get count(): number;
get maxCount(): number;
get material(): TMaterial;
set material(value: TMaterial);
/** THIS MATERIAL AND GEOMETRY CANNOT BE SHARED */
constructor(renderer: WebGLRenderer, count: number, geometry: TGeometry, material?: TMaterial);
updateInstances(onUpdate: UpdateEntityCallback>): void;
createInstances(onInstanceCreation?: UpdateEntityCallback>): void;
computeBVH(config?: BVHParams): void;
disposeBVH(): void;
setMatrixAt(id: number, matrix: Matrix4): void;
getMatrixAt(id: number, matrix?: Matrix4): Matrix4;
setVisibilityAt(id: number, visible: boolean): void;
getVisibilityAt(id: number): boolean;
setColorAt(id: number, color: ColorRepresentation): void;
getColorAt(id: number, color?: Color): Color;
setUniformAt(id: number, name: string, value: UniformValue): void;
getMorphAt(index: number, object: Mesh): void;
setMorphAt(index: number, object: Mesh): void;
raycast(raycaster: Raycaster, result: Intersection[]): void;
computeBoundingBox(): void;
computeBoundingSphere(): void;
copy(source: InstancedMesh2, recursive?: boolean): this;
dispose(): this;
}
```InstancedEntity
```ts
export type UniformValueNoNumber = Vector2 | Vector3 | Vector4 | Matrix3 | Matrix4;
export type UniformValue = number | UniformValueNoNumber;export declare class InstancedEntity {
isInstanceEntity: true;
readonly id: number;
readonly owner: InstancedMesh2;
position: Vector3;
scale: Vector3;
quaternion: Quaternion;
get visible(): boolean;
set visible(value: boolean);
get color(): Color;
set color(value: ColorRepresentation);
get matrix(): Matrix4;
get matrixWorld(): Matrix4;
constructor(owner: InstancedMesh2, index: number);
updateMatrix(): void;
setUniform(name: string, value: UniformValue): void;
copyTo(target: Mesh): void;
applyMatrix4(m: Matrix4): this;
applyQuaternion(q: Quaternion): this;
rotateOnAxis(axis: Vector3, angle: number): this;
rotateOnWorldAxis(axis: Vector3, angle: number): this;
rotateX(angle: number): this;
rotateY(angle: number): this;
rotateZ(angle: number): this;
translateOnAxis(axis: Vector3, distance: number): this;
translateX(distance: number): this;
translateY(distance: number): this;
translateZ(distance: number): this;
}
```Utils
```ts
export declare function patchShader(shader: string): string;export declare function createRadixSort(target: InstancedMesh2): typeof radixSort;
export declare function createTexture_float(count: number): DataTexture;
export declare function createTexture_vec2(count: number): DataTexture;
export declare function createTexture_vec3(count: number): DataTexture;
export declare function createTexture_vec4(count: number): DataTexture;
export declare function createTexture_mat3(count: number): DataTexture;
export declare function createTexture_mat4(count: number): DataTexture;
```## How Does It Work?
It works similarly to `BatchedMesh`: ***matrices, colors, etc.*** are stored in `Texture` instead of `InstancedAttribute`.
The only `InstancedAttribute` is used to store the indices of the instances to be rendered.
***If you create a custom material, you will need to use `Texture` instead of `InstancedBufferAttribute` (don't worry, there are utility methods).***## Installation
You can install it via npm using the following command:
```bash
npm install @three.ez/instanced-mesh
```Or you can import it from CDN:
```html
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"@three.ez/instanced-mesh": "https://cdn.jsdelivr.net/npm/@three.ez/instanced-mesh/index.js",
"bvh.js": "https://cdn.jsdelivr.net/npm/bvh.js/index.js"
}
}```
## Questions?
If you have questions or need assistance, you can ask on our [discord server](https://discord.gg/MVTwrdX3JM).
## Future Work
- LOD system
- Remove renderer from constructor parameters## Like it?
If you find this project helpful, I would greatly appreciate it if you could leave a star on this repository!
This helps me know that you appreciate my work and encourages me to continue improving it.
Thank you so much for your support! 🌟## Special thanks to
- [gkjohnson](https://github.com/gkjohnson)
- [manthrax](https://github.com/manthrax)
- [jungle_hacker](https://github.com/lambocorp)## References
- [three-mesh-bvh](https://github.com/gkjohnson/three-mesh-bvh)
- [ErinCatto_DynamicBVH](https://box2d.org/files/ErinCatto_DynamicBVH_Full.pdf)
- [BatchedMesh](https://threejs.org/docs/#api/en/objects/BatchedMesh)