I have gone through the 3D object documentation in JPCT but i couldn't find a way to scale an 3D object[a Cylinder] along y axis only. My world have multiple objects and I goal is to scale one particular object. Appreciate any leads. Thank you!!
Something like this openGL function glScalef(1,10,1).
The user AGP lists a method of achieving this here:
JPCT Forums
It basically uses the following vertex controller class, and I've used it successfully as well:
class VertexController extends GenericVertexController
{
public VertexController(Object3D toCheck) {
super.init(toCheck.getMesh(), true);
}
protected void scale(SimpleVector scale)
{
SimpleVector[] vertices = getSourceMesh();
SimpleVector[] destination = getDestinationMesh();
for (int i = 0; i < vertices.length; i++)
{
vertices[i].x *= scale.x;
vertices[i].y *= scale.y;
vertices[i].z *= scale.z;
destination[i].x = vertices[i].x;
destination[i].y = vertices[i].y;
destination[i].z = vertices[i].z;
}
this.updateMesh();
}
public void apply() {}
}
And you can call it like this:
vertexController = new VertexController(object);
Then in your onDrawFrame, or whereever required:
vertexController.scale(new SimpleVector(1,1.5f,1));
Related
Firstly, while not new to programming, I am very new to C++, so please bear with me.
I am using the Raylib library to attempt making a particle system for a game.
This consists of a struct with a few private members and public functions:
struct Particle {
Particle() {
mPosVector = {(float)GetMouseX(), (float)GetMouseY()};
mVelVector = {(float)GetRandomValue(15, 70)/100, (float)GetRandomValue(15, 70)/100};
mSize = GetRandomValue(5, 15);
}
void update(double deltaTime) {
mPosVector.x += mVelVector.x;
mPosVector.y += mVelVector.y;
}
void draw() {
DrawRectangleV(mPosVector, {(float)mSize, (float)mSize}, WHITE);
}
private:
Vector2 mPosVector;
Vector2 mVelVector;
int mSize;
};
The Vector2 type is defined by Raylib:
struct Vector2 {
float x;
float y;
};
In my main function I have an std::vector storing Particles. A particle gets added when the left mouse button is pressed. I loop through the Particles vector twice, once for updating position based on velocity and once for drawing. I was originally doing these both in one loop, but was still getting the problem that I will get onto, so tried it this way.
This is the current code:
std::vector<Particle> particles = {Particle()};
while (!WindowShouldClose()) {
deltaTime = GetFrameTime();
if (IsMouseButtonDown(0)) {
particles.push_back(Particle());
}
for (Particle part : particles) {
part.update(deltaTime);
}
BeginDrawing();
ClearBackground(BLACK);
DrawFPS(10, 10);
DrawText((numToString<double>(deltaTime*1000).substr(0, 5) + "ms").c_str(), 10, 40, 20, WHITE);
for (Particle part : particles) {
part.draw();
}
EndDrawing();
So, my problem: While particles are being instantiated as expected while pressing the left mouse button and being drawn, for some reason their positions are not being updated by their velocity. I have tried printing debug information to the console, such as the velocity, and it is as expected, but for some unknown reason to me (probably just me being stupid) their positions aren't being updated.
Any help would be greatly appreciated.
for (Particle part : particles) {
part.update(deltaTime);
}
this is making a copy of each entry , you need
for (Particle &part : particles) {
part.update(deltaTime);
}
to get a reference to the object in the vector to update it in place
To understand, think that the ranged for is just short hand for this
for(int i = 0; i < particles.size(); i++)
{
// this line copies the value
particle p = particles[i];
}
whereas the one with & in it does
for(int i = 0; i < particles.size9); i++)
{
// this line gets a reference to the ith entry
particle &p = particles[i];
}
Its nothing special to do with the ranged for loop.
I'm trying to follow the tutorial at here ( at ogldev ) mentioned in this answer .
I am however facing a few issues which I believe to be related to the Row Major Order for Assimp vs the Column major order fro GLM, although I am quite not sure.
I've tried a few variations and orders to see if any of those work, but to no avail.
Here ( Gist ) is the Class which I use to load the complete MD5 file. And the current Result I have.
And, this is the part where I think it is going wrong, when I try to update the bone transformation matrices.
void SkeletalModel::ReadNodeHierarchyAnimation(float _animationTime, const aiNode* _node,
const glm::mat4& _parentTransform)
{
std::string node_name = _node->mName.data;
const aiAnimation * p_animation = scene->mAnimations[0];
glm::mat4 node_transformation(1.0f);
convert_aimatrix_to_glm(node_transformation, _node->mTransformation);
// Transpose it.
node_transformation = glm::transpose(node_transformation);
const aiNodeAnim * node_anim = FindNodeAnim(p_animation, node_name);
if (node_anim) {
//glm::mat4 transformation_matrix(1.0f);
glm::mat4 translation_matrix(1.0f);
glm::mat4 rotation_matrix(1.0f);
glm::mat4 scaling_matrix(1.0f);
aiVector3D translation;
CalcInterpolatedPosition(translation, _animationTime, node_anim);
translation_matrix = glm::translate(translation_matrix, glm::vec3(translation.x, translation.y, translation.z));
aiQuaternion rotation;
CalcInterpolatedRotation(rotation, _animationTime, node_anim);
// Transpose the matrix after this.
convert_aimatrix_to_glm(rotation_matrix, rotation.GetMatrix());
//rotation_matrix = glm::transpose(rotation_matrix);
aiVector3D scaling;
CalcInterpolatedScaling(scaling, _animationTime, node_anim);
scaling_matrix = glm::scale(scaling_matrix, glm::vec3(scaling.x, scaling.y, scaling.z));
node_transformation = scaling_matrix * rotation_matrix * translation_matrix;
//node_transformation = translation_matrix * rotation_matrix * scaling_matrix;
}
glm::mat4 global_transformation = node_transformation * _parentTransform;
if (boneMapping.find(node_name) != boneMapping.end()) {
// Update the Global Transformation.
auto bone_index = boneMapping[node_name];
//boneInfoData[bone_index].finalTransformation = globalInverseTransform * global_transformation * boneInfoData[bone_index].boneOffset;
boneInfoData[bone_index].finalTransformation = boneInfoData[bone_index].boneOffset * global_transformation * globalInverseTransform;
//boneInfoData[bone_index].finalTransformation = globalInverseTransform;
}
for (auto i = 0; i < _node->mNumChildren; i++) {
ReadNodeHierarchyAnimation(_animationTime, _node->mChildren[i], global_transformation);
}
}
My Current Output:
I tried going through each matrix used in the code to check whether I should tranpose it or not. Whether I should change the matrix multiplication order or not. I could not find my issue.
If anyone can point out my mistakes here or direct me to a different tutorial that would help me load animations, that would be great.
Also, I see suggestions to use a basic model in the initial stages of learning this. But I was told Obj format doesn't support animations, and I have been using just Obj before this. Can I use any other formats that blender exports in a manner similar to MD5 as shown in this tutorial?
I built an animated scene a few years ago using Assimp library, basically following these tutorials. http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html and http://sourceforge.net/projects/assimp/forums/forum/817654/topic/3880745
While I was using and old X format (blender can work with X, using an extension), I can definitely confirm you need to transpose the assimp animation matrices for use with GML.
Regarding using other formats, you can use what whatever you like provided they are supported by Blender (import, Editing, Export) and by Assimp. Be prepared for a fair bit of trial and error when changing formats!
Rather then me trying to understand your code, I will post the relevant fragments from my working system, that shows the calculation of bone matrices. Hopefully this will help you, as I remember having the same problem as you describe, and taking some time to track it down. Code is plain 'C'.
You can see where the transposition takes place at the end of the code.
// calculateAnimPose() calculates the bone transformations for a mesh at a particular time in an animation (in scene)
// Each bone transformation is relative to the rest pose.
void calculateAnimPose(aiMesh* mesh, const aiScene* scene, int animNum, float poseTime, mat4 *boneTransforms) {
if(mesh->mNumBones == 0 || animNum < 0) { // animNum = -1 for no animation
boneTransforms[0] = mat4(1.0); // so, just return a single identity matrix
return;
}
if(scene->mNumAnimations <= (unsigned int)animNum)
failInt("No animation with number:", animNum);
aiAnimation *anim = scene->mAnimations[animNum]; // animNum = 0 for the first animation
// Set transforms from bone channels
for(unsigned int chanID=0; chanID < anim->mNumChannels; chanID++) {
aiNodeAnim *channel = anim->mChannels[chanID];
aiVector3D curPosition;
aiQuaternion curRotation; // interpolation of scaling purposefully left out for simplicity.
// find the node which the channel affects
aiNode* targetNode = scene->mRootNode->FindNode( channel->mNodeName );
// find current positionKey
size_t posIndex = 0;
for(posIndex=0; posIndex+1 < channel->mNumPositionKeys; posIndex++)
if( channel->mPositionKeys[posIndex + 1].mTime > poseTime )
break; // the next key lies in the future - so use the current key
// This assumes that there is at least one key
if(posIndex+1 == channel-> mNumPositionKeys)
curPosition = channel->mPositionKeys[posIndex].mValue;
else {
float t0 = channel->mPositionKeys[posIndex].mTime; // Interpolate position/translation
float t1 = channel->mPositionKeys[posIndex+1].mTime;
float weight1 = (poseTime-t0)/(t1-t0);
curPosition = channel->mPositionKeys[posIndex].mValue * (1.0f - weight1) +
channel->mPositionKeys[posIndex+1].mValue * weight1;
}
// find current rotationKey
size_t rotIndex = 0;
for(rotIndex=0; rotIndex+1 < channel->mNumRotationKeys; rotIndex++)
if( channel->mRotationKeys[rotIndex + 1].mTime > poseTime )
break; // the next key lies in the future - so use the current key
if(rotIndex+1 == channel-> mNumRotationKeys)
curRotation = channel->mRotationKeys[rotIndex].mValue;
else {
float t0 = channel->mRotationKeys[rotIndex].mTime; // Interpolate using quaternions
float t1 = channel->mRotationKeys[rotIndex+1].mTime;
float weight1 = (poseTime-t0)/(t1-t0);
aiQuaternion::Interpolate(curRotation, channel->mRotationKeys[rotIndex].mValue,
channel->mRotationKeys[rotIndex+1].mValue, weight1);
curRotation = curRotation.Normalize();
}
aiMatrix4x4 trafo = aiMatrix4x4(curRotation.GetMatrix()); // now build a rotation matrix
trafo.a4 = curPosition.x; trafo.b4 = curPosition.y; trafo.c4 = curPosition.z; // add the translation
targetNode->mTransformation = trafo; // assign this transformation to the node
}
// Calculate the total transformation for each bone relative to the rest pose
for(unsigned int a=0; a<mesh->mNumBones; a++) {
const aiBone* bone = mesh->mBones[a];
aiMatrix4x4 bTrans = bone->mOffsetMatrix; // start with mesh-to-bone matrix to subtract rest pose
// Find the bone, then loop through the nodes/bones on the path up to the root.
for(aiNode* node = scene->mRootNode->FindNode(bone->mName); node!=NULL; node=node->mParent)
bTrans = node->mTransformation * bTrans; // add each bone's current relative transformation
boneTransforms[a] = mat4(vec4(bTrans.a1, bTrans.a2, bTrans.a3, bTrans.a4),
vec4(bTrans.b1, bTrans.b2, bTrans.b3, bTrans.b4),
vec4(bTrans.c1, bTrans.c2, bTrans.c3, bTrans.c4),
vec4(bTrans.d1, bTrans.d2, bTrans.d3, bTrans.d4)); // Convert to mat4
}
}
I am currently working on a game and I want to know if there is any way of handling with the elements i am drawing . For example : if i draw in a loop 100 cubes , how can i show / hide the cube number 15 or 63 or n ... I thought that initializing elements in a list would work , but i didn't find any property of it that could help.
GLuint cube;
cube = glGenLists(1);
glNewList(cube,GL_COMPILE);
for(int i = -30; i < 3; i++) {
for(int j = -30; j < 3; j++) {
glPushMatrix();
glTranslatef(i*2.0,0,j * 2.0);
Dcube();
glPopMatrix();
}
}
glEndList();
//something like : glDeleteList(cube); but that only works with entire list not with individual objects..
You have a display list, very good. So now you're back to using your regular language primitives to simply call that function.
std::array<bool, 100> cubes;
std::fill(cubes.begin(), cubes.end(), true);
cubes[15] = false;
cubes[63] = false;
for (bool drawCube : cubes) {
if (drawCube) {
// move a bit, perhaps using glTranslate
glCallList(cube);
}
}
OpenGL isn't your statekeeper. It just draws what you tell it to, you're responsible for keeping your objects.
I am trying to add animation to my program.
I have human model created in Blender with skeletal animation, and I can skip through the keyframes to see the model walking.
Now I've exported the model to an XML (Ogre3D) format, and in this XML file I can see the rotation, translation and scale assigned to each bone at a specific time (t=0.00000, t=0.00040, ... etc.)
What I've done is found which vertices are assigned each bone. Now I'm assuming all I need to do is apply the transformations defined for the bone to each one of these vertices. Is this the correct approach?
In my OpenGL draw() function (rough pseudo-code):
for (Bone b : bones){
gl.glLoadIdentity();
List<Vertex> v= b.getVertices();
rotation = b.getRotation();
translation = b.getTranslation();
scale = b.getScale();
gl.glTranslatef(translation);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(v);
}
Vertices are usually affected by more than one bone -- it sounds like you're after linear blend skinning. My code's in C++ unfortunately, but hopefully it'll give you the idea:
void Submesh::skin(const Skeleton_CPtr& skeleton)
{
/*
Linear Blend Skinning Algorithm:
P = (\sum_i w_i * M_i * M_{0,i}^{-1}) * P_0 / (sum i w_i)
Each M_{0,i}^{-1} matrix gets P_0 (the rest vertex) into its corresponding bone's coordinate frame.
We construct matrices M_n * M_{0,n}^-1 for each n in advance to avoid repeating calculations.
I refer to these in the code as the 'skinning matrices'.
*/
BoneHierarchy_CPtr boneHierarchy = skeleton->bone_hierarchy();
ConfiguredPose_CPtr pose = skeleton->get_pose();
int boneCount = boneHierarchy->bone_count();
// Construct the skinning matrices.
std::vector<RBTMatrix_CPtr> skinningMatrices(boneCount);
for(int i=0; i<boneCount; ++i)
{
skinningMatrices[i] = pose->bones(i)->absolute_matrix() * skeleton->to_bone_matrix(i);
}
// Build the vertex array.
RBTMatrix_Ptr m = RBTMatrix::zeros(); // used as an accumulator for \sum_i w_i * M_i * M_{0,i}^{-1}
int vertCount = static_cast<int>(m_vertices.size());
for(int i=0, offset=0; i<vertCount; ++i, offset+=3)
{
const Vector3d& p0 = m_vertices[i].position();
const std::vector<BoneWeight>& boneWeights = m_vertices[i].bone_weights();
int boneWeightCount = static_cast<int>(boneWeights.size());
Vector3d p;
if(boneWeightCount != 0)
{
double boneWeightSum = 0;
for(int j=0; j<boneWeightCount; ++j)
{
int boneIndex = boneWeights[j].bone_index();
double boneWeight = boneWeights[j].weight();
boneWeightSum += boneWeight;
m->add_scaled(skinningMatrices[boneIndex], boneWeight);
}
// Note: This is effectively p = m*p0 (if we think of p0 as (p0.x, p0.y, p0.z, 1)).
p = m->apply_to_point(p0);
p /= boneWeightSum;
// Reset the accumulator matrix ready for the next vertex.
m->reset_to_zeros();
}
else
{
// If this vertex is unaffected by the armature (i.e. no bone weights have been assigned to it),
// use its rest position as its real position (it's the best we can do).
p = p0;
}
m_vertArray[offset] = p.x;
m_vertArray[offset+1] = p.y;
m_vertArray[offset+2] = p.z;
}
}
void Submesh::render() const
{
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, &m_vertArray[0]);
if(m_material->uses_texcoords())
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_DOUBLE, 0, &m_texCoordArray[0]);
}
m_material->apply();
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_vertIndices.size()), GL_UNSIGNED_INT, &m_vertIndices[0]);
glPopAttrib();
glPopClientAttrib();
}
Note in passing that real-world implementations usually do this sort of thing on the GPU to the best of my knowledge.
Your code assumes that each bone has an independent transformation matrix (you reset your matrix at the start of each loop iteration). But in reality, bones form a hierarchical structure that you must preserve when you do your rendering. Consider that when your upper arm rotates your forearm rotates along, because it is attached. The forearm may have its own rotation, but that is applied after it is rotated with the upper arm.
The rendering of the skeleton then is done recursively. Here is some pseudo-code:
function renderBone(Bone b) {
setupTransformMatrix(b);
draw(b);
foreach c in b.getChildren()
renderBone(c);
}
main() {
gl.glLoadIdentity();
renderBone(rootBone);
}
I hope this helps.
I am facing difficulty with the translate() function for objects as well as objects in general in Processing. I went through the examples and tried to replicate the manners by which they instantiated the objects but cannot seem to even get the shapes to appear on the screen no less move them. I instantiate the objects into an array using a nested for loop and expect a grid of the objects to be rendered. However, nothing at all is rendered.
My nested for loop structure to instantiate the tiles:
for(int i=0; i<102; i++){
for(int j=0; j<102; j++){
tiles[i][j]=new tile(i,0,j);
tiles[i][j].display();
}
}
And the constructors for the tile class:
tile(int x, int y, int z){
this.x=x;
this.y=y;
this.z=z;
beginShape();
vertex(x,y,z);
vertex(x+1,y,z);
vertex(x+1,y,z-1);
vertex(x,y,z-1);
endShape();
}
Nothing is rendered at all when this runs. Furthermore, if this is of any concern, my translations(movements) are done in a method I wrote for the tile class called move which simply calls translate. Is this the correct way? How should one approach this? I can't seem to understand at all how to render/create/translate individual objects/shapes.
Transformations (such as translate, rotate, etc) do not work if you use beginShape() as you're simply specifying direct coordinates to draw to. If you're relying on the result of a translate to put an object into a visible location that could be why you're not having any results.
Also, depending on how you're looking at your scene, you probably have z coming towards the camera, so your objects are being drawn with you looking at them on the side, and since they are 2d objects you won't see anything, try using x/y or y/z instead of x/z which you are doing right now.
You can definitely use pushMatrix() and translate() with beginShape() and such, it may be not completely what you expect, but it will definitely move the things around from the default origin.
What is going wrong with your above example is that you are putting the drawing() code in the constructor where you should be putting it in the display function.
so:
public void display(Processing proc) {
proc.beginShape()
etc.
}
display() also needs to be called in the draw() loop, so initialize your tiles once and then display them in draw().
You should follow #Tyler's advice on drawing in a 2D plane(x/y, y/z, x/z).
Your shapes probably do not render because you might be drawing them once, and clearing the screen in the draw() method, but I'm not sure as I can't see the rest of your code.
Here's what I mean:
tile[][] tiles;
int numTiles = 51;//x and y number of tiles
void setup() {
size(400,400,P3D);
tiles = new tile[numTiles][numTiles];
for(int i=0; i<numTiles; i++)
for(int j=0; j<numTiles; j++)
tiles[i][j]=new tile(i,j,0,5);
}
void draw() {
background(255);
translate(width * .5,height * .5);
rotateY((float)mouseX/width * PI);
rotateX((float)mouseY/height * PI);
for(int i=0; i<numTiles; i++)
for(int j=0; j<numTiles; j++)
tiles[i][j].display();
}
class tile {
int x,y,z;
tile(int x, int y, int z,int s) {//s for size
this.x=x * s;
this.y=y * s;
this.z=z * s;
}
void display(){
beginShape(QUADS);
//XY plane
//*
vertex(x,y,z);
vertex(x+x,y,z);
vertex(x+x,y+y,z);
vertex(x,y+y,z);
//*/
endShape();
}
}
Since you're only drawing squares, you could use the rect() function.
int numSquares = 51,squareSize = 10;
void setup(){
size(400,400,P3D);
smooth();
}
void draw(){
background(255);
translate(width * .5, height * .5);
rotateY((float)mouseX/width * PI);
for(int j = 0 ; j < numSquares ; j++)
for(int i = 0 ; i < numSquares ; i++)
rect(i*squareSize,j*squareSize,squareSize,squareSize);
}
HTH