Noob Question: Primitives and BEPU from Start to Finish?

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
sjom
Posts: 5
Joined: Sun Feb 14, 2010 4:17 pm

Noob Question: Primitives and BEPU from Start to Finish?

Post by sjom »

Hi, I've been messing around with BEPU for the past 2 days, and I still haven't figured out how to get it to work with Primitives. I looked through the getting started guide, but that contained some things that I had no idea how to do, and it didn't specify how to do them. I looked at the source for the Getting Started project as well, but I couldn't understand it enough to where I could put it into my own code.

Right now, I have the following code:

Game1.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using BEPUphysics;
using BEPUphysics.Entities;
using BEPUphysics.DataStructures;

namespace Sjomsites___Worlds
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Matrix worldMatrix;
        Matrix cameraMatrix;
        Matrix projectionMatrix;
        float angle = 0f;

        Space space;

        BasicEffect cubeEffect;

        BasicShape cube = new BasicShape(new Vector3(2, 2, 2), new Vector3(0, 0, 0));

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            initializeWorld();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //cube.shapeTexture = Content.Load<Texture2D>("Textures\\100x100 SM Icon");

            // Instantiate the Space Variable
            space = new Space();

            // Set the gravity variable in the space variable to a more realistic standard
            space.simulationSettings.motionUpdate.gravity = new Vector3(0, -9.81f, 0);


        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            angle += 0.5f;

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            cubeEffect.Begin();

            worldMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(angle)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(angle));
            cubeEffect.World = worldMatrix;

            foreach (EffectPass pass in cubeEffect.CurrentTechnique.Passes)
            {
                pass.Begin();
                cubeEffect.Texture = cube.shapeTexture;
                cube.RenderShape(GraphicsDevice);
                pass.End();
            }

            cubeEffect.End();

            base.Draw(gameTime);
        }

        public void initializeWorld()
        {
            cameraMatrix = Matrix.CreateLookAt(
                new Vector3(0, 30, 20), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                Window.ClientBounds.Width / Window.ClientBounds.Height, 1.0f, 50.0f);
            float tilt = MathHelper.ToRadians(22.5f);
            worldMatrix = Matrix.CreateRotationX(tilt) * Matrix.CreateRotationY(tilt);

            cubeEffect = new BasicEffect(graphics.GraphicsDevice, null);
            cubeEffect.World = worldMatrix;
            cubeEffect.View = cameraMatrix;
            cubeEffect.Projection = projectionMatrix;
            cubeEffect.TextureEnabled = true;
        }
    }
}
-----------------------------------------------------------------------------

BasicShape.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using BEPUphysics;
using BEPUphysics.Entities;
using BEPUphysics.DataStructures;

namespace Sjomsites___Worlds
{
    class BasicShape
    {
        public Vector3 shapeSize;
        public Vector3 shapePosition;
        private StaticTriangleGroup.StaticTriangleGroupVertex[] shapeVertices;
        private int shapeTriangles;
        private VertexBuffer shapeBuffer;
        public Texture2D shapeTexture;
        public TriangleMesh shapeMesh;
        public StaticTriangleGroup shapeGroup;

        public BasicShape(Vector3 size, Vector3 position)
        {
            shapeSize = size;
            shapePosition = position;
        }

