I'm setting up a website that loads 3d models in ply format, and I'm using Three.js with his PLYLOADER to do that.
My problem is that every model is loading in a different position. I've managed to make them load in a perpendicular way with the ground, but some of them are looking, some are looking far away from the camera and others look like they turn around a Y axis (not their center).
This is my HTML div:
<div id="WebGL-output"></div>
This is my JS code:
<script type="text/javascript">
var lesson7 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,
init: function() { // Initialization
// create main scene
this.scene = new THREE.Scene();
this.scene.fog = new THREE.FogExp2(0xcce0ff, 0.0003);
var SCREEN_WIDTH = '800',
SCREEN_HEIGHT = '600';
// prepare camera
var VIEW_ANGLE = 60, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(0, 0, 300);
this.camera.lookAt(new THREE.Vector3(0,0,0));
// prepare renderer
this.renderer = new THREE.WebGLRenderer({ antialias:true });
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.setClearColor(this.scene.fog.color);
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;
// prepare container
this.container = document.getElementById('WebGL-output');
this.container.appendChild(this.renderer.domElement);
// events
THREEx.WindowResize(this.renderer, this.camera);
// prepare controls (OrbitControls)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.maxDistance = 20000;
// prepare clock
this.clock = new THREE.Clock();
this.scene.add( new THREE.AmbientLight(0xFFFFFF) );
// light
var dirLight = new THREE.DirectionalLight(0x606060);
dirLight.position.set(200, 200, 1000).normalize();
this.camera.add(dirLight);
this.camera.add(dirLight.target);
// load models
this.loadModels();
},
loadModels: function() {
// prepare PLY loader and load the model
var scene = new THREE.Scene();
var oPlyLoader = new THREE.PLYLoader();
oPlyLoader.load("../models/{{ $analysis->three_d_model }}", function(geometry) {
var material = new THREE.MeshPhongMaterial( { color: 0xAAAAAA, specular: 0x111111, shininess: 0 } );
var meshMaterial = material;
// if (geometry.hasColors) { // to fix when models will have colors
meshMaterial = new THREE.MeshPhongMaterial({ opacity: 1, vertexColors: THREE.VertexColors });
// }
var mesh = new THREE.Mesh(geometry, meshMaterial);
// Solution: center mesh to world origin
mesh.geometry.computeBoundingBox();
var bbox = mesh.geometry.boundingBox;
var centerVec = new THREE.Vector3();
bbox.getCenter(centerVec); // get center of bbox into centerVec
// move mesh so that center of bbox is placed to world origin
mesh.applyMatrix(new THREE.Matrix4().makeTranslation(-centerVec.x, -centerVec.y, -centerVec.z));
// proceed doing stuff
// mesh.position.set(0, -6, 0);
// mesh.scale.set(5, 5, 5);
lesson7.scene.add(mesh);
});
}
};
// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
update();
}
// Update controls and stats
function update() {
lesson7.controls.update(lesson7.clock.getDelta());
// lesson7.stats.update();
}
// Render the scene
function render() {
if (lesson7.renderer) {
lesson7.renderer.render(lesson7.scene, lesson7.camera);
}
}
// Initialize lesson on page load
function initializeLesson() {
lesson7.init();
animate();
}
if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;
</script>
I need help to make them all load with the same camera position, looking centred and in the same size.
PS: If i can have better results loading in different extension, I'm up for it.
Thanks in advance
EDIT:
I updated the code with the help of #alex. Now my models are very well centered. The only thing missing now si a fixed camera position however the model was saved
Currently see two solutions here ( if i understand correctly )
You objects are not centered correctly. The solution would be to open the model files in a 3d editor and set them all to the same size and make their center points consistent.
Once you load the model add it to an Object3D and find its center.
Docs about that here: https://threejs.org/docs/#api/en/core/Object3D
the below code this.objectAll is the Object3D. Which is put into a box and we make the controls look at the center of that box. If all model are consistent then this should work.
this.boundingBox = new THREE.Box3();
this.boundingBox.setFromObject(this.objectAll);
this.boundingBox.center(this.controls.target);
Best of luck!
I am trying to display glued cubes, imported from blender, using Three.js.
I am using latest Version of Three.js and Blender 2.78a.
I started by making my cube object;just like this:
https://www.jonathan-petitcolas.com/2015/07/27/importing-blender-modelized-mesh-in-threejs.html
But I have different/more glued cubes.
I created above 3d object in Blender, Then exported the mesh using the Three.js .js exporter.
Here is the marmelab.json file.
{
"metadata":{
"generator":"io_three",
"faces":6,
"type":"Geometry",
"version":3,
"materials":1,
"normals":6,
"vertices":24,
"uvs":1
},
"faces":[43,8,10,12,14,0,0,1,2,3,0,0,0,0,43,16,18,20,22,0,3,0,1,2,1,1,1,1,43,9,15,19,17,0,3,0,1,2,2,2,2,2,43,1,13,21,5,0,3,0,1,2,3,3,3,3,43,3,11,23,7,0,3,0,1,2,4,4,4,4,43,2,0,4,6,0,3,0,1,2,5,5,5,5],
"materials":[{
"DbgIndex":0,
"blending":"NormalBlending",
"opacity":1,
"depthWrite":true,
"visible":true,
"transparent":false,
"colorEmissive":[0,0,0],
"colorDiffuse":[0.8,0.432941,0],
"wireframe":false,
"DbgName":"01 - Default",
"specularCoef":11,
"colorSpecular":[0.18,0.18,0.18],
"DbgColor":15658734,
"depthTest":true,
"shading":"phong"
}],
"normals":[0,0,1,0,0,-1,0,1,-0,1,0,-0,0,-1,-0,-1,0,-0],
"name":"Untitled.001Geometry.1",
"vertices":[-0.307576,-0,-0.433258,-0.002776,-0,-0.433258,-0.307576,-0,-0.738058,-0.002776,-0,-0.738058,-0.307576,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.307576,0.3048,-0.738058,-0.002776,0.3048,-0.738058,-0.307576,-0,-0.433258,-0.307576,-0,-0.433258,-0.307576,-0,-0.738058,-0.307576,-0,-0.738058,-0.002776,-0,-0.738058,-0.002776,-0,-0.738058,-0.002776,-0,-0.433258,-0.002776,-0,-0.433258,-0.307576,0.3048,-0.433258,-0.307576,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.002776,0.3048,-0.738058,-0.002776,0.3048,-0.738058,-0.307576,0.3048,-0.738058,-0.307576,0.3048,-0.738058],
"uvs":[[1,0,1,1,0,1,0,0]]
}
javascript to load the mesh and display
var scene, camera, renderer;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initMesh();
initCamera();
initLights();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(0, 3.5, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
}
function initLights() {
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
}
var mesh = null;
function initMesh() {
var loader = new THREE.JSONLoader();
loader.load('./marmelab.json', function(geometry, materials) {
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.75;
mesh.translation = THREE.GeometryUtils.center(geometry);
scene.add(mesh);
});
}
function rotateMesh() {
if (!mesh) {
return;
}
mesh.rotation.x -= SPEED * 2;
mesh.rotation.y -= SPEED;
mesh.rotation.z -= SPEED * 3;
}
function render() {
requestAnimationFrame(render);
rotateMesh();
renderer.render(scene, camera);
}
init();
render();
However in Browser the shape is rtained ; but the color is white.
What am I missing here?
Thank you so much in advance.
EDIT
I updated the Plunker with the latest version of three.js and it now works with your unedited json above - http://plnkr.co/edit/RwkgzrvfqzYlWtShhpjt?p=preview
Also successfully tested with my own, more complicated, model - http://plnkr.co/edit/SWXv1GyKIGryORiNYlgx?p=preview
The problem is caused by using an older version of three.js. I originally tested using the version provided by the website you linked to. I suspect you might have done the same given the result.
Somewhere around revision 70 they changed the exporter to no longer export the colorAmbient property, and that older version of Three.js required it.
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.
Have started working my way through a book called WebGL: Up and Running, which uses my preferred solution of THREE.js for rendering 3D objects in the browser. I'm trying to emulate the part of the book where he introduces shaders to make a dynamic, lit sun, but all I get when I try is a black sphere. Making the shader something more simple (like a Lampert shader) works fine, so it doesn't seem to be lighting - just the way the shader is implemented.
So what am I doing wrong? My javascript code to set up the scene is
var renderer, scene, camera, sunMesh, clock, uniforms;
$(function(){
//set scene size
var WIDTH = 1200,
HEIGHT = 800;
//set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
// get the DOM element to attach to
// - assume we've got jQuery to hand
var $container = $('#container');
// create a WebGL renderer, camera
// and a scene
renderer = new THREE.WebGLRenderer();
camera = new THREE.PerspectiveCamera( VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
scene = new THREE.Scene();
// the camera starts at 0,0,0 so pull it back
camera.position.z = 300;
// start the renderer
renderer.setSize(WIDTH, HEIGHT);
// attach the render-supplied DOM element
$container.append(renderer.domElement);
// Create a group to hold our sun mesh and light
var sunGroup = new THREE.Object3D();
var SUNMAP = "./images/lavatile.jpg";
var NOISEMAP = "./images/cloud.png";
uniforms = {
time: { type: "f", value: 1.0 },
texture1: { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( NOISEMAP ) },
texture2: { type: "t", value: 1, texture: THREE.ImageUtils.loadTexture( SUNMAP ) }
};
uniforms.texture1.texture.wrapS = uniforms.texture1.texture.wrapT = THREE.Repeat;
uniforms.texture2.texture.wrapS = uniforms.texture2.texture.wrapT = THREE.Repeat;
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
//USING THIS MATERIAL WORKS, SO ITS DEFINITELY THE SHADER!
//var material = new THREE.MeshLambertMaterial(
//{
// color: 0x5B92E5
//});
// Create our sun mesh
var geometry = new THREE.SphereGeometry(50, 64, 64);
sunMesh = new THREE.Mesh( geometry, material );
// Tuck away the uniforms so that we can animate them over time
// Set up a clock to drive the animation
clock = new THREE.Clock();
// Create a point light to show off our solar system
var light = new THREE.PointLight( 0xffffff );
light.position.set(0,0,100);
sunGroup.add(sunMesh);
sunGroup.add(light);
scene.add(sunGroup);
// and the camera
scene.add(camera);
renderer.render(scene, camera);
// setup an interval to loop the game loop
setInterval(gameloop, 50);
});
function gameloop() {
var delta = clock.getDelta();
uniforms.time.value += delta;
renderer.render(scene, camera);
}
I'll save the full HTML (listed at http://pastebin.com/PGLXkzkA) but that contains my shaders, which I've lifted from the source of the WebGL book. It may be that these are wrong, but I'm suspecting it is the way I'm using the shaders. From what I understand, I should see a sun with the textures changing based on a noise occlusion map, with some movement in the vertex position to make the sun pulse. As I say, I can see a sphere, but it is unlit and black.
Where have I gone wrong?
What a shame. This book appears to be already out-of-date.
Three.js is in alpha, and is changing rapidly. Learn from the three.js examples, instead. They are always current.
Check out the Migration Wiki for help in upgrading to the current version.
Some obvious errors in your code:
texture1: { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( NOISEMAP ) },
should be
texture1: { type: "t", value: THREE.ImageUtils.loadTexture( NOISEMAP ) },
THREE.Repeat should be THREE.RepeatWrapping, and uniforms.texture1.texture should be uniforms.texture1.value.
Sorry, you may have other issues, but I can only help you with the current version of three.js.
three.js r.54