THREE.JS loading an array of STL Meshes - three.js

So, I have a data base which contains a file reference column and any references to sub STL files that are needed for it. I can load one or two models into a THREE.js viewer so all of that works without issue but when I load an array of four or so things start getting hairy and the assigned Mesh Id's start to get a bit odd and multiple mesh's get the same ID. So since it works with two files I know the array kind of works and is passing data correctly.
I was wondering if my loader.load method needs to be tweaked to wait for the mesh to load and configure. Otherwise I have no idea what my problem is.
The first part here calls my createMesh function and asks for a mesh in return. As I said this seems to work for 1 or 2 meshes at a time.
'if (indSTLCol.length>1 ){ for (item in indSTLCol){
getTheName="3dfiles\\"+fRSplit[parseInt(this.id)];
getTheName = getTheName.substring(0,getTheName.length-7);
getTheName=getTheName+indSTLCol[item]+".stl";
meshArr[item]=createMesh(getTheName);
}
}
else{
getTheName="3dfiles\\"+fRSplit[parseInt(this.id)];
console.log("in ind collection"+ getTheName);
console.log ("Added to Array"+meshArr[0].id);
meshArr[0]=createMesh(getTheName);
}
console.log("BREAK");
divId=this.id;
fSizeX=700;
fSizeY=500;
}'
This part creates the mesh:
'function createMesh(getTheName){
loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
geometry = event.content;
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, 0, 0 );
mesh.rotation.set( - Math.PI / 2, 0, 0 );
mesh.scale.set( 2, 2, 2 );
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.id=getTheName;
scene.add(mesh);
/* for (a in scene.children){
console.log("Child Name "+ scene.children[a].id+" " + a);
} */
//console.log("Mesh ID# " + mesh.id+" Mesh info: " + mesh);
return mesh;
} );
mesh=loader.load( getTheName );
return mesh;
}'
I am terrible at explaining my issues so if you need more info please just ask and I can try to explain better.
Also, I iterated through scene.children to check for Id's and it works for two and below meshes. I am starting to be convinced that it takes things are happening and ID's are being assigned before the geometry is being created.
function createMeshArr(){
for (var a in scene.children){
if (scene.children[a] instanceof THREE.Mesh){
smeshArr[a]=scene.children[a];
}
}
return smeshArr;
Ok so this seemed to work and not sure why:
function createMesh(getTheNames){
loader = new THREE.STLLoader();
mesh=loader.load( getTheNames);
loader.addEventListener( 'load', function ( event ) {
var material = new THREE.MeshLambertMaterial({color: 'blue' });
var geometry = event.content;
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, 0, 0 );
mesh.rotation.set( - Math.PI / 2, 0, 0 );
mesh.scale.set( 2, 2, 2 );
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.id=getTheNames;
scene.add(mesh);
return mesh;
} );
return mesh;
}

Here is a simple example to use the callback to force the loader to wait until the previous load is completed.
var callback = function ( geometry ) {
var myMesh = new THREE.Mesh( geometry, basicMat );
meshArray.push(myMesh);
myScene.add( myMesh );
i++;
if (i < numberOfMeshesToLoad) // put the next load in the callback
myLoader.load( filename[i], callback ) ;
};
i = 0;
myLoader.load( filename[i], callback ); //initial loader call

Related

THREE.JS postprocessing: Duplicating postprocessed shader material to render properly on different meshes in the same scene