        private void BuildShape()
        {
            shapeTriangles = 12;
            shapeVertices = new StaticTriangleGroup.StaticTriangleGroupVertex[36];

            Vector3 topLeftFront = shapePosition +
                new Vector3(-1.0f, 1.0f, -1.0f) * shapeSize;
            Vector3 bottomLeftFront = shapePosition +
                new Vector3(-1.0f, -1.0f, -1.0f) * shapeSize;
            Vector3 topRightFront = shapePosition +
                new Vector3(1.0f, 1.0f, -1.0f) * shapeSize;
            Vector3 bottomRightFront = shapePosition +
                new Vector3(1.0f, -1.0f, -1.0f) * shapeSize;
            Vector3 topLeftBack = shapePosition +
                new Vector3(-1.0f, 1.0f, 1.0f) * shapeSize;
            Vector3 topRightBack = shapePosition +
                new Vector3(1.0f, 1.0f, 1.0f) * shapeSize;
            Vector3 bottomLeftBack = shapePosition +
                new Vector3(-1.0f, -1.0f, 1.0f) * shapeSize;
            Vector3 bottomRightBack = shapePosition +
                new Vector3(1.0f, -1.0f, 1.0f) * shapeSize;


            Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f) * shapeSize;
            Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f) * shapeSize;
            Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f) * shapeSize;
            Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f) * shapeSize;
            Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f) * shapeSize;
            Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f) * shapeSize;

            Vector2 textureTopLeft = new Vector2(0.5f * shapeSize.X, 0.0f * shapeSize.Y);
            Vector2 textureTopRight = new Vector2(0.0f * shapeSize.X, 0.0f * shapeSize.Y);
            Vector2 textureBottomLeft = new Vector2(0.5f * shapeSize.X, 0.5f * shapeSize.Y);
            Vector2 textureBottomRight = new Vector2(0.0f * shapeSize.X, 0.5f * shapeSize.Y);

            // Front face.
            shapeVertices[0] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftFront);
            shapeVertices[1] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftFront);
            shapeVertices[2] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightFront);
            shapeVertices[3] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftFront);
            shapeVertices[4] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightFront);
            shapeVertices[5] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightFront);

            // Back face.
            shapeVertices[6] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftBack);
            shapeVertices[7] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightBack);
            shapeVertices[8] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftBack);
            shapeVertices[9] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftBack);
            shapeVertices[10] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightBack);
            shapeVertices[11] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightBack);

            // Top face.
            shapeVertices[12] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftFront);
            shapeVertices[13] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightBack);
            shapeVertices[14] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftBack);
            shapeVertices[15] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftFront);
            shapeVertices[16] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightFront);
            shapeVertices[17] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightBack);

            // Bottom face.
            shapeVertices[18] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftFront);
            shapeVertices[19] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftBack);
            shapeVertices[20] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightBack);
            shapeVertices[21] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftFront);
            shapeVertices[22] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightBack);
            shapeVertices[23] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightFront);

            // Left face.
            shapeVertices[24] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftFront);
            shapeVertices[25] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftBack);
            shapeVertices[26] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftFront);
            shapeVertices[27] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftBack);
            shapeVertices[28] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomLeftBack);
            shapeVertices[29] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topLeftFront);

            // Right face.
            shapeVertices[30] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightFront);
            shapeVertices[31] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightFront);
            shapeVertices[32] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightBack);
            shapeVertices[33] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightBack);
            shapeVertices[34] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                topRightFront);
            shapeVertices[35] = new StaticTriangleGroup.StaticTriangleGroupVertex(
                bottomRightBack);

            int[] indicies = new int[6];
            indicies[0] = 1;
            indicies[1] = 2;
            indicies[2] = 3;
            indicies[3] = 4;
            indicies[4] = 5;
            indicies[5] = 6;

            shapeMesh = new TriangleMesh(shapeVertices, indicies);
            shapeGroup = new StaticTriangleGroup(shapeMesh);
        }

        public void RenderShape(GraphicsDevice device, Space space)
        {
            BuildShape();
            space.add(shapeGroup);
        }
    }
}
Sorry for all the code, but I have absolutely no idea how to do this.

What I'm trying to accomplish here is to be able to create primitives and let the Physics system do the work from there. I need the easiest method as possible to do this, and if you have to explain something, please say it like you're explaining it to a 3 year old. I'm trying to make a game with building blocks essentially, where the game loads an XML file with all the properties, and then builds the map accordingly. So primitives are going to be a big necessity in this situation.

Any help would be great.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by Norbo »

If you just want to create a physically simulated box, all you need to do is first create the Box:

Code: Select all

Box box = new Box(position, width, height, length, mass);
There's a few overloads for the constructor. If you don't include the mass parameter in the constructor, it will be a kinematic box. Kinematic entities (Entity is the superclass of Box and the other primitives) can be thought of as having infinite mass and inertia. They can still have velocities, but they will not stop when they collide with things. Dynamic (non-infinite mass) entities will always yield to kinematic entities.

Then, add the box to the space:

Code: Select all

space.add(box);
When an entity is added to a space, that space will manage the entity's updating and movement. You must call space.update in order for the simulation to move forward in time. Put space.update in your game's update method so that it will advance through time alongside your game.


StaticTriangleGroups are just assortments of kinematic triangle entities. They are an "Updateable" object, which means they too should be added to the space. When the space update occurs, the StaticTriangleGroup tests for collisions with other entities using an acceleration structure. Since a StaticTriangleGroup is just a bunch of kinematic triangles (that also have no velocity), they will not move. They can't fall and bounce around like a dynamic Box can.

StaticTriangleGroups are usually used for level geometry, and it's a good idea to try to get as much geometry into a single StaticTriangleGroup as possible. A bunch of separate, 12-triangle StaticTriangleGroups is far less efficient than one StaticTriangleGroup with 80,000 triangles in it.

Usually, StaticTriangleGroups are created from large models that represent the world geometry.

Just to clear up some things, right now it looks like you are initializing and adding a static triangle group to the space for every BasicShape every Draw call. Nothing will be drawn because all it's doing is filling up the space with huge numbers of identical StaticTriangleGroups. For more examples of how to draw things, you can check out the XNA website samples (http://creators.xna.com/en-US/) or some of the demos on this website.

StaticTriangleGroups should be initialized once by creating a TriangleMesh, creating the StaticTriangleGroup from the TriangleMesh, and adding the StaticTriangleGroup to the space. So you have the steps pretty much right, but you should only do it once at the beginning of your game/level/initialization.

