How to make Camera rotating around a point? - webgl

How to make Camera rotating around a point? I start to do this, but i have some problems when phi = 90 and -90, and with that in such way of rotating i don't roll camera
theta = - ( ( event.clientX - lastLeft ) * 360 /window.innerWidth ) + onMouseDownTheta;
phi = ( ( event.clientY - lastTop ) * 360 /window.innerHeight ) + onMouseDownPhi;
var cosPhi = Math.cos( phi * Math.PI / 180 );
var sinPhi = Math.sin( phi * Math.PI / 180 );
var sinTheta = Math.sin( theta * Math.PI / 180 );
var cosTheta = Math.cos( theta * Math.PI / 180 );
camera.position.x = - radious * sinTheta * cosPhi;
camera.position.y = radious * sinPhi;
camera.position.z = radious * cosTheta * cosPhi;
camera.lookAt(new THREE.Vector3(0,0,0))
if(phi > 90){
u = u*(-1);
camera.up = new THREE.Vector3(0, u, 0);
}
camera.updateMatrix();

Common types of camera controls have been built right into three.js and can be implemented easily.
Many of the examples already use these control interfaces, and you can take advantage of them yourself as simply as:
var scene, renderer, camera, controls, clock;
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH, HEIGHT);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT_RATIO, NEAR, FAR);
controls = new THREE.TrackballControls(camera);
clock = new THREE.Clock();
// ... (Scene setup)
requestAnimationFrame(update);
}
function update() {
requestAnimationFrame(update);
// Fetch the delta from THREE.js' clock.
var delta = clock.getDelta();
// Pass it to the camera controller.
controls.update(delta);
renderer.render();
}
// Load up the scene when the page finishes loading.
// This can be done a few different ways, this one is
// useful for script tags embedded in the <head> of the page.
window.addEventListener('load', init, false);
Alternatively you could take a look at the source code for the controls if you want to write your own, to see how the built-in controllers manipulate the camera:
https://github.com/mrdoob/three.js/blob/master/examples/js/controls/TrackballControls.js

Related

Initializing quaternions from a 9dof IMU with semi-correct values

I am building and application where I need to switch an IMU on for around 5 seconds, and get a good orientation within the first 1-2 seconds. I am using the Madwick quaternion algorithm and it works relatively well. The problems is, if the sensor is rotated about 180 degrees while it being off, it takes a long time to find this new orientation.
Is there a way to initialise the quaternion values from the IMU sensor (instead of the standard 1, 0, 0, 0) to something close to the correct orientation? I am trying to do this by first calculating roll, pitch and yaw from the imu with the following:
pitch = 180 * atan2(accelX, sqrt(accelY*accelY + accelZ*accelZ))/PI;
roll = 180 * atan2(accelY, sqrt(accelX*accelX + accelZ*accelZ))/PI;
mag_x = magReadX*cos(pitch) + magReadY*sin(roll)*sin(pitch) + magReadZ*cos(roll)*sin(pitch)
mag_y = magReadY * cos(roll) - magReadZ * sin(roll)
yaw = 180 * atan2(-mag_y,mag_x)/M_PI;
and the converting these to quaternions with the following:
double cy = cos(yaw * 0.5);
double sy = sin(yaw * 0.5);
double cp = cos(pitch * 0.5);
double sp = sin(pitch * 0.5);
double cr = cos(roll * 0.5);
double sr = sin(roll * 0.5);
Quaternion q;
q.w = cr * cp * cy + sr * sp * sy;
q.x = sr * cp * cy - cr * sp * sy;
q.y = cr * sp * cy + sr * cp * sy;
q.z = cr * cp * sy - sr * sp * cy;
However the heading seems very random. Please let me know if you know of a method to initialise the quaternions with semi-correct values.

OpenCV + OpenGL Using solvePnP camera pose - object is offset from detected marker

