How can i store decals in Three.js and load them? - three.js

I have a model that when i click on the faces - it's adds decals (points or marks on the face) , i wonder how can i store the selection and load it later on.
I've followed the example Here and this is then function that stores the decal in array
function shoot() {
position.copy( intersection.point );
orientation.copy( mouseHelper.rotation );
if ( params.rotate ) orientation.z = Math.random() * 2 * Math.PI;
var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
size.set( scale, scale, scale );
var material = decalMaterial.clone();
material.color.setHex( 0xffffff );
var m = new THREE.Mesh( new DecalGeometry( mesh, position, orientation, size ), material );
decals.push( m );
scene.add( m );
}
So i've tried to store the decals array and when the scene is loading - add them to the scene
scene.add( decals ); // just an idea
I also run in to this example - but still - i couldn't figure out how to do so.

Since decals are just meshes, you can serialize them with Object3D.toJSON() and restore them via ObjectLoader. I've update your fiddle to demonstrate this approach. The relevant code is:
const jsonString = JSON.stringify( scene.toJSON() );
scene = new THREE.ObjectLoader().parse( JSON.parse( jsonString ) );
When the scene object is restored, the decals should still be there. The only thing that disappears is the axes helper (since helpers can't be serialized/deserialized yet).

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!

ThreeJS mesh does not appear after adding it to scene then positioning the camera to fill its view

In this bit of code an .obj model is loaded into the scene then the camera's z position is moved to a point where the mesh's bounding box fills the view.
The problem is that the mesh does not appear unless I manually fiddle with the orbit controls or if I comment out the camera.position.z line.
Is there a way to force the camera to render or otherwise force the mesh to appear?
function loadModel() {
mesh.traverse( function ( child ) {
if ( child.isMesh ) child.material.map = texture;
} );
var geometry = mesh.children[0].geometry;
geometry.center();
mesh.scale.copy(new THREE.Vector3(100,100,100));
scene.add( mesh );
var box = new THREE.Box3().setFromObject( mesh );
var object3DWidth = box.max.x - box.min.x;
var object3DHeight = box.max.y - box.min.y;
var object3DDepth = box.max.z - box.min.z;
// position camera to fill view according to bounding box size
var size=Math.max(object3DHeight,object3DWidth);
var dist= size / 2 / Math.tan(Math.PI * cameraFov / 360);
camera.position.z = dist;
//// if I remove the line above the mesh will appear but not zoomed ////
}

Threejs / glTF - Black spots on mesh

I'm getting some weird black spots on a mesh loaded in threejs in glTF.
Anyone already have this problems before ?
The mesh is heavy 145 663 vertices, the biggest part have 89 000 vertices
I'm using version r94 of threejs and i load mesh with ths following code :
`
function setup(scene, camera, renderer) {
var loader = new THREE.GLTFLoader();
THREE.DRACOLoader.setDecoderPath( 'js/libs/draco/gltf/' );
loader.setDRACOLoader( new THREE.DRACOLoader() );
// Load a glTF resource
loader.load(
// resource URL
'mesh/ExportAllcleanNoMap.glb',
// called when the resource is loaded
function ( gltf ) {
gltf.scene.scale.set(10,10,10);
scene.add( gltf.scene );
var anim = gltf.animations[0];
mixer = new THREE.AnimationMixer( gltf.scene );
var action = mixer.clipAction(anim);
action.play();
orbitControls = new THREE.OrbitControls( camera, renderer.domElement );
orbitControls.target.set( 0, 1, 0 );
orbitControls.update();
light = new THREE.HemisphereLight( 0xbbbbff, 0x444422, 2 );
light.position.set( 0, 1, 0 );
scene.add( light );
scene.add(light);
var ambient = new THREE.AmbientLight( 0x222222 );
scene.add( ambient );
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log(error);
console.log( 'An error happened' );
}
);
}
`
Thanks.
Edit : Black spot not appear when draco compression is disabled. Looks like an issue between threejs, draco and heavy mesh.
Black spot on mesh
This looks like doubled up geometry. You somehow have two copies of the model perfectly on top of each other.
Check in your modelling software by selecting the vertices of one object and moving them and see if there is a duplicate hiding behind them.
If you are using blender, you can select a single vertex in the affected area in edit mode, and do ctrl L to select linked vertices... then move those aside and see if there is geometry hiding behind them.

Threejs Raycasting a Sprite-Canvas Object is inaccurate [duplicate]

I am trying to use THREE.Raycaster to show an html label when the user hover an object. It works fine if I use THREE.Mesh but with THREE.Sprite it looks like that there is a space that increases with the scale of the object.
The creation process is the same for both scenario, I only change the type based on USE_SPRITE variable.
if ( USE_SPRITE ) {
// using SpriteMaterial / Sprite
m = new THREE.SpriteMaterial( { color: 0xff0000 } );
o = new THREE.Sprite( m );
} else {
// using MeshBasicMaterial / Material
m = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
o = new THREE.Mesh(new THREE.PlaneGeometry( 1, 1, 1 ), m );
}
https://plnkr.co/edit/J0HHFMpDB5INYLSCTWHG?p=preview
I am not sure if it is a bug with THREE.Sprite or if I am doing something wrong.
Thanks in advance.
three.js r73
I would consider this a bug in three.js r.75.
Raycasting with meshes in three.js is exact. However, with sprites, it is an approximation.
Sprites always face the camera, can have different x-scale and y-scale applied (be non-square), and can be rotated (sprite.material.rotation = Math.random()).
In THREE.Sprite.prototype.raycast(), make this change:
var guessSizeSq = this.scale.x * this.scale.y / 4;
That should work much better for square sprites. The corners of the sprite will be missed, as the sprite is treated as a disk.
three.js r.75

How to get a point coordinate on a SkinnedMaterial for the human demo of three.js?

For the human demo http://threejs.org/examples/#webgl_morphtargets_human , I wonder how to get a point on the skin of the human. For example when I click some place on the human body, what's the coordinate of that place?
I tried using the raycaster to get that but in vain.The code is like this:
var projector;
init() {
// Others
// ...
projector = new THREE.Projector();
renderer.domElement.addEventListener('mouseup', onMouseUp, false);
}
function onMouseUp(e) {
e.preventDefault();
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1,
0.5
);
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersections = raycaster.intersectObjects( character.root.children );
if (intersections.length > 0) {
debugger;
// ...
}
}
But the intersections is always empty.
Three.js is r67
Thanks in advance.
I'm new to three.js, and i've also tried to draw plots on the human. I manage to do it, but it's not on the "visible" body. In fact, you should first use the intersect method with the recursive arg :
var intersections = raycaster.intersectObjects( scene.children, true );
Thus, you 'll be able to interact with the objects composing the body, but they are not positioned under the "skin". It seems to be that they have been "moved", because you can interact with them if you aim in front of the feet of the body. Unfortunately, I don't know for the moment why, and how to interact with their "visible representation".
Well, finally I find that it's just because the human animation. It works if I comment the animation out.

Resources