I am trying to use Ray intersect to find out if 2 collada object collide.
But so far no success :(
my code http://jsfiddle.net/XqQzF/
object.updateMatrix();
// ray
var f_vector = new THREE.Vector3( 0, 0, -1 );
var b_vector = new THREE.Vector3( 0, 0, 1 );
var l_vector = new THREE.Vector3( -1, 0, 0 );
var r_vector = new THREE.Vector3( 1, 0, 0 );
everytime i use something its removed from the newest three.js revision.
Can you help me on the way ?
I recently update my three.js examples collection to be compatible with the latest version (v56 at time of writing), and this includes an example of collision detection. Check out http://stemkoski.github.com/Three.js/Collision-Detection.html (see http://stemkoski.github.com/Three.js/#collision-detection for instructions).
The main difference in your case will be choosing a central point of your model as an origin point for the rays, and choosing a set of vertices of your model to use as endpoints for the rays, as rays are what is used for collision detection.
Hope this helps!
#Lee Stemkoski answer is ok, when we doesn't use collada data.
First what I know is that we need min. 2 Mesh objects. So I took all Mesh objects from collada object. I wrote two scripts - first tried colliding 2 collada objects. Second tried to collide collada object with wall(simple Mesh). In both cases script it doesn't recognize colliding š. So how i should write it?
At last I tried to detect mouse on collada object. It's not about this topic but it's similar problem so I write it here.
Dae file was generated by SketchUp.
Code for two collada objects:
var container;
var meshs = [];
var camera, scene, renderer, raycaster;
var controls;
var mouse = new THREE.Vector2();
var dae, dae2;
var collidableMeshList = [];
var clock = new THREE.Clock();
var keyboard = new THREEx.KeyboardState();
$(document).ready(function() {
init();
animate();
});
function init() {
container = document.createElement('div');
$('.container').append(container);
camera = new THREE.PerspectiveCamera(45, 500 / 500, 1, 1000);
group = new THREE.Group();
scene = new THREE.Scene();
scene.add(camera);
camera.position.set(10 * 10, 12 * 10, 16 * 10);
camera.lookAt(scene.position);
// Lights
group.add(new THREE.AmbientLight(0x777777));
var light = new THREE.DirectionalLight(0xdfebff, 1.25);
light.position.set(300, 400, 50);
light.position.multiplyScalar(1.3);
light.castShadow = true;
group.add(light);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xcccccc, 1);
renderer.setSize(500, 500);
container.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('./sample.dae', loadDae);
function loadDae(collada) {
dae = collada.scene;
dae.scale.x = 0.5;
dae.scale.y = 0.3;
dae.scale.z = 0.3;
dae.updateMatrix();
group.add(dae);
}
var wallGeometry = new THREE.CubeGeometry(10, 10, 20, 1, 1, 1);
var wallMaterial = new THREE.MeshBasicMaterial({
color: 0x8888ff
});
var wireMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: true
});
loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('./sample.dae', loadDae2);
function loadDae2(collada) {
dae2 = collada.scene;
dae2.scale.x = 0.5;
dae2.scale.y = 0.3;
dae2.scale.z = 0.3;
dae2.updateMatrix();
dae2.position.set(-40, 0, 0);
dae2.traverse(function(child) {
if (child instanceof THREE.Mesh) {
collidableMeshList.push(child);
}
});
group.add(dae2);
}
group.translateX(-20);
scene.add(group);
raycaster = new THREE.Raycaster();
window.addEventListener('resize', onWindowResize, false);
window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onWindowResize() {
camera.aspect = 500 / 500;
camera.updateProjectionMatrix();
renderer.setSize(500, 500);
}
function update() {
var delta = clock.getDelta();
var moveDistance = 200 * delta;
var rotateAngle = Math.PI / 2 * delta;
if (keyboard.pressed("A"))
dae.rotation.y += rotateAngle;
if (keyboard.pressed("D"))
dae.rotation.y -= rotateAngle;
if (keyboard.pressed("left"))
dae.translateX(-moveDistance);
if (keyboard.pressed("right"))
dae.translateX(moveDistance);
if (keyboard.pressed("up"))
dae.translateZ(-moveDistance);
if (keyboard.pressed("down"))
dae.translateZ(moveDistance);
meshs = [];
if (typeof dae !== 'undefined') {
dae.traverse(function(child) {
if (child instanceof THREE.Mesh) {
meshs.push(child);
}
});
$.each(meshs, function(number_mesh, mesh) {
var originPoint = mesh.position.clone();
for (var vertexIndex = 0; vertexIndex < mesh.geometry.vertices.length; vertexIndex++) {
var localVertex = mesh.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4(mesh.matrix);
var directionVector = globalVertex.sub(mesh.position);
var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize());
var collisionResults = ray.intersectObjects(collidableMeshList);
if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) {
console.log(" Hit ");
}
}
});
}
controls.update();
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function render() {
renderer.render(scene, camera);
}
Code for collada + wall(Mesh):
var container;
var meshs = [];
var camera, scene, renderer, raycaster;
var controls;
var mouse = new THREE.Vector2();
var dae;
var collidableMeshList = [];
var clock = new THREE.Clock();
var keyboard = new THREEx.KeyboardState();
$(document).ready(function() {
init();
animate();
});
function init() {
container = document.createElement('div');
$('.container').append(container);
camera = new THREE.PerspectiveCamera(45, 500 / 500, 1, 1000);
group = new THREE.Group();
scene = new THREE.Scene();
scene.add(camera);
camera.position.set(10 * 10, 12 * 10, 16 * 10);
camera.lookAt(scene.position);
// Lights
group.add(new THREE.AmbientLight(0x777777));
var light = new THREE.DirectionalLight(0xdfebff, 1.25);
light.position.set(300, 400, 50);
light.position.multiplyScalar(1.3);
light.castShadow = true;
group.add(light);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xcccccc, 1);
renderer.setSize(500, 500);
container.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('./sample.dae', loadDae);
function loadDae(collada) {
dae = collada.scene;
dae.scale.x = 0.5;
dae.scale.y = 0.3;
dae.scale.z = 0.3;
dae.updateMatrix();
group.add(dae);
}
var wallGeometry = new THREE.CubeGeometry(10, 10, 20, 1, 1, 1);
var wallMaterial = new THREE.MeshBasicMaterial({
color: 0x8888ff
});
var wireMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: true
});
var wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(-40, 5, -10);
group.add(wall);
collidableMeshList.push(wall);
var wall = new THREE.Mesh(wallGeometry, wireMaterial);
wall.position.set(-40, 5, -10);
group.add(wall);
group.translateX(-20);
scene.add(group);
raycaster = new THREE.Raycaster();
window.addEventListener('resize', onWindowResize, false);
window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onWindowResize() {
camera.aspect = 500 / 500;
camera.updateProjectionMatrix();
renderer.setSize(500, 500);
}
function update() {
var delta = clock.getDelta();
var moveDistance = 200 * delta;
var rotateAngle = Math.PI / 2 * delta;
if (keyboard.pressed("A"))
dae.rotation.y += rotateAngle;
if (keyboard.pressed("D"))
dae.rotation.y -= rotateAngle;
if (keyboard.pressed("left"))
dae.translateX(-moveDistance);
if (keyboard.pressed("right"))
dae.translateX(moveDistance);
if (keyboard.pressed("up"))
dae.translateZ(-moveDistance);
if (keyboard.pressed("down"))
dae.translateZ(moveDistance);
meshs = [];
if (typeof dae !== 'undefined') {
dae.traverse(function(child) {
if (child instanceof THREE.Mesh) {
meshs.push(child);
}
});
$.each(meshs, function(number_mesh, mesh) {
var originPoint = mesh.position.clone();
for (var vertexIndex = 0; vertexIndex < mesh.geometry.vertices.length; vertexIndex++) {
var localVertex = mesh.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4(mesh.matrix);
var directionVector = globalVertex.sub(mesh.position);
var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize());
var collisionResults = ray.intersectObjects(collidableMeshList);
if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) {
console.log(" Hit ");
}
}
});
}
controls.update();
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function render() {
renderer.render(scene, camera);
}
Detect mouse on collada object. Short code version:
var meshs = [],
raycaster,
mouse = new THREE.Vector2();
...
function init() {
...
function loadDae(collada) {
dae = collada.scene;
dae.scale.x = 0.5;
dae.scale.y = 0.3;
dae.scale.z = 0.3;
dae.updateMatrix();
group.add(dae);
dae.traverse(function(child) {
if (child instanceof THREE.Mesh) {
meshs.push(child);
}
});
}
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('./sample.dae', loadDae);
raycaster = new THREE.Raycaster();
window.addEventListener('mousemove', onDocumentMouseMove, false);
...
}
...
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function render() {
raycaster.setFromCamera(mouse, camera);
if (group.children.length === 5) {
var intersects = raycaster.intersectObjects(meshs);
if (intersects.length > 0) {
console.log(" Hit ");
}
}
renderer.render(scene, camera);
}
...
General - yes, it works, but throw errors with some $meshs š.
TypeError: undefined is not an object (evaluating 'O')
three.min.js:8304
cthree.min.js:3544
intersectObjectsthree.min.js:3609:144
Why and how to recognize which mesh is correct for intersectObjects and which not?
Related
Structure of my project 66% threejs model, 44% html (side control) using Bootstrap.
Iām trying to make mouse picker, when pointing at an object so that it is shown on which object it is pointed. As I understand it, he sees the coordination of the mouse badly.
Please help me figure out and set up the correct coordination mouse with Canvas.
Project Structure Screenshot:
Code:
export class CustomizerComponent implements OnInit {
public textureSrc: string;
public textureList = [
{ id: 1, name: 'Wood 2', src: '/assets/textures/wood2.jpg' },
{ id: 2, name: 'Wood 1', src: '/assets/textures/wood1.jpg' },
];
public selectedTexture(e): void {
let find = this.textureList.find((x) => x?.src === e.target.value);
// console.log(find?.src);
}
ngOnInit(): void {
let scene, kitchen, camera, renderer, canvas, controls, mouse, raycaster, loader, BACKGROUND_COLOR = 0xffffff;
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(BACKGROUND_COLOR);
// Renderer with canvas
canvas = document.getElementById('canvas');
renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(canvas.width * 3.5, canvas.height * 5);
console.log(canvas.clientWidth);
renderer.shadowMap.enabled = true;
// Set Camera
camera = new THREE.PerspectiveCamera(
75,
canvas.clientWidth / canvas.clientHeight,
0.1,
1000
);
camera.position.z = 5;
camera.position.x = 5;
camera.position.y = 1;
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
// Controls with options
controls = new OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 3;
controls.enableDamping = true;
controls.enablePan = false;
controls.target.set(5, 1, 0);
controls.maxAzimuthAngle = 0.8;
controls.minAzimuthAngle = -0.8;
controls.enableZoom = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false;
controls.autoRotateSpeed = 0.2; // 30
loader = new GLTFLoader();
loader.load(
'assets/3d_models/kitch.glb',
(gltf) => {
kitchen = gltf.scene;
scene.add(kitchen);
console.log(kitchen);
},
undefined,
(err) => {
console.log(err);
}
);
function hoverPieces() {
const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
// console.log(intersects);
for (let i = 0; i < intersects.length; i++) {
intersects[i].object.material = material;
}
console.log(intersects);
}
// Set Lights
setLights(scene);
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
hoverPieces();
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
}
animate();
function onMouseMove(event) {
// calculate pointer position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ((event.clientX - 250) / canvas.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / canvas.clientHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove);
}
}
function setLights(scene) {
// Add lights
var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.61);
hemiLight.name = 'hemiLight';
hemiLight.position.set(0, 50, 0);
// Add hemisphere light to scene
scene.add(hemiLight);
var dirLight = new THREE.DirectionalLight(0xffffff, 0.54);
dirLight.name = 'dirLight';
dirLight.position.set(-8, 12, 8);
// dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene
scene.add(dirLight);
var light = new THREE.DirectionalLight(0xffffff, 3);
light.name = 'light';
light.position.set(10, 10, 10);
scene.add(light);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
var width = canvas.clientWidth;
var height = canvas.clientHeight;
var canvasPixelWidth = canvas.clientWidth / window.devicePixelRatio;
var canvasPixelHeight = canvas.clientHeight / window.devicePixelRatio;
const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
Gist link :
https://gist.github.com/artur33s/e15abda28707231863ded08ccfe0887d
I suggest you use pointermove instead of mousemove and also use getBoundingClientRect() in your event listener:
function onPointerMove(event) {
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
}
window.addEventListener('pointermove', onPointerMove);
How to make the plane draggable in X , Y direction. Here I'm trying to work on box clipping where I draw a plane on X and Y direction. But I have no idea how to make it draggable to the particular direction. Can anyone help me put with the issue.
I want to make the plane to be draggable only in the own direction on mouse event
import * as THREE from '../build/three.module.js';
import Stats from './jsm/libs/stats.module.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { DragControls } from './jsm/controls/DragControls.js';
var camera, scene, renderer, startTime, object, stats;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 0.0001, 100000);
camera.position.set(0, 1.3, 8);
scene = new THREE.Scene();
// Lights
scene.add(new THREE.AmbientLight(0x505050));
scene.background = new THREE.Color('white')
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.angle = Math.PI / 5;
spotLight.penumbra = 0.2;
spotLight.position.set(2, 3, 3);
spotLight.castShadow = true;
spotLight.shadow.camera.near = 3;
spotLight.shadow.camera.far = 10;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
scene.add(spotLight);
var dirLight = new THREE.DirectionalLight(0x55505a, 1);
dirLight.position.set(0, 3, 0);
dirLight.castShadow = true;
dirLight.shadow.camera.near = 1;
dirLight.shadow.camera.far = 10;
dirLight.shadow.camera.right = 1;
dirLight.shadow.camera.left = - 1;
dirLight.shadow.camera.top = 1;
dirLight.shadow.camera.bottom = - 1;
dirLight.shadow.mapSize.width = 1024;
dirLight.shadow.mapSize.height = 1024;
scene.add(dirLight);
// ***** Clipping planes: *****
var localPlane = new THREE.Plane(new THREE.Vector3(0, - 1, 0), 1.5);
var helper1 = new THREE.PlaneHelper(localPlane, 3, 0xffff00);
scene.add(helper1);
var globalPlane = new THREE.Plane(new THREE.Vector3(- 1, 0, 0), 1);
var helper = new THREE.PlaneHelper(globalPlane, 3, 0xffff00);
scene.add(helper); // Geometry
var material = new THREE.MeshPhongMaterial({
color: 0x80ee10,
shininess: 100,
side: THREE.DoubleSide,
// ***** Clipping setup (material): *****
clippingPlanes: [localPlane],
clipShadows: true
});
var geometry = new THREE.TorusKnotBufferGeometry(0.4, 0.08, 95, 20);
object = new THREE.Mesh(geometry, material);
object.castShadow = true;
scene.add(object);
var ground = new THREE.Mesh(
new THREE.PlaneBufferGeometry(9, 9, 1, 1),
new THREE.MeshPhongMaterial({ color: 0xa0adaf, shininess: 150 })
);
ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Z
ground.receiveShadow = true;
// scene.add( ground );
// Stats
stats = new Stats();
document.body.appendChild(stats.dom);
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
window.addEventListener('resize', onWindowResize, false);
document.body.appendChild(renderer.domElement);
// ***** Clipping setup (renderer): *****
var globalPlanes = [globalPlane],
Empty = Object.freeze([]);
renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes
renderer.localClippingEnabled = true;
renderer.clippingPlanes = globalPlanes;
// Controls
var controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1, 0);
controls.update();
// controls1 = new DragControls([...globalPlane], camera, renderer.domElement);
// controls1.addEventListener('drag', render);
// GUI
var gui = new GUI(),
folderLocal = gui.addFolder('Local Clipping'),
propsLocal = {
get 'Enabled'() {
return renderer.localClippingEnabled;
},
set 'Enabled'(v) {
renderer.localClippingEnabled = v;
},
get 'Shadows'() {
return material.clipShadows;
},
set 'Shadows'(v) {
material.clipShadows = v;
},
get 'Plane'() {
return localPlane.constant;
},
set 'Plane'(v) {
localPlane.constant = v;
}
},
folderGlobal = gui.addFolder('Global Clipping'),
propsGlobal = {
get 'Enabled'() {
console.log('hitting 1')
return renderer.clippingPlanes !== Empty;
console.log(renderer.clippingPlanes);
},
set 'Enabled'(v) {
console.log('hitting 2')
renderer.clippingPlanes = v ? globalPlanes : Empty;
console.log(renderer.clippingPlanes);
},
get 'Plane'() {
return globalPlane.constant;
},
set 'Plane'(v) {
globalPlane.constant = v;
}
};
folderLocal.add(propsLocal, 'Enabled');
folderLocal.add(propsLocal, 'Shadows');
folderLocal.add(propsLocal, 'Plane', 0.3, 1.25);
folderGlobal.add(propsGlobal, 'Enabled');
folderGlobal.add(propsGlobal, 'Plane', - 0.4, 3);
// Start
startTime = Date.now();
document.addEventListener('click', onClick, false);
}
function onClick(event) {
event.preventDefault();
if (enableSelection === true) {
var draggableObjects = controls.getObjects();
draggableObjects.length = 0;
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersections = raycaster.intersectObjects(objects, true);
if (intersections.length > 0) {
var object = intersections[0].object;
if (group.children.includes(object) === true) {
object.material.emissive.set(0x000000);
scene.attach(object);
} else {
object.material.emissive.set(0xaaaaaa);
group.attach(object);
}
controls.transformGroup = true;
draggableObjects.push(group);
}
if (group.children.length === 0) {
controls.transformGroup = false;
draggableObjects.push(...objects);
}
}
render();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
// var currentTime = Date.now();
// var time = ( currentTime - startTime ) / 1000;
requestAnimationFrame(animate);
// object.position.y = 0.8;
// object.rotation.x = time * 0.5;
// object.rotation.y = time * 0.2;
// object.scale.setScalar( Math.cos( time ) * 0.125 + 0.875 );
stats.begin();
renderer.render(scene, camera);
stats.end();
}
In order to move things in three.js you use TransformControls. Check out https://threejs.org/docs/#examples/en/controls/TransformControls for its documentation and https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_transform.html for implementation example.
Here you can set the mode to "translate" and scale to "XY" to restrict the movement in X and Y direction only.
controls.axis ="XY";
I'm helping a friend to set up a Three.js scene on a page in Wordpress that's running on the Lay theme. The theme is using some kind of page transition script that doesn't reload the site when navigating to a different page, creating a new initialization of the Three.js scene each time a new page is injected on the site.
I'm looking for ways to completely remove the whole instance of Three.js when a new page is loaded. So that it gets created from scratch when The Lay theme has this function window.laytheme.on("newpageshown", function(layoutObj, type, obj){} that kind of works as a substitute for window.onload.
I've tried different methods in order to remove everything, but as my Three.js code unfortunately is a mixed result of copy and pasting from different demos and examples, I'm not quite sure what exactly that needs to be destroyed and how to do it. To be honest I'm not entirely sure how the code is setup and I have very little experience with Three.js and it's lifecycle.
Here's the code creating the scene:
(function($) {
var base_url = window.location.origin;
var container, stats;
var camera, scene, renderer, controls, manager, loader, texture;
var mouseX = 0,
mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var $currentModel;
var modelName;
var initialGridPosition;
var initialScale;
window.laytheme.on("newpageshown", function(layoutObj, type, obj){
modelName = $(".modelLink.is-active").data("model-name");
initialScale = $(".modelLink.is-active").data("model-scale");
initialGridPosition = $(".modelLink.is-active").data("model-grid-position");
init();
animate();
});
function init() {
container = document.createElement('div');
container.id = "threejs-container";
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 300;
camera.position.y = 100;
// scene
scene = new THREE.Scene();
var ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);
var pointLight = new THREE.PointLight( 0xffffff, 0.3 );
camera.add( pointLight );
scene.add(camera);
// model
var onProgress = function(xhr) {
if (xhr.lengthComputable) {
var percentComplete = xhr.loaded / xhr.total * 100;
$("#domLoader").show();
$("#domLoader").text(Math.round(percentComplete, 2) + '%');
}
};
// texture
manager = new THREE.LoadingManager();
manager.onProgress = function(item, loaded, total) {
if (total == loaded) {
$("#domLoader").hide();
$(".col--row").removeClass("is-hidden");
$currentModel = $(".modelLink.is-active");
$("#meta-title").text('stevns_klint_' + $currentModel.data("model-name"));
$("#meta-vertices").text($currentModel.data("model-vertices"));
$("#meta-faces").text($currentModel.data("model-faces"));
$("#meta-coordinates").text($currentModel.data("model-coordinates"));
$("#meta-coordinates-url").attr("href", $currentModel.data("model-coordinates-url"));
$("#meta-date").text($currentModel.data("model-date"));
$("#meta-images").text($currentModel.data("model-images"));
}
console.log(item, loaded, total);
};
texture = new THREE.Texture();
var onProgress = function(xhr) {
if (xhr.lengthComputable) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log(Math.round(percentComplete, 2) + '% downloaded');
$("#domLoader").show();
$("#domLoader").text(Math.round(percentComplete, 2) + '%');
}
};
var onError = function() {};
// Create initial model
loader = new THREE.ImageLoader(manager)
loader.setPath(base_url + '/assets/' + modelName +'/').load('texture.jpg', function(image) {
texture.image = image;
texture.needsUpdate = true;
});
loader = new THREE.OBJLoader(manager);
loader.setPath(base_url + '/assets/' + modelName +'/').load('model.obj', function(object) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
if( child.material ) {
child.material.side = THREE.DoubleSide;
}
});
object.scale.x = initialScale;
object.scale.y = initialScale;
object.scale.z = initialScale;
object.name = "mainModel";
scene.add(object);
}, onProgress, onError);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
var size = 300;
var divisions = 30;
var gridHelper = new THREE.GridHelper( size, divisions, 0x000000, 0x000000 );
scene.add( gridHelper );
gridHelper.position.y = initialGridPosition;
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.screenSpacePanning = true;
controls.minDistance = 150;
controls.maxDistance = 300;
controls.target.set(0, 0, 0);
controls.enableDamping = true;
controls.enablePan = false;
controls.autoRotate = true;
controls.enableKeys = false;
controls.autoRotateSpeed = 0.04;
controls.dampingFactor = 0.07;
controls.rotateSpeed = 0.04;
controls.update();
window.addEventListener('resize', onWindowResize, false);
// Change model on model link click
$(".modelLink").on("click", function(){
$(".modelLink").removeClass("is-active");
$(this).addClass("is-active");
$(".col--row").addClass("is-hidden");
var modelName = $(this).data("model-name");
var modelScale = Number($(this).data("model-scale"));
var modelGridPosition = $(this).data("model-grid-position");
loader = new THREE.ImageLoader(manager)
loader.setPath(base_url + '/assets/' + modelName +'/').load('texture.jpg', function(image) {
texture.image = image;
texture.needsUpdate = true;
});
loader = new THREE.OBJLoader(manager);
loader.setPath(base_url + '/assets/' + modelName + '/').load('model.obj', function(object) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
if( child.material ) {
child.material.side = THREE.DoubleSide;
}
});
object.scale.x = modelScale;
object.scale.y = modelScale;
object.scale.z = modelScale;
var selectedObject = scene.getObjectByName("mainModel");
scene.remove( selectedObject );
object.name = "mainModel";
gridHelper.position.y = modelGridPosition;
scene.add(object);
}, onProgress, onError);
})
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
render();
}
function render() {
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
})( jQuery );
There code might be some code in there that's irrelevant and have nothing to do with Three.js. But I would appreciate if someone with more experience in the Three.js structure and lifecycle could help me with reading through the script and tell me what parts of the code related to Three.js that needs to be destroyed and how this would be done.
I have created a room in three js and added various objects in it. But,
the objects are moving out of the room. Here is the url on which my code is running:
http://istation-demo.cladev.com/room.html
In order to prevent objects leaving the room or colliding with each other, you must have at least a simple form of collision detection. Most 3D engines, three.js included, have Axis-Aligned Bounding Boxes ( or AABB ) for all the 3d objects that are created. In three.js to get access to bounding boxes, the easiest way is to call
var boundingBoxHelperObject = new THREE.BoundingBoxHelper( object )
If you add it to the scene, it shows up as a wireframe grey box that fits tightly around the object. If you call update on the helper (as you will see shortly), it will update to the dimensions and movement of the 3d object in real time - pretty cool!
Then, to perform collision detection on all these boxes, you call
boundingBoxHelperObject.box.intersectsBox( anotherBoxHelperObject.box )
which outputs true if colliding or false if not colliding. Then perform the appropriate actions, such as moving the box back a frame to prevent it from escaping a room or penetrating another solid object, or changing both colliding objects' colors, or whatever you want.
Here's the final code sample with my corrected picking/dragging code from the last post you made months ago, plus the new bounding boxes and collision testing code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head lang="en">
<meta charset="UTF-8">
<title>Room</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/MTLLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJMTLLoader.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="workspace"></div>
<script>
//define global variables here
var container, renderer;
var camera, scene, projector, mouseVector, controls;
var mouseX, mouseY, draggable;
var pen, c_mesh, interactiveObj = [], rotateObj = [], groundRaycastObj = [];
var wallWidth = 1200;
var wallHeight = 400;
var chair_model, sofa_model;
var chair_selected = false;
var sofa_selected = false;
var raycaster;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
var oldIntersectPoint = new THREE.Vector3();
var newIntersectPoint = new THREE.Vector3();
var intersectOffset = new THREE.Vector3();
var chairOldPosition = new THREE.Vector3();
var sofaOldPosition = new THREE.Vector3();
var chair_rotate = false;
var walls;
var mesh_box;
var wallright, wallleft, wallback, wallfront, ceiling, ground;
var strDownloadMime = "image/octet-stream";
var chairBox, sofaBox;
var wallrightBox, wallleftBox, wallbackBox, wallfrontBox;
init();
animate();
function init() {
container = document.getElementById('workspace'); //document.createElement('div');
document.body.appendChild(container);
//camera
//camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 10000);
// camera.position.set(0, -wallHeight / 2 + 10, wallWidth);
// camera.lookAt(new THREE.Vector3(10, 10, 10));
//renderer
renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x889988);
renderer.shadowMapEnabled = true;
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
raycaster = new THREE.Raycaster();
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z= wallWidth;
camera.position.y= wallWidth/2;
controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
//controls.enableZoom = false;
//walls
walls = new THREE.Object3D();
var groundGeo_2 = new THREE.PlaneGeometry(wallWidth, wallWidth); //for roof and floor
var groundGeo = new THREE.PlaneGeometry(wallWidth, wallHeight);
var wallTextureRight = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
wallTextureRight.map.needsUpdate = true;
var wallTextureLeft = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
var wallTextureFront = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
var wallTextureBack = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall3.png')
});
var floorTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/floor.jpg')
});
floorTexture.map.needsUpdate = true;
var ceilTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
ground = new THREE.Mesh(groundGeo_2, floorTexture);
ground.overdraw = true;
ground.position.set(0, 0, 0);
ground.rotation.x = -Math.PI / 2;
walls.add(ground);
console.log(ground);
wallleft = new THREE.Mesh(groundGeo, wallTextureLeft);
wallleft.overdraw = true;
wallleft.position.set(-wallWidth / 2, wallHeight / 2, 0);
wallleft.rotation.y = Math.PI / 2;
walls.add(wallleft);
wallleftBox = new THREE.BoundingBoxHelper( wallleft );
wallleftBox.update(wallleft);
//wallleftBox.box.min.x -= 0.1;
//wallleftBox.box.max.x += 0.1;
//scene.add(wallleftBox);
wallright = new THREE.Mesh(groundGeo, wallTextureRight);
wallright.overdraw = true;
wallright.position.set(wallWidth / 2, wallHeight / 2, 0);
wallright.rotation.y = -Math.PI / 2;
walls.add(wallright);
wallrightBox = new THREE.BoundingBoxHelper( wallright );
wallrightBox.update(wallright);
//wallrightBox.box.min.x -= 0.1;
//wallrightBox.box.max.x += 0.1;
//scene.add(wallrightBox);
wallback = new THREE.Mesh(groundGeo, wallTextureBack);
wallback.overdraw = true;
wallback.position.set(0, wallHeight / 2, -wallWidth / 2);
walls.add(wallback);
wallbackBox = new THREE.BoundingBoxHelper( wallback );
wallbackBox.update(wallback);
//scene.add(wallbackBox);
wallfront = new THREE.Mesh(groundGeo, wallTextureFront);
wallfront.overdraw = true;
wallfront.position.set(0, wallHeight / 2, wallWidth / 2);
wallfront.rotation.y = -Math.PI;
walls.add(wallfront);
wallfrontBox = new THREE.BoundingBoxHelper( wallfront );
wallfrontBox.update(wallfront);
//scene.add(wallfrontBox);
ceiling = new THREE.Mesh(groundGeo_2, ceilTexture);
ceiling.position.set(0, wallHeight, 0);
ceiling.rotation.x = Math.PI / 2;
walls.add(ceiling);
scene.add(walls);
groundRaycastObj.push(walls);
//load bed texture
var bed_texture = new THREE.ImageUtils.loadTexture("textures/cb-rochelle-gray_baked.png");
var bedMaterial = new THREE.MeshBasicMaterial({
map: bed_texture,
side: THREE.DoubleSide
});
//load bed
var loader = new THREE.JSONLoader();
loader.load('js/sofa.js', function (geometry) {
sofa_model = new THREE.Mesh(geometry, bedMaterial);
for (var i = 0; i < sofa_model.children.length; i++) {
sofa_model.children[i].material = material;
sofa_model.children[i].userDataParent = sofa_model;
sofa_model.children[i].name = 'sofa_model';
}
sofa_model.position.set(200,0, -200);
sofa_model.rotation.set(0, 0, 0);
sofa_model.scale.set(3, 3, 3);
sofa_model.name = 'sofa_model';
interactiveObj.push(sofa_model);
scene.add(sofa_model);
sofaBox = new THREE.BoundingBoxHelper( sofa_model );
// comment next line out if you don't want to see the wireframe sofa boxHelper
scene.add(sofaBox);
});
//load chair texture
var chair_texture = new THREE.ImageUtils.loadTexture("textures/chair.png");
var chairMaterial = new THREE.MeshBasicMaterial({
map: chair_texture,
side: THREE.DoubleSide
});
//load chair
var loader = new THREE.JSONLoader();
loader.load('js/chair_model.js', function (geometry) {
chair_model = new THREE.Mesh(geometry, chairMaterial);
for (var i = 0; i < chair_model.children.length; i++) {
chair_model.children[i].material = material;
chair_model.children[i].userDataParent = sofa_model;
chair_model.children[i].name = 'chair_model';
}
chair_model.position.set(-300,0, -200);
chair_model.rotation.set(0, 0, 0);
chair_model.scale.set(3, 3, 3);
chair_model.name = 'chair_model';
interactiveObj.push(chair_model);
scene.add(chair_model);
chairBox = new THREE.BoundingBoxHelper( chair_model );
// comment next line out if you don't want to see the wireframe chair boxHelper
scene.add(chairBox);
});
//IE, Chrome, Safari, Opera
document.addEventListener('mousewheel', onDocumentMouseWheel, false);
//Firefox
document.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
chair_model.rotation.y += 0.02;
chairBox.update(chair_model);
sofaBox.update(sofa_model);
//wallrightBox.update(wallright);
//wallleftBox.update(wallleft);
//wallfrontBox.update(wallfront);
//wallbackBox.update(wallback);
controls.update();
// Render the frame
//Don't render twice, it will slow down your animation!
//render();
renderer.render(scene, camera);
}
function render() {
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
//controls.handleResize();
}
function onDocumentMouseDown(event) {
draggable = true;
event.preventDefault();
var testIntersects;
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0)
oldIntersectPoint.copy(testIntersects[0].point);
// find intersections
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
controls.enabled=false;
if (intersects[0].object.name == 'chair_model') {
container.style.cursor = 'pointer';
chair_selected = true;
//oldIntersectPoint.copy(chair_model.position);
chairBox.material.color.set('white');
} else if (intersects[0].object.name == 'sofa_model') {
container.style.cursor = 'pointer';
sofa_selected = true;
//oldIntersectPoint.copy(sofa_model.position);
sofaBox.material.color.set('white');
}
else {
chair_selected = false;
sofa_selected = false;
}
draggable = false;
}
}
function onDocumentMouseUp(event) {
draggable = false;
chair_selected = false;
sofa_selected = false;
chair_rotate = false;
container.style.cursor = 'auto';
controls.enabled=true;
oldIntersectPoint.set(0,0,0);
newIntersectPoint.set(0,0,0);
intersectOffset.set(0,0,0);
chairBox.material.color.set('grey');
sofaBox.material.color.set('grey');
}
function onDocumentMouseMove(event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
var testIntersects;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
container.style.cursor = 'pointer';
//addRotationLine(intersects[0].object);
} else {
container.style.cursor = 'auto';
}
if (draggable) {
} else if (chair_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
var okToMove = true;
chairOldPosition.copy(chair_model.position);
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
// store old intersect point for next frame
oldIntersectPoint.copy(newIntersectPoint);
chair_model.position.add(intersectOffset);
chair_model.updateMatrixWorld(true);
//chairBox.updateMatrixWorld(true);
chairBox.update(chair_model);
// default
chairBox.material.color.set('white');
if( chairBox.box.intersectsBox(sofaBox.box) ) {
okToMove = false;
chairBox.material.color.set('red');
}
else if( chairBox.box.intersectsBox(wallrightBox.box) ) {
okToMove = false;
chairBox.material.color.set('red');
}
else if( chairBox.box.intersectsBox(wallleftBox.box) ) {
okToMove = false;
chairBox.material.color.set('red');
}
else if( chairBox.box.intersectsBox(wallfrontBox.box) ) {
okToMove = false;
chairBox.material.color.set('red');
}
else if( chairBox.box.intersectsBox(wallbackBox.box) ) {
okToMove = false;
chairBox.material.color.set('red');
}
// if NOT ok to move and chair is hitting something,
if ( !okToMove ) {
// put chair back where it was
chair_model.position.copy(chairOldPosition);
}
}
// clamp chair position to the ground
chair_model.position.y = 0;
} else if (chair_rotate == true) {
rotate_object(chair_model, event);
}
else if (sofa_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
var okToMove = true;
sofaOldPosition.copy(sofa_model.position);
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
oldIntersectPoint.copy(newIntersectPoint);
sofa_model.position.add(intersectOffset);
sofa_model.updateMatrixWorld(true);
//sofaBox.updateMatrixWorld(true);
sofaBox.update(sofa_model);
// default
sofaBox.material.color.set('white');
if( sofaBox.box.intersectsBox(chairBox.box) ) {
okToMove = false;
sofaBox.material.color.set('red');
}
else if( sofaBox.box.intersectsBox(wallrightBox.box) ) {
okToMove = false;
sofaBox.material.color.set('red');
}
else if( sofaBox.box.intersectsBox(wallleftBox.box) ) {
okToMove = false;
sofaBox.material.color.set('red');
}
else if( sofaBox.box.intersectsBox(wallfrontBox.box) ) {
okToMove = false;
sofaBox.material.color.set('red');
}
else if( sofaBox.box.intersectsBox(wallbackBox.box) ) {
okToMove = false;
sofaBox.material.color.set('red');
}
// if NOT ok to move and sofa is hitting something,
if ( !okToMove ) {
// put sofa back where it was
sofa_model.position.copy(sofaOldPosition);
}
}
// clamp sofa position to the ground
sofa_model.position.y = 0;
}
mouseX = event.clientX;
mouseY = event.clientY;
//render(); // no need to render
}
function onDocumentMouseWheel(event) {
// This is automatically handled for you by orbitControls.js,
// but you can't disable zoom on the controls - so don't type controls.enableZoom = false;
//mouseDelta = (-event.wheelDeltaY || event.detail);
//camera.position.z += mouseDelta * 1;
//render(); // no need to render
}
function addRotationLine(objModel) {
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
linewidth: 6
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-10, 500, 0),
new THREE.Vector3(1000, 500, 0)
);
var line = new THREE.Line(geometry, material);
objModel.add(line);
}
function rotate_object(object, event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
object.rotation.y += deltaX * 0.02;
object.rotation.y += deltaY * 0.01;
}
</script>
</body>
</html>
Hope this works for you! :)
I am have having issue in moving the object, when using mouse. When i move the object initially it works fine, but after rotating the scene about the y-axis, the object starts moving in opposite direction of the mouse. I have created a jsfiddle for that. Here is the code:
//define global variables here
var container, renderer;
var camera, scene, projector,mouseVector,controls;
var mouseX, mouseY, draggable;
var pen,c_mesh,interactiveObj = [];
var cube_selected=false;
var wallWidth=1200;
var wallHeight=400;
var raycaster = new THREE.Raycaster(); // create once
var mouse = new THREE.Vector2(); // create once
init();
animate();
function init(){
container=document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 10000);
//camera.position.set(0,-wallWidth/2+10,wallWidth);
camera.position.set(0,-wallHeight/2+10,wallWidth);
// camera.lookAt(new THREE.Vector3(0,-wallWidth/2 +10,10));
//camera.lookAt(new THREE.Vector3(10,10,10));
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x666666);
scene.add(ambient);
//walls
walls = new THREE.Object3D();
var groundMat = new THREE.MeshPhongMaterial({color:0x808080});
var groundGeo_2 = new THREE.PlaneGeometry(wallWidth, wallWidth); //for roof and floor
var ground = new THREE.Mesh(groundGeo_2, groundMat);
ground.overdraw = true;
ground.position.set(0, -wallHeight, 0);
ground.rotation.x = -Math.PI/2;
walls.add(ground);
var cube_geometry = new THREE.CubeGeometry(500,300,100);
var c_material = new THREE.MeshNormalMaterial();
c_mesh = new THREE.Mesh(cube_geometry, c_material);
c_mesh.overdraw = true;
c_mesh.name = "first_cube";
c_mesh.position.set(0, -wallHeight+100/2-1 ,0); //c_mesh.add(camera);
c_mesh.rotation.x = Math.PI * 0.1;
interactiveObj.push(c_mesh);
walls.add(c_mesh);
scene.add(walls);
//projector = new THREE.Projector();
mouseVector = new THREE.Vector3();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
container.appendChild( renderer.domElement );
//IE, Chrome, Safari, Opera
document.addEventListener('mousewheel',onDocumentMouseWheel, false);
//Firefox
document.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown(event){
draggable = true;
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( interactiveObj, true );
console.log(intersects);
if ( intersects.length > 0 ) {
if(intersects[0].object == c_mesh) {
renderer.domElement.style.cursor = 'pointer';
console.log("cube selected "); cube_selected=true;
}else{
cube_selected=false;
}
draggable = false;
}
}
function onDocumentMouseUp(event){
draggable = false;
cube_selected=false;
renderer.domElement.style.cursor = 'auto';
}
function onDocumentMouseMove( event ) {
if(draggable){
if(mouseX != 0 && mouseY != 0){
deltaX = event.clientX - mouseX;
deltaY = event.clientY - mouseY;
walls.rotation.y += deltaX * 0.01;
walls.rotation.x += deltaY * 0.01;
}
}else if (cube_selected==true){
if(mouseX != 0 && mouseY != 0){
deltaX = event.clientX - mouseX;
deltaY = event.clientY - mouseY;
c_mesh.position.x += deltaX*1.5 ;
c_mesh.position.y -= deltaY*1.5 ;
}
}
mouseX = event.clientX;
mouseY = event.clientY;
render();
}
function onDocumentMouseWheel( event ) {
mouseDelta = (-event.wheelDeltaY|| event.detail);
camera.position.z += mouseDelta * 1 ;
// console.log("camera position : "+ camera.position.z);
render();
}
https://jsfiddle.net/pn1pL4jb/5/
What i have done:
Added object in plane, rather then to scene because I want the same rotation of plane and object in the same direction.
The problem was that when you rotated the walls/ground with the mouse drag, the cube's rotation was not updated to match. So when you went to drag the cube after a world rotation, the cube is still operating in its old coordinate system. Also, be careful how you add children to parents. The cube should be a child of the scene (or world coordinate system) if you intend to move it all around. If it is a child of the floor or walls, things can get wacky really fast. :)
I updated your JSFiddle as well as cleaned up the code a little bit:
https://jsfiddle.net/aucyekux/
The magic is happening on line 126 with the copying of the wall's rotation into the cube's rotation so it matches:
c_mesh.rotation.copy(walls.rotation);
I won't assume how much 3D math you know, but I'll just say that copying the ground's rotation essentially creates a new frame of reference or local coordinate system for the cube so that when you drag it to the right, and the code says x = x + ... , its new coordinate system has its x direction pointing in the same direction as the ground/walls' new x direction (after the rotation you performed on them).
Hope this makes sense - don't worry if you don't quite get how it all works. I am a veteran 3D game programmer hobbyist for 20 years and I still sometimes forget how this all works and I have struggled with it plenty. I also refresh my understanding by browsing scratchapixel (the 3D math / coordinate system primer section) every now and then. It keeps all this 3D math stuff fresh. Good luck to you! :)
The following is a 2nd answer to this problem. The OP has added orbitControls.js to his app and now all the objects are more manageable. Here is the whole app's .html file, updated to reflect the changes. Now, you can click/drag to rotate the whole room. And then you can click/drag on the chair or the sofa and drag them around the room. Their Y positions are clamped to the ground (0.0) so that they do not incorrectly penetrate the floor.
The sliding of objects along the floor is achieved through raycasting against just the ground (when the mouse has selected them and dragging them). The raycast returns the intersection point in world space, so I just copy that into the draggable objects' position. Then a post-clamp of their Y positions prevent them from falling through the floor.
Even better, is the fact that we don't need localToWorld and worldToLocal matrix 'hacks' anymore because everything is relative to the Scene, which is how it is in real life. :) To prove this, I added an example update the chair's rotation around its own Y axis, so we have a spinning chair that is able to rotate around its own local center of gravity, while also being able to move its position relative to the Scene (world).
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head lang="en">
<meta charset="UTF-8">
<title>Room</title>
</head>
<body>
<script src="http://alexan0308.github.io/threejs/build/three.min.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/MTLLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJMTLLoader.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="workspace"></div>
<script>
//define global variables here
var container, renderer;
var camera, scene, projector, mouseVector, controls;
var mouseX, mouseY, draggable;
var pen, c_mesh, interactiveObj = [], rotateObj = [], groundRaycastObj = [];
var wallWidth = 1200;
var wallHeight = 400;
var chair_model, sofa_model;
var chair_selected = false;
var sofa_selected = false;
var raycaster;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
var oldIntersectPoint = new THREE.Vector3();
var newIntersectPoint = new THREE.Vector3();
var intersectOffset = new THREE.Vector3();
var chair_rotate = false;
var walls;
var mesh_box;
var wallright, wallleft, wallback, wallfront, ceiling, ground;
var strDownloadMime = "image/octet-stream";
init();
animate();
function init() {
container = document.getElementById('workspace'); //document.createElement('div');
document.body.appendChild(container);
//camera
//camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 10000);
// camera.position.set(0, -wallHeight / 2 + 10, wallWidth);
// camera.lookAt(new THREE.Vector3(10, 10, 10));
//renderer
renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x889988);
renderer.shadowMapEnabled = true;
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
raycaster = new THREE.Raycaster();
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z= wallWidth;
camera.position.y= wallWidth/2;
controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
//controls.enableZoom = false;
//walls
walls = new THREE.Object3D();
var groundGeo_2 = new THREE.PlaneGeometry(wallWidth, wallWidth); //for roof and floor
var groundGeo = new THREE.PlaneGeometry(wallWidth, wallHeight);
var wallTextureRight = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
wallTextureRight.map.needsUpdate = true;
var wallTextureLeft = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
var wallTextureFront = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
var wallTextureBack = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall3.png')
});
var floorTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/floor.jpg')
});
floorTexture.map.needsUpdate = true;
var ceilTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
ground = new THREE.Mesh(groundGeo_2, floorTexture);
ground.overdraw = true;
ground.position.set(0, 0, 0);
ground.rotation.x = -Math.PI / 2;
walls.add(ground);
console.log(ground);
wallleft = new THREE.Mesh(groundGeo, wallTextureLeft);
wallleft.overdraw = true;
wallleft.position.set(-wallWidth / 2, wallHeight / 2, 0);
wallleft.rotation.y = Math.PI / 2;
walls.add(wallleft);
wallright = new THREE.Mesh(groundGeo, wallTextureRight);
wallright.overdraw = true;
wallright.position.set(wallWidth / 2, wallHeight / 2, 0);
wallright.rotation.y = -Math.PI / 2;
walls.add(wallright);
wallback = new THREE.Mesh(groundGeo, wallTextureBack);
wallback.overdraw = true;
wallback.position.set(0, wallHeight / 2, -wallWidth / 2);
walls.add(wallback);
wallfront = new THREE.Mesh(groundGeo, wallTextureFront);
wallfront.overdraw = true;
wallfront.position.set(0, wallHeight / 2, wallWidth / 2);
wallfront.rotation.y = -Math.PI;
walls.add(wallfront);
ceiling = new THREE.Mesh(groundGeo_2, ceilTexture);
ceiling.position.set(0, wallHeight, 0);
ceiling.rotation.x = Math.PI / 2;
walls.add(ceiling);
scene.add(walls);
groundRaycastObj.push(walls);
//load bed texture
var bed_texture = new THREE.ImageUtils.loadTexture("textures/cb-rochelle-gray_baked.png");
var bedMaterial = new THREE.MeshBasicMaterial({
map: bed_texture,
side: THREE.DoubleSide
});
//load bed
var loader = new THREE.JSONLoader();
loader.load('js/sofa.js', function (geometry) {
sofa_model = new THREE.Mesh(geometry, bedMaterial);
for (var i = 0; i < sofa_model.children.length; i++) {
sofa_model.children[i].material = material;
sofa_model.children[i].userDataParent = sofa_model;
sofa_model.children[i].name = 'sofa_model';
}
sofa_model.position.set(200,0, -200);
sofa_model.rotation.set(0, 0, 0);
sofa_model.scale.set(3, 3, 3);
sofa_model.name = 'sofa_model';
interactiveObj.push(sofa_model);
scene.add(sofa_model);
});
//load chair texture
var chair_texture = new THREE.ImageUtils.loadTexture("textures/chair.png");
var chairMaterial = new THREE.MeshBasicMaterial({
map: chair_texture,
side: THREE.DoubleSide
});
//load chair
var loader = new THREE.JSONLoader();
loader.load('js/chair_model.js', function (geometry) {
chair_model = new THREE.Mesh(geometry, chairMaterial);
for (var i = 0; i < chair_model.children.length; i++) {
chair_model.children[i].material = material;
chair_model.children[i].userDataParent = sofa_model;
chair_model.children[i].name = 'chair_model';
}
chair_model.position.set(-200,0, -200);
chair_model.rotation.set(0, 0, 0);
chair_model.scale.set(3, 3, 3);
chair_model.name = 'chair_model';
interactiveObj.push(chair_model);
scene.add(chair_model);
});
//IE, Chrome, Safari, Opera
document.addEventListener('mousewheel', onDocumentMouseWheel, false);
//Firefox
document.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
chair_model.rotation.y += 0.02;
controls.update();
// Render the frame
//Don't render twice, it will slow down your animation!
//render();
renderer.render(scene, camera);
}
function render() {
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
//controls.handleResize();
}
function onDocumentMouseDown(event) {
draggable = true;
event.preventDefault();
var testIntersects;
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0)
oldIntersectPoint.copy(testIntersects[0].point);
// find intersections
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
controls.enabled=false;
if (intersects[0].object.name == 'chair_model') {
container.style.cursor = 'pointer';
chair_selected = true;
} else if (intersects[0].object.name == 'sofa_model') {
container.style.cursor = 'pointer';
sofa_selected = true;
}
else {
chair_selected = false;
sofa_selected = false;
}
draggable = false;
}
}
function onDocumentMouseUp(event) {
draggable = false;
chair_selected = false;
sofa_selected = false;
chair_rotate = false;
container.style.cursor = 'auto';
controls.enabled=true;
oldIntersectPoint.set(0,0,0);
newIntersectPoint.set(0,0,0);
intersectOffset.set(0,0,0);
}
function onDocumentMouseMove(event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
var testIntersects;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
container.style.cursor = 'pointer';
//addRotationLine(intersects[0].object);
} else {
container.style.cursor = 'auto';
}
if (draggable) {
} else if (chair_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
oldIntersectPoint.copy(newIntersectPoint);
chair_model.position.add(intersectOffset);
}
// clamp chair position to the ground
chair_model.position.y = 0;
} else if (chair_rotate == true) {
rotate_object(chair_model, event);
}
else if (sofa_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
oldIntersectPoint.copy(newIntersectPoint);
sofa_model.position.add(intersectOffset);
}
// clamp sofa position to the ground
sofa_model.position.y = 0;
}
mouseX = event.clientX;
mouseY = event.clientY;
//render(); // no need to render
}
function onDocumentMouseWheel(event) {
// This is automatically handled for you by orbitControls.js,
// but you can't disable zoom on the controls - so don't type controls.enableZoom = false;
//mouseDelta = (-event.wheelDeltaY || event.detail);
//camera.position.z += mouseDelta * 1;
//render(); // no need to render
}
function addRotationLine(objModel) {
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
linewidth: 6
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-10, 500, 0),
new THREE.Vector3(1000, 500, 0)
);
var line = new THREE.Line(geometry, material);
objModel.add(line);
}
function rotate_object(object, event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
object.rotation.y += deltaX * 0.02;
object.rotation.y += deltaY * 0.01;
}
</script>
</body>
</html>
Change line 143 of the fiddle to:
c_mesh.position.x += deltaX*1.5 * Math.cos( walls.rotation.y);
When you rotate the scene, the plane the mouse moves on doesn't rotate with it. So you have to act like it does by scaling by the cosine of the rotation.