Jump to content
  • Advertisement
Sign in to follow this  
Seoushi

[.net] Need some help with my editor (MDX)

This topic is 4836 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been learning managed direct x off tutorials/blogs etc online so please bear with me. So far I have been making a 3d isometric map editor and I've now become stuck on a few things. The first question is picking (selecting certain objects with the mouse). From a few things I've found online I sorta understand how it works but I really don't know how to do it in code. Basically from my understanding you get the x,y of the mouse and make a rays straight into the scene then see if that ray intersects with anything in the screen. I've read that mdx has a fuction for meshes to find if it intersects a specific mesh, problem is I'm using index buffers to store all my info. So this question sorta has two parts, how do I do picking (some example code/explaination instead of theory would be nice) and would making my tile boxes (how I represent the map) be easier if they represented as a mesh? Taking off of my last question, how would I change my index buffers into mesh objects? I've seen methods for rendering them and loading them from a file but I haven't seen anything for creating meshes on the fly (how I generate my map). Another problem that I have right now is the fact that everything draws even if it isn't on the screen. I know I need some sorta culling in place, is there an easy built in method for this? or a tutorial for mdx? Being new to 3d programming I don't even know what to search so anything will help here. Relating back to my first question, I was wondering if my implementation of boxes is ok or if there is an easy/more efficent way. First off at the making of a map I allocate however many boxes are the user wants in an array called boxes, my boxes class looks like this,
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MapEd
{
    class cBox
    {
        Device myDevice;
        float[] vertexHeights;
        cVertexGroup[] faces;
        Texture[] textures;

        //public cBox(Device device, string[] tex, float northHeight, float eastHeight, float southHeight, float westHeight)
        public cBox(Device device, ref Texture[] tex, float northHeight, float eastHeight, float southHeight, float westHeight)
        {
            myDevice = device;
            textures = tex;

            //make the box
            MakeBox(northHeight, eastHeight, southHeight, westHeight);
        }

        public void MakeBox(float northHeight, float eastHeight, float southHeight, float westHeight)
        {
            //combine everything into a simple array
            vertexHeights = new float[4];
            vertexHeights[0] = northHeight;
            vertexHeights[1] = eastHeight;
            vertexHeights[2] = southHeight;
            vertexHeights[3] = westHeight;

            //default values for vertices and the vertex index buffer
            CustomVertex.PositionTextured[] verts;
            int[] inds = { 0, 1, 2, 
                           1, 3, 2 };

            //case where all vertexes are the same height
            if ( (vertexHeights[0] == vertexHeights[1]) &&  (vertexHeights[1] == vertexHeights[2]) && (vertexHeights[2] == vertexHeights[3]))
            {
                // calculate the number of faces:  height of vertices * 4 (sides) + 1 (top)
                faces = new cVertexGroup[((int)vertexHeights[0] * 4) + 1];

                //make top face
                verts = new CustomVertex.PositionTextured[4];
                verts[0] = new CustomVertex.PositionTextured(new Vector3(1, vertexHeights[0], 1), 0, 0); //north
                verts[1] = new CustomVertex.PositionTextured(new Vector3(1, vertexHeights[1], 0), 1, 0); //east
                verts[2] = new CustomVertex.PositionTextured(new Vector3(0, vertexHeights[2], 1), 0, 1); //west
                verts[3] = new CustomVertex.PositionTextured(new Vector3(0, vertexHeights[3], 0), 1, 1); //south
                faces[0] = new cVertexGroup(verts, inds, ref textures[0], myDevice);

                //lets create the sides
                for (int i = 0; i < vertexHeights[0]; i++)
                {
                    inds = new int[]{ 0, 1, 2, 1, 3, 2 };

                    //south east
                    verts = new CustomVertex.PositionTextured[4];
                    verts[0] = new CustomVertex.PositionTextured(new Vector3(1, i + 1, 0), 0, 0); //north
                    verts[1] = new CustomVertex.PositionTextured(new Vector3(1, i,     0), 1, 0); //east
                    verts[2] = new CustomVertex.PositionTextured(new Vector3(0, i + 1, 0), 0, 1); //west
                    verts[3] = new CustomVertex.PositionTextured(new Vector3(0, i,     0), 1, 1); //south
                    faces[(i * 4) + 1] = new cVertexGroup(verts, inds, ref textures[1], myDevice);

                    //south west
                    verts = new CustomVertex.PositionTextured[4];
                    verts[0] = new CustomVertex.PositionTextured(new Vector3(0, 1, i + 1), 0, 0); //north
                    verts[1] = new CustomVertex.PositionTextured(new Vector3(0, 1, i    ), 1, 0); //east
                    verts[2] = new CustomVertex.PositionTextured(new Vector3(0, 0, i + 1), 0, 1); //west
                    verts[3] = new CustomVertex.PositionTextured(new Vector3(0, 0, i    ), 1, 1); //south
                    faces[(i * 4) + 2] = new cVertexGroup(verts, inds, ref textures[1], myDevice);

                    //swap drawing for counter clockwise (backfacing)
                    inds = new int[]{ 2, 1, 0, 2, 3, 1 };

                    //north east
                    verts = new CustomVertex.PositionTextured[4];
                    verts[0] = new CustomVertex.PositionTextured(new Vector3(1, i + 1, 1), 0, 0); //north
                    verts[1] = new CustomVertex.PositionTextured(new Vector3(1, i,     1), 1, 0); //east
                    verts[2] = new CustomVertex.PositionTextured(new Vector3(0, i + 1, 1), 0, 1); //west                    
                    verts[3] = new CustomVertex.PositionTextured(new Vector3(0, i,     1), 1, 1); //south
                    faces[(i * 4) + 3] = new cVertexGroup(verts, inds, ref textures[1], myDevice);

                    //north west
                    verts = new CustomVertex.PositionTextured[4];
                    verts[0] = new CustomVertex.PositionTextured(new Vector3(1, 1, i + 1), 0, 0); //north
                    verts[1] = new CustomVertex.PositionTextured(new Vector3(1, 1, i    ), 1, 0); //east
                    verts[2] = new CustomVertex.PositionTextured(new Vector3(1, 0, i + 1), 0, 1); //west
                    verts[3] = new CustomVertex.PositionTextured(new Vector3(1, 0, i    ), 1, 1); //south
                    faces[(i * 4) + 4] = new cVertexGroup(verts, inds, ref textures[1], myDevice);
                }
            }
        }

        public void ReloadResources(Device myDevice) 
        { 
            foreach (cVertexGroup vg in faces) 
            { 
                vg.ReloadResources(myDevice); 
            } 
        }

        public void Dispose()
        {
            foreach (cVertexGroup j in faces)
            {
                j.Dispose();
            }
        }
        
        public void Render(Device myDevice)
        {
            foreach (cVertexGroup vg in faces) 
            { 
                vg.Render(myDevice);
            } 
        }
    }
}