I have a problem in my iOS application where i attempt to obtain a view matrix using solvePnP and render a 3d cube using modern OpenGL. While my code attempts to render a 3d cube directly on top of the detected marker, it seems to render with a certain offset from the marker (see video for example)
https://www.youtube.com/watch?v=HhP5Qr3YyGI&feature=youtu.be
(on the bottom right of the image you can see an opencv render of the homography around the tracker marker. the rest of the screen is an opengl render of the camera input frame and a 3d cube at location (0,0,0).
the cube rotates and translates correctly whenever i move the marker, though it is very telling that there is some difference in the scale of translations (IE, if i move my marker 5cm in the real world, it hardly moves by 1cm on screen)
these are what i believe to be the relevant parts of the code where the error could come from :
Extracting view matrix from homography :
AVCaptureDevice *deviceInput = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceFormat *format = deviceInput.activeFormat;
CMFormatDescriptionRef fDesc = format.formatDescription;
CGSize dim = CMVideoFormatDescriptionGetPresentationDimensions(fDesc, true, true);
const float cx = float(dim.width) / 2.0;
const float cy = float(dim.height) / 2.0;
const float HFOV = format.videoFieldOfView;
const float VFOV = ((HFOV)/cx)*cy;
const float fx = abs(float(dim.width) / (2 * tan(HFOV / 180 * float(M_PI) / 2)));
const float fy = abs(float(dim.height) / (2 * tan(VFOV / 180 * float(M_PI) / 2)));
Mat camIntrinsic = Mat::zeros(3, 3, CV_64F);
camIntrinsic.at<double>(0, 0) = fx;
camIntrinsic.at<double>(0, 2) = cx;
camIntrinsic.at<double>(1, 1) = fy;
camIntrinsic.at<double>(1, 2) = cy;
camIntrinsic.at<double>(2, 2) = 1.0;
std::vector<cv::Point3f> object3dPoints;
object3dPoints.push_back(cv::Point3f(-0.5f,-0.5f,0));
object3dPoints.push_back(cv::Point3f(+0.5f,-0.5f,0));
object3dPoints.push_back(cv::Point3f(+0.5f,+0.5f,0));
object3dPoints.push_back(cv::Point3f(-0.5f,+0.5f,0));
cv::Mat raux,taux;
cv::Mat Rvec, Tvec;
cv::solvePnP(object3dPoints, mNewImageBounds, camIntrinsic, Mat(),raux,taux); //mNewImageBounds are the 4 corner of the homography detected by perspectiveTransform (the green outline seen in the image)
raux.convertTo(Rvec,CV_32F);
taux.convertTo(Tvec ,CV_64F);
Mat Rot(3,3,CV_32FC1);
Rodrigues(Rvec, Rot);
// [R | t] matrix
Mat_<double> para = Mat_<double>::eye(4,4);
Rot.convertTo(para(cv::Rect(0,0,3,3)),CV_64F);
Tvec.copyTo(para(cv::Rect(3,0,1,3)));
Mat cvToGl = Mat::zeros(4, 4, CV_64F);
cvToGl.at<double>(0, 0) = 1.0f;
cvToGl.at<double>(1, 1) = -1.0f; // Invert the y axis
cvToGl.at<double>(2, 2) = -1.0f; // invert the z axis
cvToGl.at<double>(3, 3) = 1.0f;
para = cvToGl * para;
Mat_<double> modelview_matrix;
Mat(para.t()).copyTo(modelview_matrix); // transpose to col-major for OpenGL
glm::mat4 openGLViewMatrix;
for(int col = 0; col < modelview_matrix.cols; col++)
{
for(int row = 0; row < modelview_matrix.rows; row++)
{
openGLViewMatrix[col][row] = modelview_matrix.at<double>(col,row);
}
}
i made sure the camera intrinsic matrix contains correct values, the portion which converts the opencv Mat to an opengl view matrix i believe to be correct as the cube translates and rotates in the right directions.
once the view matrix is calculated, i use it to draw the cube as follows :
_projectionMatrix = glm::perspective<float>(radians(60.0f), fabs(view.bounds.size.width / view.bounds.size.height), 0.1f, 100.0f);
_cube_ModelMatrix = glm::translate(glm::vec3(0,0,0));
const mat4 MVP = _projectionMatrix * openGLViewMatrix * _cube_ModelMatrix;
glUniformMatrix4fv(glGetUniformLocation(_cube_program, "ModelMatrix"), 1, GL_FALSE, value_ptr(MVP));
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
Is anyone able to spot my error?
You should create perspective matrix as explained here: http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl
Here is quick code:
const float fx = intrinsicParams(0, 0); // Focal length in x axis
const float fy = intrinsicParams(1, 1); // Focal length in y axis
const float cx = intrinsicParams(0, 2); // Primary point x
const float cy = intrinsicParams(1, 2); // Primary point y
projectionMatrix(0, 0) = 2.0f * fx;
projectionMatrix(0, 1) = 0.0f;
projectionMatrix(0, 2) = 0.0f;
projectionMatrix(0, 3) = 0.0f;
projectionMatrix(1, 0) = 0.0f;
projectionMatrix(1, 1) = 2.0f * fy;
projectionMatrix(1, 2) = 0.0f;
projectionMatrix(1, 3) = 0.0f;
projectionMatrix(2, 0) = 2.0f * cx - 1.0f;
projectionMatrix(2, 1) = 2.0f * cy - 1.0f;
projectionMatrix(2, 2) = -(far + near) / (far - near);
projectionMatrix(2, 3) = -1.0f;
projectionMatrix(3, 0) = 0.0f;
projectionMatrix(3, 1) = 0.0f;
projectionMatrix(3, 2) = -2.0f * far * near / (far - near);
projectionMatrix(3, 3) = 0.0f;
For more information about intrinsic matrix: http://ksimek.github.io/2013/08/13/intrinsic

How to make sprite move in a sinusoidal in cocos2d?

I have a sprite (paper plane, for example). I'd like make it move like in the picture below.
I can use lots of MoveTo and RotateBy actions to define the path by points, but it seems a bad idea to me. How can it be implemented ?
I thought it might be good to post an answer that showed the basics of how the update would work if you had explicit control over the sprite.
I was not sure if you were using Cocos2d or Cocos2d-X, but the technique applies in either case. The code is in C++ using Cocos2d-x.
The idea is that, based on time, you (manually) update the position of the sprite. The position of the sprite at any time is determined by the number of seconds since the animation begun. The line nominally follows a straight path from (x0,y0) to (x1,y0). You can then project the line onto a line drawn at any angle using some trigonometry. This gives the ability to have a sinusoidal path along any direction.
Here is the basic code (the main work is done in UpdateAnimation()):
// This assumes the frame rate is relatively constant
// at 60 fps.
const double SECONDS_PER_TICK = 1.0/60;
const double DURATION = 8.0; // Seconds for total animation.
const double X_START = 100; // Pixels
const double Y_START = 200; // Pixels
const double X_STOP = 800; // Pixels
const double X_SPEED = (X_STOP-X_START)/DURATION;
const double Y_PERIOD = 4.0; // Seconds for y cycle.
const double Y_HEIGHT = 100;
const double LAUNCH_ANGLE = M_PI/4; // Angle for line.
const CCPoint ANCHOR(X_START,Y_START);
CCPoint RotatePointAboutAnchor(const CCPoint& pt,double theta,const CCPoint& anchor)
{
double xPrime = cos(theta) * (pt.x-anchor.x) - sin(theta) * (pt.y-anchor.y) + anchor.x;
double yPrime = sin(theta) * (pt.x-anchor.x) + cos(theta) * (pt.y-anchor.y) + anchor.y;
return CCPoint(xPrime,yPrime);
}
void HelloWorld::InitAnimation()
{
_ticks = 0;
_ticksTotal = DURATION/SECONDS_PER_TICK;
}
void HelloWorld::UpdateAnimation()
{
if(_ticks <= _ticksTotal)
{
double seconds = _ticks*SECONDS_PER_TICK;
double xPos = X_START + seconds*X_SPEED;
double yPos = Y_START + Y_HEIGHT*sin(seconds*2*M_PI/Y_PERIOD);
CCPoint pos = RotatePointAboutAnchor(CCPoint(xPos,yPos), LAUNCH_ANGLE, ANCHOR);
// Set the position of the sprite
_sprite->setPosition(pos);
CCLOG("Tick: %d, Seconds: %5.2f, Position: (%f,%f)",_ticks,seconds,pos.x,pos.y);
if(_ticks%10 == 0)
{ // Add a trail
CCSprite* marker = CCSprite::create("Icon-72.png");
marker->setScale(0.1);
marker->setPosition(_sprite->getPosition());
marker->setZOrder(50);
addChild(marker);
}
// Increment the ticks count for next time.
_ticks++;
}
}
void HelloWorld::draw()
{
CCLayer::draw();
CCPoint start;
CCPoint stop;
start = RotatePointAboutAnchor(CCPoint(X_START,Y_START), LAUNCH_ANGLE, ANCHOR);
stop = RotatePointAboutAnchor(CCPoint(X_STOP,Y_START), LAUNCH_ANGLE, ANCHOR);
ccDrawLine(start,stop);
start = RotatePointAboutAnchor(CCPoint(X_START,Y_START+Y_HEIGHT), LAUNCH_ANGLE, ANCHOR);
stop = RotatePointAboutAnchor(CCPoint(X_STOP,Y_START+Y_HEIGHT), LAUNCH_ANGLE, ANCHOR);
ccDrawLine(start,stop);
start = RotatePointAboutAnchor(CCPoint(X_START,Y_START-Y_HEIGHT), LAUNCH_ANGLE, ANCHOR);
stop = RotatePointAboutAnchor(CCPoint(X_STOP,Y_START-Y_HEIGHT), LAUNCH_ANGLE, ANCHOR);
ccDrawLine(start,stop);
}
void HelloWorld::onEnterTransitionDidFinish()
{
InitAnimation();
scheduleUpdate();
}
void HelloWorld::onExitTransitionDidStart()
{
unscheduleUpdate();
}
void HelloWorld::update(float dt)
{
UpdateAnimation();
}
I drew some markers to show the path and also drew the lines "around" the path that should be followed. Here is what it looks like:
You can change the LAUNCH_ANGLE as you like to make it move along different angles.
Obviously this is not production code, but it does demonstrate the idea that you can follow a sinusoidal path in any direction. You should encapsulate it into something more in line with your application.
The entire code base is available on git hub.
And there are more posts about stuff like this in this blog.
There is an action for moving a sprite along a path and orienting to the path also. Not at my computer ATM but will try find it.
Here you go... https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKAction_Ref/Reference/Reference.html#//apple_ref/doc/uid/TP40013017-CH1-SW3
Actually you could probably link together a repeating sequence moving up and down with a moving forward action and create sinusoidal movement that way.
Thank for your question!
Sinusoidal cocos2d action below :)
class NDActionSineMoveBy : public ActionInterval
{
public:
static NDActionSineMoveBy* create(float duration, float sines, float sineSize, const Vec2& deltaPosition);
//
// Overrides
//
virtual NDActionSineMoveBy* clone() const override;
virtual NDActionSineMoveBy* reverse() const override;
virtual void startWithTarget(Node *target) override;
/**
* #param time in seconds
*/
virtual void update(float time) override;
CC_CONSTRUCTOR_ACCESS:
NDActionSineMoveBy() {}
virtual ~NDActionSineMoveBy() {}
/** initializes the action */
bool initWithDuration(float duration, float sines, float sineSize, const Vec2& deltaPosition);
protected:
Vec2 rotate(const Vec2 & point, float angle, const Vec2 & anchor);
protected:
float _sines;
float _sineSize;
float _baseAngle;
Vec2 _positionDelta;
Vec2 _startPosition;
Vec2 _previousPosition;
float _currentAngle;
float _distance;
private:
CC_DISALLOW_COPY_AND_ASSIGN(NDActionSineMoveBy);
};
NDActionSineMoveBy* NDActionSineMoveBy::create(float duration, float sines, float sineSize, const Vec2& deltaPosition)
{
NDActionSineMoveBy *ret = new (std::nothrow) NDActionSineMoveBy();
if (ret && ret->initWithDuration(duration, sines, sineSize, deltaPosition))
{
ret->autorelease();
return ret;
}
delete ret;
return nullptr;
}
bool NDActionSineMoveBy::initWithDuration(float duration, float sines, float sineSize, const Vec2& deltaPosition)
{
bool ret = false;
if (ActionInterval::initWithDuration(duration))
{
_sines = sines;
_sineSize = sineSize;
_positionDelta = deltaPosition;
_baseAngle = atan2f(_positionDelta.y, _positionDelta.x);
_currentAngle = _sines * (M_PI * 2);
ret = true;
}
return ret;
}
NDActionSineMoveBy* NDActionSineMoveBy::clone() const
{
// no copy constructor
return NDActionSineMoveBy::create(_duration, _sines, _sineSize, _positionDelta);
}
void NDActionSineMoveBy::startWithTarget(Node *target)
{
ActionInterval::startWithTarget(target);
_previousPosition = _startPosition = target->getPosition();
_distance = _positionDelta.length();
}
NDActionSineMoveBy* NDActionSineMoveBy::reverse() const
{
return NDActionSineMoveBy::create(_duration, _sines, _sineSize, -_positionDelta);
}
void NDActionSineMoveBy::update(float t)
{
if (_target)
{
Vec2 newPos;
newPos.x = _distance * t;
newPos.y = sin(_currentAngle * t) * _sineSize;
newPos = rotate(newPos, _baseAngle, Vec2::ZERO);
#if CC_ENABLE_STACKABLE_ACTIONS
Vec2 currentPos = _target->getPosition();
Vec2 diff = currentPos - _previousPosition;
_startPosition = _startPosition + diff;
newPos += _startPosition;
_target->setPosition(newPos);
_previousPosition = newPos;
#else
newPos += _startPosition;
_target->setPosition(newPos);
#endif // CC_ENABLE_STACKABLE_ACTIONS
}
}
Vec2 NDActionSineMoveBy::rotate(const Vec2& point, float angle, const Vec2& anchor)
{
Vec2 res;
res.x = cos(angle) * (point.x - anchor.x) - sin(angle) * (point.y - anchor.y) + anchor.x;
res.y = sin(angle) * (point.x - anchor.x) + cos(angle) * (point.y - anchor.y) + anchor.y;
return res;
};