Think of my question as a more complicated version of this question here:
Three.js, sharing ShaderMaterial between meshes but with different uniform sets
tl;dr: I'm relying on a skin shader from THREE.js r100 to enable subsurface scattering on the meshes to make them appear more realistic but only one of them actually has the proper postprocessing to enable the effects.
The postprocessing effect is desired on all three meshes but only exists on one, the center. You can tell because the lighting looks right, and you can see where light travels "through" the mesh in thinner areas, like on the bottom part of the neck area and the ears (and if you get reaaaallly close it's in the nose too :D ).
Where the code is hosted has some wild things going on to generate the mesh, but that's beside the point, the main problem that I'm having is what you can see, only the center mesh (the one that was first added to the scene) actually has the proper effects applied to its material, the others have cloned versions of that shader but they don't render with the proper post-processing, I have a feeling it's because they're sharing uniforms or something related to that, but I'm not sure how to duplicate it properly. I was wondering how I can fix it? Do I add more render passes?
Is the way to adjust the render passes for the shader materials by adding even more passes relevant to the materials or just merely editing the uniforms (as stated in the linked question)?
I'm a bit lost and I've tried a lot to get this to work (though I'm definitely new to THREE.js post-processing passes), but you're my last hope. To someone experienced with this I feel like the solution will be very straightforward, I have a feeling I'm missing something very basic.
to view the output: https://abrasive-likeable-gateway.glitch.me/index-shader.html
to view the code: https://glitch.com/edit/#!/abrasive-likeable-gateway?path=index-shader.html%3A655%3A0
the filesystem is visible on the left side, you can remix the project on glitch (w/ a free account) to edit the code. I would've used codepen but I didn't want to deal with linking all of the three.js dependencies.
at index-shader.html on line 655 is where the setup begins for postprocessing
and at SS-xfer-placement.js on line 2838 is where rendering happens
in the same document, between lines 1900 - 2048 is the following code, which I suspect is where things are wrong, it looks like this, and imports the mesh, adds a material to it (that was set up in the html file after line 655) and adds it to the scene
the code looks like this:
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
console.log(scene)
//send the model from ONE to THREE, init THREE
// document.querySelector("#ONEJS").style.height = "10vh!important;"
// document.querySelector("#ONEJS").style.width = "10vw!important;"
document.querySelector("#ONEJS").style.position = `absolute`
document.querySelector("#ONEJS").style.top = `0px`
document.querySelector("#ONEJS").style.right = `0px`
initTHREE();
let materialClone = window.THREEmaterial.clone()
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
THREEscene.add( transferMesh );
// console.log("test",transferMesh)
// console.log(THREEscene)
//
}, 1000)
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial.clone() );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
transferMesh.position.set(transferMesh.position.x+200, transferMesh.position.y, transferMesh.position.z)
THREEscene.add( transferMesh );
var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, window.THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
THREEcomposer.addPass( THREErenderModelUV );
//TODO: write a stack overflow question about copying shaders!!!
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial.clone() );
// \var transferMesh = new THREE.Mesh( geometryConvert, new THREE.MeshPhongMaterial({color:0xffffff}) );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
transferMesh.position.set(transferMesh.position.x-200, transferMesh.position.y, transferMesh.position.z)
THREEscene.add( transferMesh );
// var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, window.THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
// THREEcomposer.addPass( THREErenderModelUV );
var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
// var THREEeffectCopy = new THREE.ShaderPass( THREE.CopyShader );
// var THREEeffectBloom1 = new THREE.BloomPass( 1, 15, 2, 512 );
// var THREEeffectBloom2 = new THREE.BloomPass( 1, 25, 3, 512 );
// var THREEeffectBloom3 = new THREE.BloomPass( 1, 25, 4, 512 );
// THREEeffectBloom1.clear = true;
// THREEeffectBloom2.clear = true;
// THREEeffectBloom3.clear = true;
// THREEeffectCopy.renderToScreen = true;
// //
// var THREEpars = {
// generateMipmaps: true,
// minFilter: THREE.LinearMipmapLinearFilter,
// magFilter: THREE.LinearFilter,
// format: THREE.RGBFormat,
// stencilBuffer: false
// };
// var THREErtwidth = 512;
// var THREErtheight = 512;
//
// THREEcomposer = new THREE.EffectComposer( THREErenderer, new THREE.WebGLRenderTarget( THREErtwidth, THREErtheight, THREEpars ) );
THREEcomposer.addPass( THREErenderModelUV );
console.log(THREEcomposer)
}, 2000)
}, 2000)
}, 2000)
}, 2000)
},1000)
in other areas of the project, I wouldn't recommend looking at since it's really not relevant to this issue, the points that I highlighted are the only areas that deal with rendering, adding the mesh, and applying postprocessing.
the uniforms for the material are set up in the html file "index-shader.html" between lines 655 and 700 which may also be where I'd need to duplicate the uniforms and apply them properly, but I can't seem to figure out how to do that.
Please let me know if you have any help, thank you for reading!

THREE.js Many instances of the same simple mesh killing framerate

I have a basic scene in which I'm using each loops to add multiple meshes ( hundreds of simple cylinders ) to a group (for each line), and grouping the lines to cover the surfaces. The result is this:
This is the code to add these cylinders:
var base_material = new THREE.MeshPhongMaterial( {
color: 0x666666,
side: THREE.FrontSide,
});
var cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 1, 4 );
var floor_geometry = new THREE.PlaneGeometry( 68, 10000, 10 );
var floor = new THREE.Mesh( floor_geometry, base_material );
floor.receiveShadow = true;
scene.add( floor );
floor.position.set(0,-15,-530);
floor.rotation.x = -Math.PI / 2;
// Add Floor Studs
for ( var i = 0; i < 15; i++ ) {
var lineGroup = new THREE.Group();
for ( var n = 0; n < 1000; n++ ) {
var cylinder = new THREE.Mesh( cylinderGeometry, base_material );
// cylinder.castShadow = true;
// cylinder.receiveShadow = true;
lineGroup.add( cylinder );
posZ = 0 - n*6;
cylinder.position.set(0,0, posZ);
}
scene.add( lineGroup );
posX = -28.4 + i*4.1;
lineGroup.position.set(posX,-14.7,0);
}
When I animate the camera to traverse through the scene the framerate is dire. Potential approaches I've come across include merging the geometry, possibly rendering out and loading in a single GLTF model with all of these cylinders, or duplicating them somehow. As you can see the geometry and material is created once and reused, however the mesh is recreated each time which I suspect is the culprit..
My question is, what is the most optimum of these approaches to do this, is there a standard best practice method?
Thanks in advance!