Basically a box is a collection of indexbuffers each with an assigned texture. a box is created based on the height of the 4 verticies. Just so you can see the vertexgroup class looks like this.
using System;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

// code based from
// http://www.thehazymind.com/archives/2005/06/tutorial_4_more.html
// only real difference is it uses a refence to a texture not a path to a texture

namespace MapEd
{
    class cVertexGroup
    {
        private CustomVertex.PositionTextured[] verts;
        private int[] inds;
        private Texture myTexture;
        private VertexBuffer vb;
        private IndexBuffer ib;

        public cVertexGroup(CustomVertex.PositionTextured[] vertex, int[] index, ref Texture tex, Device myDevice)
        {
            verts = vertex;
            inds = index;
            //myPath = imagePath;
            myTexture = tex;
            ReloadResources(myDevice);
        }

        public void ReloadResources(Device myDevice)
        {
            vb = new VertexBuffer(typeof(CustomVertex.PositionTextured), verts.Length, myDevice, Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default);
            vb.SetData(verts, 0, LockFlags.None);
            ib = new IndexBuffer(typeof(int), inds.Length, myDevice, Usage.WriteOnly, Pool.Default);
            ib.SetData(inds, 0, LockFlags.None);
        }

        public void Dispose()
        {
            vb.Dispose();
            ib.Dispose();
        }
        
        public void Render(Device myDevice)
        { 
            myDevice.VertexFormat = CustomVertex.PositionTextured.Format;
            myDevice.SetTexture(0, myTexture); myDevice.SetStreamSource(0, vb, 0);

            myDevice.Indices = ib;
            myDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, verts.Length, 0, inds.Length / 3);
        }
    }
}


