Assimp, OpenGL: rotating bone around its origin - c++

I have collada (dae) model created in blender. I load the dae file using assimp and display it using opengl in c++. The model displays correctly (image below).
The sceleton has following structre:
root, id: 0
Armature, id: 2
Armature_SPINE_NAVAL, id: 3
Armature_SPINE_CHEST, id: 4
Armature_NECK, id: 5
Armature_HEAD, id: 6
Armature_NOSE, id: 7
Armature_CLAVICLE_RIGHT, id: 8
Armature_SHOULDER_RIGHT, id: 9
Armature_ELBOW_RIGHT, id: 10
Armature_WRIST_RIGHT, id: 11
Armature_HAND_RIGHT, id: 12
Armature_HANDTIP_RIGHT, id: 13
Armature_THUMB_RIGHT, id: 14
Armature_CLAVICLE_LEFT, id: 15
Armature_SHOULDER_LEFT, id: 16
Armature_ELBOW_LEFT, id: 17
Armature_WRIST_LEFT, id: 18
Armature_HAND_LEFT, id: 19
Armature_HANDTIP_LEFT, id: 20
Armature_THUMB_LEFT, id: 21
Armature_HIP_RIGHT, id: 22
Armature_KNEE_RIGHT, id: 23
Armature_ANKLE_RIGHT, id: 24
Armature_FOOT_RIGHT, id: 25
Armature_HIP_LEFT, id: 26
Armature_KNEE_LEFT, id: 27
Armature_ANKLE_LEFT, id: 28
Armature_FOOT_LEFT, id: 29
I want to apply rotation to a single bone. However after applying rotation of 90 degrees around z axis to the Armature_WRIST_RIGHT, the bone arm gets streached. Below is my code. What I'm doing wrong?
void CalculateBoneTransform(Bone* bone, glm::mat4 parentTransform)
{
std::string boneName = bone->GetBoneName();
glm::mat4 boneTransform = bone->GetLocalTransform(); // aiNode->mTransformation
if(boneName == "Armature_WRIST_RIGHT"){
float RotationAngle = 1.5708;
float x = 0 * sin(RotationAngle / 2); // x * sin()
float y = 0 * sin(RotationAngle / 2); // y * sin()
float z = 1 * sin(RotationAngle / 2); // z * sin()
float w = cos(RotationAngle / 2);
glm::quat rot(w,x,y,z);
boneTransform *= glm::toMat4(rot);
}
glm::mat4 globalTransformation = parentTransform * boneTransform;
if (m_BoneInfoMap.find(boneName) != m_BoneInfoMap.end())
{
int index = m_BoneInfoMap[boneName].id;
glm::mat4 offset = m_BoneInfoMap[boneName].offset; // mesh->mBones[boneIndex]->mOffsetMatrix
m_FinalBoneMatrices[index] = globalTransformation * offset * glm::scale(glm::vec3(0.2f,0.2f,0.2f));
}
for (Bone* j : bone->getChildren())
CalculateBoneTransform(j, globalTransformation);
}
Above is my code. What I'm doing wrong? How should I apply the rotation transform to correctly rotate chosen bones?

Related

How to move an object along a line given two points?

