Homebrew Citro3D: How to get 3D objects to always face the camera while picking up?

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
You know about the Companion Cube in Portal, and noticed how when you pick the cube up and move the camera around, the cube's facing will always stay the same to the player's camera view.

I'm working on something similar to that using matrix math.

This is my current progress:

SKTPc3X.gif


And here's my current code:

Code:
void Player::Manipulate(std::shared_ptr<GameObject> obj, C3D_Mtx& currentViewMatrix, C3D_Mtx& modelMatrix){
    if (this->cameraManipulateFlag){
        //Matrix and C3D_Mtx.
        //C3D_Mtx requires all rows to be [WZYX], instead of the other way around (XYZW).
        //This is due to how the GPU reads the matrix data.
       
        C3D_Mtx inverse, result;
        Mtx_Copy(&inverse, &currentViewMatrix);
        Mtx_Inverse(&inverse);
       
        Mtx_Multiply(&result, &modelMatrix, &this->oldViewMatrix);
        Mtx_Multiply(&modelMatrix, &result, &inverse);
    }
    else {
        Mtx_Copy(&this->oldViewMatrix, &currentViewMatrix);
    }
}

As you can see, I am using the following algorithm to calculate the rotation:

worldRotNew = worldRotOrig * viewRotOrig * inv(viewRotCurrent)

However, it doesn't seem to be correct. When I set cameraManipulateFlag to true, the cube is slanted like a forward slash ( / ), which is when I'm picking up the cube. If the variable is set to false, I am not picking up the cube, so things are normal. I'm just trying to get it to rotate, so the cube always look towards the camera. I'm not worried about translation and scaling for now.

My question is: How to get the cube to always look at the camera when rotating the player's camera?

Attached is the current source code. I really do appreciate any help! Thanks!
 

Attachments

  • 3dsgame.zip
    833.9 KB · Views: 171

Urbanshadow

Well-Known Member
Member
Joined
Oct 16, 2015
Messages
1,578
Trophies
0
Age
33
XP
1,723
Country
Uhm. There's two aproximations to achieve this:

1) You cancel the rotations and translations made to the scene in the object to center it on the screen. This makes the cube center itself in the viewport, while the scene moves. When you want to stop moving the cube you just, well, stop canceling the scene rotation on the cube (stop moving the cube, duh)

2) You implement it so you can choose to perform the rotations and translations of the scene before or after drawing the object. This allows you to rotate the scene but it won't affect the object (since it's rendered after the translate/rotate). It will look centered on the screen until you decide otherwise. You ned update it's coordinates to where its new location is before choosing to be rendered before the rotations again.

Either way is good, but #2 looks less intensive to the gpu to me.
 
Last edited by Urbanshadow,
  • Like
Reactions: Quantumcat

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Thanks for the approximations.

However, I'm currently focused only on the rotations, and nothing but the rotations. No translations and scaling. The code will definitely be optimized later on.

It's just the matrix math not giving me the right rotation when looking up, down, left, and right, that I'm not sure if the code I implemented for the rotation is correct or not.

Maybe I should ask if the matrix math operations is programmed correctly or not...?
 

Urbanshadow

Well-Known Member
Member
Joined
Oct 16, 2015
Messages
1,578
Trophies
0
Age
33
XP
1,723
Country
I have read the code, and I suggest you to change the way you work with the modelview matrix to a MtxStack instead.
To setup two MtxStacks (one for projection and another from modelview) you can:
Code:
   C3D_MtxStack projection, modelview;

   MtxStack_Init(&projection);
   MtxStack_Bind(&projection, GPU_VERTEX_SHADER, VSH_FVEC_projMtx, VSH_ULEN_projMtx);
   Mtx_Identity(MtxStack_Cur(&projection));
   MtxStack_Update(&projection);

   MtxStack_Init(&modelview);
   MtxStack_Bind(&modelview, GPU_VERTEX_SHADER, VSH_FVEC_mdlvMtx, VSH_ULEN_mdlvMtx);
   Mtx_Identity(MtxStack_Cur(&modelview));
   MtxStack_Update(&modelview);

With MtxStacks you should be able to directly:
Code:
void Entity::Render(C3D_MtxStack *projection, C3D_MtxStack *modelview){
 
   // This makes a copy of the current modelview C3D_Matrix and puts it on top of the stack
    MtxStack_Push(modelview);

    // Rotation of just this object in space, MtxStack_Cur(modelview) gets the top modelview matrix from the stack
    Mtx_RotateX(MtxStack_Cur(modelview), rotation[0], true);
    Mtx_RotateY(MtxStack_Cur(modelview), rotation[1], true);
    Mtx_RotateZ(MtxStack_Cur(modelview), rotation[2], true);

    // Position of just this object in space
    Mtx_Translate(MtxStack_Cur(modelview),position[0],position[1],position[2]);

    MtxStack_Update(modelview);

    // Bind the texture if this object has a valid texture pointer
    if(texture != 0)
        C3D_TexBind(0, texture);

    // Any kind of draw should be placed here, inmediate or object buffer, doesn't matter.
    // (Even another entity! -->  anotherentity.Render(projection,modelview))

    // Once we do the pop, every change to modelview since the push is reverted.
    MtxStack_Pop(modelview);
}
You can see something similar to this code in action in the example provided by DevKitPro: graphics\gpu\gputest

And spicing it up a little bit:

