Three.JS Black screen on attaching camera to a GLTF object - javascript

So I am writing a bit of stuff if Three.JS and I seem to have hit a stump with the camera. I'm attempting to attach the camera to an imported model object and it would seem that it IS attaching, however it would seem as if shadows are negated, the distance is far off from the actual field I've created. As well as some other annoying issues like Orbit controls would be inverted and non-functional. Here is my code (with certain things blocked out because I'm hotlinking script files hosted on my server...):
// This is basically everything to setup for a basic THREE.JS field to do our work in
var scene = new THREE.Scene(); // Empty Space
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); // Perspective Camera (Args, FOV, Aspect = W/H, Min View Dist, Max View Dist)
//var controls = new THREE.OrbitControls(camera); // We will use this to look around
camera.position.set(0, 2, 5); // Note that depth into positon is mainly the opposite of where you normally want it to be.
camera.rotation.x = -0.3 // This is an attempt to rotate the angle of the camera off of an axis
var renderer = new THREE.WebGLRenderer({antialias: true}); // Our Renderer + Antialiasing
renderer.shadowMap.enabled = true; // This allows shadows to work in our 3D animation
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // This one isn't as blocky as THREE.PCFShadowMap
renderer.setClearColor("#00CCCC"); // Note: same as 0x000000
renderer.setSize(window.innerWidth, window.innerHeight); // Renderer Dimensions
document.getElementById("container").appendChild(renderer.domElement); // Add our renderer creation to our div named "container"
// Lighting (It's not necessary but it looks cool!)
var light = new THREE.PointLight("#FFFFFF", 5, 1000); // Color, intensity, range(lighting will not exceed render distance)
light.castShadow = true;
light.position.set(5, 5, 0); // This will treat the light coming from an angle!
scene.add(light);
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500;
// We will make a cube here
var cubeGeo = new THREE.BoxGeometry(1, 1, 1); // This is the shape, width, height and length of our cube. Note BoxGeometry IS the current shape!
var cubeMat = new THREE.MeshStandardMaterial({color: "#FF0000"}); // Create a basic mesh with undefined color, you can also use a singular color using Basic rather than Normal, There is also Lambert and Phong. Lambert is more of a Matte material while Phong is more of a gloss or shine effect.
var cube = new THREE.Mesh(cubeGeo, cubeMat); // Create the object with defined dimensions and colors!
cube.castShadow = true; // This will allow our cube to cast a shadow outward.
cube.recieveShadow = false // This will make our cube not recieve shadows from other objects (Although it isn't needed because it's default, you show make a habit of writing it anyways as some things default to true!)
scene.add(cube); // scene.add(object) is what we will use for almost every object we create in THREE.JS
//cube.add(camera); // This is an attempt to attach the camera to the cube...
// Loader
var ship;
var loader = new THREE.GLTFLoader();
loader.load("http://ipaddress:port/files/models/raven/scene.gltf", function(gltf) {
scene.add(gltf.scene);
ship = gltf.scene;
ship.scale.multiplyScalar(0.005);
ship.rotation.y = Math.PI;
}, undefined, function(error) {
console.error(error);
});
// Lest make a floor to show the shadow!
var floorGeo = new THREE.BoxGeometry(1000, 0.1, 1000);
var floorMat = new THREE.MeshStandardMaterial({color: "#0000FF"});
var floor = new THREE.Mesh(floorGeo, floorMat);
floor.recieveShadow = true; // This will allow the shadow from the cube to portray itself unto it.
floor.position.set(0, -3, 0);
scene.add(floor);
// Now let's create an object on the floor so that we can distance ourself from our starting point.
var buildingGeo = new THREE.BoxGeometry(10, 100, 10);
var buildingMat = new THREE.MeshNormalMaterial();
var building = new THREE.Mesh(buildingGeo, buildingMat);
building.position.z = -100;
scene.add(building);
var rotation = 0;
// Controls
var keyState = {};
window.addEventListener('keydown',function(e){
keyState[e.keyCode || e.which] = true;
},true);
window.addEventListener('keyup',function(e){
keyState[e.keyCode || e.which] = false;
},true);
document.addEventListener("keydown", function(event) {
console.log(event.which);
});
var camAdded = false;
var render = function() {
requestAnimationFrame(render); // This grabs the browsers frame animation function.
if (rotation == 1) {
ship.rotation.x += 0.01; // rotation is treated similarly to how two dimensional objects' location is treated
ship.rotation.y += 0.01; // however it will be based on an axis point plus the width/height and subtract but keep it's indice location!
ship.rotation.z += 0.01;
}
if (keyState[87]) { // Up
ship.rotateX(0.01);
}
if (keyState[83]) { // Down
ship.rotateX(-0.01);
}
if (keyState[65]) { // Left
ship.rotateY(0.03);
}
if (keyState[68]) { // Right
ship.rotateY(-0.03);
}
if (keyState[81]) {
ship.rotateZ(0.1);
}
if (keyState[69]) {
ship.rotateZ(-0.1);
}
if (keyState[82]) { // Reset
for (var i = 0; i < 10; i++) {
if (!ship.rotation.x == 0) {
if (ship.rotation.x > 0) {
ship.rotation.x -= 0.005;
} else if (ship.rotation.x < 0){
ship.rotation.x += 0.005;
}
}
if (!ship.rotation.z == 0) {
if (ship.rotation.z > 0) {
ship.rotation.z -= 0.01;
} else if (ship.rotation.z < 0){
ship.rotation.z += 0.01;
}
}
}
}
ship.translateZ(0.2); // This will translate our ship forward in the direction it's currently facing so that it will look as if it is flyimg.
renderer.render(scene, camera); // This will render the scene after the effects have changed (rotation!)
window.addEventListener('resize', onWindowResize, false);
}
render(); // Finally, we need to loop the animation otherwise our object will not move on it's own!
function onWindowResize() {
var sceneWidth = window.innerWidth - 20;
var sceneHeight = window.innerHeight - 20;
renderer.setSize(sceneWidth, sceneHeight);
camera.aspect = sceneWidth / sceneHeight;
camera.updateProjectionMatrix();
}
<!DOCTYPE htm>
<html>
<head>
<meta charset="utf-8">
<title>Basic Three.JS</title>
</head>
<body style="background-color: #2B2B29; color: #FFFFFF; text-align: center;">
<div id="container"></div>
<script>
window.onload = function() {
document.getElementById("container").width = window.innerWidth - 20;
document.getElementById("container").height = window.innerHeight - 20;
}
</script>
<script src="http://ipaddress:port/files/scripts/three.min.js"></script>
<script src="http://ipaddress:port/files/scripts/GLTFLoader.js"></script>
<script src="http://ipaddress:port/files/scripts/OrbitControls.js"></script>
<script src="http://ipaddress:port/files/scripts/basicthree.js"></script> <!-- This is the code below -->
</body>
</html>

