Problems rendering a list of threeJs renderers - javascript

I'm attempting to display a list of renderer/scene/camera "groups" on the same html page. I ran into problems when trying to implement a render loop on all of them, using requestAnimationFrame. I am, however, unable to successfully render even a single frame by looping over the list.
calling renderer.render on each of the "groups" in the same function that I instantiate them in works fine. I refactored the code to allow calling render on each of the renderers outside of said function, and my blue spheres disappear;
I have some elements...
<div class="1"></div>
<div class="2"></div>
<div class="3"></div>
<div class="4"></div>
that I add some threeJs things to
$(document).ready(function(){
addResizeandCenterListener(window, $("#container"), 4/3);
addResizeListener($("#container"), $(".playerhalf"), 2/1);
for (let item of [".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8"]) {
var threeDetails = addThreeRendererToElem($(item), item);
var scene = threeDetails["scene"];
addClickListenerToElem($(item), scene);
}
});
threeList = [];
//this adds a renderer/scene/camera to the element "elem"
function addThreeRendererToElem(elem, name){
var width = elem.innerWidth();
var height = elem.innerHeight();
var scene = newScene();
scene.name = name;
addDefaultLight(scene);
addBlueBall(scene);
var camera = addDefaultCamera(scene, width, height);
var renderer = newRenderer(width, height);
elem[0].appendChild(renderer.domElement);
//this works fine regardless of how many renderer/scene/camera groups I have placed in the DOM
//renderer.render(scene, camera);
var threeDict = {"renderer":renderer, "scene":scene, "camera":camera}
threeList.push(threeDict)
//this does not work, even if I only place a single renderer/scene camera group in the DOM
renderList();
}
function renderList() {
var tlist = threeList;
for (var i = 0; i< tlist;i++) {
var threeDict = tlist[i];
threeDict["renderer"].render(threeDict["scene"],
threeDict["camera"]);
}
}
function newScene(){
scene = new THREE.Scene();
return scene;
}
function newCamera(w, h){
camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
return camera;
}
function newRenderer(w, h){
renderer = new THREE.WebGLRenderer({alpha:true, antialias:true});
renderer.setSize(w, h);
return renderer;
}
function addObjectToScene(obj, scene){
scene.add(obj);
}
function ballObj(color){
var ballGeometry = new THREE.SphereGeometry(3, 16, 16);
var ballMaterial = new THREE.MeshPhongMaterial({color: 0x33aaff});
var ball = new THREE.Mesh(ballGeometry, ballMaterial);
return ball;
}
function spotLightObj(){
var spot = new THREE.SpotLight(0xffffff);
return spot;
}
function addDefaultCamera(scene, width, height){
var camera = newCamera(width, height);
camera.position.set(1,1,20);
camera.lookAt(scene.position);
return camera;
}
function addDefaultLight(scene){
var spot = spotLightObj();
spot.position.set(1, 10, 4);
addObjectToScene(spot, scene);
}
function addBlueBall(scene){
var hexcolor = "0x33aaff";
bball = ballObj(hexcolor);
addObjectToScene(bball, scene);
}
I expect to see some blue spheres, like when I am calling render from inside addThreeRendererToElem, but I don't. Instead, the canvases are all blank. The canvases are in the correct places on the web page, inspecting threeList in the console shows that all the objects are indeed there. There are no error messages.

Incorrect ending condition on your for-loop inside the renderList function.
You are missing .length. Should be:
function renderList() {
var tlist = threeList;
for ( var i = 0; i < tlist.length; i++ ) {
var threeDict = tlist[i];
threeDict["renderer"].render( threeDict["scene"], threeDict["camera"] );
}
}

Related

Imported mesh to BABYLON.Mesh

I have a problem to use CSG on imported meshes, here's my code:
var a;
var b;
BABYLON.SceneLoader.ImportMesh("", "./public/Models/", "model1.stl", scene, function (newMeshes) {
// Set the target of the camera to the first imported mesh
camera.target = newMeshes[0];
a = newMeshes[0];
});
BABYLON.SceneLoader.ImportMesh("", "./public/Models/", "model2.stl", scene, function (newMeshes) {
// Set the target of the camera to the first imported mesh
//camera.target = newMeshes[0];
b = newMeshes[0];
});
var aCSG = BABYLON.CSG.FromMesh(a);
var bCSG = BABYLON.CSG.FromMesh(b);
"var a" and "var b" are undefined and Debug told me that
 "BABYLON.CSG: Wrong Mesh type, must be BABYLON.Mesh"
