eugene.litvinov Posted March 16, 2011 Share Posted March 16, 2011 I working on "gravity gun" realization (this functional allow users take and carry physical game objects). I have some base realization and some questions :), so I start from code, that I already have: PlayerActor actor; // player Object mouseSelectedObject; // object, that will be carried float levitationPowerFactor = 28.0f; // speed of object movement to destination position float levitationMaxVelocity = 10.0f; // max speed of object float snatchSize = 1.8f; // distance of "player hands" // this method call in flush() method void updateGravityGun() { if (mouseSelectedObject != NULL ) { // gravity forces calculations BodyRigid body = mouseSelectedObject.getBodyRigid(); vec3 pos = mouseSelectedObject.getPosition(); vec3 p0 = Math::decomposePositionXYZ(actor.getIModelview()); // take position of PlayerActor's camera vec3 p1 = actor.getViewDirection(); vec3 p2 = p0 + p1 * snatchSize; // calculate destination position (position of players hands) vec3 force = p2 - pos; // target movement direction force *= levitationPowerFactor; // movement coefficient // speed correction to max possible if (length(force) > levitationMaxVelocity) force = normalize(force) * levitationMaxVelocity; body.setLinearVelocity(force); // apply force } } namespace Math { vec3 decomposePositionXYZ(mat4 transform) { return transform.m03m13m23; } } This section work fine, and looks like in many other games. Link to comment
eugene.litvinov Posted March 16, 2011 Author Share Posted March 16, 2011 This realization have one omission: when player turns around our selected object don't change it rotation (see screenshot): It's not correct: we must see object in one angle, so we must take into account angular forces: void updateGravityGun() { if (mouseSelectedObject != NULL ) { // gravity forces calculations . . . // angular forces calculations float angleVert = actor.getThetaAngle(); // vertical rotation of player's camera float angleHorz = actor.getPhiAngle(); // horizontal rotation of player's camera quat yRotation = quat(0, 1, 0, -angleVert); // create rotation quaternion for y axis (player's camera rotation) quat zRotation = quat(0, 0, 1, angleHorz); // create rotation quaternion for z axis (player's camera rotation) quat playerRotation = yRotation * zRotation; // rotation quaternion of player's camera quat currRotation = mouseSelectedObject.getRotation(); // rotation quaternion of selected object quat needRotation = -levitationRotation * playerRotation; // rotation quaternion, determine necessary rotation of selected object. levitationRotation - we save object rotation, when we try to select it, and add to it rotation of player's camera quat difRotation = currRotation - needRotation; // difference of current rotation and necessary orientation vec3 angularForce; float angularAngle; Math::toAxisAngle(difRotation, angularForce, angularAngle); // get axis vector and angle from quaternion angularForce *= angularAngle; // rotation coefficient body.setAngularVelocity(-angularForce); // apply force namespace Math { void toAxisAngle(quat q1, vec3 &axis, float &angle) { // if w > 1 acos and sqrt will produce errors, this cant happen if quaternion is normalised if (q1.w > 1) q1 = quaternionNormalize(q1); angle = 2 * acos(q1.w) * RAD2DEG; double s = sqrt(1 - q1.w * q1.w); // assuming quaternion normalised then w is less than 1, so term always positive. if (s < 0.001)//EPSILON)) { // test to avoid divide by zero, s is always positive due to sqrt // if s close to zero then direction of axis not important axis.x = q1.x; // if it is important that axis is normalised then replace with x=1; y=z=0; axis.y = q1.y; axis.z = q1.z; } else { axis.x = q1.x / s; // normalise axis axis.y = q1.y / s; axis.z = q1.z / s; } } } } Adding angular forces in this manner add strange behavior: for some angles all good, for some - not: -150 — 116 — object oriented on camera, correct < -150 — object «convulsive» rotates in all axis > 116 – object rotates in Z axis to left (relatively player) I'm thinking I not correct get vector and angle from quaternion. Could you offer other patterns to determine difference with current and needed rotation of object or any thinks about? Link to comment
frustum Posted March 17, 2011 Share Posted March 17, 2011 Try more simple approach: JointFixed between physical body and player body. I use this function to get axis angle inside the engine: void quat::get(vec3 &axis,float &angle) const { float ilength = Math::rsqrtf(x * x + y * y + z * z); axis.x = x * ilength; axis.y = y * ilength; axis.z = z * ilength; angle = Math::acosf(clamp(w,-1.0f,1.0f)) * RAD2DEG * 2.0f; if(angle > 180.0f) angle -= 360.0f; } Link to comment
eugene.litvinov Posted March 17, 2011 Author Share Posted March 17, 2011 I use this function to get axis angle inside the engine: void quat::get(vec3 &axis,float &angle) const { float ilength = Math::rsqrtf(x * x + y * y + z * z); axis.x = x * ilength; axis.y = y * ilength; axis.z = z * ilength; angle = Math::acosf(clamp(w,-1.0f,1.0f)) * RAD2DEG * 2.0f; if(angle > 180.0f) angle -= 360.0f; } This code can be accessed through Unigine script? I can't do this and add it to my Math namespace. Thanks. Next problem, that we have, is different result on different FPS-level (although we use flush() method to calculate forces). Do we need use in flush() method some coefficient, based on game.getIFps() or something else? Link to comment
eugene.litvinov Posted March 21, 2011 Author Share Posted March 21, 2011 I have some additional: I change my quaternion to axis and angle function to frustum's void quat::get(vec3 &axis,float &angle). My function works incorrect. But this changes not gives me correct result. So I decide, that I have errors in quaternion difference determination, so I replace: quat currRotation = mouseSelectedObject.getRotation(); // rotation quaternion of selected object quat needRotation = -levitationRotation * playerRotation; // rotation quaternion, determine necessary rotation of selected object. levitationRotation - we save object rotation, when we try to select it, and add to it rotation of player's camera quat difRotation = currRotation - needRotation; // difference of current rotation and necessary orientation on next code: quat currRotation = mouseSelectedObject.getRotation() * playerRotation; quat needRotation = levitationRotation * playerRotation; quat inverse = Math::quaternionInverse(currRotation); // find inverse quaternion, I do this to calculate difference between rotation quaternions for current object rotation and rotation, that he need have quat difRotation = inverse * needRotation; // I read in articles about quaternions that we can calculate difference between rotation quaternions like inverse first quaternion multiplied on second quaternion vec3 angularForce; float angularAngle; Math::getQuat(difRotation, angularForce, angularAngle); // this difference I can convert to axis and angle body.setAngularVelocity(-angularForce); // and apply it to object This code works no correct too, the force for angular velocity too small in this realization, but if I multiply force, then object rotate very quick... How do use think, this way is correct to do this? Thanks. Link to comment
eugene.litvinov Posted March 22, 2011 Author Share Posted March 22, 2011 This code works no correct too, the force for angular velocity too small in this realization, but if I multiply force, then object rotate very quick... How do use think, this way is correct to do this? Thanks. Answer by myself: this is working pattern, I had some errors when I get rotation difference quaternion. To work with quaternions I use next functions: #ifndef __MATH_H__ #define __MATH_H__ namespace Source { namespace Math { quat quaternionConjugate(quat qa) { quat result; result.w = qa.w; result.x = -qa.x; result.y = -qa.y; result.z = -qa.z; return result; } quat quaternionInverse(quat qa) { quat result = quaternionNormalize(qa); return quaternionConjugate(result); } float quaternionMagnitude(quat qa) { return sqrt( qa.w * qa.w + qa.x * qa.x + qa.y * qa.y + qa.z * qa.z ); } quat quaternionNormalize(quat qa) { return qa / quaternionMagnitude(qa); } void getQuat(quat q, vec3 &axis,float &angle) { float ilength = sqrt(q.x * q.x + q.y * q.y + q.z * q.z); axis.x = q.x * ilength; axis.y = q.y * ilength; axis.z = q.z * ilength; angle = acos(clamp(q.w,-1.0f,1.0f)) * RAD2DEG * 2.0f; if(angle > 180.0f) angle -= 360.0f; } }; }; #endif Frustum, maybe you add getQuat and quaternionInverse functions support in Unigine script? Link to comment
frustum Posted March 22, 2011 Share Posted March 22, 2011 Frustum, maybe you add getQuat and quaternionInverse functions support in Unigine script? quaternionInverse() function is already existed as inverse(quat). You can use dot(quat,quat) as quaternionMagnitude() function. normalize(quat) function is also presented. getQuat() is very specific function, I will add it later into the core/script library. Why you don't use JointFixed for GravityGun? Link to comment
eugene.litvinov Posted March 23, 2011 Author Share Posted March 23, 2011 quaternionInverse() function is already existed as inverse(quat). Oh, I see... in some reason I thought this function does not work with quaternions :blink: I will use it Why you don't use JointFixed for GravityGun? We try use JointFixed, but we can't tune it so like we want, and we had problem: joint give opposite impulse to player, so when we lean on joint object player can fly on it :) Link to comment
Recommended Posts