I'm working on a project in the medical field using threejs, and OBJ files shin bone, my goal is that I get to cut part of the tibia on the basis of a given position, for now I can do that by replacing the vertix of the object, but my problem is that I can not directly update the object, so every time I delete the old object and I upload it, although i put verticeNeedUpdate …
My question is if it's possible to directly update the object?
loader.load('img/cube.obj',function(object){
var material = new THREE.MeshLambertMaterial({color:0xA0A0A0});
slice_onZ.onChange(function(z){
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
geometry = new THREE.Geometry().fromBufferGeometry(child.geometry);
for(var i=0 ; i< geometry.vertices.length ; i++) {
if (geometry.vertices[ i ].x != 0) {
geometry.vertices[ i ].x = z;
}
if (geometry.vertices[ i ].y != 0) {
geometry.vertices[ i ].y = z;
}
if (geometry.vertices[ i ].z != 0) {
geometry.vertices[ i ].z = z;
}
}
geometry.verticesNeedUpdate = true;
mesh = new THREE.Mesh( geometry, material );
console.log(mesh.geometry.attributes);
scene.add(mesh);
}
});
});
},onProgress,onError);
}
there is my code, i try to update geometry but it's doesnt work, i have to reload everytime the object and delete the old one for display the new on.
Related
When I create a sprite, OrbitControls works perfectly.
But when I switch to FirstPersonControls, everything freezes and the console gives this error:
Uncaught TypeError: Cannot read property 'matrixWorld' of undefined
at Sprite.raycast (three.js:26051)
at intersectObject (three.js:44583)
at Raycaster.intersectObjects (three.js:44658)
at animate (mainview.php?project_id=46&tipo=1:936)
Raycasting code:
if (firstorbit) {
if (l % 5 == 0) {
var vector = new THREE.Vector3(0, -1, 0);
vector = camera.localToWorld(vector);
vector.sub(camera.position);
var raycasterGround = new THREE.Raycaster(camera.position, vector);
var intersectsGround = raycasterGround.intersectObjects(scene.children, true);
if (intersectsGround.length > 0) {
if (intersectsGround[0].distance < 1.21) {
camera.position.y = (camera.position.y + (1.21 - intersectsGround[0].distance));
}
if (intersectsGround[0].distance > 1.36 && intersectsGround[0].distance < 1.45) {
camera.position.y = (camera.position.y - (intersectsGround[0].distance - 1.215));
}
}
}
if (l % 3 == 0) {
var vector1 = new THREE.Vector3(0, 0, -1);
vector1 = camera.localToWorld(vector1);
vector1.sub(camera.position);
var raycasterFront = new THREE.Raycaster(camera.position, vector1);
var intersectsFront = raycasterFront.intersectObjects(scene.children, true);
if (intersectsFront.length > 0) {
if (intersectsFront[0].distance < 0.3) {
controls.moveForward = false;
controls.w = false;
} else {
controls.w = true;
}
} else controls.w = true;
}
}
Raycasting code that I use on mouse click to measure something:
function getIntersections( event ) {
var vectorMouse = new THREE.Vector2();
vectorMouse.set(
mouse.x,
mouse.y );
var raycasterClick = new THREE.Raycaster();
raycasterClick.setFromCamera( vectorMouse, camera );
var intersectsClick = raycasterClick.intersectObjects( scene.children,true );
return intersectsClick;
}
I am not sure if the problem is with the sprite or with my raycasting code. But when I remove the sprite, everything works perfectly. And when I remove the Raycasting code, everything also works perfectly.
I have a hierarchy of groups, A -> B -> C. I wish to create a clone of this hierarchy, A2 -> B2 -> C2.
But Object3D.clone() removes the parent reference of the group.
Other than manually setting the parent for each of the child-groups after cloning, what other way is there?
If the hierarchy is deep this could get take to compute.
Thanks for the help!
Maybe you can check out this question Will three.js Object3D.clone() create a deep copy of the geometry?
I extends the copyand clone methods in Object3D to deep clone mesh materials.
And in your case , this should works too.
First,extends two new methods in THREE:
THREE.Object3D.prototype.deepClone = function ( recursive ) {
return new this.constructor().deepCopy( this, recursive );
},
THREE.Object3D.prototype.deepCopy = function( source, recursive ) {
if ( recursive === undefined ) recursive = true;
this.name = source.name;
this.up.copy( source.up );
this.position.copy( source.position );
this.quaternion.copy( source.quaternion );
this.scale.copy( source.scale );
this.matrix.copy( source.matrix );
this.matrixWorld.copy( source.matrixWorld );
if(source.material){
//changed
this.material = source.material.clone()
}
if(source.geometry){
//changed
this.geometry = source.geometry.clone()
}
this.matrixAutoUpdate = source.matrixAutoUpdate;
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
this.layers.mask = source.layers.mask;
this.visible = source.visible;
this.castShadow = source.castShadow;
this.receiveShadow = source.receiveShadow;
this.frustumCulled = source.frustumCulled;
this.renderOrder = source.renderOrder;
this.userData = JSON.parse( JSON.stringify( source.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < source.children.length; i ++ ) {
var child = source.children[ i ];
this.add( child.deepClone() ); //changed
}
}
return this;
}
Second,when you want to deep clone a Object3D or Scene named originalObj.just do var newObj = originalObj.deepClone()
Can someone explain why part of my model flickers black when a material canvas material is added to it and when the camera moves?
Here's a shot of it before an image is added:
And after when an image has been added and camera rotated:
Notice the black marks at the top. Is this to do with the material I'm using possibly? Here's how I apply it:
for(let i = 0; i < sidesModified.length; i++) {
let sideName = sidesModified[i];
let side = this.getSideInLayersObservable(view, sideName);
let maxAnisotropy = (this.scene && this.scene['renderer']) ? this.scene['renderer'].getMaxAnisotropy() : 2;
let canvasTexture = new THREE.CanvasTexture(ctx.canvas, THREE.UVMapping, this.scene.materials.wrapS, this.scene.materials.wrapT, THREE.LinearFilter, THREE.LinearMipMapLinearFilter, THREE.RGBAFormat, THREE.UnsignedByteType, maxAnisotropy);
for(let j = 0; j < side.length; j++) {
canvasTexture.wrapS = this.scene.materials.wrapS;
canvasTexture.wrapT = this.scene.materials.wrapT;
this._reverseSideIf(this.sceneName, view, sideName, canvasTexture);
this._drawImageOnCtx(side, j, object, ctx);
}
let materialIndex = this.getMaterialIndexForBag(this.sceneName, view, sideName);
let material = this._setupMaterial(canvasTexture);
if(this.sceneName === 'two-d-scene') {
if(view === 'outside') {
this.selectedBag[0].children[materialIndex].material = material;
} else {
this.selectedBag[1].children[materialIndex].material = material;
}
} else {
this.selectedBag.children[materialIndex].material = material
}
canvasTexture.needsUpdate = true;
}
I'm loading models using the collada loader. The loader returns an Object3D, "dae", with many child meshes. I'd like to instantiate the parent "dae" object many times without duplicating the meshes. Can I just use dae.clone()?
Put another way: I'd like to make shallow copies that all have their own transformation matrix but share the same geometry. What's the most efficient way to do this?
By default Object3D.clone() does create a deep copy. Let's take a look at the source
clone: function ( object, recursive ) {
if ( object === undefined ) object = new THREE.Object3D();
if ( recursive === undefined ) recursive = true;
object.name = this.name;
object.up.copy( this.up );
object.position.copy( this.position );
object.quaternion.copy( this.quaternion );
object.scale.copy( this.scale );
object.renderDepth = this.renderDepth;
object.rotationAutoUpdate = this.rotationAutoUpdate;
object.matrix.copy( this.matrix );
object.matrixWorld.copy( this.matrixWorld );
object.matrixAutoUpdate = this.matrixAutoUpdate;
object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
object.visible = this.visible;
object.castShadow = this.castShadow;
object.receiveShadow = this.receiveShadow;
object.frustumCulled = this.frustumCulled;
object.userData = JSON.parse( JSON.stringify( this.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < this.children.length; i ++ ) {
var child = this.children[ i ];
object.add( child.clone() );
}
}
return object;
}
As we can see the clone function accepts two optional arguments:
the object to clone the Object3D into.
a flag to indicate whether or not to recursively clone the children.
So yes it is possible to make shallow copies with respect to the Object3D.children, but that's not what you want (based on your comment).
I believe you can actually use the default behavior of Object3D.clone() to get what you are after. Mesh.clone() does not clone the Geometry and Material properties.
THREE.Mesh.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
Here is a couple of function I use to clone object's materials deeply. You could modify this to your needs
Also consider this information : https://github.com/mrdoob/three.js/issues/5754
/** Gives the aptitude for an object3D to clone recursively with its material cloned (normal clone does not clone material)*/
THREE.Object3D.prototype.GdeepCloneMaterials = function() {
var object = this.clone( new THREE.Object3D(), false );
for ( var i = 0; i < this.children.length; i++ ) {
var child = this.children[ i ];
if ( child.GdeepCloneMaterials ) {
object.add( child.GdeepCloneMaterials() );
} else {
object.add( child.clone() );
}
}
return object;
};
THREE.Mesh.prototype.GdeepCloneMaterials = function( object, recursive ) {
if ( object === undefined ) {
object = new THREE.Mesh( this.geometry, this.material.clone() );
}
THREE.Object3D.prototype.GdeepCloneMaterials.call( this, object, recursive );
return object;
};
You can refer to the copyand clone methods in Object3D to deep clone mesh materials.
First,extends two new methods in THREE:
THREE.Object3D.prototype.deepClone = function ( recursive ) {
return new this.constructor().deepCopy( this, recursive );
},
THREE.Object3D.prototype.deepCopy = function( source, recursive ) {
if ( recursive === undefined ) recursive = true;
this.name = source.name;
this.up.copy( source.up );
this.position.copy( source.position );
this.quaternion.copy( source.quaternion );
this.scale.copy( source.scale );
this.matrix.copy( source.matrix );
this.matrixWorld.copy( source.matrixWorld );
if(source.material){
//changed
this.material = source.material.clone()
}
if(source.geometry){
//changed
this.geometry = source.geometry.clone()
}
this.matrixAutoUpdate = source.matrixAutoUpdate;
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
this.layers.mask = source.layers.mask;
this.visible = source.visible;
this.castShadow = source.castShadow;
this.receiveShadow = source.receiveShadow;
this.frustumCulled = source.frustumCulled;
this.renderOrder = source.renderOrder;
this.userData = JSON.parse( JSON.stringify( source.userData ) );
if ( recursive === true ) {
for ( var i = 0; i < source.children.length; i ++ ) {
var child = source.children[ i ];
this.add( child.deepClone() ); //changed
}
}
return this;
}
Second,when you want to deep clone a Object3D or Scene named originalObj.just do var newObj = originalObj.deepClone()
three.js Object3D.clone() will not create a deep copy of geometry and material. It will reference geometry and material of the cloned object instead.
You can see this in three.module.js:
Mesh.prototype = ... {
...,
copy: function ( source ) {
Object3D.prototype.copy.call( this, source );
...
>>>>>>> this.material = source.material; <<<<<<<
>>>>>>> this.geometry = source.geometry; <<<<<<<
return this;
}
...
}
So, regarding the OP's question: Yes, you can just use dae.clone().
For a deep clone including geometry and material, just change two lines of the above Mesh.prototype.copy() function:
this.material = source.material.clone();
this.geometry = source.geometry.clone();
Keep in mind this is a performance issue because three.js has to calculate separate geometries for each object3D, including all Meshes.
i dont have the code here so i hope you can understand, i'l edit and add the code when i get home.
I have built a class move that once initialized adds a jquery event (keyDown) to the rendered dom element.
the event checks what key is down in a switch case,
if the key is one of the cases the camera will be moved accordingly.
The camera does move, but for some reason it flickers a bit, like little jumps.
the speed for each camera move is 0.05;
when i did this in anouter app but via a javascript keydown event from the main script (no special class) it worked fine..
any idea why would it do this ?
edit: code
main script :
<script>
var moveMec = null;
var loopProg = null;
var renderer = null;
var scene = null;
var camera = null;
var mesh = null;
var earth = null;
var sun = null;
$(document).ready(
function() {
var container = document.getElementById("container");
renderer = new THREE.WebGLRenderer({ antialias: true } );
renderer.setSize(container.offsetWidth,container.offsetHeight);
container.appendChild(renderer.domElement)
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45,
container.offsetWidth / container.offsetHeight, 1, 4000 );
earth = new Earth();
sun = new Sun({x:-10,y:0,z:20});
scene.add(earth.getEarth);
scene.add(sun.sunLight);
camera.position.set( 0, 0, 3 );
moveMec = new moveMechanics(camera,renderer.domElement)
loopProg = new loopProgram();
loopProg.add(function(){earth.update()});
//loopProg.add(function(){renderer.render( scene, camera );});
loopProg.solarLoop();
}
);
</script>
move script :
var moveMechanics = function (camera,domElement,speed)
{
moveMechanics.camera = camera;
moveMechanics.speed = speed != undefined ? speed : 0.01;
moveMechanics.domElement = domElement;
$(document).keydown(function(event)
{
switch(event.which)
{
case KeyEvent.DOM_VK_W:
camera.position.z -= moveMechanics.speed;
break;
case KeyEvent.DOM_VK_S:
camera.position.z += moveMechanics.speed;
break;
case KeyEvent.DOM_VK_D:
camera.position.x += moveMechanics.speed;
break;
case KeyEvent.DOM_VK_A:
camera.position.x -= moveMechanics.speed;
break;
}
});
}
loop code :
function loopProgram()
{
this.functionsToRun = new Array();
this.solarLoop= function()
{
jQuery.each(loopProg.functionsToRun, function(index,value)
{
value ? value() : null;
});
requestAnimationFrame(loopProg.solarLoop);
}
this.add = function(func)
{
this.functionsToRun[this.functionsToRun.length] = func;
}
}