Is there any method to convert imported mesh to BABYLON.MESH?
Thank you very much
This is because the ImportMesh is async, you have to move your code in the callbacks section:
BABYLON.SceneLoader.ImportMesh("", "./public/Models/", "model1.stl", scene, function (newMeshes) {
// Set the target of the camera to the first imported mesh
camera.target = newMeshes[0];
a = newMeshes[0];
BABYLON.SceneLoader.ImportMesh("", "./public/Models/", "model2.stl", scene, function (newMeshes) {
// Set the target of the camera to the first imported mesh
//camera.target = newMeshes[0];
b = newMeshes[0];
var aCSG = BABYLON.CSG.FromMesh(a);
var bCSG = BABYLON.CSG.FromMesh(b);
});
});

Javascript members not updated after function call

I am trying create a simple three.js app, but I'm getting stuck on some of the nuances of javascript. I don't really know how to explain the problem without an example:
var Model = function() {
THREE.Object3D.call(this);
this.loadedMesh = null;
this.meshIsLoaded = false;
};
Model.prototype = {
constructor: Model,
load: function(path, mtlName, objName) {
var onProgress = function(xhr) {...};
var onError = function(err) {...};
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader());
mtlLoader.setPath(path);
mtlLoader.load(mtlName, function(materials) {
materials.preload();
objLoader.setMaterials(materials);
objLoader.setPath(path);
objLoader.load(objName, function(object) {
object.position.set(0, -5, 10);
object.scale.set(5, 5, 5);
object.rotation.y = Math.PI; // 180 degrees
scene.add(object);
this.loadedMesh = object;
this.meshIsLoaded = true;
});
}, onProgress, onError);
}
};
This wrapper should be a container for a three model. The loading works fine, but when I try and save the Model into a variable, none of the internal members are altered outside of the Model scope.
var spaceman = new Model();
spaceman.load("ACES/", "acesjustforroomshow.mtl", "acesjustforroomshow.obj");
...
function update() {
if (spaceman.meshIsLoaded)
spaceman.loadedMesh.rotation.x += 0.1;
}
It's weird because if I debug the app and wait until the model is loaded, I can hover over the internal members and they are populated. When I hover over the spaceman object, the values for loadedMesh and meshIsLoaded remain the default.
Is there some aspect of prototyping that I'm not understanding here?
My guess is that the issue is the this variable. Within the callback function you're passing to objLoader.load, it may no longer be set to your object. If I'm right, either of the below fixes should work:
Using bind:
objLoader.load(objName, function(object) {
object.position.set(0, -5, 10);
object.scale.set(5, 5, 5);
object.rotation.y = Math.PI; // 180 degrees
scene.add(object);
this.loadedMesh = object;
this.meshIsLoaded = true;
}.bind(this));
Capturing this in another variable:
var that = this;
objLoader.load(objName, function(object) {
object.position.set(0, -5, 10);
object.scale.set(5, 5, 5);
object.rotation.y = Math.PI; // 180 degrees
scene.add(object);
that.loadedMesh = object;
that.meshIsLoaded = true;
});

Three.js pick object after remove