touch object GLKIT or OpenGL any functions?

I am looking for a description to touch my OpenGL object or get an Event if I touch it.
Have the GLKit or OpenGL ES some functions to use?
Or I have to calculate the position of my Object and have to compare it with the coordination ob my Touch?
There is no built in one, but here is a ported version of the gluProject function that will put a point from your object in screen coordinates, so you can see if your touch is near that point:
GLKVector3 gluProject(GLKVector3 position,
GLKMatrix4 projMatrix,
GLKMatrix4 modelMatrix,
CGRect viewport
)
{
GLKVector4 in;
GLKVector4 out;
in = GLKVector4Make(position.x, position.y, position.z, 1.0);
out = GLKMatrix4MultiplyVector4(modelMatrix, in);
in = GLKMatrix4MultiplyVector4(projMatrix, out);
if (in.w == 0.0) NSLog(#"W = 0 in project function\n");
in.x /= in.w;
in.y /= in.w;
in.z /= in.w;
/* Map x, y and z to range 0-1 */
in.x = in.x * 0.5 + 0.5;
in.y = in.y * 0.5 + 0.5;
in.z = in.z * 0.5 + 0.5;
/* Map x,y to viewport */
in.x = in.x * (viewport.size.width) + viewport.origin.x;
in.y = in.y * (viewport.size.height) + viewport.origin.y;
return GLKVector3Make(in.x, in.y, in.z);
}
- (GLKVector2) getScreenCoordOfPoint {
GLKVector3 out = gluProject(self.point, modelMatrix, projMatrix, view.frame);
GLKVector2 point = GLKVector2Make(out.x, view.frame.size.height - out.y);
return point;
}

Spherical Coordinates within a range / restriction

The function below returns points on a sphere with a given radius. I want to add restriction such that points cannot be plotted within 30 degrees of the poles of the sphere.
public static function randomPoint(radius:Number):Number3D
{
var inclination:Number = Math.random() * Math.PI*2;
var azimuth:Number = Math.random() * Math.PI*2;
var point:Number3D = new Number3D(
radius * Math.sin(inclination) * Math.cos(azimuth),
radius * Math.sin(inclination) * Math.sin(azimuth),
radius * Math.cos(inclination)
);
return point;
}
Thanks in advance!
Sounds like you can just restrict the inclination:
var inclination:Number = (Math.PI/6) + Math.random()*(2*Math.PI-2*Math.PI/6)
Feel free to resolve those constant values, just kept them in to show the working.
Here's what I have so far... this does what I want, restricted north and south poles. Any improvements welcome!
var point:Number3D = sphericalPoint(100, inclination, azimuth);
public static function sphericalPoint(r:Number, inc:Number, az:Number):Number3D
{
var point:Number3D = new Number3D(
r * Math.sin(inc) * Math.cos(az),
r * Math.sin(inc) * Math.sin(az),
r * Math.cos(inc)
);
//cheat and use a transform matrix
var obj:Object3D = new Object3D();
obj.rotationX = 90;
point.rotate(point, obj.transform);
return point;
}
//a number between 0 and 180
protected function get inclination():Number
{
//default
//_inclination = Math.random() * Math.PI;
//number between 60 and 120
_inclination = Math.random() * (Math.PI*5/6 - Math.PI/6) + Math.PI/6;
return _inclination;
}
//a number between 0 and 360
protected function get azimuth():Number
{
//revolve around the Y axis
_azimuth = Math.random() * Math.PI*2;
return _azimuth;
}

Resources