SceneKit. How to rotate SCNNode around its axis - rotation

I'm trying to achieve same rotation as in scene editor but with code so object always rotate around selected axis, however if I look at angles(x,y,z) in editor they changes quite randomly
![Local node axis][1]
I've tried to use quaternions, but can't get it working
PS. my bad was using rotation property instead of orientation both SCNVector4, have read doc properly)

Seems you was really close, you had to swap parameters in GLKQuaternionMultiply call. I used solution in https://stackoverflow.com/a/39813058/4124265 to achieve rotation only by Z axis:
let orientation = modelNode.orientation
var glQuaternion = GLKQuaternionMake(orientation.x, orientation.y, orientation.z, orientation.w)
// Rotate around Z axis
let multiplier = GLKQuaternionMakeWithAngleAndAxis(0.5, 0, 0, 1)
glQuaternion = GLKQuaternionMultiply(glQuaternion, multiplier)
modelNode.orientation = SCNQuaternion(x: glQuaternion.x, y: glQuaternion.y, z: glQuaternion.z, w: glQuaternion.w)
To rotate arround Y:
// Rotate around Y axis
let multiplier = GLKQuaternionMakeWithAngleAndAxis(0.5, 0, 1, 0)
glQuaternion = GLKQuaternionMultiply(glQuaternion, multiplier)

By setting the rotation property, the absolute rotation of the object is changed, rather than the relative rotation.
Here's some pseudo code
Compute the quaternion represents the relative rotation. I would use the GLKQuaternionMakeWithAngleAndAxis function to do so.
Apply the rotation to the orientation property:
let initial_object_orientation = rotateNode.orientation;
new_orientation = GLKQuaternionMultiply(rotation_quaternion, initial_object_orientation)
Assign the new orientation
rotatNode.orientation = new_orientation
Hope it helps.

Related

Using quaternions for rotation causes my object to scale at specific angle

I am trying to build a rotation controller for my threejs objects. My rotation method is the following:
function rotate(axis, angle) {
rotMat = new THREE.Matrix4().makeRotationAxis(axis, angle);
rotMat.multiply(mesh.matrix);
rotQuat = new THREE.Quaternion().setFromRotationMatrix(rotMat);
mesh.quaternion.copy(rotQuat);
mesh.updateMatrix();
}
I need to do it this way in order to have a rotation around the world axes and not the local axes (related to this post -> I also cannot use the Euler rotation member here because of a problem i describe here)
Getting to my problem:
I made this JSFiddle which shows the issue pretty good.
How to recreate:
1) Open the fiddle link.
2) Press X, Y or Z on your keyboard to enter the rotation mode for the desired axis.
3) Hold 'Arrow Up' key and rotate as long as the 'strange' scaling occurs. Should happen at an angle of 90-100 degrees. Note that the scaling continues if you keep rotating
Also note that i decrease the rotation step size (rotation speed) when getting to the specific angle area. The scaling only occurs when the rotation step size is quite small.
My question is:
Does somebody know why a rotation is causing a scale?
The reason why this is happening is because you need to feed a 'pure' rotation matrix to the Quaternion.setFromRotationMatrix method. So changing the rotate function to the following will work:
function rotate (axis, angle) {
rotMat = new THREE.Matrix4().makeRotationAxis(axis, angle);
rotMat.multiply(mesh.matrix);
var rotMat2 = new THREE.Matrix4().extractRotation(rotMat);
rotQuat = new THREE.Quaternion().setFromRotationMatrix(rotMat2);
mesh.quaternion.copy(rotQuat);
mesh.updateMatrix();
}

How to affect background brightness based on camera rotation using OrbitControls.js