Picking and objects code is one of the most popular:
function Picking(event) {
var raycaster = new THREE.Raycaster();
event.preventDefault();
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
}
} else {
INTERSECTED = null;
}
}
The description: in scene are two objects - cube and sphere. Sphere is first to camera and cube is second. The Sphere has ID1 and cube ID2. Picking is working.
The problem: after deleting the sphere (scene.remove(sphere)); Picking is giving the ID1, so, it seems like sphere is invisible. What is the problam?
The picture of the example: Picture
This code is not giving the result:
for (i = sphere.children.length - 1; i >= 0 ; i--) {
object = sphere.children[i];
object.geometry.remove;
object.material.remove;
object.geometry.dispose();
object.material.dispose();
scene.remove(object);
sphere.remove(object);
}
or
recur(sphere);
for (var i in objects) {
objects[i].geometry.remove;
objects[i].material.remove;
objects[i].geometry.dispose();
objects[i].material.dispose();
scene.remove(objects[i]);
sphere.remove(objects[i]);
}
function recur(obj) {
if (obj instanceof THREE.Mesh) {
objects.push(obj);
}
for (var i in obj.children) {
recur(obj.children[i]);
}
}
The code that add objects:
var ObjLoader = new THREE.ObjectLoader();
var ii;
var group = new THREE.Object3D();
var oldModel = scene.getObjectByName('group');
if (oldModel !== undefined) { scene.remove(oldModel); }
ObjLoader.load("path/model.json", addModelToScene);
group.name = "group";
scene.add(group);
function addModelToScene(model) {
recur(model);
for (var i in objects) {
objects[i].castShadow = true;
objects[i].receiveShadow = true;
}
model.name = "ModelName";
group.add(model)
}
So, The .json model consists of some objects (1..n);
This .json model is added into group.
With picking the side of the cube (may be material) is selectable, but not removable. but the position can be changed.
Thank you.
Change your picking logic a little, store all your objects into array when creating them:
/*var objects = [];
var group = new THREE.Object3D();
create your mesh add to array*/
objects.push(MyMesh);
/*now we have a reference all the time in objects[0] , objects[1]*/
objects[0].displose(); check syntax been a while*/
customMaterial = new THREE.ShaderMaterial(
{
uniforms: customUniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
//wireframe: true,
side: THREE.FrontSide
} );
function ground (){
var map = new THREE.PlaneGeometry(1024,1024, 128, 128);//Use planebufferGeo
map.dynamic = true;
_terrain = new THREE.Mesh( map, customMaterial );
_terrain.material.needsUpdate = true;
_terrain.geometry.dynamic = true;
_terrain.geometry.vertexColors = true;
_terrain.material.uvsNeedUpdate = true;
_terrain.geometry.normalsNeedUpdate = true;
_terrain.rotation.x = -Math.PI / 2;
objects.push(_terrain);
scene.add(_terrain );
}
So I used this in a situation where it was a picking game type, I used an array to store the mesh and i could manipulate the vertices / faces etc edit the array to make changes.
It was an easier way to compare things and check data.
function animate() {
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects);
if ( intersects.length > 0 ) {
var faceIndex = intersects[0].faceIndex;
var obj = intersects[0].object;
var geom = obj.geometry;
//etc

Textgeometry facing user when camera turns

I am creating this 3d graph that represents a network of people. The nodes in the network are the names of people and between them lines are drawn whi9ch represents a connection. The purpose of this network is that people can turn it around and look through it to see what their network looks like, however, when I turn the camera around the network, the 3d text that I creat3ed with textgeometry is stuck to the same position and unformtunately therefore unreadable for the user when they want to look at the network from a different side. I know that this question has been asked several times before and I have read most of the answers on here, but I still cannot get it to work.
The problem that I encounter mostly is that when I try to do something like this:
nodeRenderer.lookAt(camera.position);
I get a problem that the camera is not defined. The camera is added to the scene, however this happens in a different .js document. This is where the scene gets created and the camera gets added (/ngraph.three/index.js):
var THREE = require('./lib/three');
module.exports = function (graph, settings) {
var merge = require('ngraph.merge');
settings = merge(settings, {
interactive: true
});
var beforeFrameRender;
var isStable = false;
var disposed = false;
var layout = createLayout(settings);
var renderer = createRenderer(settings);
var camera = createCamera(settings);
var scene = settings.scene || new THREE.Scene();
(...)
function renderNode(nodeId) {
nodeRenderer(nodeUI[nodeId]);
}
(...)
function initNode(node) {
var ui = nodeUIBuilder(node);
if (!ui) return;
// augment it with position data:
ui.pos = layout.getNodePosition(node.id);
// and store for subsequent use:
nodeUI[node.id] = ui;
scene.add(ui);
}
(...)
function createCamera(settings) {
if (settings.camera) {
return settings.camera;
}
var container = renderer.domElement;
var camera = new THREE.PerspectiveCamera(75, container.clientWidth/container.clientHeight, 0.1, 3000);
camera.position.z = 400;
return camera;
}
And this is where the nodes get created (ngraph.three/lib/defaults.js):
var THREE = require('./three');
module.exports.createNodeUI = createNodeUI;
module.exports.createLinkUI = createLinkUI;
module.exports.nodeRenderer = nodeRenderer;
module.exports.linkRenderer = linkRenderer;
function createNodeUI(node) {
var nodeMaterial = new THREE.MeshFaceMaterial( [
new THREE.MeshPhongMaterial( { color: 0x00cccc, shading: THREE.FlatShading } ), // front
new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side
] );
var nodeGeometry = new THREE.TextGeometry( node.data, { size: 5, height: 2, curveSegments: 6, font: "helvetiker", weight: "normal", style: "normal" });
var nodeDirection = new THREE.Quaternion (THREE.Camera.Quaternion);
return new THREE.Mesh(nodeGeometry, nodeMaterial);
}
function createLinkUI(link) {
var linkGeometry = new THREE.Geometry();
linkGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
linkGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
var linkMaterial = new THREE.LineBasicMaterial({ color: 0x00cccc });
return new THREE.Line(linkGeometry, linkMaterial);
}
(...)
function nodeRenderer(node) {
node.position.x = node.pos.x;
node.position.y = node.pos.y;
node.position.z = node.pos.z;
}
(...)
I tried ui.quaternion = camera.quaternion; in /ngraph.three/index.js, but this didn't do anything, and I've tried nodeUI.lookAt(camera.position); but this gave an error: TypeError: nodeUI.lookAt is not a function.
PS: I'm using Three.js Rev. 68, Ngraph and node.js.
I can only guess since a lot of code is missing but your createNodeUI returns a THREE.Mesh which is a subclass of Object3D which has the function .lookAt(vector) to align its local z-axis to the given vector.
And you said you receive this Error : TypeError: nodeUI.lookAt is not a function that looks like you re trying to call .lookAt of the array and not of the node itself.
so try nodeUI[node.id].lookAt(camera.position)

Animating canvas with a javascript constructor

Hello stackoverflow community!
First I must say that I dont have much experience with constructors.
So. What I am trying to do, is to animate a parachutist to fly from top to bottom of the screen.
I thought I could use a constructor to set up a parachutist:
var parachute = function() {
this.height = 35;
this.width = 30;
this.speed = 50;
this.xPos = Math.round(Math.random() * (window.width - this.width));
this.animate = function() {
this.img = new Image();
this.yPos = 0;
this.img.onload = function() {
ctxPara.globalCompositeOperation = 'copy';
ctxPara.translate(0, this.yPos);
ctxPara.drawImage(this.img, this.xPos, 0);
};
this.img.src = 'para.png';
this.yPos++;
};
};
This constructor is used in a function called 'fly':
var fly = function() {
var newParachute = new parachute();
setInterval(newParachute.animate, newParachute.speed);
};
And this 'fly' function is triggered when the window loads:
window.onload = function() {
var canvasBg = document.getElementById('canvasBg');
// I splitt the Background and the parachutists in two canvas elements
// handling the problem (to erase content and draw new content) with
// the canvas animation.
var canvasPara = document.getElementById('canvasPara');
ctxPara = canvasPara.getContext('2d');
canvasPara.width = window.width;
canvasPara.height = window.height;
canvasBg.width = window.width;
canvasBg.height = window.height;
fly();
clouds(); // background is loading here
};
What you should see, is a Parachutist flying down the screen. But unfortunately you don't...
Now, after that Long text. (Iam very sorry that it is so long :-( ) My question is: Do you know what I am doing wrong? Is my constuctor correct? Is, what i am trying to do, supposed to be written like this? Any advices or suggestions for a succesfull opportunity? (I hope my english isn't that terrible I think it is :-) )
Oh i forgot to mention the error. It's a TypeMissMatchError.
That means 'this.img' is not an img element at this line:
ctxPara.drawImage(this.img, this.xPos, 0);
Now, I followed the example of markE.
Instead of showing me a parachutist. It shows me an error in this line: ctxPara.drawImage(this.img, this.xPos, this.yPos);
var fly = function () {
var newParachute = new parachute();
newParachute.img.load.call(newParachute);
setInterval(newParachute.animate.call(newParachute), newParachute.speed);
};
var parachute = function () {
this.height = 35;
this.width = 30;
this.speed = 25;
this.xPos = Math.round(Math.random() * (window.innerWidth - this.width));
this.img = new Image();
this.yPos = 0;
this.img.isLoaded = false;
this.img.load = function () {
this.img.isLoaded = true;
};
this.img.src = 'parachute.png';
this.animate = function () {
if (this.img.isLoaded) {
ctxPara.clearRect(0, 0, canvasPara.width, canvasPara.height);
ctxPara.drawImage(this.img, this.xPos, this.yPos); // ERROR: 'Unknown Error'.
this.yPos++;
console.log('animating');
}
};
};
I am stuck again. But now i don't even know the reason... Please help!?
Demo: http://jsfiddle.net/m1erickson/ym55y/
A couple of issues:
(1) To get the window width you can use:
window.innerWidth
(2) setInterval calls newParachute.animate.
setInterval(newParachute.animate, newParachute.speed);
But this inside animate the window object--not the Parachute object.
To give the correct this to animate you can use the call method like this:
var newParachute = new parachute();
setInterval(function(){newParachute.animate.call(newParachute);}, newParachute.speed);
(3) You need to deal with clearing previously drawn images or they will still show on your canvas.

Categories

Resources