Animating a THREE.Points object

I'm trying to load a model from Blender, apply a PointsMaterial to it and animate it. So far, I've been able to load the model and animate it successfully as long as I use a material other than THREE.PointsMaterial to create the mesh.
When I create a THREE.Points object, the animation doesn't play. I noticed that when I set the PointsMaterial's morphTargets property to true, there's a warning saying that there is no such property of PointsMaterial. Does Threejs not support animation of Points objects using morph targets?
monster refers to a mesh that works when animated. It uses the loaded geometry and material. ParticleSystem is the the THREE.Points object.
Code:
function loadObject(path){
var loader = new THREE.JSONLoader();
loader.load(
path,
function(geometry, materials){
var material = materials[ 0 ];
material.morphTargets = true;
material.color.setHex( 0xffaaaa );
monster = new THREE.Mesh( geometry, materials );
monster.position.set( 0, 0, 0 );
var s = .003;
monster.scale.set( s, s, s );
var particleMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: .005,
morphTargets: true
});
particleSystem = new THREE.Points(geometry, particleMaterial);
particleSystem.morphTargetInfluences = monster.morphTargetInfluences;
particleSystem.morphTargetDictionary = monster.morphTargetDictionary;
particleSystem.position.set(0, 0, 0);
particleSystem.scale.set(s, s, s);
particleSystem.matrixAutoUpdate = false;
particleSystem.updateMatrix();
particleSystem.geometry.verticesNeedUpdate = true;
scene.add(particleSystem);
mixer.clipAction( geometry.animations[ 0 ], particleSystem )
.setDuration( 5 ) // one second
.startAt( - Math.random() ) // random phase (already running)
.play(); // let's go
}
)
}

Raycaster.intersectObjects() not returning anything?

I set up my scene as follows:
document.addEventListener('mousedown', onDocumentMouseDown, false);
var container = document.getElementById("myCanvas");
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth*0.99, window.innerHeight*0.99 );
container.appendChild( renderer.domElement );
room_material = new THREE.MeshBasicMaterial({color: 0x00ff00});
room_material.side = THREE.DoubleSide;
objects = [];
camera.position.z = 19;
camera.position.x = 5;
camera.position.y = 30;
I have an array of objects that i'm trying to detect if a click intersects with them, defined as follows:
var thing0 = new THREE.Shape();
thing0.moveTo(-12.1321728566, 35.3935535858);
thing0.lineTo(7.10021556487,35.3935535858);
thing0.lineTo(7.10021556487,19.7039735578);
thing0.lineTo(5.12636517425,19.7166264449);
thing0.lineTo(5.12636517425,33.6221493891);
thing0.lineTo(-12.1377356984,33.6439534769);
var thing0Geom = new THREE.ShapeGeometry(thing0);
var thing0Mesh = new THREE.Mesh( thing0Geom, room_material );
thing0Mesh.name = "abcd";
scene.add(thing0Mesh);
objects.push(thing0Mesh);
I then render the scene with the following code:
renderer.render(scene, camera);
requestAnimationFrame(render);
And lastly I use the following code for the mouse click event:
function onDocumentMouseDown(event) {
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
vector = vector.unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(objects, true);
alert("well, you clicked!");
if (intersects.length > 0) {
alert("wow, it worked");
}
}
However, no matter what I do the alert never gets called when it follows raycaster.intersectObjects(objects, true); However it does get called when it is placed anywhere before it. It seems that raycaster.intersectObjects(objects, true); is a bit of a black hole in this case?
I assume I simply have something wrong in my setup? Any help would be appreciated!
There are two things I tried that worked:
1) Make sure you're using Three.DoubleSide if your mesh face is not pointing towards origin of the ray. This is directly from the documentation:
"Note that for meshes, faces must be pointed towards the origin of the ray in order to be detected; intersections of the ray passing through the back of a face will not be detected. To raycast against both faces of an object, you'll want to set the material's side property to THREE.DoubleSide."
2) Use mesh.updateMatrixWorld() prior to raycasting. This comes from another stackoverflow post: threejs raycasting does not work
mesh.updateMatrixWorld(); // add this
raycaster.set(from, direction);
I think i have found the problem. I have to add the meshes to an extra array. An intersectObjects over the scene.children don't work, because there are other objects in there with the meshes.
So when i give the intersectObjects( mesh[] ) an mesh array than it works.
For more code detail see https://github.com/mrdoob/three.js/issues/8081
This is an old question, but I was running into a similar issue and thought what I learned might help someone else. My code setup is very similar to JohnnyDevNull's, so I won't repeat it.
The Problem
In my case, calling intersectObjects with scene.children doesn't work because it picks up other objects like ambientLighting, etc. I believe this is what the OP mentions in his own answer.
My Solution
The function below takes an empty array (intersects). It searches each child in scene for THREE Group objects and Mesh objects and adds Mesh objects to the array. For Groups, it sets recursive to true, which applies .intersectObject to each child. Once the array is built up, it calls your supplied callback function on the array.
function raycastMeshes(intersects, callback, theScene, theRaycaster) {
var scene = theScene || scene || new THREE.Scene();
var raycaster = theRaycaster || raycaster || new THREE.Raycaster();
for (var i in scene.children) {
if (scene.children[i] instanceof THREE.Group) {
intersects = raycaster.intersectObjects(scene.children[i].children, true);
} else if (scene.children[i] instanceof THREE.Mesh) {
intersects.push(raycaster.intersectObject(scene.children[i]));
}
}
if (intersects.length > 0) {
return callback(intersects);
} else {
return null;
}
}
Adapting to your Use Case
This is obviously a fairly naive and specific use case, but should serve as a launching board for you if you're having a similar problem.

