8

I have to create a 2d menu, over a 3d model, using XNA. Right now, I have created spritebatch for 2D, and a 3d model. But, as i have noticed, and was mentioned in other places, model is not being displayed properly, because of z buffer issue. According to the tutorial, I am supposed to enable the DepthBuffer again, in the draw method. But, somehow, when I use:

GraphicsDevice.DepthStencilState.DepthBufferEnable = true;

code throws an error during debugging, saying,

Cannot change read-only DepthStencilState. State objects become read-only the first time they are bound to a GraphicsDevice. To change property values, create a new DepthStencilState instance.

Now, I have tried to create a new instance of DepthStencilState too, but, even that doesn't seem to work. I get the same error always, even though, the help document suggests its a Read/Write value.

Kindly help me figure out how to get the 3D model displayed properly.

Here is the Draw code for reference.

protected override void Draw(GameTime gameTime)
{
     GraphicsDevice.Clear(Color.CornflowerBlue);

     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.DirectionalLight0.Enabled = true;
             //effect.DirectionalLight0.DiffuseColor = Color.AntiqueWhite.ToVector3();
             //effect.DirectionalLight0.Direction = new Vector3(0, 0, 0);

             effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(myModelRotation) * Matrix.CreateTranslation(myModelPosition);
             effect.View = Matrix.CreateLookAt(new Vector3(0, 0, 3000), Vector3.Zero, Vector3.Up);
             effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f),
             GraphicsDevice.Viewport.AspectRatio, 1, 5000);
         }

         mesh.Draw();
    }

    spriteBatch.Begin();
    spriteBatch.Draw(myTexture, new Vector2(0, 0), Color.White);
    spriteBatch.End();

    DepthStencilState d = new DepthStencilState();
    d.DepthBufferEnable = true;
    GraphicsDevice.DepthStencilState = d;

    base.Draw(gameTime);
}
Omar
  • 16,329
  • 10
  • 48
  • 66
jitendragarg
  • 945
  • 1
  • 14
  • 54

4 Answers4

8

You cannot modify a DepthStencilState object once it has been set on the device. You should create a single DepthStencilState object for each unique depth/stencil setting you want to use. Optimally, these states should only be created once in your program and then set onto the device when required.

For more information see State Objects in XNA Game Studio 4.0 by Shawn Hargreaves.

Additional Details:

You are setting DepthBufferEnable to true but not DepthBufferWriteEnable to true as well. Instead of trying to set these properties individually, you would be better off using the prebuilt DepthStencilState objects provided by the framework. Before rendering your model set the GraphicsDevice.DepthStencilState to DepthStencilState.Default. This will set both the DepthBufferEnable and DepthBufferWriteEnable properties to true.

You also need to set the BlendState on the device to BlendState.Opaque.

You need to set these renderstates before you draw your model because SpriteBatch.Begin() automatically changes the renderstate to DepthStencilState.None and BlendState.AlphaBlend (see the MSDN Documentation for SpriteBatch.Begin).

Empyrean
  • 1,823
  • 12
  • 10
  • So, how to get my 3D model displayed along with 2D sprite? – jitendragarg Feb 15 '11 at 19:28
  • Just before you draw your model, set the 3d depth stencil state on the device. Then call SpriteBatch.Begin using the overload that allows you to pass the 2D depth stencil state as a parameter. Make the 2d and 3d depth stencil state objects class members and create them only once during initialization, not in your draw method. In the XNA 4.0 render state paradigm it is better to set the entire state using existing state objects before each draw call instead of trying to reset the state to some default after drawing. – Empyrean Feb 16 '11 at 05:34
  • Oops! too confusing! sorry, I am just starting out, so, I am basically unable to understand anything in your response. – jitendragarg Feb 16 '11 at 15:17
  • Ok. A simpler version. Just before you render your model (that is before the foreach loop in your draw function) add the following line of code: GraphicsDevice.DepthStencilState = DepthStencilState.Default; Remove the code at the bottom of your draw function that is setting the DepthStencilState. – Empyrean Feb 17 '11 at 02:24
  • Still not working. Model actually looks 3d, but some parts are still translucent. Anyways here is the link to the complete solution, to help you understand the issue. http://rapidshare.com/files/448435742/TestGame.zip (Already scanned for malwares, still scan it once, just to be on safe side). – jitendragarg Feb 17 '11 at 14:36
  • Add GraphicsDevice.BlendState = BlendState.Opaque; just below GraphicsDevice.DepthStencilState = DepthStencilState.Default; The SpriteBatch.Begin() method is also setting the BlendState to BlendState.AlphaBlend. See http://msdn.microsoft.com/en-us/library/bb195108.aspx. – Empyrean Feb 18 '11 at 02:53
  • Awesome! It worked! Finally I can continue with further lessons in XNA. Thanks a lot for the help, man! :D – jitendragarg Feb 18 '11 at 20:30
4

Solution is to add the following before your 3D render code:

GraphicsDevice.DepthStencilState = DepthStencilState.Default;
Omar
  • 16,329
  • 10
  • 48
  • 66
David D.
  • 41
  • 1
1

I've tested this code before posting it. By creating a new DepthStencilState, you can set the GraphicsDevice property to your new State, like so:


DepthStencilState d = new DepthStencilState();
d.DepthBufferEnable = true;
GraphicsDevice.DepthStencilState = d;
Darkhydro
  • 1,992
  • 4
  • 24
  • 43
  • Tried that too. Used it in Draw method. Model is still not displayed properly. Although, the game doesn't throw an error. I am updating the question with the code in the Draw method, if that helps. – jitendragarg Feb 15 '11 at 19:32
0

Like Empyrean said, all you need to do is place the following line of code directly above the code you use to draw your model in the Draw() function:

GraphicsDevice.DepthStencilState = DepthStencilState.Default;