I have a three.js scene with an object.
I let the user take a snapshot of what is painted on the canvas.
Although, I want all the snapshots to have also an other object. This object will never appear on the canvas though. So I thought that I will have this object in my scene, but I will position it very far so it will never be visible. But when the user clicks the link to download the snapshot, I will bring the object for some miliseconds in a position where is visible, and when the snapshot of the canvas is done I will move it again vary far.
document.getElementById('download').addEventListener('click', function() {
appearMesh();
downloadCanvas(this , "screenshot");
disappearMesh();
}, false);
function downloadCanvas(link, filename) {
link.href = document.getElementsByTagName("canvas")[0].toDataURL();
link.download = filename;
};
That doesn't work though : http://jsfiddle.net/95t964o0/21/
Nothing appears in the image.
Any ideas?
thanks
I think you should call a render after you move the object:
function appearMesh(){
meshToAppear.position.x = 0;
renderer.render( scene, camera );
}
function disappearMesh(){
meshToAppear.position.x = 1000;
renderer.render( scene, camera );
}
Related
Im started to develop my project, where fabric js canvas used as a texture on my 3d object, loaded by three js library. Unfortunately I don`t have a full working snippet now, maybe you can show me solution or right way to solve my problem
The problem is the controls drawing on the lower canvas and its transfer on my 3d object, i want to render it on the upper canvas. Is it possible?
The second problem is the brush at first rendered on the upper canvas and after event 'mouse: up' its rendered on the lower canvas. How to render brush line by event 'mouse; move' on the lower canvas at the moment drawing?
p.s. sorry for my english, its really hard to say for me. Hope, you understand my mind
Here you can see my first issue:
https://1drv.ms/u/s!AqtIFlFVWz4MhNtBc3g1DQElfA6_7Q?e=y7ly37
Thank you for you response!
Woow! this is a very good code example! And it works pretty smooth too. It shows the problem a lot better.
I found a solution for the first problem (that is kind of a three.js problem) in the fabric js demo: http://fabricjs.com/controls-customization.
I'm not good with fabric so there might be a more elegant solution than this. You can hide the controls, render to three.js and then show them again. You also have to force the canvas to render, it won't work otherwise.
function animate() {
requestAnimationFrame(animate);
//find the selected object on the fabric canvas
let selected = canvas.getActiveObject();
//hide controles and render on the fabric canvas;
if (selected){
selected.hasControls = false;
selected.hasBorders = false;
canvas.renderAll();
}
//update texture and render
texture.needsUpdate = true;
renderer.render(scene, camera);
//show controls and render on the fabric canvas
if (selected){
selected.hasControls = true;
selected.hasBorders = true;
canvas.renderAll();
}
}
The second problem is a bit harder to understand. I'm not sure what the 'lower canvas' is and if that is a part of fabric that i don't understand. This is the closest i can get. I almost made it work on the three.js renderer. If i understood the idea correctly...
You need to have a variable to know if you are pressing the mouse or not. Only if you are pressing the mouse AND moving the mouse, it draws a circle.
To do the same with fabric 'mouse:move' you will at least have to disable that selectionbox on the fabric canvas. But i do not know how that works in fabric.
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var mouseDown = false;///////////////////////////// global boolean
var onClickPosition = new THREE.Vector2();
container.addEventListener("mousedown", function(){
mouseDown = true;
canvas.selection = false;
onMouseEvt(); //draw circle
}, false);
container.addEventListener("mouseup", function(){
mouseDown = false
canvas.selection = true;
}, false);
container.addEventListener("mousemove", function(e){
if (mouseDown){
onMouseEvt(); //draw circle
}
}, false);
///This code does not work, it goes into selection mode..
canvas.on('mouse:down', function(){
mouseDown = true;
addCircle();
}, false);
canvas.on('mouse:up', function(){
mouseDown = false;
}, false);
canvas.on('mouse:move', function(){
if ( mouseDown ){
addCircle();
}
}, false);
This is a hard question and it's not really a three.js thing, it has more to do with fabric.js and drawing to a canvas. The three.js part of texturing the object seems to work fine.
If I understand correctly you have an overlaying canvas on which you want to render the controls, but they are also rendered to the actual texture canvas? If in case you are actually rendering the controls on the texture; don't do that :p
It should be possible to make it work the way you want to. It would be helpful to post a small code example on https://jsfiddle.net/ or something.
This is my code:
var sprite = new THREE.Sprite(material);
sprite.renderDepth = 10;
The above renderDepth setting is invalid, it does not work for sprites.
How to solve this problem?
You want one sprite to always be on top.
Since SpriteMaterial does not support a user-specified renderDepth, you have to implement a work-around.
Sprites are rendered last when using WebGLRenderer.
The easiest way to do what you want is to have two scenes and two render passes, with one sprite in the second scene like so:
renderer.autoClear = false;
scene2.add( sprite2 );
then in the render loop
renderer.render( scene, camera );
renderer.clearDepth();
renderer.render( scene2, camera );
three.js r.64
I've tried improving rendering time on my project by creating meshes and putting them as part of a larger geometry, and having just that single geometry as the object I add to the scene. I thought that I would still be able to manage picking of objects by having an array of the original meshes, and pass those to the raycaster. I used the following code:
var vector = new THREE.Vector3( ( loc_x / window.innerWidth ) * 2 - 1, - ( loc_y / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var objects = [];
var i = active_regions.length;
while (i--) {
objects = objects.concat(active_regions[i].mesh_entities);
}
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
console.log("Intersection: " + intersects);
}
So in the above code, active_regions contains the original individual meshes, and I create an array on the fly to specify which objects I want to select from. Unfortunately intersects comes up empty.
If I modify my project slightly so that I have all those mesh_entities added to the scene individually, then the above code works and I can successfully select objects. Unfortunately, the whole scene then renders slowly.
What's a good way (or some good ways) for me to successfully check for intersection with the ray, without slowing down my rendering?
Thanks!
You need to update the Matrices for the objects not in the render scene manually as its done as part of the render process so if you are using your ghost scene, you don't need to render it, just update the matrices before doing the intersection:
scene_ghost.updateMatrixWorld(true);
I solved this by having a ghost scene. Essentially, I added all objects to the ghost scene as their individual meshes, and then when I use raycaster it works.
However, I had to use functions along these lines:
function flip_render_ghost(yes) {
if (yes == true) {
scene_ghost.add(camera);
render_ghost = true;
} else {
scene.add(camera);
render_ghost = false;
}
render();
}
function render() {
if (render_ghost == true) {
renderer.render( scene_ghost, camera );
} else {
renderer.render( scene, camera );
}
}
Whenever I am about to check for collisions, I flip to rendering ghost scene, check for hits, then flip back to normal rendering.
Edit: I have since discovered that objects cannot belong to multiple scenes (though geometries can be shared). So what I have done is created simple meshes for the picking scene. This requires more memory, but gives the option of having a simpler mesh to use for selection for faster picking. Also, it seemed to work for me to send the children of the ghost scene itself to the raycaster. You may need to, like me, add a property to each object in the ghost scene that references the main object you are trying to pick.
I am doing something similar in this and have verified that you need to render the scene to do proper raycasting. It is easy to optimize this however to just render both screens and have one clear over the other. You should be able to change your code to this, granted the second render call will clear over the first screen:
function render() {
renderer.render( scene_ghost, camera );
renderer.render( scene, camera );
}
Removing OLD Objects on the Scene and make Scene available to add New Objects
I've developed an application which has 3D custom objects added to the Mesh and there upon Mesh is added to scene. I also have a Button on my page, when I clicked it, the already created objects should be removed from mesh which is a children of scene and scene should be ready to add new objects like it was the at the first time. I mean, if possible whole mesh can be removed and new mesh with new set of objects should add to the scene.
function xyz() // Button click function
{
// should remove the objects in the scene if exists and make scene available to add
// new objects
for ex: scene.remove(mesh)....???
// Also needed to clear the renderer??
.
.
.
do something
}
function init()
{
// adds the objects to the scene and instantiate renderer
mesh = New THREE.Mesh({// some material});
cube = new THREE.CubeGeometry(1,1,1);
object = new THREE.Mesh(cube,material);
mesh.add(object);
scene.add(mesh);
}
function animate()
{
requestAnimationFrame(animate);
render();
}
function render()
{
renderer.render(scene, camera);
}
I'm making alot of assumptions about your code based on your snippet you included so maybe try:
function xyz() {
var l = scene.children.length
;
//remove everything
while (l--) {
if(scene.children[l] instanceof THREE.Camera) continue; //leave camera in the scene
scene.remove(scene.children[l]);
}
//reinitialise your stuff
init();
}
I cant get my collada model to render with three.js until I interact with the browser window. In Chrome, it can be as simple as moving my mouse anywhere on the page (actual canvas is smaller than entire window), and on Safari, it requires a click and drag for the model to render. What's odd is that the rest of the scene is loading properly (I have a grid that I render to the scene and a couple of lights), but the model won't show until I do something. Any thoughts on how I can trick the scene into knowing it should show the model?
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('../MODELS/teapot.dae', function colladaReady( collada ) {
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 10;
dae.position.y = 75;
dae.updateMatrix();
scene.add(dae);
});
document.getElementById('myCanvas').appendChild(renderer.domElement);
render();
The loading is occurring asynchronously. You are only showing a code fragment, but it looks like you can move the render() call to the last line of the colladaReady() callback function.