Nevermind, I have found a solution - shoddy as it may be...
if (typeof ship != "undefined") {
// Previous code inside of the main three.js loop...
ship.translateZ(0.2); // Move ship
camera.position.set(ship.position.x, ship.position.y, ship.position.z); // Set the camera's position to the ships position
camera.translateZ(10); // Push the camera back a bit so it's not inside the ship
camera.rotation.set(ship.rotation.x, ship.rotation.y, ship.rotation.z); // Set the rotation of the ship to be the exact same as the ship
camera.rotateX(0.3); // Tilt the camera downwards so that it's viewing over the ship
camera.rotateY(Math.PI); // Flip the camera so it's not facing the head of the ship model.
// Note: many bits of code I have are inverted due to the ship's model being backwards (or so it seems)...
}

Related

WebXR controllers for button pressing in three.js

I would like to figure out how to map out the controls for my oculus quest and other devices, using three.js and webXR. The code works, and allows me to move the controller, maps a cylinder to each control, and allows me to use the trigger to controls to change the color of the cylinders. This is great, but I can't find any documentation on how to use axis controls for the joy stick, the grip and the other buttons. Part of me wants to believe it's as simple as knowing which event to call, because I don't know what other events are available.
Here is a link to the tutorial I based this off of. https://github.com/as-ideas/webvr-with-threejs
Please note that this code works as expected, but I don't know how totake it further and do more.
function createController(controllerID, videoinput) {
//RENDER CONTROLLER AS YELLOW TUBE
const controller = renderer.vr.getController(controllerID);
const cylinderGeometry = new CylinderGeometry(0.025, 0.025, 1, 32);
const cylinderMaterial = new MeshPhongMaterial({ color: 0xffff00 });
const cylinder = new Mesh(cylinderGeometry, cylinderMaterial);
cylinder.geometry.translate(0, 0.5, 0);
cylinder.rotateX(-0.25 * Math.PI);
controller.add(cylinder);
cameraFixture.add(controller);
//TRIGGER
controller.addEventListener('selectstart', () => {
if (controllerID === 0) {
cylinderMaterial.color.set('pink')
} else {
cylinderMaterial.color.set('orange');
videoinput.play()
}
});
controller.addEventListener('selectend', () => {
cylinderMaterial.color.set(0xffff00);
videoinput.pause();
console.log('I pressed play');
});
}
As of three.js 0.119, integrated 'events' from the other buttons, trackpads, haptics, and thumbsticks of a touch controller are not provided, only select and squeeze events are available. three.js has a functional model of 'just working' regardless of what type of input device you have and only provides for managing events that can be produced by all input devices (ie. select)
Luckily, we are not limited by what three.js has made available and can just poll the controller data directly.
Touch controllers follow the model of 'gamepad' controls and just report their instantanous values. We will poll the gamepad for its current values of the various buttons and keep track of their state and create 'events' within our code for button pushes, trackpad and thumbstick axis changes.
To access the instantaneous data from a touch controller while within a webXR session
const session = renderer.xr.getSession();
let i = 0;
if (session) {
for (const source of session.inputSources) {
if (source && source.handedness) {
handedness = source.handedness; //left or right controllers
}
if (!source.gamepad) continue;
const controller = renderer.xr.getController(i++);
const old = prevGamePads.get(source);
const data = {
handedness: handedness,
buttons: source.gamepad.buttons.map((b) => b.value),
axes: source.gamepad.axes.slice(0)
};
//process data accordingly to create 'events'
Haptic feedback is provided through a promise (Note not all browsers currently support the webXR haptic feedback, but Oculus Browser and Firefox Reality on quest do)
When availble, the haptic feedback is produced through a promise:
var didPulse = sourceXR.gamepad.hapticActuators[0].pulse(0.8, 100);
//80% intensity for 100ms
//subsequent promises cancel any previous promise still underway
To demonstrate this solution I have modified threejs.org/examples/#webXR_vr_dragging example by adding the camera to a 'dolly' that can be moved around with the touch controllers thumbsticks when within a webXR session and provide various haptic feedback for events such as raycasting onto an object or axis movements on thumbsticks.
For each frame, we poll the data from the touch controllers and respond accordingly. We have to store the data from frame to frame to detect changes and create our events, and filter out some data (false 0's and up to 20% randomdrift from 0 in thumbstick axis values on some controllers) For proper 'forward and sideways' dolly movement the current heading and attitude of the webXR camera is also needed each frame and accessed via:
let xrCamera = renderer.xr.getCamera(camera);
xrCamera.getWorldDirection(cameraVector);
//heading vector for webXR camera now within cameraVector
Example codepen here:
codepen.io/jason-buchheim/pen/zYqYGXM
With 'ENTER VR' button exposed (debug view) here:cdpn.io/jason-buchheim/debug/zYqYGXM
Full code with modifications of original threejs example highlighted with comment blocks
//// From webxr_vr_dragging example https://threejs.org/examples/#webxr_vr_dragging
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.119.1/build/three.module.min.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.119.1/examples/jsm/controls/OrbitControls.min.js";
import { VRButton } from "https://cdn.jsdelivr.net/npm/three#0.119.1/examples/jsm/webxr/VRButton.min.js";
import { XRControllerModelFactory } from "https://cdn.jsdelivr.net/npm/three#0.119.1/examples/jsm/webxr/XRControllerModelFactory.min.js";
var container;
var camera, scene, renderer;
var controller1, controller2;
var controllerGrip1, controllerGrip2;
var raycaster,
intersected = [];
var tempMatrix = new THREE.Matrix4();
var controls, group;
////////////////////////////////////////
//// MODIFICATIONS FROM THREEJS EXAMPLE
//// a camera dolly to move camera within webXR
//// a vector to reuse each frame to store webXR camera heading
//// a variable to store previous frames polling of gamepads
//// a variable to store accumulated accelerations along axis with continuous movement
var dolly;
var cameraVector = new THREE.Vector3(); // create once and reuse it!
const prevGamePads = new Map();
var speedFactor = [0.1, 0.1, 0.1, 0.1];
////
//////////////////////////////////////////
init();
animate();
function init() {
container = document.createElement("div");
document.body.appendChild(container);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x808080);
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
500 //MODIFIED FOR LARGER SCENE
);
camera.position.set(0, 1.6, 3);
controls = new OrbitControls(camera, container);
controls.target.set(0, 1.6, 0);
controls.update();
var geometry = new THREE.PlaneBufferGeometry(100, 100);
var material = new THREE.MeshStandardMaterial({
color: 0xeeeeee,
roughness: 1.0,
metalness: 0.0
});
var floor = new THREE.Mesh(geometry, material);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
scene.add(new THREE.HemisphereLight(0x808080, 0x606060));
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 200, 0); // MODIFIED SIZE OF SCENE AND SHADOW
light.castShadow = true;
light.shadow.camera.top = 200; // MODIFIED FOR LARGER SCENE
light.shadow.camera.bottom = -200; // MODIFIED FOR LARGER SCENE
light.shadow.camera.right = 200; // MODIFIED FOR LARGER SCENE
light.shadow.camera.left = -200; // MODIFIED FOR LARGER SCENE
light.shadow.mapSize.set(4096, 4096);
scene.add(light);
group = new THREE.Group();
scene.add(group);
var geometries = [
new THREE.BoxBufferGeometry(0.2, 0.2, 0.2),
new THREE.ConeBufferGeometry(0.2, 0.2, 64),
new THREE.CylinderBufferGeometry(0.2, 0.2, 0.2, 64),
new THREE.IcosahedronBufferGeometry(0.2, 3),
new THREE.TorusBufferGeometry(0.2, 0.04, 64, 32)
];
for (var i = 0; i < 100; i++) {
var geometry = geometries[Math.floor(Math.random() * geometries.length)];
var material = new THREE.MeshStandardMaterial({
color: Math.random() * 0xffffff,
roughness: 0.7,
side: THREE.DoubleSide, // MODIFIED TO DoubleSide
metalness: 0.0
});
var object = new THREE.Mesh(geometry, material);
object.position.x = Math.random() * 200 - 100; // MODIFIED FOR LARGER SCENE
object.position.y = Math.random() * 100; // MODIFIED FOR LARGER SCENE
object.position.z = Math.random() * 200 - 100; // MODIFIED FOR LARGER SCENE
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.setScalar(Math.random() * 20 + 0.5); // MODIFIED FOR LARGER SCENE
object.castShadow = true;
object.receiveShadow = true;
group.add(object);
}
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true;
renderer.xr.enabled = true;
//the following increases the resolution on Quest
renderer.xr.setFramebufferScaleFactor(2.0);
container.appendChild(renderer.domElement);
document.body.appendChild(VRButton.createButton(renderer));
// controllers
controller1 = renderer.xr.getController(0);
controller1.name="left"; ////MODIFIED, added .name="left"
controller1.addEventListener("selectstart", onSelectStart);
controller1.addEventListener("selectend", onSelectEnd);
scene.add(controller1);
controller2 = renderer.xr.getController(1);
controller2.name="right"; ////MODIFIED added .name="right"
controller2.addEventListener("selectstart", onSelectStart);
controller2.addEventListener("selectend", onSelectEnd);
scene.add(controller2);
var controllerModelFactory = new XRControllerModelFactory();
controllerGrip1 = renderer.xr.getControllerGrip(0);
controllerGrip1.add(
controllerModelFactory.createControllerModel(controllerGrip1)
);
scene.add(controllerGrip1);
controllerGrip2 = renderer.xr.getControllerGrip(1);
controllerGrip2.add(
controllerModelFactory.createControllerModel(controllerGrip2)
);
scene.add(controllerGrip2);
//Raycaster Geometry
var geometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, -1)
]);
var line = new THREE.Line(geometry);
line.name = "line";
line.scale.z = 50; //MODIFIED FOR LARGER SCENE
controller1.add(line.clone());
controller2.add(line.clone());
raycaster = new THREE.Raycaster();
////////////////////////////////////////
//// MODIFICATIONS FROM THREEJS EXAMPLE
//// create group named 'dolly' and add camera and controllers to it
//// will move dolly to move camera and controllers in webXR
dolly = new THREE.Group();
dolly.position.set(0, 0, 0);
dolly.name = "dolly";
scene.add(dolly);
dolly.add(camera);
dolly.add(controller1);
dolly.add(controller2);
dolly.add(controllerGrip1);
dolly.add(controllerGrip2);
////
///////////////////////////////////
window.addEventListener("resize", onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onSelectStart(event) {
var controller = event.target;
var intersections = getIntersections(controller);
if (intersections.length > 0) {
var intersection = intersections[0];
var object = intersection.object;
object.material.emissive.b = 1;
controller.attach(object);
controller.userData.selected = object;
}
}
function onSelectEnd(event) {
var controller = event.target;
if (controller.userData.selected !== undefined) {
var object = controller.userData.selected;
object.material.emissive.b = 0;
group.attach(object);
controller.userData.selected = undefined;
}
}
function getIntersections(controller) {
tempMatrix.identity().extractRotation(controller.matrixWorld);
raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
return raycaster.intersectObjects(group.children);
}
function intersectObjects(controller) {
// Do not highlight when already selected
if (controller.userData.selected !== undefined) return;
var line = controller.getObjectByName("line");
var intersections = getIntersections(controller);
if (intersections.length > 0) {
var intersection = intersections[0];
////////////////////////////////////////
//// MODIFICATIONS FROM THREEJS EXAMPLE
//// check if in webXR session
//// if so, provide haptic feedback to the controller that raycasted onto object
//// (only if haptic actuator is available)
const session = renderer.xr.getSession();
if (session) { //only if we are in a webXR session
for (const sourceXR of session.inputSources) {
if (!sourceXR.gamepad) continue;
if (
sourceXR &&
sourceXR.gamepad &&
sourceXR.gamepad.hapticActuators &&
sourceXR.gamepad.hapticActuators[0] &&
sourceXR.handedness == controller.name
) {
var didPulse = sourceXR.gamepad.hapticActuators[0].pulse(0.8, 100);
}
}
}
////
////////////////////////////////
var object = intersection.object;
object.material.emissive.r = 1;
intersected.push(object);
line.scale.z = intersection.distance;
} else {
line.scale.z = 50; //MODIFIED AS OUR SCENE IS LARGER
}
}
function cleanIntersected() {
while (intersected.length) {
var object = intersected.pop();
object.material.emissive.r = 0;
}
}
function animate() {
renderer.setAnimationLoop(render);
}
function render() {
cleanIntersected();
intersectObjects(controller1);
intersectObjects(controller2);
////////////////////////////////////////
//// MODIFICATIONS FROM THREEJS EXAMPLE
//add gamepad polling for webxr to renderloop
dollyMove();
////
//////////////////////////////////////
renderer.render(scene, camera);
}
////////////////////////////////////////
//// MODIFICATIONS FROM THREEJS EXAMPLE
//// New dollyMove() function
//// this function polls gamepad and keeps track of its state changes to create 'events'
function dollyMove() {
var handedness = "unknown";
//determine if we are in an xr session
const session = renderer.xr.getSession();
let i = 0;
if (session) {
let xrCamera = renderer.xr.getCamera(camera);
xrCamera.getWorldDirection(cameraVector);
//a check to prevent console errors if only one input source
if (isIterable(session.inputSources)) {
for (const source of session.inputSources) {
if (source && source.handedness) {
handedness = source.handedness; //left or right controllers
}
if (!source.gamepad) continue;
const controller = renderer.xr.getController(i++);
const old = prevGamePads.get(source);
const data = {
handedness: handedness,
buttons: source.gamepad.buttons.map((b) => b.value),
axes: source.gamepad.axes.slice(0)
};
if (old) {
data.buttons.forEach((value, i) => {
//handlers for buttons
if (value !== old.buttons[i] || Math.abs(value) > 0.8) {
//check if it is 'all the way pushed'
if (value === 1) {
//console.log("Button" + i + "Down");
if (data.handedness == "left") {
//console.log("Left Paddle Down");
if (i == 1) {
dolly.rotateY(-THREE.Math.degToRad(1));
}
if (i == 3) {
//reset teleport to home position
dolly.position.x = 0;
dolly.position.y = 5;
dolly.position.z = 0;
}
} else {
//console.log("Right Paddle Down");
if (i == 1) {
dolly.rotateY(THREE.Math.degToRad(1));
}
}
} else {
// console.log("Button" + i + "Up");
if (i == 1) {
//use the paddle buttons to rotate
if (data.handedness == "left") {
//console.log("Left Paddle Down");
dolly.rotateY(-THREE.Math.degToRad(Math.abs(value)));
} else {
//console.log("Right Paddle Down");
dolly.rotateY(THREE.Math.degToRad(Math.abs(value)));
}
}
}
}
});
data.axes.forEach((value, i) => {
//handlers for thumbsticks
//if thumbstick axis has moved beyond the minimum threshold from center, windows mixed reality seems to wander up to about .17 with no input
if (Math.abs(value) > 0.2) {
//set the speedFactor per axis, with acceleration when holding above threshold, up to a max speed
speedFactor[i] > 1 ? (speedFactor[i] = 1) : (speedFactor[i] *= 1.001);
console.log(value, speedFactor[i], i);
if (i == 2) {
//left and right axis on thumbsticks
if (data.handedness == "left") {
// (data.axes[2] > 0) ? console.log('left on left thumbstick') : console.log('right on left thumbstick')
//move our dolly
//we reverse the vectors 90degrees so we can do straffing side to side movement
dolly.position.x -= cameraVector.z * speedFactor[i] * data.axes[2];
dolly.position.z += cameraVector.x * speedFactor[i] * data.axes[2];
//provide haptic feedback if available in browser
if (
source.gamepad.hapticActuators &&
source.gamepad.hapticActuators[0]
) {
var pulseStrength = Math.abs(data.axes[2]) + Math.abs(data.axes[3]);
if (pulseStrength > 0.75) {
pulseStrength = 0.75;
}
var didPulse = source.gamepad.hapticActuators[0].pulse(
pulseStrength,
100
);
}
} else {
// (data.axes[2] > 0) ? console.log('left on right thumbstick') : console.log('right on right thumbstick')
dolly.rotateY(-THREE.Math.degToRad(data.axes[2]));
}
controls.update();
}
if (i == 3) {
//up and down axis on thumbsticks
if (data.handedness == "left") {
// (data.axes[3] > 0) ? console.log('up on left thumbstick') : console.log('down on left thumbstick')
dolly.position.y -= speedFactor[i] * data.axes[3];
//provide haptic feedback if available in browser
if (
source.gamepad.hapticActuators &&
source.gamepad.hapticActuators[0]
) {
var pulseStrength = Math.abs(data.axes[3]);
if (pulseStrength > 0.75) {
pulseStrength = 0.75;
}
var didPulse = source.gamepad.hapticActuators[0].pulse(
pulseStrength,
100
);
}
} else {
// (data.axes[3] > 0) ? console.log('up on right thumbstick') : console.log('down on right thumbstick')
dolly.position.x -= cameraVector.x * speedFactor[i] * data.axes[3];
dolly.position.z -= cameraVector.z * speedFactor[i] * data.axes[3];
//provide haptic feedback if available in browser
if (
source.gamepad.hapticActuators &&
source.gamepad.hapticActuators[0]
) {
var pulseStrength = Math.abs(data.axes[2]) + Math.abs(data.axes[3]);
if (pulseStrength > 0.75) {
pulseStrength = 0.75;
}
var didPulse = source.gamepad.hapticActuators[0].pulse(
pulseStrength,
100
);
}
}
controls.update();
}
} else {
//axis below threshold - reset the speedFactor if it is greater than zero or 0.025 but below our threshold
if (Math.abs(value) > 0.025) {
speedFactor[i] = 0.025;
}
}
});
}
///store this frames data to compate with in the next frame
prevGamePads.set(source, data);
}
}
}
}
function isIterable(obj) { //function to check if object is iterable
// checks for null and undefined
if (obj == null) {
return false;
}
return typeof obj[Symbol.iterator] === "function";
}
////
/////////////////////////////////////

How to select single material using Raycaster and GLTF loader

I am loading a model through GLTF loader. I want to select a mesh on mouse hover. Everything is going cool, but the main problem is when hovering its changing the color all material whose name is same (as per my researches). When i am debugging its INTERSECTED returning single material. I don't know why its happening. After many researches i am asking this question here.
Please see my code below.
<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
var container, scene, camera, renderer, controls, stats;
var clock = new THREE.Clock();
var xyzz;
// custom global variables
var cube;
var projector,
mouse = {
x: 0,
y: 0
},
INTERSECTED;
init();
animate();
// FUNCTIONS
function init() {
// SCENE
scene = new THREE.Scene();
// CAMERA
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
NEAR = 0.1,
FAR = 20000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0, 0, 0);
camera.lookAt(scene.position);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
container = document.getElementById("ThreeJS");
container.appendChild(renderer.domElement);
// EVENTS
// CONTROLS
controls = new THREE.OrbitControls(camera, renderer.domElement);
// STATS
stats = new Stats();
stats.domElement.style.position = "absolute";
stats.domElement.style.bottom = "0px";
stats.domElement.style.zIndex = 100;
container.appendChild(stats.domElement);
// LIGHT
const skyColor = 0xb1e1ff; // light blue
const groundColor = 0xb97a20; // brownish orange
const intensity = 5;
const light = new THREE.HemisphereLight(
skyColor,
groundColor,
intensity
);
scene.add(light);
scene.background = new THREE.Color("#fff");
// GLTF Loader
function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.Math.degToRad(camera.fov * 0.5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
// compute a unit vector that points in the direction the camera is now
// in the xz plane from the center of the box
const direction = new THREE.Vector3()
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
// move the camera to a position distance units way from the center
// in whatever direction the camera was from the center already
camera.position.copy(
direction.multiplyScalar(distance).add(boxCenter)
);
// pick some near and far values for the frustum that
// will contain the box.
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
// point the camera to look at the center of the box
// camera.position.set(0, 150, 400);
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
var loader = new THREE.GLTFLoader();
loader.load(
// resource URL
"models/gltf/DamagedHelmet/glTF/50423_ Revit Model.gltf",
// called when the resource is loaded
function(gltf) {
const root = gltf.scene;
scene.add(root);
// console.log(dumpObject(root).join("\n"));
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
// set the camera to frame the box
frameArea(boxSize * 1, boxSize, boxCenter, camera);
// update the Trackball controls to handle the new size
controls.maxDistance = boxSize * 10;
controls.target.copy(boxCenter);
controls.update();
},
// called while loading is progressing
function(xhr) {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
// called when loading has errors
function(error) {
debugger;
console.log("An error happened");
}
);
projector = new THREE.Projector();
// when the mouse moves, call the given function
document.addEventListener("mousemove", onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function update() {
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
vector.unproject(camera);
var ray = new THREE.Raycaster(
camera.position,
vector.sub(camera.position).normalize()
);
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects(scene.children, true);
// INTERSECTED = the object in the scene currently closest to the camera
// and intersected by the Ray projected from the mouse position
// if there is one (or more) intersections
if (intersects.length > 0) {
// if the closest object intersected is not the currently stored intersection object
if (intersects[0].object != INTERSECTED) {
// restore previous intersection object (if it exists) to its original color
if (INTERSECTED) {
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
}
// store reference to closest object as current intersection object
INTERSECTED = intersects[0].object;
console.log(INTERSECTED);
// store color of closest object (for later restoration)
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// set a new color for closest object
INTERSECTED.material.color.setHex(0xffff00);
}
}
// there are no intersections
else {
// restore previous intersection object (if it exists) to its original color
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
}
controls.update();
stats.update();
}
function render() {
renderer.render(scene, camera);
}
function dumpObject(obj, lines = [], isLast = true, prefix = "") {
const localPrefix = isLast ? "└─" : "├─";
lines.push(
`${prefix}${prefix ? localPrefix : ""}${obj.name || "*no-name*"} [${
obj.type
}]`
);
const newPrefix = prefix + (isLast ? " " : "│ ");
const lastNdx = obj.children.length - 1;
obj.children.forEach((child, ndx) => {
const isLast = ndx === lastNdx;
dumpObject(child, lines, isLast, newPrefix);
});
return lines;
}
Please help me out.
I didn't read through all of the code, but I think this might already help:
In your intersection-handler, you are updating the color of the material assigned to the object (INTERSECTED.material.color.setHex(...)). This will cause the problems you describe as identical materials are very likely reused for multiple objects. To prevent that, you could use a different material:
const hightlightMaterial = new MeshStandardMaterial(...);
and instead of just updating the color, replace the material:
INTERSECTED.originalMaterial = INTERSECTED.material;
INTERSECTED.material = highlightMaterial;
Restore the original when "unhighlighting" the object:
INTERSECTED.material = INTERSECTED.originalMaterial;
delete INTERSECTED.originalMaterial;
If you need the highlightMaterial to retain other material-properties from the original, you can do this to copy over all material properties beforehand:
highlightMaterial.copy(INTERSECTED.material);
highlightMaterial.color.copy(highlightColor);

smooth terrain from height map three js

I am currently trying to create some smooth terrain using the PlaneBufferGeometry of three.js from a height map I got from Google Images:
https://forums.unrealengine.com/filedata/fetch?id=1192062&d=1471726925
but the result is kinda choppy..
(Sorry, this is my first question and evidently I need 10 reputation to post images, otherwise I would.. but here's an even better thing: a live demo! left click + drag to rotate, scroll to zoom)
I want, like i said, a smooth terrain, so am I doing something wrong or is this just the result and i need to smoothen it afterwards somehow?
Also here is my code:
const IMAGE_SRC = 'terrain2.png';
const SIZE_AMPLIFIER = 5;
const HEIGHT_AMPLIFIER = 10;
var WIDTH;
var HEIGHT;
var container = jQuery('#wrapper');
var scene, camera, renderer, controls;
var data, plane;
image();
// init();
function image() {
var image = new Image();
image.src = IMAGE_SRC;
image.onload = function() {
WIDTH = image.width;
HEIGHT = image.height;
var canvas = document.createElement('canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;
var context = canvas.getContext('2d');
console.log('image loaded');
context.drawImage(image, 0, 0);
data = context.getImageData(0, 0, WIDTH, HEIGHT).data;
console.log(data);
init();
}
}
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(0, 1000, 0);
// initialize scene
scene = new THREE.Scene();
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.append(renderer.domElement);
// initialize controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = .05;
controls.rotateSpeed = .1;
// initialize plane
plane = new THREE.PlaneBufferGeometry(WIDTH * SIZE_AMPLIFIER, HEIGHT * SIZE_AMPLIFIER, WIDTH - 1, HEIGHT - 1);
plane.castShadow = true;
plane.receiveShadow = true;
var vertices = plane.attributes.position.array;
// apply height map to vertices of plane
for(i=0, j=2; i < data.length; i += 4, j += 3) {
vertices[j] = data[i] * HEIGHT_AMPLIFIER;
}
var material = new THREE.MeshPhongMaterial({color: 0xFFFFFF, side: THREE.DoubleSide, shading: THREE.FlatShading});
var mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = - Math.PI / 2;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
plane.computeFaceNormals();
plane.computeVertexNormals();
scene.add(mesh);
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
}
The result is jagged because the height map has low color depth. I took the liberty of coloring a portion of the height map (Paint bucket in Photoshop, 0 tolerance, non-continuous) so you can see for yourself how large are the areas which have the same color value, i.e. the same height.
The areas of the same color will create a plateau in your terrain. That's why you have plateaus and sharp steps in your terrain.
What you can do is either smooth out the Z values of the geometry or use a height map which utilizes 16bits or event 32bits for height information. The current height map only uses 8bits, i.e. 256 values.
One thing you could do to smooth things out a bit is to sample more than just a single pixel from the heightmap. Right now, the vertex indices directly correspond to the pixel position in the data-array. And you just update the z-value from the image.
for(i=0, j=2; i < data.length; i += 4, j += 3) {
vertices[j] = data[i] * HEIGHT_AMPLIFIER;
}
Instead you could do things like this:
get multiple samples with certain offsets along the x/y axes
compute an (weighted) average value from the samples
That way you would get some smoothing at the borders of the same-height areas.
The second option is to use something like a blur-kernel (gaussian blur is horribly expensive, but maybe something like a fast box-blur would work for you).
As you are very limited in resolution due to just using a single byte, you should convert that image to float32 first:
const highResData = new Float32Array(data.length / 4);
for (let i = 0; i < highResData.length; i++) {
highResData[i] = data[4 * i] / 255;
}
Now the data is in a format that allows for far higher numeric resolution, so we can smooth that now. You could either adjust something like the StackBlur for the float32 use-case, use ndarrays and ndarray-gaussian-filter or implement something simple yourself. The basic idea is to find an average value for all the values in those uniformly colored plateaus.
Hope that helps, good luck :)

Incrementally display three.js TubeGeometry

I am able to display a THREE.TubeGeometry figure as follows
Code below, link to jsbin
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
<script>
// global variables
var renderer;
var scene;
var camera;
var geometry;
var control;
var count = 0;
var animationTracker;
init();
drawSpline();
function init()
{
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor('lightgray', 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = 0;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
}
function drawSpline(numPoints)
{
var numPoints = 100;
// var start = new THREE.Vector3(-5, 0, 20);
var start = new THREE.Vector3(-5, 0, 20);
var middle = new THREE.Vector3(0, 35, 0);
var end = new THREE.Vector3(5, 0, -20);
var curveQuad = new THREE.QuadraticBezierCurve3(start, middle, end);
var tube = new THREE.TubeGeometry(curveQuad, numPoints, 0.5, 20, false);
var mesh = new THREE.Mesh(tube, new THREE.MeshNormalMaterial({
opacity: 0.9,
transparent: true
}));
scene.add(mesh);
renderer.render(scene, camera);
}
</script>
</body>
</html>
However, I would like to display incrementally, as in, like an arc that is loading, such that it starts as the start point, draws incrementally and finally looks the below arc upon completion.
I have been putting in some effort, and was able to do this by storing all the points/coordinates covered by the arc, and drawing lines between the consecutive coordinates, such that I get the 'arc loading incrementally' feel. However, is there a better way to achieve this? This is the link to jsbin
Adding the code here as well
<!DOCTYPE html>
<html>
<head>
<title>Incremental Spline Curve</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<script>
// global variables
var renderer;
var scene;
var camera;
var splineGeometry;
var control;
var count = 0;
var animationTracker;
// var sphereCamera;
var sphere;
var light;
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
// renderer.setClearColor(0x000000, 1.0);
renderer.setClearColor( 0xffffff, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = 0;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// //init for sphere
// sphereCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// sphereCamera.position.y = -400;
// sphereCamera.position.z = 400;
// sphereCamera.rotation.x = .70;
sphere = new THREE.Mesh(new THREE.SphereGeometry(0.8,31,31), new THREE.MeshLambertMaterial({
color: 'yellow',
}));
light = new THREE.DirectionalLight('white', 1);
// light.position.set(0,-400,400).normalize();
light.position.set(0,10,10).normalize();
//get points covered by Spline
getSplineData();
}
//save points in geometry.vertices
function getSplineData() {
var curve = new THREE.CubicBezierCurve3(
new THREE.Vector3( -5, 0, 10 ),
new THREE.Vector3(0, 20, 0 ),
new THREE.Vector3(0, 20, 0 ),
new THREE.Vector3( 2, 0, -25 )
);
splineGeometry = new THREE.Geometry();
splineGeometry.vertices = curve.getPoints( 50 );
animate();
}
//scheduler loop
function animate() {
if(count == 50)
{
cancelAnimationFrame(animationTracker);
return;
}
//add line to the scene
drawLine();
renderer.render(scene, camera);
// renderer.render(scene, sphereCamera);
count += 1;
// camera.position.z -= 0.25;
// camera.position.y -= 0.25;
animationTracker = requestAnimationFrame(animate);
}
function drawLine() {
var lineGeometry = new THREE.Geometry();
var lineMaterial = new THREE.LineBasicMaterial({
color: 0x0000ff
});
console.log(splineGeometry.vertices[count]);
console.log(splineGeometry.vertices[count+1]);
lineGeometry.vertices.push(
splineGeometry.vertices[count],
splineGeometry.vertices[count+1]
);
var line = new THREE.Line( lineGeometry, lineMaterial );
scene.add( line );
}
// calls the init function when the window is done loading.
window.onload = init;
</script>
<body>
</body>
</html>
Drawback : The drawback of doing it the above way is that, end of the day, I'm drawing a line between consecutive points, and so I lose out on a lot of the effects possible in TubeGeometry such as, thickness, transparency etc.
Please suggest me an alternative way to get a smooth incremental load for the TubeGeometry.
THREE.TubeGeometry returns a THREE.BufferGeometry.
With THREE.BufferGeometry, you have access to a property drawRange that you can set to animate the drawing of the mesh:
let nEnd = 0, nMax, nStep = 90; // 30 faces * 3 vertices/face
...
const geometry = new THREE.TubeGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );
nMax = geometry.attributes.position.count;
...
function animate() {
requestAnimationFrame( animate );
nEnd = ( nEnd + nStep ) % nMax;
mesh.geometry.setDrawRange( 0, nEnd );
renderer.render( scene, camera );
}
EDIT: For another approach, see this SO answer.
three.js r.144
Normally you would be able to use the method .getPointAt() to "get a vector for point at relative position in curve according to arc length" to get a point at a certain percentage of the length of the curve.
So normally if you want to draw 70% of the curve and a full curve is drawn in 100 segments. Then you could do:
var percentage = 70;
var curvePath = new THREE.CurvePath();
var end, start = curveQuad.getPointAt( 0 );
for(var i = 1; i < percentage; i++){
end = curveQuad.getPointAt( percentage / 100 );
lineCurve = new THREE.LineCurve( start, end );
curvePath.add( lineCurve );
start = end;
}
But I think this is not working for your curveQuad since the getPointAt method is not implemented for this type. A work around is to get a 100 points for your curve in an array like this:
points = curve.getPoints(100);
And then you can do almost the same:
var percentage = 70;
var curvePath = new THREE.CurvePath();
var end, start = points[ 0 ];
for(var i = 1; i < percentage; i++){
end = points[ percentage ]
lineCurve = new THREE.LineCurve( start, end );
curvePath.add( lineCurve );
start = end;
}
now your curvePath holds the line segments you want to use for drawing the tube:
// draw the geometry
var radius = 5, radiusSegments = 8, closed = false;
var geometry = new THREE.TubeGeometry(curvePath, percentage, radius, radiusSegments, closed);
Here a fiddle with a demonstration on how to use this dynamically
I'm not really that familiar with three.js. But I think I can be of assistance. I have two solutions for you. Both based on the same principle: build a new TubeGeometry or rebuild the current one, around a new curve.
Solution 1 (Simple):
var CurveSection = THREE.Curve.create(function(base, from, to) {
this.base = base;
this.from = from;
this.to = to;
}, function(t) {
return this.base.getPoint((1 - t) * this.from + t * this.to);
});
You define a new type of curve which just selects a segment out of a given curve. Usage:
var curve = new CurveSection(yourCurve, 0, .76); // Where .76 is your percentage
Now you can build a new tube.
Solution 2 (Mathematics!):
You are using for your arc a quadratic bezier curve, that's awesome! This curve is a parabola. You want just a segment of that parabola and that is again a parabola, just with other bounds.
What we need is a section of the bezier curve. Let's say the curve is defined by A (start), B (direction), C (end). If we want to change the start to a point D and the end to a point F we need the point E that is the direction of the curve in D and F. So the tangents to our parabola in D and F have to intersect in E. So the following code will give us the desired result:
// Calculates the instersection point of Line3 l1 and Line3 l2.
function intersection(l1, l2) {
var A = l1.start;
var P = l2.closestPointToPoint(A);
var Q = l1.closestPointToPoint(P);
var l = P.distanceToSquared(A) / Q.distanceTo(A);
var d = (new THREE.Vector3()).subVectors(Q, A);
return d.multiplyScalar(l / d.length()).add(A);
}
// Calculate the tangentVector of the bezier-curve
function tangentQuadraticBezier(bezier, t) {
var s = bezier.v0,
m = bezier.v1,
e = bezier.v2;
return new THREE.Vector3(
THREE.CurveUtils.tangentQuadraticBezier(t, s.x, m.x, e.x),
THREE.CurveUtils.tangentQuadraticBezier(t, s.y, m.y, e.y),
THREE.CurveUtils.tangentQuadraticBezier(t, s.z, m.z, e.z)
);
}
// Returns a new QuadraticBezierCurve3 with the new bounds.
function sectionInQuadraticBezier(bezier, from, to) {
var s = bezier.v0,
m = bezier.v1,
e = bezier.v2;
var ns = bezier.getPoint(from),
ne = bezier.getPoint(to);
var nm = intersection(
new THREE.Line3(ns, tangentQuadraticBezier(bezier, from).add(ns)),
new THREE.Line3(ne, tangentQuadraticBezier(bezier, to).add(ne))
);
return new THREE.QuadraticBezierCurve3(ns, nm, ne);
}
This is a very mathematical way, but if you should need the special properties of a Bezier curve, this is the way to go.
Note: The first solution is the simplest. I am not familiar with Three.js so I wouldn't know what the most efficient way to implement the animation is. Three.js doesn't seem to use the special properties of a bezier curve so maybe solution 2 isn't that useful.
I hope you have gotten something useful out of this.

Dynamic height of cube with fixed floor

I am creating a dynamic cube that can be dynamically changed by scaling its mesh. The issue is, I would like to keep it fixed to the floor when modifying its height. This is a snippet of my code:
function init() {
// Floor position
floor = new THREE.Mesh( shadowGeo, shadowMaterial );
floor.position.y = 0;
floor.rotation.x = - Math.PI / 2;
scene.add( floor );
// Defines the cube and its original position
var BoxGeometry = new THREE.BoxGeometry(50, 50, 50);
var boxMaterial = new THREE.MeshLambertMaterial({color: 0x000088});
cube = new THREE.Mesh(BoxGeometry, boxMaterial);
cube.position.set(0,30,0);
scene.add(cube);
// GUI PANEL INTERACTION
// Now the GUI panel for the interaction is defined
gui = new dat.GUI();
parameters = {
height: 1,
reset: function() {resetCube()}
}
// Define the second folder which takes care of the scaling of the cube
var folder1 = gui.addFolder("Dimensions");
var cubeHeight = folder2.add(parameters, "height").min(0).max(200).step(1);
folder1.open();
// Function taking care of the cube changes
cubeHeight.onChange(function(value){cube.scale.y = value;});
gui.open();
}
// Update cube characteristics
function updateCube() {
cube.scale.y = parameters.y;
}
// Reset cube settings
function resetCube() {
parameters.height = 1;
updateCube();
}
// Rest of the code
I have searched around and I saw this similar topic, but still it does not properly explain how to modify dimensions when the object with a reference floor. Do you know how can I solve this issue?
Changed your .onChange() function to have the cube stay on the ground:
// Function taking care of the cube changes
cubeHeightScale.onChange(
function(value)
{
cube.scale.y = value;
cube.position.y = (cubeHeight * value) / 2;
} );
Here is a fiddle to check the changes live: http://jsfiddle.net/Lsjh965o/
three.js r71

Categories

Resources