PDA

View Full Version : Rotating several objects



d_ash77
02-10-2017, 08:21 AM
Hello, I am a very beginner of OpenGL. I'm trying to make a Rubiks Cube and I don't know how to rotate every segment. I've made 27 cubes with GL_QUADS and I storing them in 27 arrays[24][3] ([vertexs][xyz]) here's the sample:

float cube1pos[24][3] = {
15, 15, 75,
30, 15, 75,
30, 30, 75,
15, 30, 75,

30, 15, 60,
15, 15, 60,
15, 30, 60,
30, 30, 60,

15, 15, 60,
30, 15, 60,
30, 15, 75,
15, 15, 75,

30, 30, 60,
15, 30, 60,
15, 30, 75,
30, 30, 75,

15, 15, 60,
15, 15, 75,
15, 30, 75,
15, 30, 60,

30, 15, 75,
30, 15, 60,
30, 30, 60,
30, 30, 75
};

Next I'm loading them one by one by:

void drawSmallCube(float cubePosArray[24][3])
{
for(int i=0; i<24; i++)
{
glVertex3f(cubePosArray[i][0], cubePosArray[i][1], cubePosArray[i][2]);
}
}

And drawing whole cube:

void drawSmallCubes()
{
glBegin(GL_QUADS);
drawSmallCube(cube1pos);
.
.
.
drawSmallCube(cube27pos);
glEnd();
}
Is there any simple way to rotate every segment?

Greetings.

BBeck1
02-11-2017, 07:12 AM
Wow, this is kind of an ugly problem. I had to think about how I would do it for a sec. But I think I have it.

So, you could think of each sub-cube as having it's own model/world/object matrix. However, I would probably parent it to a model matrix for the whole cube. So, it would be

SmallCube1DrawWorld = RubiksCubeWorld * SmallCube1World;

where Smallcube1DrawWorld is the matrix you use to draw it rather than it's own world matrix. I may have got the order of multiplication backwards there. I always have to try it both ways to remember which direction it is. One rotates around the local axis and the other around the global axis.

Anyway, you would need to rotate 8 small cubes together at once as one of the "rings" in the Rubiks cube. You would orbit them around the origin. Again, the order of multiplication when multiplying two matrices determines whether it rotates or orbits. GLM seems to obfuscate rotations, which really annoys me. So, I force it to give me a rotation matrix rather than letting it handle rotations.

That looks something like this:
Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));

glm::mat4() is an identity matrix. So, I'm feeding it an identity matrix so that it returns a rotation matrix that I can use to do my rotation. I generally want rotations rather than orbiting, but in your case you need the "ring" of small cubes to orbit, not rotate individually.

To keep things simple. I would make the Rubik's cube stationary and not have it's own world matrix. Then only the small cubes would have their own world matrices. And I would center everything on the origin. So, then this problem is fairly straight forward. An orbit would probably looks something like this.

SmallCube1WorldMatrix = glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)) * SmallCube1WorldMatrix;

So, forget about what I originally said about the whole thing having a world matrix; that just complicates things (and allows you to rotate the whole cube as a single unit/parent). Get it working without the main cube having a world matrix first.

Anyway, just do the same orbit operation on every small cube in the ring. As long as the final rotation is in increments of 90 degrees, I would think everything would work for the inner rings.

The outer rings are more of a problem. There you need to be orbiting an offset. However, if your small cubes are centered at 0,0,0 in their vertices, you can use that to your advantage. That means the small cube in the center of the face will have the exact position you need to orbit around. Even it's own orbit will work properly if it orbits that point producing a rotation.

Now you're really getting me to think. This is difficult because you can't attach the pieces together; corners have to belong to 3 faces and can orbit the centers of all 3 faces. So, no attachments can be permanent. I haven't tried parent-child relationships where the parent is a non-existent phantom that goes in and out of existence.

Ok. Let me invent an imaginary scenario in my mind here to keep this straight. You have an axis aligned cube. We want to turn the face of the cube on the positive X side. The center small cube is offset on the X axis (right), zero'ed on the Y axis (up), and zero'ed on the Z axis (forward). That makes it basically centered on the X axis. We want the face to rotate around the X axis. Ok, so the first problem is that the center small cube is not really centered at it's center. It's centered at the origin and it's position offset from there. You could move the whole face to center on the origin, rotate it, and move it back. But let's see if we can avoid that.

Ok. I think you can extract the position out of the center small cube's matrix like:

Position = (glm::vec3) SmallCube13[3];

assuming SmallCube13 is the center small cube of the face. You can use that to build a translation matrix. I think if you multiply the translation matrix times the rotation matrix and apply it to every small cube in the face it will work. The order of multiplication here would be critical, as it always is with matrices. And I'm not sure which order is the correct one off the top of my head.

I wish I had an environment where I could throw together a quick test. I could spend all day today putting one together but I don't have time.

But I think if you use the offset of the center cube of the face and make sure the rotations end on 90 degree increments, it should work. It's going to go really bad if you ever get it slightly off 90 degrees though. It would probably be easiest to just do 90 degree rotations in a single frame rather than animating it. For the animation, you could not allow the user to directly turn the small cubes, but queue them up and let the computer do 180 half degree rotations as an animation, and then do the next rotation in the queue. Or wait until one is finished before allowing the next.

I would say these rotations are not a "beginner" project. The rest of it is not so difficult, but I've seen very few people who would know how to do these rotations. I myself would really need to code it up and play with it a bit to get it to work. And the real problem is those outer face rotations where the origin of rotation has to change between 3 different positions for any corner small cube. This is slightly more complex than anything I've ever done. I could probably figure it out, but I definitely need to try some things out in code to figure out exactly how to pull it off.

But I think it's mostly about multiplying the rotation matrix times an offset before applying it to all the small cubes in the face. Where the offset is a translation matrix built from the position of the center small cube of the face's position (not a copy of it's world matrix because you don't want it's rotation I don't think).

john_connor
02-11-2017, 11:53 AM
lets break down the big problem into smaller problems:

what is a cube ?
-- consists of 6 quads, using 4 * 6 = 24 vertices (or just 8 if indexed drawing)
-- your application doesnt care about its vertices, only the graphics part does! (whehn you render the scene)
-- your application sees a cube as the following:

struct Cube {
vec3 Position;
quat Rotation; // quaternion
};

what is a RubiksCube ?
-- a big cube consisting of 3 x 3 x 3 = 27 smaller cubes
-- can be rotated at each edge (to change the colors)

struct RubiksCube {
Cube cubes[3][3][3];
void RotateEdge(int face);
};

your task is to correctly implement a void RotateEdge(int face); member to edit the rubikscube somehow ...

maybe its a good idea (?) to further simplify by introducing another type "Face":

struct RubiksCubeFace {
Cube* cubes[3][3]; // pointer to cubes
};

or another way of thinking: you need just a way to determine the cubes belonging to a certain face
maybe a "union" type of struct would be useful ...