Physijs object goes through concaveMesh

I've a problem that seems to be known: my "bounding" object doesn't collide with "floor" concaveMesh.
I already read that this issue could be caused by an error in scaling concaveMesh together with the model, so I exported my floor model scaled as I need it and after I applied a concaveMesh (as follow) but it doesn't work.
I red this: https://github.com/chandlerprall/Physijs/issues/102 and a lot of other things about this topic (Physijs Load model three.js collisions don't work and a similar) and I made the following code but nothing to do :(
I really don't understand why "bounding" goes through the floor.
Here my code:
Physijs.scripts.worker = './libs/chandlerprall-Physijs-7e3837b/physijs_worker.js';
Physijs.scripts.ammo = './examples/js/ammo.js';
var gravityVector = new THREE.Vector3( 0, -100, 0 );
//renderer
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xffffff, 0);
renderer.setSize( window.innerWidth, window.innerHeight );
//canvas
var canvas = renderer.domElement;
canvas.setAttribute("width", window.innerWidth);
canvas.setAttribute("height", window.innerHeight);
document.body.appendChild( canvas );
var perspectiveCamera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight, 1, 200000);
//scene
var rttScene = new Physijs.Scene();
var bounding = new Physijs.SphereMesh(new THREE.SphereGeometry(100, 100, 100),
Physijs.createMaterial(
new THREE.MeshBasicMaterial({color: '#ff0000'}),
1.0, // friction
0.0 // restitution
),50 //mass
);
bounding.position.set(200,1200,-5000);
loader.load("http://0.0.0.0:8000/Models/Isola/pavimento.js", function( geometry, materials){
var groundMaterial = Physijs.createMaterial(new THREE.MeshFaceMaterial(materials),
0.8, // friction
0.2 // restitution
);
floor = new Physijs.ConcaveMesh(geometry,groundMaterial,0);
floor.name = "pavimento";
rttScene.add(floor);
initScene();
render();
});
function initScene() {
rttScene.setGravity(gravityVector);
rttScene.add(envModel);
rttScene.add(bounding);
bounding.setAngularFactor(new THREE.Vector3(0, 0, 0));
bounding.setCcdMotionThreshold( 0.1 );
bounding.setCcdSweptSphereRadius( 1 );
var ambientLight = new THREE.AmbientLight(0xD9B775 );
rttScene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xc7af81);
directionalLight.target.position.copy( rttScene.position );
directionalLight.position.set(-550,1950,1950).normalize();
directionalLight.intensity = 0.7;
rttScene.add(directionalLight);
perspectiveCamera.position.set(200,1200,-3000);
perspectiveCamera.lookAt(bounding.position);
}
function render() {
requestAnimationFrame(render);
renderer.clear();
rttScene.simulate();
renderer.render(rttScene, perspectiveCamera);
}
I also tried this into render() function:
var originPoint = bounding.position.clone();
var ray = new THREE.Raycaster(originPoint, new THREE.Vector3(0, -1, 0));
var collisionResults = ray.intersectObjects(rttScene.children)
if (collisionResults.length > 0) {
console.log(collisionResults[0].distance);
}
In console i can read the distance between "bounding" and "floor". This should mean that floor exist as a collider but it doesn't stop bounding from falling. Why?

Resources