i'm drawing several primitives with a camera class that supplies with view, world and projection. With these i can move the camera and see all 3 of my primitives. The problem is that i now wan't to add a 3d model in the project, but the ways i know of to draw a 3d model won't work with the camera class. I have tried using:
Matrix[] transforms = new Matrix[mymodel.Bones.Count];
mymodel.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in mymodel.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] *
Matrix.CreateRotationY(0.0f)
* Matrix.CreateTranslation(modelposistion);
effect.View = Matrix.CreateLookAt(cameraPosistion, Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, 1.0f, 10000.0f);
}
mesh.Draw();
}
But this draws the model in the center of the screen, while i want it stationary, so i can view other models in the same project.
Any help to how this can be achieved?
If I understand your problem: "as you move your camera, the model follows".
Most likely, it isn't following, you are just moving the camera without changing where the camera is "looking at". Your view matrix calls for it to look at (camera to be pointed at) Vector3.Zero no matter what position the camera is altered to. This means that if your model position does not change, and your view matrix is always looking at the same world location, then the model will not appear to change position. It may get bigger or smaller or you might see it from different angles, but it will always be in the same spot on your screen. It's because your telling the camera to always look at the same position in your world.
Changing the camera position isn't enough to freely look around your world, you must also change where the camera is looking at ( the 2nd param of the View matrix).
Also, it is best to think of the camera as having just a view and projection matrices. The world matrix should be thought of as part of (or supplied by) the model.
Related
I am implementing an Arcball rotation in an existing project I wrote several years ago, using OpenTK & C#, and have got stuck at the final hurdle.
This is 'old-style' non-shader OpenGL. I am confident that the Arcball rotation is working correctly, the problem is just applying the resulting matrix. It should be fairly straightforward but it isn't working out that way.
I get the Arcball rotation as a quaternion (qCurrent) then convert that to a matrix. I have then tried a couple of approaches:
Simply apply that as an additional rotation to the existing scene:
GL.PushMatrix();
Matrix4 arcball_rot = Matrix4.CreateFromQuaternion(qCurrent);
GL.MultMatrix(ref arcball_rot);
... render objects in scene
GL.PopMatrix();
This applies the correct rotation around the correct origin (the camera target), but with respect to the world coordinate system, not the viewer. This makes sense because I am effectively applying the rotations in the wrong sequence.
Apply the rotations in sequence, with the arcball rotation first, which means starting from scratch. Here, rtn_complete is a matrix storing the modelview matrix as initialised, before the Arcball rotation:
Matrix4 modelview = Matrix4.LookAt(camera.camerapos, camera.cameratarget, camera.cameraup);
rtn_complete = modelview;
Then...
Matrix4 mm = Matrix4.CreateFromQuaternion(qCurrent) * rtn_complete;
GL.LoadIdentity();
GL.MultMatrix(ref mm);
This applies the correct rotation wrt the viewer, but around the wrong centre of rotation. It is a point a long way away from the camera target.
Obviously this approach needs a forward/backward translation either side of the rotation, but I have tried pretty much every possible combination of these and none of them work.
Camera and target positions are as follows :-
camera.camerapos = (-51.3, -67.9, 37.7), and
camera.cameratarget = (0.0, 0.6, 7.3)
When I add translations as below (and countless other permutations) I still get the scene rotating around the wrong origin.
GL.LoadIdentity();
GL.Translate(camera.cameratarget);
GL.MultMatrix(ref arcball_rot);
GL.Translate(-camera.cameratarget);
GL.MultMatrix(ref rtn_complete);
My feeling is that the root of the problem is likely to be the translation applied by using LookAt, which I am not taking into account when doing the above transformations.
However, when I check the rtn_complete matrix (which is the modelview matrix following the LookAt) the fourth column does not contain a translation. The matrix looks like this:
0.80, 0.20, -0.56, 0.00
-0.60, 0.27, -0.75, 0.00
0.00, 0.94, 0.34, 0.00
0.38, -7.02, -92.8, 1.00
I would have expected to a see a translation here.
EDIT 1:
Found it eventually. I was on the right track with my suspicion about the translation resulting from using Matrix4.LookAt().
The way I had listed the matrices for debugging resulted in a transpose, so the translation was there for me to see but I was missing it but it was in the fourth row not the fourth column. The translation is (0.38, -0.72, -92.8) Applying this translation on either side of the Arcball rotation results in the expected rotation behaviour, initially.
GL.LoadIdentity();
GL.Translate(0.38, -7.02, -92.8);
GL.MultMatrix(ref mm);
GL.Translate(-0.38, 7.02, 92.8);
GL.MultMatrix(ref rtn_complete);
EDIT 2:
Having worked out the above I am very close, but it's still not quite right when I move somewhere else in the scene. Again I have got a couple of problems, and can solve one or the other but not both simulataneously.
Because of the Matrix4.LookAt I separated out the translation and the rotation components of the rtn_complete matrix (respectively 'rtn_complete_trans' and 'rtn_complete_rot').
If I do this, the rotations are spot on:
GL.LoadIdentity();
GL.Translate(rtn_complete_trans); // modelview translation component
GL.Translate(camera.cameratarget); // translate to rotation centre
GL.MultMatrix(ref arcball_rot); // ongoing Arcball rotation
GL.MultMatrix(ref rtn_complete_rot); // modelview rotation component
GL.Translate(-camera.cameratarget); // translate back from rotation centre
But there is an unwanted translation each time the view is initialised. If I rotate, release and repeat a few times the object gradually moves off screen.
If I change the position of the second camera translation this unwanted shift no longer happens, but the centre of rotation is off:
GL.LoadIdentity();
GL.Translate(rtn_complete_trans); // modelview translation component
GL.Translate(camera.cameratarget); // translate to rotation centre
GL.MultMatrix(ref arcball_rot); // ongoing Arcball rotation
GL.Translate(-camera.cameratarget); // translate back from rotation centre
GL.MultMatrix(ref rtn_complete_rot); // modelview rotation component
Can anyone explain?
Ok, this is hard to explain without pictures. I made a very limited model of a glock in Blender with an extruded cube making up the base and a scaled cube making up the barrel.
In Blender, It looks fine:
However, after exporting the model to .fbx and loading it into the compiler, it comes out like this:
I don't know exactly what is going on. Everything on the model is properly UV Mapped and the coordinates are correct, It just seems the translation is.. off..
Here is my drawModel code:
private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = world;
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
Any pointers will be helpful!
EDIT: After Applying rotation, location, and scale, I was able to get them in the right position, but why does it look transparent?
Thank you for all the help!
As Nikola mentioned, in the later versions of Blender, they have proper XNA support.
Here's a screenshot of the settings I chose when exporting a model:
Why this happens:
Your Depth Buffer is not enabled, so when your GPU draws the barrel first, then the stock/handle, the handle is drawn on top of the barrel, even though some parts of it are supposed to be invisible because they would be hidden by the barrel.
How to fix:
Enable Depth Buffer as other answer suggests. And/Or order meshes in your model based on distance from camera, and draw tehm in order from the nearset to the farthest. This may not seem usefull now, but when you start working with transparent objects, you will see why this is a good thing to learn.
Enable depth buffer:
GraphicsDevice.DepthStencilState = DepthStencilStates.Default;
And to Draw a model in Xna you can follow this tutorial, that shows how the bone transforms have to be applied to get the meshes in right position.
I didnt quite understand whats is the effect.World and effect.View etc. and why we put the matricies in them?
foreach (ModelMesh mesh in model1.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);
effect.View = Matrix.CreateLookAt(Vector3.Zero, Vector3.Zero, Vector3.Up);
}
}
effect.World is not a matrix representing the world. It is a matrix representing a 3d object's (mesh, model) position and orientation relative to the 3d game world. Each object will have a different effect.World matrix if they are positioned differently &/or pointed differently.
effect.View is a matrix that represents (in an inverted form) the position & orientation of the camera relative to that same 3d game world. Most of the time, there is only one camera, but there can be more (say, a rear view mirror will have it's own view matrix as opposed to the main screen showing the view out the windshield of a car racing game).
a model's vertices are hard valued to model local space.
Then the effect.World transforms them to game world space.
then the effect.View transforms them to camera space.
Then the effect.Projection transforms them to 2d screen space and 'volia', your pixel shader knows where to draw what.
From MSDN;
BasicEffect.World
Gets or sets the world matrix. Use this matrix to change the position
of the model, using world coordinates.
BasicEffect.View
Gets or sets the view matrix. Use this matrix to change the position
and direction of the camera.
I think bold lines explain what is exactly their difference. Also I found some helpful articles;
How to: Use BasicEffect
Does every Entity in an XNA game need it's own BasicEffect instance?
Understanding Half-Pixel and Half-Texel Offsets
http://blogs.msdn.com/b/shawnhar/archive/2010/04/05/spritebatch-and-custom-shaders-in-xna-game-studio-4-0.aspx
http://msdn.microsoft.com/en-us/library/bb219690(VS.85).aspx
I wanted to know if my method used below is the correct way to determine the position of my model before drawing on the screen.
Because when I want to create a floor duplicating my model. I find myself faced with the "Z-fighting" problem ^ ^.
thank you very much
Code sample to st position on my model before drawing :
model_Position.X = (float) 1.0;
model_Position.Z = (float) 3.0;
model_Position.Y = (float) 5.0;
// Draw the model. A model can have multiple meshes, so loop.
foreach (ModelMesh mesh in model_ground_land1.Meshes)
{
// This is where the mesh orientation is set, as well
// as our camera and projection.
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] *
Matrix.CreateRotationY(model_Rotation) * Matrix.CreateTranslation(model_Position);
effect.View = View;
effect.Projection = Projection;
}
// Draw the mesh, using the effects set above.
mesh.Draw();
}
As far as I can see, your method is correct for positioning the model. What comes to the Z fighting problem, it happens when two 3D faces are drawn in the same plane overlapping each other. As you say that you "duplicate your model" it sounds like this is the problem, not the model positioning itself. To resolve the fighting, you can set some nonzero value to GraphicsDevice.RasterizerState.DepthBias before drawing the second copy of the model. From the documentation:
"Polygons with a high z-bias value appear in front of polygons with a
low value, without requiring sorting for drawing order. For example,
polygons with a value of 1 appear in front of polygons with a value of
0."
So I know there are a few questions/answers regarding building a 2D Camera for XNA however people seem to just be happy posting their code without explanation.
I'm looking for more of an explanation of what I'm doing wrong.
First off, I understand the whole World -> View - > Projection - > Screen transformation.
My goal is to have a camera object that is centered in the center of the viewport and that when the camera's position moves up it correlates to moving up in the viewport and when it moves to the right it correlates moving right in the viewport.
I'm having difficulty implementing that functionality because the Y value of the viewport is inverted.
//In Camera Class
private void UpdateViewTransform()
{
//My thinking here was that I would create a projection matrix to center the camera and then flip the Y axis appropriately
Matrix proj = Matrix.CreateTranslation(new Vector3(_viewport.Width * 0.5f, _viewport.Height * 0.5f, 0)) *
Matrix.CreateScale(new Vector3(1f, -1f, 1f));
//Here is the camera Matrix. I have to give the Inverse of this matrix to the Spritebatch I believe since I want to go from World Coordinates to Camera Coordinates
_viewMatrix = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1.0f)) *
Matrix.CreateTranslation(_position.X, _position.Y, 0.0f);
_viewMatrix = proj * _viewMatrix;
}
Can someone help me understand how I can build my view transformation to pass into the SpriteBatch so that I achieve what I'm looking for.
EDIT
This as a transformation seems to work however I am unsure why. Can someone perhaps break it down for me in understanding:
Matrix proj = Matrix.CreateTranslation(new Vector3(_viewport.Width * 0.5f, _viewport.Height * 0.5f, 0));
_viewMatrix = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1.0f)) *
Matrix.CreateTranslation(-1 * _position.X, _position.Y, 0.0f);
_viewMatrix = proj * _viewMatrix;
I've built a raytracer before so I should understand your understanding, my confusion lies with the fact it's 2D and that SpriteBatch is hiding what it's doing from me.
Thanks!
Farid
If you flip everything on the Y axis with your scale matrix, that means you are flipping the models that SpriteBatch is drawing (textured quads). This means you also have to change your winding-order (ie: backface culling is interpreting that you are drawing the backs of the triangles facing the camera, so it culls them, so you have to change the rule that it uses).
By default, SpriteBatch uses RasterizerState.CullCounterClockwise. When you call SpriteBatch.Begin you need to pass in RasterizerState.CullClockwise instead.
And, of course, Begin is where you pass in your transformation matrix.
I haven't carefully checked your matrix operations - although I have a suspicion that the order is incorrect. I recommend you create a very simple testing app and build up your transformations one at a time.
I've fought with XNA a lot trying to get it to be like other engines I have worked with before... my reccomendation, it isn't worth it... Just go with the XNA standards and use the Matrix helper methods for creating your Perspective / Viewport matrices.
So after just thinking logically, I was able to deduce the proper transformation.
I'll outline the steps here incase anyone wants the real breakdown:
It is important to understand what is a Camera Transformation or View Transformation.
A View Transformation is normally what is needed to go from Coordinates relative to your Camera to World-Space coordinates. The inverse of the View Transformation would then make a world coordinate relative to your Camera!
Creating a View Matrix
Apply the camera's rotation. We are doing it in the Z axis only because this is for a 2D camera.
Apply the transformation of the Camera. This make sense since we want the World coordinate which is the sum of the camera coordinate and the object relative to the camera.
You might notice that I multiplied the Y Component by -1. This is so that increasing the Camera's Y Position correlates to moving up since the screen's Y value points downward.
Apply the camera's zoom
Now the inverse of the matrix will do World Coordinate -> View Cordinates.
I also chose to center my camera in the center of the screen, so I included a pre-appended translation.
Matrix proj = Matrix.CreateTranslation(new Vector3(_viewport.Width * 0.5f, _viewport.Height * 0.5f, 0));
_viewMatrix = Matrix.CreateRotationZ(moveComponent.Rotation) *
Matrix.CreateTranslation(moveComponent.Position.X, -1 * moveComponent.Position.Y, 0.0f) *
Matrix.CreateScale(new Vector3(zoomComponent.Zoom, zoomComponent.Zoom, 1.0f));
_viewMatrix = proj * Matrix.Invert(_viewMatrix);