In my game I'm working on, I have a tower that will shoot at an enemy. I'm trying to get my tower shoot to the enemy when the enemy becomes in range. My fireball shows up, however I'm having problem moving the fireball to the enemy. My latest attempt just makes the ball dance around on the screen.
the ball has an x and y position (which resembles where the ball starts.
The ball also has a newX and newY position in which is where the enemy is. I was thinking about doing the Pythagorean theorm, but that only gets me the length of the line I want to travel in. Then I was searching online and found out about sin, cos, and atan. I was using atan to get the angle of the slope that I calculated, then plugging the angle in for sin and cos.
My Ball::Update function looks like this:
int dx = newX - x;
int dy = newY - y;
int ang = atan(dy, dx);
x += cos(ang);
y += sin(ang);
That code there makes the ball appear to 'dance' on the screen. It doesn't move at all. I tested my code with these lines:
x += cos(45);
y += sin(45);
And the ball moves in a 45 degree angle (heading south east) which is expected. So I'm guessing that my error is when calculating the angle. Any math experts that can help?
This is a small sample to demonstrate linear interpolation for pairs (x, y):
#include <iostream>
int main()
{
double x0 = 2.0, y0 = 3.5;
double x1 = 7.0, y1 = 2.0;
// do linear interpolation in n steps
enum { n = 10 };
for (int i = 0; i <= n; ++i) {
double t = (double)i / n;
double xT = (1.0 - t) * x0 + t * x1;
double yT = (1.0 - t) * y0 + t * y1;
std::cout << i << ":\tt: " << t << ",\tx: " << xT << ",\ty: " << yT << '\n';
}
// your code goes here
return 0;
}
Output:
0: t: 0, x: 2, y: 3.5
1: t: 0.1, x: 2.5, y: 3.35
2: t: 0.2, x: 3, y: 3.2
3: t: 0.3, x: 3.5, y: 3.05
4: t: 0.4, x: 4, y: 2.9
5: t: 0.5, x: 4.5, y: 2.75
6: t: 0.6, x: 5, y: 2.6
7: t: 0.7, x: 5.5, y: 2.45
8: t: 0.8, x: 6, y: 2.3
9: t: 0.9, x: 6.5, y: 2.15
10: t: 1, x: 7, y: 2
Life demo on ideone

Calculate enclosing rectangle of a given aspect ratio

Given the height and the width of a rectangle of any size and an aspect ratio, how can I calculate the height and width of a minimum enclosing rectangle of the given aspect ratio?
In code, the function signature would look like this
public Size getMinimumEnclosingRectangle(Size originalRectangle, float aspectNumerator, float aspectDenomiator);
Calls to this function would look like
originalRectangle AspectRatio Result
-------------------------------------------
100x100 1:2 100x200
64x32 1:1 64x64
125x100 3:2 150x100
100x345 1:3 115x345
This may not be the best way, but the way I do this calculation is to calculate the change in aspect ratio and then base the resulting width/height calculation of of that. Here is some code to illustrate this algorithm in practice:
var sourceImages = [
{width: 100, height: 100, toAspectRatio:1/2 },
{width: 64, height: 32, toAspectRatio:1/1 },
{width: 125, height: 100, toAspectRatio:3/2 },
{width: 100, height: 345, toAspectRatio:1/3 },
{width: 345, height: 100, toAspectRatio:1/3 }
];
function calculateNewSize( sourceWidth, sourceHeight, toAspectRatio )
{
var aspectRatioChange = (sourceWidth / sourceHeight) / toAspectRatio;
var fitWidth = aspectRatioChange < 1 ? sourceHeight * toAspectRatio : sourceWidth;
var fitHeight = aspectRatioChange >= 1 ? sourceWidth / toAspectRatio : sourceHeight;
console.log('(' + aspectRatioChange + ') ' + sourceWidth + " x " + sourceHeight + " -> "
+ toAspectRatio + ' -> ' + fitWidth + ' x ' + fitHeight);
}
sourceImages.forEach(function(source) {
calculateNewSize(source.width, source.height, source.toAspectRatio);
});

raphael js drawing arcs starting at 6 o'clock that cocentric

I was able to find an example of a Polar clock at http://raphaeljs.com/polar-clock.html
I modified it to draw concentric circles, but I need the arc to start at 6 o'clock. I am trying to dissect how it works, but haven't been able to figure it out.
JS Fiddle:
http://jsfiddle.net/5frQ8/
var r = Raphael("holder", 600, 600);
// Custom Attribute
r.customAttributes.arc = function (value, total, R, color)
{
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = 300 + R * Math.cos(a),
y = 300 - R * Math.sin(a),
path;
if (total == value)
{
path = [["M", 300, 300 - R], ["A", R, R, 0, 1, 1, 299.99, 300 - R]];
}
else
{
path = [["M", 300, 300 - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
}
return {path: path, stroke: color,"stroke-width": 30};
};
//West
r.path().attr({arc: [575, 2000, 200, '#19A69C']});
//Total#
r.path().attr({arc: [1000, 2000, 160, '#FEDC38']});
//East
r.path().attr({arc: [425, 2000, 120, '#7BBD26']});
I have modified the main function to make the arcs start from 6 o'clock equivalent position. Please note that the formulae to find a point in polar coordinates are always:
x = centerX + radius * cos(angle)
y = centerY + radius * sin(angle)
Find the starting and ending points accordingly.
To change the starting angle by "delta", all angles should be added by "delta". Thus,
newAngle = angle + delta
The values of delta are -90 and +90 for the arcs to start from 12 o'clock and 6 o'clock respectively.
The arc drawing function is changed accordingly.
// Custom Attribute
r.customAttributes.arc = function (value, total, R, color)
{
var angleShift = 90,
alpha = 360 / total * value,
a = (alpha + angleShift) * Math.PI / 180,
x = 300 + R * Math.cos(a),
y = 300 + R * Math.sin(a),
path;
if (total == value)
{
path = [["M", 300, 300 + R], ["A", R, R, 0, 1, 1, 300.01, 300 + R]];
}
else
{
path = [["M", 300, 300 + R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
}
return {path: path, stroke: color,"stroke-width": 30};
};

RaphaelJS - I need help understanding a transform

I have a question about the following demo - http://raphaeljs.com/hand.html.
Here is code from the sample...
var r = Raphael("holder", 640, 480), angle = 0;
while (angle < 360) {
var color = Raphael.getColor();
(function(t, c) {
r.circle(320, 450, 20).attr({
stroke : c,
fill : c,
transform : t,
"fill-opacity" : .4
}).click(function() {
s.animate({
transform : t,
stroke : c
}, 2000, "bounce");
}).mouseover(function() {
this.animate({
"fill-opacity" : .75
}, 500);
}).mouseout(function() {
this.animate({
"fill-opacity" : .4
}, 500);
});
})("r" + angle + " 320 240", color);
angle += 30;
}
Raphael.getColor.reset();
var s = r.set();
s.push(r.path("M320,240c-50,100,50,110,0,190").attr({
fill : "none",
"stroke-width" : 2
}));
s.push(r.circle(320, 450, 20).attr({
fill : "none",
"stroke-width" : 2
}));
s.push(r.circle(320, 240, 5).attr({
fill : "none",
"stroke-width" : 10
}));
s.attr({
stroke : Raphael.getColor()
});
The question I have is about the following line of code...
("r" + angle + " 320 240", color);
In the anonymous function the circle is initially drawn at 320, 450 with a radius of 20. Then a transform is applied, for example ("r30 320 240") when the angle is 30.
How does this transform work? The way I read this transform is to rotate the circle 30 degrees around 320,450 , then move 320 horizontally (to the right) and 240 vertically down.
But i'm obviously reading this transform wrong because this is not what is happening.
What am i missing?
Thanks
The transform "r30 320 240" sets the rotation of the object about the point (320,240) by 30 degrees. It does not add to the rotation. It overrides any previous transformations.
If you look at this example:
http://jsfiddle.net/jZyyy/1/
You can see that I am setting the rotation of the circle about the point (0,0). If you consider the point (0,0) to be the centre of a clock, then the circle begins at 3 o'clock. If I use the transform "r90 0 0" the circle will be rotated from 3 o'clock to 6 o'clock. If I then later set the transform to be "r30 0 0" the circle will be at 4 o'clock, rotated 30 degrees from the original 3 o'clock position about the point (0,0).

drawing centered arcs in raphael js

I need to draw concentric arcs of various sizes using raphael.js. I tried to understand the code behind http://raphaeljs.com/polar-clock.html, which is very similar to what I want, but, whithout comments, it is quite difficult to fathom.
Ideally, I would need a function that creates a path that is at a given distance from some center point, starts at some angle and ends at some other angle.
That answer is ok, but cant be animated. I ripped the important stuff out of polar-clock for you. Here is a red arc that animates growing. enjoy.
// Custom Arc Attribute, position x&y, value portion of total, total value, Radius
var archtype = Raphael("canvas", 200, 100);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [50, 50, 0, 100, 30]
});
my_arc.animate({
arc: [50, 50, 40, 100, 30]
}, 1500, "bounce");
Here's how I have done it. The following code allows you to specify a start and end angle as well as an inner and outer radius (useful for doing those trendy donut style pie charts). The solution doesn't rely on approximating a curve with line segments and can be animated as per the clock example mentioned in the original question.
First create your Raphael drawing area; the following assumes a div with id "raphael_paper" in your HTML file:
var paper = Raphael("raphael_paper", 800, 800);
to this Raphael object we add a custom arc attribute, a function which takes the center of a circle (x and y coords), a start angle, an end angle, an inner radius and an outer radius:
paper.customAttributes.arc = function (centerX, centerY, startAngle, endAngle, innerR, outerR) {
var radians = Math.PI / 180,
largeArc = +(endAngle - startAngle > 180);
// calculate the start and end points for both inner and outer edges of the arc segment
// the -90s are about starting the angle measurement from the top get rid of these if this doesn't suit your needs
outerX1 = centerX + outerR * Math.cos((startAngle-90) * radians),
outerY1 = centerY + outerR * Math.sin((startAngle-90) * radians),
outerX2 = centerX + outerR * Math.cos((endAngle-90) * radians),
outerY2 = centerY + outerR * Math.sin((endAngle-90) * radians),
innerX1 = centerX + innerR * Math.cos((endAngle-90) * radians),
innerY1 = centerY + innerR * Math.sin((endAngle-90) * radians),
innerX2 = centerX + innerR * Math.cos((startAngle-90) * radians),
innerY2 = centerY + innerR * Math.sin((startAngle-90) * radians);
// build the path array
var path = [
["M", outerX1, outerY1], //move to the start point
["A", outerR, outerR, 0, largeArc, 1, outerX2, outerY2], //draw the outer edge of the arc
["L", innerX1, innerY1], //draw a line inwards to the start of the inner edge of the arc
["A", innerR, innerR, 0, largeArc, 0, innerX2, innerY2], //draw the inner arc
["z"] //close the path
];
return {path: path};
};
now we can use this to draw arcs of a specified thickness, starting and ending wherever we want them to eg.
var redParams = {stroke: "#f00", "stroke-width": 1, fill:"#eee"},
greenParams = {stroke: "#0f0", "stroke-width": 1, fill:"#eee"},
blueParams = {stroke: "#00f", "stroke-width": 1, fill:"#eee"},
cx = 300, cy = 300, innerRadius = 100, outerRadius = 250,
var red = paper.path().attr(redParams).attr({arc: [cx, cy, 0, 90, innerRadius, outerRadius]});
var green = paper.path().attr(greenParams).attr({arc: [cx, cy, 270, 320, innerRadius, outerRadius]});
var blue = paper.path().attr(blueParams).attr({arc: [cx, cy, 95, 220, innerRadius, outerRadius]});
This should result in three grey arc segments with red, blue and green 1px borders.
Actually found the answer myself. I first thought of something fancy involving bezier curves, but this just works.
-> creates a path using SVG path syntax, which works as is with raphael
function arc(center, radius, startAngle, endAngle) {
angle = startAngle;
coords = toCoords(center, radius, angle);
path = "M " + coords[0] + " " + coords[1];
while(angle<=endAngle) {
coords = toCoords(center, radius, angle);
path += " L " + coords[0] + " " + coords[1];
angle += 1;
}
return path;
}
function toCoords(center, radius, angle) {
var radians = (angle/180) * Math.PI;
var x = center[0] + Math.cos(radians) * radius;
var y = center[1] + Math.sin(radians) * radius;
return [x, y];
}
Just to remove some guesswork from user592699's answer, this is the complete code that works:
<script src="raphael.js"></script>
<script>
var paper = Raphael(20, 20, 320, 320);
function arc(center, radius, startAngle, endAngle) {
angle = startAngle;
coords = toCoords(center, radius, angle);
path = "M " + coords[0] + " " + coords[1];
while(angle<=endAngle) {
coords = toCoords(center, radius, angle);
path += " L " + coords[0] + " " + coords[1];
angle += 1;
}
return path;
}
function toCoords(center, radius, angle) {
var radians = (angle/180) * Math.PI;
var x = center[0] + Math.cos(radians) * radius;
var y = center[1] + Math.sin(radians) * radius;
return [x, y];
}
paper.path(arc([100, 100], 80, 0, 270)); // draw an arc
// centered at (100, 100),
// radius 80, starting at degree 0,
// beginning at coordinate (80, 0)
// which is relative to the center
// of the circle,
// going clockwise, until 270 degree
</script>
For those who want the arc to be made with closed path and not stroke, I have extended genkilabs answer to make a solution. In cases when you need to give outer stroke to your arc, this might help.
// Custom Arc Attribute, position x&y, value portion of total, total value, Radius, width
var archtype = Raphael("canvas", 200, 100);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R, width) {
if(!width) width = R * 0.4;
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
w = width / 2,
r1 = R + w,
r2 = R - w,
x1 = xloc + r1 * Math.cos(a),
y1 = yloc - r1 * Math.sin(a),
x2 = xloc + r2 * Math.cos(a),
y2 = yloc - r2 * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - r1],
["A", r1, r1, 0, 1, 1, xloc - 0.01, yloc - r1],
["Z"],
["M", xloc - 0.01, yloc - r2],
["A", r2, r2, 0, 1, 0, xloc, yloc - r2],
["Z"]
];
} else {
path = [
["M", xloc, yloc - r1],
["A", r1, r1, 0, +(alpha > 180), 1, x1, y1],
["L", x2, y2],
["A", r2, r2, 0, +(alpha > 180), 0, xloc, yloc - r2],
["L", xloc, yloc - r1],
["Z"]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"fill": "#00f",
"stroke": "#f00",
"stroke-width": 5,
arc: [50, 50, 0, 100, 30]
});
my_arc.animate({
arc: [50, 50, 40, 100, 30]
}, 1500, "bounce");
JSFiddle
You can also do this without having to use loops. The following achieves this and works with negative angles as well.
Pass in a Raphael object as r. The angles start with 0 degrees, which is the top of the circle rather than the right as was listed in a couple of other solutions.
function drawArc(r, centerX, centerY, radius, startAngle, endAngle) {
var startX = centerX+radius*Math.cos((90-startAngle)*Math.PI/180);
var startY = centerY-radius*Math.sin((90-startAngle)*Math.PI/180);
var endX = centerX+radius*Math.cos((90-endAngle)*Math.PI/180);
var endY = centerY-radius*Math.sin((90-endAngle)*Math.PI/180);
var flg1 = 0;
if (startAngle>endAngle)
flg1 = 1;
else if (startAngle<180 && endAngle<180)
flg1 = 0;
else if (startAngle>180 && endAngle>180)
flg1 = 0;
else if (startAngle<180 && endAngle>180)
flg1 = 0; // edited for bugfix here, previously this was 1
else if (startAngle>180 && endAngle<180)
flg1 = 1;
return r.path([['M',startX, startY],['A',radius,radius,0,flg1,1,endX,endY]]);
};
I have adapted genkilabs answer to include rotation and inversion abilities. Also, how much of the ring is filled was changed to a single-number percent. (The inversion was adapted from this post). Hope it's helpful!
paper.customAttributes.arc = function (xloc, yloc, percent, rad, rot, invert) {
var alpha = 3.6 * percent,
a = (90 - alpha) * Math.PI / 180,
x = xloc + rad * Math.cos(a),
y = yloc - rad * Math.sin(a),
path;
if (invert) {
x = xloc - rad * Math.cos(a);
}
if (percent >= 100) {
path = [
["M", xloc, yloc - rad],
["A", rad, rad, 0, 1, 1, xloc - 0.01, yloc - rad]
];
} else {
path = [
["M", xloc, yloc - rad],
["A", rad, rad, 0, +(alpha > 180), +(!invert), x, y]
];
}
return {
path: path,
transform: "r"+rot+","+xloc+","+yloc,
};
};