Last active
February 24, 2021 14:44
-
-
Save nicoptere/5f79ed905f473269b897147e50dc7ab1 to your computer and use it in GitHub Desktop.
create InstancedGeometries from GLTF
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
import { | |
BufferAttribute, | |
Group, | |
InstancedBufferAttribute, | |
InstancedBufferGeometry, | |
Mesh, | |
MeshStandardMaterial, | |
Vector3, | |
} from "three"; | |
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; | |
export default class Instances extends Group { | |
constructor(model_url) { | |
super(); | |
//load a GLTF file | |
let loader = new GLTFLoader(); | |
loader.load(model_url, this.onModelLoaded.bind(this)); | |
} | |
onModelLoaded(obj) { | |
//creata a point cloud to place all the objects | |
let cloud = this.getPointCloud(1000); | |
//create the values of an instanced attribute (this will position the meshes in space) | |
let offset = []; | |
cloud.forEach((v) => { | |
offset.push(v.x, v.y, v.z); | |
}); | |
//this is the root of the loaded GLTF (not necessarily a Scene Object) | |
let scene = obj.scene; | |
console.log("model.scene", scene); | |
//find each mesh of the scene | |
scene.traverse((source) => { | |
//with each mesh | |
if (source.isMesh) { | |
//create the "blueprint" mesh, the one that is being copied | |
let src = source.geometry; | |
let geom = new InstancedBufferGeometry(); | |
//blueprint attributes | |
//copies the positions from the loaded mesh | |
geom.setAttribute( | |
"position", | |
new BufferAttribute(src.getAttribute("position").array, 3) | |
); | |
//copies the uvs from the loaded mesh | |
geom.setAttribute( | |
"uv", | |
new BufferAttribute(src.getAttribute("uv").array, 2) | |
); | |
//copies the normals from the loaded mesh | |
geom.setAttribute( | |
"normal", | |
new BufferAttribute(src.getAttribute("normal").array, 3) | |
); | |
//copiesthe indices of the loaded mesh | |
geom.setIndex(src.getIndex()); | |
//instances attributes | |
//add the instanced attribute to the geometry | |
geom.setAttribute( | |
"offset", | |
new InstancedBufferAttribute(new Float32Array(offset), 3) | |
); | |
//material: | |
//here it becomes tricky, you can't reuse the loaded object's material directly | |
//so instead we create a new one and copy values from the loaded object's properties | |
let mat = source.material; | |
let material = new MeshStandardMaterial({ | |
map: mat.map, | |
color: mat.color, | |
envMapIntensity: mat.envMapIntensity, | |
envMap: mat.envMap, | |
normalMap: mat.normalMap, | |
specularMap: mat.specularMap, | |
roughness: mat.roughness, | |
metalness: mat.metalness, | |
emissive: mat.emissive, | |
emissiveMap: mat.emissiveMap, | |
}); | |
//second tricky part: we need a custom shader | |
//for this we'll just hack into an existing material but we could use a custom shader | |
//this site shows what the 'needles' (<common>, <begin_vertex>) are being replaced with | |
// https://ycw.github.io/three-shaderlib-skim/#/latest/standard/vertex | |
material.onBeforeCompile = (mat) => { | |
let vs = mat.vertexShader; | |
vs = vs.replace( | |
"<common>", | |
`<common> | |
attribute vec3 offset;// add the 'offset' attribute to the vertex shader | |
` | |
); | |
vs = vs.replace( | |
"<begin_vertex>", | |
`<begin_vertex> | |
transformed += offset;// this adds the offset poistion ot the model's position | |
` | |
); | |
mat.vertexShader = vs; | |
}; | |
//create and add the instanced Mesh | |
let mesh = new Mesh(geom, material); | |
this.add(mesh); | |
} | |
}); | |
//have a cigar, pat self in the back | |
} | |
//go nuts! | |
getPointCloud(count) { | |
let points = []; | |
while (count--) { | |
let x = (Math.random() - 0.5) * 50; | |
let y = (Math.random() - 0.5) * 50; | |
let z = (Math.random() - 0.5) * 50; | |
points.push(new Vector3(x, y, z)); | |
} | |
return points; | |
} | |
} |
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
import Instances from "./Instances"; | |
let inst = new Instances("path/to/file.glb"); | |
scene.add(inst); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment