cannonjs with Three.js associating a body with mesh - javascript

I am using the following code to generate two spheres in cannonjs with Three.js as rendering module.
var world, mass, body, shape, timeStep=1/60,
camera, scene, renderer, geometry, material, mesh;
initThree();
initCannon();
animate();
function initCannon() {
world = new CANNON.World();
world.gravity.set(0,0,0);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
geometry = new THREE.SphereGeometry( 3);
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } );
mesh = new THREE.Mesh( geometry, material );
body = new CANNON.Body({mass:0.2});
body.angularVelocity.set(0,5,0);
body.angularDamping = 0.9;
world.add(body);
scene.add(mesh);
geometry = new THREE.SphereGeometry(2);
material = new THREE.MeshBasicMaterial( { color: 0x00ff00, wireframe: true } );
mesh = new THREE.Mesh(geometry,material);
body = new CANNON.Body({mass: 1});
body.angularVelocity.set(0,10,0);
body.angularDamping = 0.5;
world.add(body);
scene.add(mesh);
}
function initThree() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.z = 5;
scene.add( camera );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
updatePhysics();
render();
}
function updatePhysics() {
// Step the physics world
world.step(timeStep);
// Copy coordinates from Cannon.js to Three.js
mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);
}
function render() {
renderer.render( scene, camera );
}
As a result, I do get two spheres on display, but only the second body declared in initCannon() animates. The cannonjs body is somehow not associated with the Three.js mesh. I tried "body.mesh=mesh", but it doesn't help.
The declaration for the mesh was originally done in initThree() but I don't figure if that matters anyway.
What am I doing wrong? Please help. Ask for any more details if needed.

You're overwriting variables. In the beginning you declare a variable body, and then you say body = new CANNON.Body... and after that you use the same sentence for the second sphere, so the first one gets overwritten. You should have a variable body2, a variable mesh2 to avoid this. This approach would work ok if you only need two bodies, but if you're planning to have more you need a different approach: Arrays. In an Array you can store all your bodies and then loop through it and handle each one singularly. Example:
Here we create 10 bodies:
var bodies= []; //create the array
for (var i = 0; i < 10; i++) {
bodies.push({ //insert into the array an object that contains the information for each body/mesh
body: new CANNON.Body({mass:0.2}),
mesh = new THREE.Mesh(geometry,material)
})
};
And then we use them:
for (var i=0, l=bodies.length; i < l; i++) { //we loop through each body
var body = bodies[i]; //this will be the current body
//MOVE THE BODY AND UPDATE THE MESH HERE
}
This would be a better approach. It's more maintainable and you can have as many bodies as the browser can handle. If you only need two, your fastest fix would to add a second variable for the second body and handle it separately.
EDIT:
Watch this: https://www.youtube.com/watch?v=tW6pmzd34Hc he stores the circles into an array as well. I hope it makes it a little bit clearer.

Related

Texture not loading even after running file through local server

Texture loading as black. I tried running the.html file through a local server (Servez) but that does not seem to be the issue. I am using visual studio code. The code seems to work fine, the console does not show any errors.
This is my code:
window.onload = function() {
//Define the WebGL renderer:
var renderer = new THREE.WebGLRenderer(); //specify the we will use WebGL
renderer.setSize( 800, 600 ); //scene size (Width, Height)
document.body.appendChild( renderer.domElement );
//Initialize (create) the scene:
var scene = new THREE.Scene();
//Define the camera:
var camera = new THREE.PerspectiveCamera(
35, // Field of view
800 / 600, // Aspect ratio
0.1, // Near plane
10000 // Far plane
);
camera.position.x= 0; //default value anyway
camera.position.y= 0; //default value anyway
camera.position.z = 300;
camera.lookAt( scene.position );
//Define the objects for the scene:
var skygeo = new THREE.SphereGeometry(150, 40, 20);
var skytexture = new THREE.TextureLoader().load("sky.jpg");
var skymat = new THREE.MeshBasicMaterial({map:skytexture});
skymat.side = THREE.Backside;
var skydome = new THREE.Mesh(skygeo, skymat);
scene.add(skydome);
// create a point light:
var pointLight = new THREE.PointLight(0xFFFFFF);
// set the light position:
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 100;
// add the light to the scene:
scene.add(pointLight);
// it renders the scene:
renderer.render( scene, camera );
};
I found here(https://discourse.threejs.org/t/texture-loading-not-working-on-server/14910/10) that it may be because the image files were corrupted. I also found somewhere else that it may be because of browser extensions like AdBlock, but removing extensions did not fix it.
What is the reason for this?
You're calling renderer.render( scene, camera ); only once, immediately after initializing your texture loader. Texture loading is asynchronous, it takes some time, so you're rendering the scene before the texture has loaded. You need to call render after your texture has finished loading. To do this you can use the onLoad callback, or you could see the basic docs on creating a scene for an example on how to render continuously:
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();

Trying to access function arguments from inner function

I'm using three.js and a script similar to OrbitControls as my controller. In my main.js file I have a THREE.Group() that is being passed to the controller as an argument. From there, I'm attempting to rotate the entire group.
Problem 1: once the group is passed to the controller, I can no longer access its properties without making a copy
Problem 2: a copy does not contain the entire THREE.Group(), rather only the first child
I've been working on this for hours now and I've tried about 50 different things including anything relevant on stackoverflow. I'm completely out of ideas on how to combat this problem.
main.js
let container;
let camera;
let controls;
let game;
let renderer;
let scene;
function init() {
container = document.querySelector('#scene-container');
scene = new THREE.Scene();
const fov = 35;
const aspect = container.clientWidth / container.clientHeight;
const near = 0.1;
const far = 100;
camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
//***** This is the important line ******
controls = new THREE.ObjectControls(camera, container, game);
game = new THREE.Group();
scene.add(game);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial();
var mesh1 = new THREE.Mesh(geometry, material);
game.add(mesh1);
var mesh2 = new THREE.Mesh(geometry, material);
mesh2.position.set(0,1,0);
game.add(mesh2);
renderer = new THREE.WebGLRenderer();
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
}
init();
ObjectControls.js
THREE.ObjectControls = function (camera, domElement, objectToMove) {
mesh = objectToMove;
domElement.addEventListener('mousemove', mouseMove, false);
function mouseMove(e) {
//** objectToMove is undefined :( **
mesh.rotation.y += 3;
}
};
Expected result is that the entire THREE.Group() game will be rotated, but the result that I get is that only the first child of game is rotated, in this case mesh1.
controls = new THREE.ObjectControls(camera, container, game);
game = new THREE.Group();
There is an error in your code since you pass the undefined variable game to the ctor of ObjectControls. If you assign a new object to game one line later, ObjectControls does not have a reference to this variable.
The idea is to assign the group object to game first and then create ObjectControls. You essentially switch both lines.
three.js R105

Change color of three.js mesh using gui.dat

I have a three.js mesh loaded from an STL file:
var loader = new THREE.STLLoader();
var materialmodel = new THREE.MeshPhongMaterial(
{
color: 0xFF0000,
specular: 0x222222,
shininess: 75
}
);
function model()
{
loader.load( 'models/stl/binary/model.stl', function ( geometry ) {
var meshMaterial = materialmodel;
var model = new THREE.Mesh( geometry, meshMaterial );
model.scale.set( 0.02, 0.02, 0.02 );
model.castShadow = true;
model.receiveShadow = true;
model.geometry.center();
scene.add(model);
render();
} );
}
model();
When I call the model function in my page, the model renders as expected.
I want to use dat.gui to as a lightweight interface for on the fly changes.
My first experiment is changing the color of the model.
The code I'm using is this:
var params = {
modelcolor: 0xff0000, //RED
};
var gui = new dat.GUI();
var folder = gui.addFolder( 'Model Colour' );
folder.addColor( params, 'modelcolor' )
.name('Model Color')
.listen()
.onChange( function() { materialmodel.MeshPhongMaterial.color.set( params.modelcolor); } );
folder.open();
DAT.GUIs color picker appears fine, and I can select a color from the picker and the new hex value will display.
However, the model/mesh itself doesn't update with the newly selected colour.
I'm wondering if it's something to do with how I'm changing the color materialmodel.MeshPhongMaterial.color.set( params.modelcolor); (I've tried different ways of doing this with no luck).
I've seen a post here (one of the answers) where they're doing this using model.material.color.set(params.color) in their example. My owen material properties are defined in a variable using a THREE.MeshPhongMaterial.....
Assuming this is where I've gone wrong, how can I change the color dynamically of a nested prroperty buried in a variable like this?
I didn't get why did you use .listen(), possibly there's a certain reason.
In .onUpdate function you're using materialmodel, which is a material itself, and then you're setting .MeshPhongMaterial property that doesn't exist. Looks like you simply overlooked it.
Here is a working example:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var materialmodel = new THREE.MeshPhongMaterial({
color: 0xFF0000,
specular: 0x222222,
shininess: 75
});
var geometrymodel = new THREE.SphereBufferGeometry(5, 32, 16);
var model = new THREE.Mesh(geometrymodel, materialmodel);
scene.add(model);
var params = {
modelcolor: "#ff0000"
};
var gui = new dat.GUI();
var folder = gui.addFolder('Model Colour');
folder.addColor(params, 'modelcolor')
.name('Model Color')
.onChange(function() {
materialmodel.color.set(params.modelcolor);
});
folder.open();
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.min.js"></script>