And when I render a box heres what happens (to trim down the code I'm just posting the render fucntion)
public void Render()
        {
            if (this.Visible)
            {
                if (deviceLost)
                {
                    // Try to get the device back
                    AttemptRecovery();
                }

                // If we couldn't get the device back, don't try to render
                if (deviceLost)
                {
                    return;
                }

                // Clear the back buffer
                device.Clear(ClearFlags.Target, Color.White, 1.0F, 0);
                device.BeginScene();

                SetupMatrices();

                for (int i = 0; i < main.getCols(); i++)
                {
                    for (int j = 0; j < main.getRows(); j++)
                    {
                        //translate to position
                        worldMatrix = Matrix.Translation(-i, 0, -j);

                        //translate for the angle view
                        worldMatrix.Multiply(Matrix.RotationY((float)Math.PI / -4.0F));
                        worldMatrix.Multiply(Matrix.RotationX((float)Math.PI / -4.0F));

                        //move to camera position
                        worldMatrix.Multiply(cameraPosition);

                        device.Transform.World = worldMatrix;

                        boxes[i*j].Render(device);
                    }
                }

                device.EndScene();


                try
                {
                    // Copy the back buffer to the display
                    device.Present();
                }
                catch (DeviceLostException)
                {
                    // Indicate that the device has been lost
                    deviceLost = true;
                }
            }
        }



The one thing I really see thats not so efficent about my code is it has to make the rotation matrix and cube position for the cube everytime it renders, I think I'm gonna put this code inside the box class. Is it normal to stream the vertices for each indexbuffer every time you render? if not what is a better solution for me? I realize this is a lot to look at, thanks for your time, oh and if you wana see my editor so far you can look at my blog page for it http://www.seoushi.com/maped/ . [Edited by - Seoushi on August 22, 2005 9:34:02 AM]

Share this post


Link to post
Share on other sites
Advertisement
1. Picking - one day I (or someone) will rewrite the old pikc sample from the old DX SDKs. But until then you just need to understand the following. The ray from the mouse pointer (x,y) into the screen is a vector (x,y,1) in screen coordinates. You want them in object coordinates. For rendering you STRT with object coordinates and directX makes them screen coordinates by mutliplying by the world, view, and projection matrices. So to go back the other way take (x,y,z) and multiply my the inverses in the opposite order. Or use the UnProject function (does the same thing). Then you need to walk through every triangle in your box and see if the ray interects. If you are already having perf issues becuase of the number of boxes this will be very slow. In this case you can make a bounding sphere around each box and check the intersection with that first as its much faster than the triangle intersection.

2. I'm not sure I understand your implementation (each tile is a cube?) but if so yes its not an efficient way. Your entire terrain should be one vertex buffer so that you can render it in one go.

3. No built in culling. If you continute with boxes you can use the boundign spheres and intersect them with the view frustum (search for frustum culling) to see if they are visible. PRoblem is if I zoom out all the way then the whole mesh becomes visible and you still will get the slowdown. You need to make your rendering much more efficient.

4. DOn't post so much code, most people really don't have time to read through all of that.

Share this post


Link to post
Share on other sites
2: yeah its a cube for the most part (the top can be slanted and its not always uniform height/width/length). The problem I see with put it all in one vertex buffer is the fact that you wouldn't be able to tell what cube is selected and what faces belong to which cube, also the cubes are being updated often and require the buffer to change a lot (it is an editor :) ) so one big vertex buffer would be a pain to update just for a single cube. Maybe you mean I should make a big buffer every frame then stream it over then draw? or is there something else you had in mind.

3: Thanks, I'll look up frustum culling and see what in entails.

4: Yeah I realize it is a lot of code, I figured if someone was bored enough and want to help me out more they would look at it.


Also is there a good book that covers basic topics like this. It seems things like this are extremely common but aren't covered in many of the beginer books I see. I looked at Tom Millers MDX kickstart book in a store and it really only had really basic graphic stuff then it jumped from beginer to advanced (shaders, skeletal animations and what not). I haven't been able to find his other book (begining 3d game programming) in a local store so I'm weary of buying it. Anyone vouche for that book in realition to what I need to learn? I'm really just looking for a good book for an intro to 3d game programming, not just how to draw stuff but how to interact with it and common 3d game routines(perferably in c#).

Thanks for your help.

Share this post


Link to post
Share on other sites
2. There are other ways to indicate which one is selected - change the texture coords to show a different color, draw something over the top etc. My guess is that the main reason for your bad perf (and it can only get worse) is all the calls to draw for each cube.
Yes updates to a mesh can be expensive but this is an editor, it doesn't matter too much if there is a 1fps blip - no worse than the 10fps drawing you have at the moment.

There's no c# books that cover this. There's only 4 c# books and one c++ bok that someone converted the code to c# http://www.thezbuffer.com/categories/tutorials.aspx

Toms other book has *some* extra stuff, but not what you need.

Share this post


Link to post
Share on other sites
Thanks for the tips. I'm gonna rethink how to handle my map. Right now I'm sorta leaning towards 10x10 box meshes (roughly a few thousand vertices) that should make it so I can update the map without chaning everything and they are big enough that it's not constantly streaming over the system bus. Makes it a bit harder to code because nto everything is a simple unit but it will help out a lot. Thanks again.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!