Calculating matrix for bone to look at object in Three.js - javascript

I am having problems doing the calculation for a bone to "look at" an object. First off, the lookAt function is not working for me. I can kind of understand this because the bone's matrix is an identity matrix, in local space so it wont work out of the box. (doing lookAt produces strange results).
Here is what I have managed to far. It rotates the head left to right, but up and down I haven't calculated yet. I should add, it doesn't work very well at the moment :(
//local looking forawrd vector
var v1 = new THREE.Vector3(0, 0, 1);
//vector represting camera in local space?
var v2 = new THREE.Vector3(
camera.position.x - headBone.matrixWorld.elements[12],
0,
camera.position.z - headBone.matrixWorld.elements[14]
);
v2.normalize();
//seem to need this to determine whether to rotate left or right
var mult = 1.0;
if(camera.position.x - headBone.matrixWorld.elements[12] < 0.0000) {
mult = -1.0;
}
var fAng = v1.angleTo(v2) * mult;
//var headUD = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), 0.0);
headBone.quaternion.setFromAxisAngle(new THREE.Vector3(0,0,-1), fAng);
//headBone.quaternion.multiply(headUD);
Suggestions on how to do this properly would be GREATLY appeciated as I can't figure out the math at the moment. In the mean time I will continue to do what feels like hacking away...
If it helps, below is the worldMatrix of the headBone at rest:
0.9950730800628662, -0.02474924363195896, 0.09600517153739929, 0
-0.09914379566907883, -0.2472541183233261, 0.9638656973838806, 0
-0.00011727288801921532, -0.9686352014541626, -0.24848966300487518, 0
0.5000047087669373, 1.5461946725845337, -0.01913299970328808, 1
UPDATE
I have updated my code. It does work when I rotate on 1 axis (x axis to move head up and down towards camera or z axis to move it left or right towards camera) but when I attempt to combine those 2 rotations things go haywire when I am at the sides of the character the head starts to spin.
function lookAt() {
var v1 = new THREE.Vector3(0, 0, 1);
var v2 = new THREE.Vector3(
camera.position.x - headBone.matrixWorld.elements[12],
0,
camera.position.z - headBone.matrixWorld.elements[14]
).normalize();
var mult = 1.0;
if(camera.position.x - headBone.matrixWorld.elements[12] < 0.0000) {
mult = -1.0;
}
var fAng = v2.angleTo(v1) * mult;
v2 = new THREE.Vector3(
0,
camera.position.y - headBone.matrixWorld.elements[13],
camera.position.z - headBone.matrixWorld.elements[14]
);
mult = 1.0;
if(camera.position.y - headBone.matrixWorld.elements[13] > 0.0000) {
mult = -1.0;
}
fAng2 = v2.angleTo(v1) * mult;
headBone.rotation.set(
Math.max(-0.5, Math.min(fAng2, 0.1)),
0,
-Math.max(-1.0, Math.min(fAng, 1.0))
);
}
I can separate out the quaternions like so:
//head left and right to match camera
var Q1 = new THREE.Quaternion().setFromEuler( new THREE.Euler(0,0,-fAng), false );
//head up and down to match camera
var Q2 = new THREE.Quaternion().setFromEuler( new THREE.Euler(fAng2,0,0), false );
Q2.multiply(Q1);
headBone.quaternion.copy(Q2);
But this still does not track the camera properly. Note that applying one or the other does work.

I have reached an acceptable answer. I found a way to make the bone look at an arbitrary point. Perhaps this could be expanded on and included in THREE.js.
This still suffers from problems and does not seem 100% accurate. For example I placed this on my head bone, and it goes upside down when I am pointing behind the character. Does anyone know how it can be improved? Please comment!
Also note that I am pointing the bone's Z axis THREE.Vector3(0,0,1);. It could be moved to a parameter.
function boneLookAt(bone, position) {
var target = new THREE.Vector3(
position.x - bone.matrixWorld.elements[12],
position.y - bone.matrixWorld.elements[13],
position.z - bone.matrixWorld.elements[14]
).normalize();
var v = new THREE.Vector3(0,0,1);
var q = new THREE.Quaternion().setFromUnitVectors( v, target );
var tmp = q.z;
q.z = -q.y;
q.y = tmp;
bone.quaternion.copy(q);
}

Related

Prevent model from rolling when applying quaternion

How do I rotate a model and keep the model from rolling sideways? Using quaternions work fine when applied directly on an axis. As soon as I do more than one axis, the model starts twisting.
Original orientation is 0,0,1
Rotated to 1,0,0 works fine.
Rotated to 1,1,0 does not work. It starts rolling/twisting around its original z axis.
Does anyone know how to fix this? It essentially needs to lock the z-axis when applying the quaternion.
Here is the basic code:
//Box natural direction
let vB = new THREE.Vector3(0, 0, 1);
//Cable direction
let vC = new THREE.Vector3(1, 1, 0).normalize();
//Quatonion
let q = new THREE.Quaternion();
q.setFromUnitVectors(vB, vC);
//Box with connection
const boxG = new THREE.BoxGeometry(4, 8, 10);
const boxM = new THREE.Mesh(boxG, matSS);
const conG = new THREE.CylinderGeometry(0.5, 0.5, 2, 16);
conG.rotateX(Math.PI / 2);
const conM = new THREE.Mesh(conG, matSS);
conM.translateZ(5);
boxM.applyQuaternion(q);
boxM.add(conM);
scene.add(boxM);
//Cable to connect
const cabG = new THREE.CylinderGeometry(0.25, 0.25, 100, 16);
cabG.rotateX(Math.PI / 2);
const cabM = new THREE.Mesh(cabG, matBL);
const cabP = vC.clone().setLength(50);
cabM.applyQuaternion(q);
cabM.position.set(cabP.x,cabP.y,cabP.z);
scene.add(cabM);
Tried a number of things like rotating the Z-axis back, but that did not work.
Depending on how z-axis to be locked (respect to which plane), solution varies.
Solution #1: Polar angle rotation respect to Z-axis + azimuthal angle rotation in X-Y plane
In case box natural direction coincides pole,
it's possible to control the box direction by simply specifying another box-axis (e.g. rotation axis) to adjust roll angle.
//Box natural direction
let vB = new THREE.Vector3(0, 0, 1);
//Cable direction
let vC = new THREE.Vector3(1, 1, 0).normalize();
//Quatonion
let q = new THREE.Quaternion();
q.setFromUnitVectors(vB, vC);
console.log("q before", q);
// Box's rotation axis
let vAxisFrom = new THREE.Vector3(0, 1, 0); // or (1, 0, 0)
// Axis of quaternion rotation (from vB to vC)
let vAxisTo = vB.clone().cross(vC).normalize();
let q_roll = new THREE.Quaternion();
q_roll.setFromUnitVectors(vAxisFrom, vAxisTo);
q.multiply(q_roll);
console.log("q after", q);
Solution #2: Polar angle rotation respect to Y-axis + azimuthal angle rotation in Z-X plane
In this case, composing quaternion as a combination of yaw angle rotation around Y-axis and pitch angle rotation around X axis would be a simple way.
//Box natural direction
let vB = new THREE.Vector3(0, 0, 1);
//Cable direction
let vC = new THREE.Vector3(1, 1, 0).normalize();
//Quatonion
let q = new THREE.Quaternion();
let vAxisInPlane = vC.clone().setY(0.0).normalize();
if (vAxisInPlane.lengthSq() < 1.0) {
q.setFromUnitVectors(vB, vC);
}
else {
q.setFromUnitVectors(vB, vAxisInPlane);
console.log("q before", q);
let q_pitch = new THREE.Quaternion();
q_pitch.setFromUnitVectors(vAxisInPlane, vC);
q.premultiply(q_pitch);
console.log("q after", q);
}

How can I change this Three.js ConvexGeometry to a non-convex geometry?

I'm worked with Three.JS before, but not on meshes. I think I am approaching my problem the right way, but I'm not sure.
The Goal
I'm trying to make a 3D blobby object that has specific verticies. The direction of the verticies are fixed, but their radius from center varies. You can imagine it sort of like an audio equalizer, except radial and in 3D.
I'm open to scrapping this approach and taking a totally different one if there's some easier way to do this.
Current Progress
I took this example and cleaned/modified it to my needs. Here's the HTML and JavaScript:
HTML (disco-ball.html)
<!DOCTYPE html>
<html>
<head>
<title>Disco Ball</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/ConvexGeometry.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style type='text/css'>
/* set margin to 0 and overflow to hidden, to go fullscreen */
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<div id="Stats-output"></div>
<div id="WebGL-output"></div>
<script type="text/javascript" src="01-app.js"></script>
</body>
</html>
And the JavaScript (01-app.js):
window.onload = init;
const PARAMS = {
SHOW_SURFACE : true,
SHOW_POINTS : true,
SHOW_WIREFRAME : true,
SHOW_STATS : true
};
// once everything is loaded, we run our Three.js stuff.
function init() {
var renderParams = {
webGLRenderer : createWebGLRenderer(),
step : 0,
rotationSpeed : 0.007,
scene : new THREE.Scene(),
camera : createCamera(),
};
// Create the actual points.
var points = getPoints(
100, // Number of points (approximate)
10, // Unweighted radius
// Radius weights for a few points. This is a multiplier.
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2]
);
if (PARAMS.SHOW_STATS) {
renderParams.stats = initStats();
}
if (PARAMS.SHOW_SURFACE) {
renderParams.surface = getHullMesh(points);
renderParams.scene.add(renderParams.surface);
}
if (PARAMS.SHOW_POINTS) {
renderParams.sphereGroup = getSphereGroup(points);
renderParams.scene.add(sphereGroup);
}
render(renderParams);
}
function render(params) {
if (params.stats) {
params.stats.update();
}
if (params.sphereGroup) {
params.sphereGroup.rotation.y = params.step;
}
params.step += params.rotationSpeed;
if (params.surface) {
params.surface.rotation.y = params.step;
}
// render using requestAnimationFrame
requestAnimationFrame(function () {render(params)});
params.webGLRenderer.render(params.scene, params.camera);
}
// ******************************************************************
// Helper functions
// ******************************************************************
function getPoints (count, baseRadius, weightMap) {
// Because this is deterministic, we can pass in a weight map to adjust
// the radii.
var points = distributePoints(count,baseRadius,weightMap);
points.forEach((d,i) => {
points[i] = new THREE.Vector3(d[0],d[1],d[2]);
});
return points;
}
// A deterministic function for (approximately) evenly distributing n points
// over a sphere.
function distributePoints (count, radius, weightMap) {
// I'm not sure why I need this...
count *= 100;
var points = [];
var area = 4 * Math.PI * Math.pow(radius,2) / count;
var dist = Math.sqrt(area);
var Mtheta = Math.round(Math.PI / dist);
var distTheta = Math.PI / Mtheta
var distPhi = area / distTheta;
for (var m = 0; m < Mtheta; m++) {
let theta = (Math.PI * (m + 0.5)) / Mtheta;
let Mphi = Math.round((2 * Math.PI * Math.sin(theta)) / distPhi);
for (var n = 0; n < Mphi; n++) {
let phi = ((2 * Math.PI * n) / Mphi);
// Use the default radius, times any multiplier passed in through the
// weightMap. If no multiplier is present, use 1 to leave it
// unchanged.
points.push(createPoint(radius * (weightMap[points.length] || 1),theta,phi));
}
}
return points;
}
function createPoint (radius, theta, phi) {
var x = radius * Math.sin(theta) * Math.cos(phi);
var y = radius * Math.sin(theta) * Math.sin(phi);
var z = radius * Math.cos(theta);
return [Math.round(x), Math.round(y), Math.round(z)];
}
function createWebGLRenderer () {
// create a render and set the size
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
return webGLRenderer;
}
function createCamera () {
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 50;
camera.lookAt(new THREE.Vector3(0, 0, 0));
return camera;
}
function getSphereGroup (points) {
sphereGroup = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial({color: 0xFF0000, transparent: false});
points.forEach(function (point) {
var spGeom = new THREE.SphereGeometry(0.2);
var spMesh = new THREE.Mesh(spGeom, material);
spMesh.position.copy(point);
sphereGroup.add(spMesh);
});
return sphereGroup;
}
function getHullMesh (points) {
// use the same points to create a convexgeometry
var surfaceGeometry = new THREE.ConvexGeometry(points);
var surface = createMesh(surfaceGeometry);
return surface;
}
function createMesh(geom) {
// assign two materials
var meshMaterial = new THREE.MeshBasicMaterial({color: 0x666666, transparent: true, opacity: 0.25});
meshMaterial.side = THREE.DoubleSide;
var wireFrameMat = new THREE.MeshBasicMaterial({color: 0x0000ff});
wireFrameMat.wireframe = PARAMS.SHOW_WIREFRAME;
// create a multimaterial
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial, wireFrameMat]);
return mesh;
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
What I'm Missing
You can see that there are two points on the "ball" for which I've doubled the radius (big spikes). Of course, since I'm using a ConvexGeometry, the shape is convex... so a number of the points are hidden. What kind of ... non-convex geometry can I use to make those points no longer be hidden?
I would like to subdivide the mesh a bit so it's not simply vertex-to-vertex, but a bit smoother. How can I do that (the spikes less spikey and more blobby)?
I'd like to modify the mesh so different points spike different amounts every few seconds (I have some data arrays that describe how much). How do I modify the geometry after its been made? Ideally with some kind of tweening, but I can do without of that's extremely hard =)
Thanks!
Smooth and animate a mesh.
Three provides a huge range of options. These are just suggestions, your best bet is to read the Three documentation start point and find what suits you.
A mesh is just a set of 3D points and an array of indexes describing each triangle. Once you have built the mesh you only need to update the verts and let Three update the shader attributes, and the mesh normals
Your questions
Q1. Use Three.Geometry for the mesh.
Q2. As you are building the mesh you can use the curve helpers eg Three.CubicBezierCurve3 or Three.QuadraticBezierCurve3 or maybe your best option Three.SplineCurve
Another option is to use a modifier and create the simple mesh and then let Three subdivide the mesh for you. eg three example webgl modifier subdivision
Though not the fastest solution, if the vert count is low it will do this each frame without any loss of frame rate.
Q3. Using Three.Geometry you can can set the mesh morph targets, an array of vertices.
Another option is to use a modifier, eg three example webgl modifier subdivision
Or you can modify the vertices directly each frame.
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
geometry.vertices[ i ].x = ?;
geometry.vertices[ i ].y = ?;
geometry.vertices[ i ].z = ?;
}
mesh.geometry.verticesNeedUpdate = true;
How you do it?
There are a zillion other ways to do this. Which is the best will depend on the load and amount of complexity you want to create. Spend some time and read the doc's, and experiment.
What I would do! maybe?
I am not too sure what you are trying to achieve but the following is a way of getting some life into the animation rather than the overdone curves that seem so ubiquitous these days.
So if the vert count is not too high I would use a Three.BufferGeometry and modify the verts each frame. Rather than use curves I would weight subdivision verts to follow a polynomial curve f(x) = x^2/(x^2 + (1-x)^2) where x is the normalized distance between two control verts (note don't use x=0.5 rather subdivide the mesh in > 2 times)
EG the two control points and two smoothing verts
// two control points
const p1 = {x,y,z};
const p2 = {x,y,z};
// two weighted points
// dx,dy,dz are deltas
// w is the weighted position s-curve
// wa, and wd are acceleration and drag coefficients. Try to keep their sum < 1
const pw1 = {x, y, z, dx, dy, dz, w : 1/3, wa : 0.1,wd : 0.7};
const pw2 = {x, y, z, dx, dy, dz, w : 2/3, wa : 0.1,wd : 0.7};
// Compute w
pw1.w = Math.pow(pw1.w,2) / ( Math.pow(pw1.w,2) + Math.pow(1 - pw1.w,2));
pw2.w = Math.pow(pw2.w,2) / ( Math.pow(pw2.w,2) + Math.pow(1 - pw2.w,2));
Then for each weighted point you can find the new delta and update the position
// do for x,y,z
x = (p2.x - p1.x); // these points are updated every frame
// get the new pw1 vert target position
x = p1.x + x * w;
// get new delta
pw1.dx += (x - pw1.x) * pw1.wa; // set delta
pw1.dx *= pw1.wd;
// set new position
pw1.x += pw1.dx;
Do for all weighted points then set geometry.vertices
The wa,wd coefficients will change the behaviour of the smoothing, you will have to play with these values to suit your own taste. Must be 0 <= (wa,wd) < 1 and the sum should be wa + wd < 1. High sumed values will result in oscillations, too high and the oscillations will be uncontrolled.

three.js: rotate object around world axis combined with tween.js

I'm currently trying to tween-rotate a cube in 3D and thanks to this post (How to rotate a object on axis world three.js?) the rotation without tweening works without any problems. So currently I'm trying to transfer the rotation done by setFromRotationMatrix to something I can use as end rotation for my tween.
EDIT:
Here is what I have at the moment:
// function for rotation dice
function moveCube() {
// reset parent object rotation
pivot.rotation.set( 0, 0, 0 );
pivot.updateMatrixWorld();
// attach dice to pivot object
THREE.SceneUtils.attach( dice, scene, pivot );
// set variables for rotation direction
var rotateZ = -1;
var rotateX = -1;
if (targetRotationX < 0) {
rotateZ = 1;
} else if (targetRotationY < 0) {
rotateX = 1;
}
// check what drag direction was higher
if (Math.abs(targetRotationX) > Math.abs(targetRotationY)) {
// rotation
var newPosRotate = {z: rotateZ * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
//rotateAroundWorldAxis(dice, new THREE.Vector3(0, 0, rotateZ), Math.PI / 2);
} else {
// rotation
var newPosRotate = {x: -rotateX * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
//rotateAroundWorldAxis(dice, new THREE.Vector3(-rotateX, 0, 0), Math.PI / 2);
}
// detach dice from parent object
THREE.SceneUtils.detach( dice, pivot, scene );
}
Thanks to WestLangley I think I'm finally close to a solution that is easy to do and will serve my purpose. When initializing the pivot object I set it to the exact same position as the dice, so the rotation will still be around the center of the dice.
var loader = new THREE.JSONLoader();
loader.load(
'models/dice.json',
function ( geometry, materials ) {
material = new THREE.MeshFaceMaterial( materials );
dice = new THREE.Mesh( geometry, material );
dice.scale.set(1.95, 1.95, 1.95);
dice.position.set(2.88, 0.98, 0.96);
scene.add( dice );
pivot = new THREE.Object3D();
pivot.rotation.set( 0, 0, 0 );
pivot.position.set(dice.position.x, dice.position.y, dice.position.z);
scene.add( pivot );
}
);
The solution I have atm (upper snippet) does not attach the dice to the pivot object as parent. I'm probably overlooking something very basic ...
EDIT END
As I thought it was a really simple thing I had to do, to get it working:
I only needed to move the detachment of the child object (the dice) to the beginning of the function, instead of having it at the end of it and it works the charm.
Here's the working code:
// function for rotating dice
function moveCube() {
// detach dice from parent object first or attaching child object won't work as expected
THREE.SceneUtils.detach( dice, pivot, scene );
// reset parent object rotation
pivot.rotation.set( 0, 0, 0 );
pivot.updateMatrixWorld();
// attach dice to pivot object
THREE.SceneUtils.attach( dice, scene, pivot );
// set variables for rotation direction
var rotateZ = -1;
var rotateX = -1;
if (targetRotationX < 0) {
rotateZ = 1;
} else if (targetRotationY < 0) {
rotateX = 1;
}
// check what drag direction was higher
if (Math.abs(targetRotationX) > Math.abs(targetRotationY)) {
// rotation
var newPosRotate = {z: rotateZ * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
} else {
// rotation
var newPosRotate = {x: -rotateX * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
}
}
Thanks a lot for helping!

three.js: transforming coordinates from camera space to scene space

I'm trying to place a cube relative to the camera, rather than relative to the scene. The thing is, to place it in the scene (which I have to do make it show), I have to know the scene coordinates that correspond to the cubes camera space coordinates. I found this function "projectionMatrixInverse" in THREE.Camera. It has a nice function called "multiplyVector3" which I hoped would enable me to transform a vector (1,1,1) back to scene space like this:
var camera, myvec, multvec; // (and others)
camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 2000, 1000 );
camera.position.x = 200;
camera.position.y = 100;
camera.position.z = 200;
myvec = new THREE.Vector3(1,1,1);
console.log("myvec: ", myvec);
multvec = camera.projectionMatrixInverse.multiplyVector3(THREE.Vector3(1,1,1));
console.log("multvec: ", multvec);
the thing is, on the console i get:
myvec: Object { x=1, y=1, z=1}
TypeError: v is undefined
var vx = v.x, vy = v.y, vz = v.z;
multiplyVector3 simply doesn't accept my myvec, or says it's undefined, even though the console says it's an object. I don't get it.
The camera is located at the origin of it's coordinate system, and looks down it's negative-Z axis. A point directly in front of the camera has camera coordinates of the form ( 0, 0, z ), where z is a negative number.
You convert a point p
p = new THREE.Vector3(); // create once and reuse if you can
p.set( x, y, z );
from camera coordinates to world coordinates like so:
p.applyMatrix4( camera.matrixWorld );
camera.matrixWorld is by default updated every frame, but if need be, you can update it yourself by calling camera.updateMatrixWorld();
three.js r.95
This may also be what you're after:
scene.add( camera );
brick.position.set( 0, 0, -1 );
camera.add( brick );

Calculate near/far plane vertices using THREE.Frustum

I need some help to deal with THREE.Frustum object.
My problem:
I need to calculate near/far plane vertices; I've taken a look at these tutorials
http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/
http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/
and I've sketched this function implementing exactly (I hope so) the procedure explained (just to get top-left/right vertices, assuming the camera can only look left and right):
// Near Plane dimensions
hNear = 2 * Math.tan(camera.fov / 2) * camera.near; // height
wNear = hNear * camera.aspect; // width
// Far Plane dimensions
hFar = 2 * Math.tan(camera.fov / 2) * camera.far; // height
wFar = hFar * camera.aspect; // width
getVertices : function() {
var p = camera.position.clone();
var l = getCurrentTarget(); // see below
var u = new THREE.Vector3(0, 1, 0);
var d = new THREE.Vector3();
d.sub(l, p);
d.normalize();
var r = new THREE.Vector3();
r.cross(u, d);
r.normalize();
// Near Plane center
var dTmp = d.clone();
var nc = new THREE.Vector3();
nc.add(p, dTmp.multiplyScalar(camera.near));
// Near Plane top-right and top-left vertices
var uTmp = u.clone();
var rTmp = r.clone();
var ntr = new THREE.Vector3();
ntr.add(nc, uTmp.multiplyScalar(hNear / 2));
ntr.subSelf(rTmp.multiplyScalar(wNear / 2));
uTmp.copy(u);
rTmp.copy(r);
var ntl = new THREE.Vector3();
ntl.add(nc, uTmp.multiplyScalar(hNear / 2));
ntl.addSelf(rTmp.multiplyScalar(wNear / 2));
// Far Plane center
dTmp.copy(d);
var fc = new THREE.Vector3();
fc.add(p, dTmp.multiplyScalar(camera.far));
// Far Plane top-right and top-left vertices
uTmp.copy(u);
rTmp.copy(r);
var ftr = new THREE.Vector3();
ftr.add(fc, uTmp.multiplyScalar(hFar / 2));
ftr.subSelf(rTmp.multiplyScalar(wFar / 2));
uTmp.copy(u);
rTmp.copy(r);
var ftl = new THREE.Vector3();
ftl.add(fc, uTmp.multiplyScalar(hFar / 2));
ftl.addSelf(rTmp.multiplyScalar(wFar / 2));
getCurrentTarget : function() {
var l = new THREE.Vector3(0, 0, -100);
this.camera.updateMatrixWorld();
this.camera.matrixWorld.multiplyVector3(l);
return l;
}
This seems to work but...
My Question:
Can I obtain the same result in a more elegant (maybe more correct) way, using a THREE.Frustum object?
Three.Frustum is not really going to help you -- it is a set of planes. The good news is your solution appears correct, but there is an easier way to think about this.
The upper right corner of the near plane is a point in camera space with these coordinates:
var ntr = new THREE.Vector3( wNear / 2, hNear / 2, -camera.near );
using your definition of wNear and hNear, which are correct.
Now, making sure that camera.matrixWorld is updated, you convert that point to world coordinates like so:
camera.updateMatrixWorld();
ntr.applyMatrix4( camera.matrixWorld );
Now, flip the signs to get the other three corners, and then repeat the calculation for the far plane.
See, you had it right; you just took a more complicated route. :-)
EDIT: updated to three.js r.66

Categories

Resources