Meshes are in Scene however nothing but the renderer's clear color appears

So I'm working with Three.js and jQuery to create a small visual application. At the moment all I want is for all the meshes I have, to appear on screen.
The Problem: None of the meshes appear on screen whatsoever.
Exceptions: The renderer's clear color appears (0x00bfff) and console.log(scene) confirms that all the meshes are in the scene.
Attempts to Fix: Use THREE.Projector, THREE.Raycaster, change camera positioning, and many more attempts.
I'm still very new to Three.js and programming in general so please be very critical of my work. Anything helps! Thanks!
WORLD.JS
$(document).ready(function() {
initialize();
animate();
});
var initialize = function() {
clock = new THREE.Clock(); // timer used to calculate time between rendering frames
scene = new THREE.Scene(); // list of objects that are to be "read" (rendered)
camera = new THREE.PerspectiveCamera(35, // FOV
window.innerWidth / window.innerHeight, // Aspect Ratio
.1, // Near
10000); // Far
camera.position.set( 25, 25, 125 );
camera.lookAt( scene.position );
setupEnvironment();
setupAI();
renderer = new THREE.WebGLRenderer(); // renderer will draw as WebGL rather than HTML5 Canvas
renderer.setSize( window.innerWidth, window.innerHeight ); // size of the canvas that renderer will draw on
renderer.setClearColor( 0x00bfff, 1 );
document.body.appendChild( renderer.domElement ); // adds the canvas to the document
};
var animate = function() { // animates the scene with frames
requestAnimationFrame(animate); // works recursively
render(); // update and display
}
var render = function() {
var delta = clock.getDelta() // gets the seconds passed since the last call to this method
// AI collision needed
// AI update needed
renderer.render( scene, camera ) // repaint
}
var setupEnvironment = function() {
ground = new BoxMesh( 10, 0.1, 10, 0x6C4319, 1 );
positionThenAdd( ground, [[ 0, 0 ]] );
light1 = new THREE.PointLight( 0xFFFFFF, .5 );
light1.position.set( 10, 10, 10 );
scene.add( light1 );
light2 = new THREE.PointLight( 0xFFFFFF, 1 );
light2.position.set( -10, -10, 10 );
scene.add( light2 );
};
var setupAI = function() {
sheep = new BoxMesh( 1, 1, 1, 0xFFFFFF, 3 );
positionThenAdd( sheep, [[ 0, 0 ],
[ 4.5, 0 ],
[ 9.5, 0 ]]);
sheepHerder = new BoxMesh( 1, 1, 1, 0x996633, 1 );
positionThenAdd( sheepHerder, [[ 4.5, 7.5 ]] );
};
function BoxMesh( width, height, depth, hexColor, amount ) { // creates one or more box meshes
this.width = width;
this.height = height;
this.depth = depth;
this.hexColor = hexColor;
this.amount = amount; // amount of box meshes to be made
boxSize = new THREE.BoxGeometry( width, height, depth );
boxMaterial = new THREE.MeshLambertMaterial( { color: hexColor } );
var all = []; // will contain all of the box meshes
for(var n = 1; n <= amount; n++) { // adds a new box mesh to the end of the all array
all.push(new THREE.Mesh( boxSize, boxMaterial )); // uses the attributes given by the BoxMesh constructor's parameters
}
return all; // returns all of the created box meshes as an array;
}
var positionThenAdd = function( varMesh, posArrXByZ ) { // positions an object and then adds it to the scene
this.varMesh = varMesh; // variable name of the mesh(es) array
this.posArrXByZ = posArrXByZ; // posArrXByZ stands for "array of positions in the format of X-by-Z"
// posArrXByZ is a 2 dimensional array where the first dimension is for the specific mesh to be positioned...
// and the second dimension is the positional coordinates.
// posArrXByZ = [ [x0,z0], [x1,z1], ...[xn,zn] ]
for(var mesh = 0; mesh < varMesh.length; mesh++) { // mesh accesses the varMesh array
varMesh[mesh].position.set( varMesh[mesh].geometry.parameters.width/2 + posArrXByZ[mesh][0], // the x coordinate, varMesh[mesh].width/2 makes the x coordinate act upon the closest side
varMesh[mesh].geometry.parameters.height/2 + ground.height, // the y coordinate, which is pre-set to rest on top of the ground
varMesh[mesh].geometry.parameters.depth/2 + posArrXByZ[mesh][1] ); // the z coordinate, varMesh[mesh].height/2 makes the y coordinate act upon the closest side
scene.add( varMesh[mesh] ); // adds the specific mesh that was just positioned
}
};
HTML FILE
<!DOCTYPE html>
<html>
<head>
<title>Taro's World</title>
<style>
body {
margin: 0;
padding: 0;
border: 0;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="mrdoob-three.js-d6384d2/build/Three.js"></script>
<script src="mrdoob-three.js-d6384d2/examples/js/renderers/Projector.js"></script>
<script src="world.js"></script>
</head>
<body></body>
</html>
Two things are broken in your script :
in your positionThenAdd function, at position.set(...), you wrote somewhere ground.height. ground is an array, you probably meant varMesh[mesh].geometry.parameters.height.
your console should print that positionThenAdd is not a function. While you declared previous functions writing function myFunction(){....} you declared this one that way : var positionThenAdd = function () { ... };. The difference in javascript is that, as any variable, positionThenAdd will then be reachable in the script order. Since you write it at the end, nothing can reach it. You just have to modify its declaration to function positionThenAdd(){...}. See var functionName = function() {} vs function functionName() {}
Your scene : http://jsfiddle.net/ba8vvkyg/1/

Three.js update scale object-size via view

i'm new in three.js and have a problem by scaling a object. I already looked at the documentation (https://github.com/mrdoob/three.js/wiki/Updates), but i don't really understand it at all.
My Problem: By changing a HTML-select-element the CubeGeometry should be scaled in the x-direction. That is already working BUT the "old" Cube do not disappear. So i have 2 Cubes. But i want only one Cube with the current size. I hope you can understand my problem ;-)
So here is my Code from the View:
$(document).on('change',".configurator > form select",function(event){
// update 3D-object - is that a nice way???
$.getScript("/javascripts/3D-animation.js.erb", function(){
// here comes the update
OBJECT3D.updateObject();
});
})
And here is my 3D-animation.js.erb:
var drawing_area;
var renderer;
var camera;
var obj;
var scene;
var OBJECT3D = {};
$(function() {
// get drawing_area
drawing_area = document.getElementById("canvas_wrapper");
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(drawing_area.clientWidth, drawing_area.clientHeight);
renderer.setClearColor( 0xffffff, 1);
// add renderer to drawing_area
drawing_area.appendChild(renderer.domElement);
// initialize camera
camera = new THREE.PerspectiveCamera(45, drawing_area.clientWidth/drawing_area.clientHeight, 1, 100000);
camera.position.z = 1000;
camera.position.y = 100;
camera.position.x = 300;//-0.78;
// create texture
var texture = THREE.ImageUtils.loadTexture( "/images/materials/texture_1.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
// create object
var obj_form = new THREE.CubeGeometry(250,250,250);
var obj_material = new THREE.MeshLambertMaterial( { map: texture,ambient: 0x999999 } );
OBJECT3D.obj = new THREE.Mesh(obj_form, obj_material);
// so what do i need here?
OBJECT3D.obj.geometry.dynamic = true;
// OBJECT3D.obj.geometry.__dirtyVertices = true;
// OBJECT3D.obj.geometry.__dirtyNormals = true;
// OBJECT3D.obj.geometry.verticesNeedUpdate = true;
// create scene
scene = new THREE.Scene();
scene.add(camera);
scene.add(OBJECT3D.obj);
// create lights
pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 400;
pointLight.position.y = 200;
pointLight.position.z = 1300;
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xffffff);
scene.add( ambientLight );
requestAnimationFrame(render);
function render(){
requestAnimationFrame(render);
OBJECT3D.obj.rotation.y += 0.005;
OBJECT3D.obj.rotation.z += 0.005;
renderer.render(scene, camera);
};
// update object
OBJECT3D.updateObject = function () {
console.log("in update");
OBJECT3D.obj.scale.x = 2.5; // SCALE
OBJECT3D.obj.geometry.needsUpdate = true;
//render();
}
});
Sorry, if the Code is not the best one, but i'm really new in this stuff! :) hope you can help me!
Thanks!
I think it is because you are using $.getScript method. This will load your script each time again and create a new instance of OBJECT3D.
I propose that you make sure that your code in 3D-animation.js.erb will be included and called only once upon the load of a page (include it just like any other regular javascript file) and then call your update directly like this:
$(document).on('change',".configurator > form select", function(event) {
OBJECT3D.updateObject();
});
also, I believe you can drop following lines of code:
OBJECT3D.obj.geometry.needsUpdate = true;
and
OBJECT3D.obj.geometry.dynamic = true;

Categories