Also, if you did want to make a StaticTriangleGroup representing a cube, you should verify the vertices and indices. Right now, you only have 6 indices. You need 36 indices to create 12 triangles (three indices represents one triangle).
sjom
Posts: 5
Joined: Sun Feb 14, 2010 4:17 pm

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by sjom »

Okay, I think I get what you're saying. So when setting up the TriangleMesh, the verticies are the Vector3s that are used to pinpoint the position of the actual vertices on the cube? In that case, what are the indicies? I'm confused by the terms.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by Norbo »

The vertex list and index list act a bit like vertex buffers and index buffers from rendering. You can read more about them if you want.

First, think of a system where you don't use an index list. To create triangles, you simply list out each position. Three positions would be one triangle. So, a cube would have 36 positions in the vertex list.

When you introduce an index list, it works a little different. The vertices are positions, and the indices define the triangles. That way, you don't need to repeat vertices. For example, a single triangle could have vertices:

(0,0,0), (1,0,0), (0,0,1)

and indices:
0, 1, 2

To create a triangle, the system looks at the index list and 'indexes' into the vertex list to get the triangle's position. So in this simple single triangle case, it would be:
vertices[0], vertices[1], vertices[2].

To see why this can be useful, try extending it up to a cube. The vertices would be:
(0,0,0), (0,0,1), (0,1,0), (0,1,1), (1,0,0), (1,0,1), (1,1,0), (1,1,1)

And you'd have 36 indices, forming 12 triangles, forming 6 sides, forming a cube. The start of the index list could be:
0, 1, 2, 1, 2, 3...

But again, having a single large StaticTriangleGroup is better than having a lot of tiny ones. Also, keep in mind that the StaticTriangleGroup is not for dynamic geometry. Entities are the 'primitives' in BEPUphysics.
sjom
Posts: 5
Joined: Sun Feb 14, 2010 4:17 pm

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by sjom »

Alright, I think I get what you're saying. So if the StaticTriangleGroups are not the best way to go, let me explain my case.

I am creating a 'building block' game where the user has these building blocks that they can change in any way they feel like.

My goal is to be able to have 'building blocks' where the following properties exist:
  • Position
  • Rotation
  • Vertexes
  • Textures
  • Anything else you can imagine
So basically something that is as customizable as possible. Now, I need primitives to be able to do this. So can you explain in extreme detail how to create primitives in BEPUphysics? Thanks for your help so far, it's helped. I just need to know how to go about this.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by Norbo »

The brief two lines in the previous post pretty much covers how to create a Box primitive. It's extremely simple to begin with. A box can be defined with a position, width, height, length, and mass.

Once you create an entity, you toss it on over to the space for management.

Entities all have a centerPosition, orientationMatrix, and a variety of other properties that you can investigate. If you have questions about particular properties I'll try to explain them.

Textures and many other non-physical parameters are not handled at all by the physics engine. One option is to create a "game logic" object that has the physical representation (its entity 'body') as a field, as well as the other non-physical properties like texture and graphical model. Entities also have a 'tag' object field that you can set if you need to reference the game logic object from the entity.

While it's pretty easy to figure out where the corners of a Box are by using its orientation matrix, center position and its width/length/height properties, you cannot set the corners directly. It's a box, not an arbitrary shape.

However, there are many different kinds of shapes in the BEPUphysics.Entities namespace. A ConvexHull can take arbitrary vertices and create a solid hull around them. You can see in the BEPUphysicsDemos on the download page (http://www.bepu-games.com/BEPUphysics/downloads.htm) a few examples of ConvexHulls (Demo #29, specifically). Familiarize yourself with the different kinds of shapes that are possible to see what would work best for you. If you have questions about how certain shapes work, feel free to ask.
sjom
Posts: 5
Joined: Sun Feb 14, 2010 4:17 pm

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by sjom »

Alright, I think this is my last question for now. I think I've got it all added to the space object. Now, how do I draw them? I think that's what I've been getting at was how to render them. The getting started code was too difficult with all the classes and stuff. I just need a solid block of code that renders the space and gets the camera ready.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by Norbo »

Right now there's nothing simpler in any of the demos than the BasicSetupDemo/GettingStartedDemo as far as rendering goes. You could try to extract the BEPUphysicDemos renderer if you'd like, but it's a little messy. I'd recommend perusing the creators club samples to get a better idea of common approaches to rendering.

The BasicSetupDemo and other demos have Cameras in them that you can fiddle with. As far as geometry is concerned, you'll probably end up just setting up a graphical model that you render at the position of an entity. In the BEPUphysicsDemos project, this is done by creating the vertices/indices in-code, which you can look at in the source. You can also just load a model through the content pipeline. The actual method of putting the pixels on the screen is probably best learned from the XNA website/forums samples, though.
sjom
Posts: 5
Joined: Sun Feb 14, 2010 4:17 pm

Re: Noob Question: Primitives and BEPU from Start to Finish?

Post by sjom »

Okay, thanks for your help.
Post Reply