(Hello, it's my first ever post here)
here's what I'd like to incorporate in this simple example:
I would like to make the background turn from light to dark gradually when the user is closer to a particular orientation – in this case (example above) the desired orientation is a steep angle so that the foreshortened anamorphic image looks like a regular skull (the value of the background indicating the angle user should aim for – kind of like playing Hot and Cold)
when the user reaches the desired orientation (the background is then accordingly 100% dark) I would like it to lock rotation and trigger a video file in the background or a pop up window.
I assume it has to do with accessing the camera rotation values inside OrbitControls and setting some kind of an Event?? i have no idea how to access it.
Any kind of help, suggestions to edit the thread or explanation would be greatly appreciated, thank you so much in advance!
You could use camera.position to calculate the best vantage point. First, you have to figure out what the desired position is (I'm not sure how the wooden board is being placed, but this position seems to be close to: { x: 6.8, y: 0.6, z: -1.8})
var vantagePoint = new THREE.Vector3(6.8, 0.6, -1.8);
var distance = 100;
var normalized = 1;
var endColor = new THREE.Color(0xff9900);
var startColor = new THREE.Color(0x0099ff);
scene.background = startColor;
animate() {
distance = vantagePoint.distanceTo(camera.position);
normalized = THREE.Math.smoothstep(distance, 5, 100); // Converts [1, 100] => [0, 1]
// Resets the color on each frame
startColor.set(0x0099ff);
startColor.lerp(endColor, normalized);
}
The closer to 0 you are, the closer you are to seeing the skull. You can then use that value to change the color of scene.background. Anything beyond 10 and you're 'cold', and you get hotter as you approach 0.
https://threejs.org/docs/#api/en/math/Vector3.distanceTo
Update:
You can then transform the distance to a normalized value in the range of [0, 1] by using Math.smoothstep(). Then interpolate the value of the colors with this normalized value using Color.lerp

Rotate camera based on angle

I would like to rotate an object on a certain angle along Y axis.
Based on this answer How to rotate a Three.js Vector3 around an axis? I suppose to get an updated vector.
My code is :
var vec = new THREE.Vector3( 0,0,0 );
var axis = new THREE.Vector3( 0,1,0 );
var angle = Math.PI / 2;
vec.applyAxisAngle( axis, angle );
I'm using r67 and it returns me 0,0,0. I've tried r69 as well and it is returns me the same. I'm not quiet ready to move to r69. Could you guys tell me please how to do the same thing but using r67. Thanks.
Your are rotating vector (0, 0, 0) which is center and whatever angle you use to rotate center around any axis you will always get (0, 0, 0). Just imagine you are doing simple 2d rotation. After all, rotation around Y axis can be viewed as 2d rotation in X-Z plane.
Try with some other values for vec variable, for example (1, 0, 0) or (0, 0, 1) and you will see results

Three.js: change the pivot point of a sprite

I've created a 3D map and I'm labelling points on this map through Sprites. This in itself works fine, except for the positioning of the sprite labels.
Because I'm creating a map the camera can tilt from 0 to 90 degrees, while ideally the label always stays some distance directly above the item it is labelling on the screen. But unfortunately, as sprites are always centred around their origin and that overlaps the item, I have to move the sprite up on the Y world axis and with that the centre location of the sprite changes as the camera is tilted. This looks weird if the item looked at is off centre, and doesn't work too well when the camera is looking straight down.
No jsfiddle handy, but my application at http://leeft.eu/starcitizen/ should give a good impression of what it looks like.
The code of THREE.SpritePlugin suggests to me it should be possible to use "matrixWorld" to shift the sprite some distance up on the screen's Y axis while rendering, but I can't work out how to use that, nor am I entirely sure that's what I need to use in the first place.
Is it possible to shift the sprites up on the screen while rendering, or perhaps change their origin? Or is there maybe some other way I can achieve the same effect?
Three.js r.67
As suggested by WestLangley, I've created a workable solution by changing the sprite position based on the viewing angle though it took me hours to work out the few lines of code needed to get the math working. I've updated my application too, so see that for a live demo.
With the tilt angle phi and the heading angle theta as computed from the camera in OrbitControls.js the following code computes a sprite offset that does exactly what I want it to:
// Given:
// phi = tilt; 0 = top down view, 1.48 = 85 degrees (almost head on)
// theta = heading; 0 = north, < 0 looking east, > 0 looking west
// Compute an "opposite" angle; note the 'YXZ' axis order is important
var euler = new THREE.Euler( phi + Math.PI / 2, theta, 0, 'YXZ' );
// Labels are positioned 5.5 units up the Y axis relative to its parent
var spriteOffset = new THREE.Vector3( 0, -5.5, 0 );
// Rotate the offset vector to be opposite to the camera
spriteOffset.applyMatrix4( new THREE.Matrix4().makeRotationFromEuler( euler ) );
scene.traverse( function ( object ) {
if ( ( object instanceof THREE.Sprite ) && object.userData.isLabel ) {
object.position.copy( spriteOffset );
}
} );
Note for anyone using this code: that the sprite labels are children of the object group they're referring to, and this only sets a local offset from that parent object.
I had a similar problem, but with flat sprites; I put trees on a map and wanted them to rotate in such a way that they'd rotate around their base, rather than their center. To do that, i simply edited the image files of the trees to be twice as tall, with the bottom as just a transparency:
http://imgur.com/ogFxyFw
if you turn the first image into a sprite, it'll rotate around the tree's center when the camera rotates. The second tree will rotate around it's base when the camera rotates.
For your application, if you resize the textbox in such a way that the center of it would be coincide with the star; perhaps by adding a few newlines or editing the height of the sprite
This is very much a hack, but if you will only use sprites in this way, and could tolerate a global change to how sprites were rendered, you could change the following line in the compiled three.js script:
Find (ctrl+F) THREE.SpritePlugin = function, and you'll see:
this.init = function ( renderer ) {
_gl = renderer.context;
_renderer = renderer;
vertices = new Float32Array( [
- 0.5, - 0.5, 0, 0,
0.5, - 0.5, 1, 0,
0.5, 0.5, 1, 1,
- 0.5, 0.5, 0, 1
] );
I changed the definition of the array to the following:
var vertices = new Float32Array( [
- 0.5, - 0.0, 0, 0,
0.5, - 0.0, 1, 0,
0.5, 1.0, 1, 1,
- 0.5, 1.0, 0, 1
] );
And now all my sprites render with the rotation origin at the bottom.
If you use the minified version, search for THREE.SpritePlugin=function and move the cursor right until you find the Float32Array defined, and make the same changes there.
Note: this changes how things render only when using WebGL. For the canvas renderer you'll have to play a function called renderSprite() in the THREE.CanvasRenderer. I suspect playing with these lines will do it:
var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite
_elemBox.min.set( v1.x - dist, v1.y - dist );
_elemBox.max.set( v1.x + dist, v1.y + dist );
This function will also be a lot more difficult to find in the minified version, since renderSprite() is not an outward facing function, it'll likely be renamed to something obscure and small.
Note 2: I did try making these modifications with "polyfills" (or rather, redefining the SpritePlugin after Three is defined), but it caused major problems with things not being properly defined for some reason. Scoping is also an issue with the "polyfill" method.
Note 3: My version of three.js is r69. So there may be differences above.

Love2d Rotating an image

I would like to rotate an image in Love2D.
I have found a documentation on love2d.org: https://love2d.org/wiki/love.graphics.rotate
But I can't seem to get it to work when I try to load an image.
Heres my code:
local angle = 0
function love.load()
g1 = love.graphics.newImage("1.png")
end
function love.draw()
width = 100
height = 100
love.graphics.translate(width/2, height/2)
love.graphics.rotate(angle)
love.graphics.translate(-width/2, -height/2)
love.graphics.draw(g1, width, height)
end
function love.update(dt)
love.timer.sleep(10)
angle = angle + dt * math.pi/2
angle = angle % (2*math.pi)
end
Could anyone show me an simple example of rotating an image in love2d?
https://love2d.org/wiki/love.graphics.draw
You may be better off using the fourth argument, shown as 'r' to rotate images, such as:
love.graphics.draw(image, x, y, math.pi/4)
It's not worth the trouble of using the translate functions for a single draw, and keeping those for when you're batching many draws at once.
Your code worked perfectly for me, aside from a small unrelated issue (love.timer.sleep uses seconds in LÖVE 0.8.0).
We will be able to help you better, and perhaps reproduce your error, if you provide us with more information.
When you say
I can't seem to get it to work when I try to load an image
..what is the result?
Is the image a white box? Does the application crash? Is there nothing on the screen?
All of these imply a image loading issue, rather than a rotation issue. Although, it could be the case that the image is rotating off of the screen.
If you continue to use translate, rotate, and scale (which is usually a good idea), I recommend you take a look at the push and pop functions.
They allow you to 'stack' transformations so you can render sub elements.
Example uses are rendering a GUI (each child pushes its translation and then renders the children) and drawing sprites on a scrolling map (the camera translates the entire map and then does for entity in entities do push() entity:draw() pop() end. Each entity can translate and rotate in local coordinates (0,0 = centre of sprite)).
love.graphics.draw( drawable, x, y, r, sx, sy, ox, oy, kx, ky )
the R is the rotation.. why don't you just set it to a variable and change it as you please? ... I'm new to programming so I may be wrong but this is how I would do it.
Example of rotating at center of image using LOVE 11.3 (Mysterious Mysteries):
function love.draw()
love.graphics.draw(img, 400,300, wheel.r, wheel.sx, wheel.sy, wheel.w / 2, wheel.h / 2)
end
function love.update(dt)
wheel.r = wheel.r + dt
end
function love.load()
wheel = {x = 0, y = 0, w = 0, h = 0, sx = 0.5, sy = 0.5, r = 0, image = "wheel.png"}
img = love.graphics.newImage(wheel.image)
wheel.w = img:getWidth()
wheel.h = img:getHeight()
end
Normaly the axis for rotating is the upper left corner. To center the axis to the middle of an image you have to use the parameters after the r parameter to half of width and half of height of the image.

Resources