If you declare an Entity class as pure virtual like this:
Code:
  class Entity{
     public:
       virtual void Render(C3D_MtxStack*,C3D_MtxStack*) = 0;
   };

Every class that inherits from entity, like Player (class Player : public Entity{}) has to provide the implementation of Render, as is pure virtual. Since every render-able class will inherit from Entity you can assume every Entity is render-able and is possible to make a vector or an array of Entities that can simply be rendered in order as in a Zbuffer this way:
Code:
  void RenderEntities(C3D_MtxStack *projection, C3D_MtxStack *modelview, Entity *entities, int numEntities)
  {
    int i;
    for(i = 0; i < numEntities;i++)
      entities[i].Render(projection, modelview);
  }
And the entities in said array can be anything that implements Entity: player, objects, particles... whatever.
Check more info in pure virtual classes for c++ here.
 
Last edited by Urbanshadow,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Should the MtxStack class member be maintained in the engine.cpp (as part of the Core Engine)? Or it should be maintained as a Player Entity's class member? The way you put it, feels like it can be separated into a Player Entity's class member, instead of a "global Entity, Core Engine tiered" class member, therefore allowing all entities to have access to the MtxStack.

EDIT: Also, what is VSH_FVEC_projMtx, and VSH_ULEN_projMtx? Where did you get this?

EDIT 2: Nevermind, it's a uniform variable declared in the vertex shader of type .fvec .

EDIT 3: No, that's not it. Couldn't get it to generate the VSH_FVEC_... macros and values.
 
Last edited by delete12345,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
I have read the code, and I suggest you to change the way you work with the modelview matrix to a MtxStack instead.
To setup two MtxStacks (one for projection and another from modelview) you can:
Code:
   C3D_MtxStack projection, modelview;

   MtxStack_Init(&projection);
   MtxStack_Bind(&projection, GPU_VERTEX_SHADER, VSH_FVEC_projMtx, VSH_ULEN_projMtx);
   Mtx_Identity(MtxStack_Cur(&projection));
   MtxStack_Update(&projection);

   MtxStack_Init(&modelview);
   MtxStack_Bind(&modelview, GPU_VERTEX_SHADER, VSH_FVEC_mdlvMtx, VSH_ULEN_mdlvMtx);
   Mtx_Identity(MtxStack_Cur(&modelview));
   MtxStack_Update(&modelview);

Is it possible not to use the C3D_MtxStack? A few reasons:
  • I get compiler errors telling me VSH_FVEC_projMtx and VSH_ULEN_projMtx is not defined nor declared. I have no idea how to produce these two values, let along binding the ModelView matrix stack afterwards.
  • I have already separated the ModelView matrices into 4 components: the projection matrix, the view matrix, the model matrix, and the world vertex positions set at origin of (0, 0, 0) in the source code.
  • I cannot fix errors related to setting the vertices to the right coordinates/positions in the vertex shaders.
What I'm doing is I reset the model matrix (matrix that handles individual entity matrix transformations) to identity matrix each time I am rendering the entity. The end results look just like how matrix stacks work for the entities, and it works just the same because I am resetting every matrices that I touch upon. This is used in order to implement the Entity-Component System in the source codes.

I'm still going to look into just the rotation part of the whole matrix transformation. I'm just wondering if the code to apply the matrix math rotation is correct or not.
 

Urbanshadow

Well-Known Member
Member
Joined
Oct 16, 2015
Messages
1,578
Trophies
0
Age
33
XP
1,723
Country
Is it possible not to use the C3D_MtxStack? A few reasons:
  • I get compiler errors telling me VSH_FVEC_projMtx and VSH_ULEN_projMtx is not defined nor declared. I have no idea how to produce these two values, let along binding the ModelView matrix stack afterwards.
  • I have already separated the ModelView matrices into 4 components: the projection matrix, the view matrix, the model matrix, and the world vertex positions set at origin of (0, 0, 0) in the source code.
  • I cannot fix errors related to setting the vertices to the right coordinates/positions in the vertex shaders.
What I'm doing is I reset the model matrix (matrix that handles individual entity matrix transformations) to identity matrix each time I am rendering the entity. The end results look just like how matrix stacks work for the entities, and it works just the same because I am resetting every matrices that I touch upon. This is used in order to implement the Entity-Component System in the source codes.

I'm still going to look into just the rotation part of the whole matrix transformation. I'm just wondering if the code to apply the matrix math rotation is correct or not.
You can check the example mentioned for vertex shader setup and you can still use Mtx_Rotate with a C3D_Matrix to compare the results with your code.
 

Urbanshadow

Well-Known Member
Member
Joined
Oct 16, 2015
Messages
1,578
Trophies
0
Age
33
XP
1,723
Country
The way you put it, feels like it can be separated into a Player Entity's class member, instead of a "global Entity, Core Engine tiered" class member, therefore allowing all entities to have access to the MtxStack.
Yeah, thats like the whole point of using MtxStack: to raise the matrix setup to the core, or managers and each entity have separated, individual render code compatible in any situation. Its just a responsability change.
 
Last edited by Urbanshadow,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Yeah, thats like the whole point of using MtxStack: to raise the matrix setup to the core, or managers and each entity have separated, individual render code compatible in any situation. Its just a responsability change.
Thanks!

And yes, mtheall confirmed the Mtx_Inverse() I wrote for Citro3D is broken. :P I'm so sorry for all the troubles. The rotation math works.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    SylverReZ @ SylverReZ: But I bet that would be more for a flashcart than a consumer repro board.