3
\$\begingroup\$

I currently read out XML data with the following scheme: Position x,y,z

I try to add one new Vertex for each position node and add it to a Vertex array.

Then I try to render this list of vertices so I get all those lines connected to represent a "path" through space. (Space is 3D)

What I tried so far:

    [STAThread]
    static void Main()
    {
        var form = new RenderForm("SlimDX - MiniTri Direct3D9 Sample");
        var device = new Device(new Direct3D(), 0, DeviceType.Hardware, form.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters()
        {
            BackBufferWidth = form.ClientSize.Width,
            BackBufferHeight = form.ClientSize.Height

        });
        form.Height = 900;
        form.Width = 900;

        LineVertex[] waypoints = new LineVertex[1024];

        XmlDocument paths = new XmlDocument();
        paths.Load("data.xml");
        XmlNode rootNode = paths.DocumentElement;

        int index = 0;
        foreach (XmlNode waypoint in rootNode)
        {
            foreach (XmlNode position in waypoint)
            {
                Console.WriteLine(position.ChildNodes[0].InnerText);
                float b = 1000.0f;
                float x, y, z;

                x = (float.Parse(position.ChildNodes[0].InnerText) / (b * b));
                y = (float.Parse(position.ChildNodes[1].InnerText) / (b * b);
                z = (float.Parse(position.ChildNodes[2].InnerText) / (b * b));

                Console.WriteLine(x.ToString() + " / " + y.ToString() + " / " + z.ToString());

                if (index % 2 > 0)
                    waypoints[index].Color = Color.Yellow.ToArgb();
                else
                    waypoints[index].Color = Color.Red.ToArgb();
                waypoints[index].Position = new Vector3(x, y, 0.0f);
            }
        }

        MessagePump.Run(form, () =>
        {
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue.ToArgb(), 0f, 1);
            // Since we don't have any lights in our scene, our lines won't receive 
            // any light and will therefore be black...
            // ...but we want them golden as an asinus aureus, so let's disable lighting.
            device.SetRenderState(RenderState.Lighting, true);
            device.BeginScene();

            // Our struct was meant to describe a position and a color,
            // let's tell the device about it.
            device.VertexFormat = VertexFormat.Position | VertexFormat.Diffuse;

            // In this particular case SlimDX does not need the 
            // vertex stride, it will be automatically calculated.
            device.DrawUserPrimitives(PrimitiveType.LineList, 0, waypoints.Length / 2, waypoints);

            device.EndScene();
            device.Present();

        });

        foreach (var item in ObjectTable.Objects)
            item.Dispose();
    }
}

But this does not work, and I am sure it is the complete wrong approach!

EDIT: So far one solution worked out, but not very well. And I also need the Vertices to be changeable at runtime, so I locked at the documentation and samples, they always use VertexBuffer() to make it possible to dynamically change the Vertices by using a DataStream as Input for the VertexBuffer.

A question still open to me is, since I am used to "Update(Time time)" formats the procedural still of DX/OpenGL oftens confuses me. So I assume that MessagePump.Run() represents the drawing call that's repeated each frame. So if some values before the drawing call change like a Vertex, that's put into the datastream, I can instantly see the updated result on the screen. Is that assumption right?

\$\endgroup\$
7
  • \$\begingroup\$ Not knowing SlimDX I can only guess, but when declaring the VertexBuffer you pass in VertexFormat.None, I suggest trying a different VertexFormat, one with only the position? \$\endgroup\$
    – Elva
    Aug 7, 2011 at 14:08
  • \$\begingroup\$ I have to use PrimitiveType.LineList or something like that, and then pass in the vertex list. That's what I think. \$\endgroup\$ Aug 7, 2011 at 14:25
  • \$\begingroup\$ I meant the line: "new VertexBuffer(device, 3 * 20, Usage.WriteOnly, VertexFormat.None, Pool.Managed);" The LineList seems correct, if it does at all what it says it does :) \$\endgroup\$
    – Elva
    Aug 7, 2011 at 16:09
  • 1
    \$\begingroup\$ @Yourdoom, it's okay to create a vertex buffer with no vertex format so long as the vertex format or vertex declaration is manually set on the device prior to rendering with that buffer (this is D3D behavior, not something specific to SlimDX). \$\endgroup\$
    – user1430
    Aug 7, 2011 at 19:08
  • 1
    \$\begingroup\$ Your most recent edit is really a substantially different question, you should probably post it as such. \$\endgroup\$
    – user1430
    Aug 10, 2011 at 14:57

2 Answers 2

5
+50
\$\begingroup\$

It's been a while since I looked at SlimDX, so I feel like drawing some lines to remember how it feels :) Well, it looks like several colored lines are all we need, so lets arrange vertices as a list, describe it's order and draw it:

// Simple vertex with position and color.
public struct LineVertex
{
    public Vector3 Position;
    public int Color;
}

/* ...typical device initialization skipped here... */

var lines  = new LineVertex[100];
var random = new Random();

// Prepare line vertices. 
// You will fill this array from XML instead of random generator. 
// LineList will be used as a 3D primitive, so a line will be drawn
// between each pair of vertices, total of 50 lines in our case.
for (int i = 0; i < lines .Length; i++)
{
    lines[i] = new LineVertex();
    float x = (float)(random.NextDouble() * 2) - 1;
    float y = (float)(random.NextDouble() * 2) - 1;
    lines[i].Position = new Vector3(x, y, 0f);
    lines[i].Color = Color.Gold.ToArgb();
}

MessagePump.Run(form, () =>
{
    device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue.ToArgb(), 0f, 1);
    // Since we don't have any lights in our scene, our lines won't receive 
    // any light and will therefore be black...
    // ...but we want them golden as an asinus aureus, so let's disable lighting.
    device.SetRenderState(RenderState.Lighting, false);  
    device.BeginScene();

    // Our struct was meant to describe a position and a color,
    // let's tell the device about it.
    device.VertexFormat = VertexFormat.Position | VertexFormat.Diffuse;

    // In this particular case SlimDX does not need the 
    // vertex stride, it will be automatically calculated.
    device.DrawUserPrimitives(PrimitiveType.LineList, 0, lines.Length / 2, lines);

    device.EndScene();
    device.Present();
});

You may notice that these lines don't look very beautiful. Later in your development cycle you will probably want to replace them by lines made from triangles with a custom texture. By this point it may be easier to check out some nice book about DirectX rather than fighting it all by yourself.

EDIT: You wanted vertex buffers and interconnected lines? Actually, it would be a nice exercise to remember how this things work, so lets try and do it:

/* skipped device initialization */

var rnd = new Random();

// The maximum number of vertices we will ever have in a buffer, 
// basically a buffer size limit.
const int maxVertexCount = 1024;
int lineVertexSize = Marshal.SizeOf(typeof(LineVertex));

// Create buffers once.
// You may notice Dynamic flag. This usage flag causes Direct3D to optimize for frequent lock operations.
// It's only useful when the buffer is locked frequently, 
// otherwise a static vertex or index buffer should be used.
var vertexBuffer = new VertexBuffer(device, maxVertexCount * lineVertexSize,
    Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default);

// Why such number of max indices? Read further :)
var indexBuffer = new IndexBuffer(device, sizeof(int) * maxVertexCount * 2 - 2, 
    Usage.WriteOnly | Usage.Dynamic, Pool.Default, false);

MessagePump.Run(form, () =>
{
    device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue.ToArgb(), 0f, 1);
    device.SetRenderState(RenderState.Lighting, false);

    device.BeginScene();

    device.VertexFormat = VertexFormat.Position | VertexFormat.Diffuse;

    // Simulate variable vertex count.
    int vertexCount = rnd.Next(1, 51) * 2;

    // Create a new vertex data each frame and setup vertices by changing their properties randomly.
    var vertexData = new LineVertex[vertexCount];
    for (int i = 0; i < vertexData.Length; i++)
    {
        float x = (float)(rnd.NextDouble() * 2) - 1;
        float y = (float)(rnd.NextDouble() * 2) - 1;
        vertexData[i].Position = new Vector3(x, y, 0f);
        vertexData[i].Color = (rnd.Next(2) == 0) ? Color.Gold.ToArgb() : Color.GreenYellow.ToArgb();
    }

    // Setup indices each frame in a special interconnected way: 
    // the end point of the first line is the start point of the second line.
    var indices = new int[(vertexCount * 2) - 2];
    for (int i = 0; i < vertexCount - 1; i++)
    {
        indices[i * 2] = i;
        indices[(i * 2) + 1] = i + 1;
    }

    // Write generated vertices to the vertex buffer.
    // Lets receive a free performance improvement and use a 
    // Discard flag since we aren't using old buffer data.
    // This flag tells Direct3D not to need to keep the old vertex or
    // index data in the buffer.
    var vertexStream = vertexBuffer.Lock(0, 0, LockFlags.Discard);
    vertexStream.WriteRange(vertexData);
    vertexBuffer.Unlock();

    // Write generated indices to the index buffer.
    var indexStream = indexBuffer.Lock(0, 0, LockFlags.Discard);
    indexStream.WriteRange(indices);
    indexBuffer.Unlock();

    device.SetStreamSource(0, vertexBuffer, 0, lineVertexSize);
    device.Indices = indexBuffer;
    device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, vertexCount, 0, indices.Length / 2);

    device.EndScene();

    device.Present();
});
\$\endgroup\$
8
  • \$\begingroup\$ Nice example; +1. \$\endgroup\$
    – user1430
    Aug 7, 2011 at 19:25
  • \$\begingroup\$ In your version colors work, mine doesn't quite suspicious. \$\endgroup\$ Aug 10, 2011 at 20:35
  • 1
    \$\begingroup\$ Check your render state. If it has lighting enabled, and line vertexes do not receive lighting (there aren't any light sources in the scene for example), they will be black. \$\endgroup\$
    – EnoughTea
    Aug 10, 2011 at 20:50
  • \$\begingroup\$ lighning is off. I already checked that. \$\endgroup\$ Aug 10, 2011 at 21:14
  • 1
    \$\begingroup\$ Drawing looks fine, the only place for errors is the XML-related part. I noticed "index" variable don't change, and vertex count looks unrelated to the actual amount of waypoints, so I rewrote it like this, see if it works: pastebin.com/AVY4Yi1A . Also read Josh's answer about pre-transformed positions. Generally you need to calculate world, view and projection matrices, and use Device.SetTransform(). This is a rather big topic, link for initial googling: [Spaces and Matrix in Direct3D]( toymaker.info/Games/html/matrices.html) \$\endgroup\$
    – EnoughTea
    Aug 11, 2011 at 7:18
1
\$\begingroup\$

There are a few things you are doing that you shouldn't. You also appear to have a type on or near the line where you call WriteRange, as there is an errant } in the code that will render it non-compilable. Similarly with the call to DrawIndexedPrimitives -- it makes it difficult to track down the actual cause of your problem. It would also help if you described the failure better than "it doesn't work."

That said, here are some things I noticed about your code:

  1. You create an array of 1024 vertices, which you write into your vertex buffer using WriteRange. This is bad because it will attempt to write all 1024 elements of the array (it utilizes the array's Length property), and your buffer is only 60 bytes.
  2. You hard-code the size of the vertex buffer, the primitive count, and the stride of the vertex. You should instead derive this information from the vertex type and the list of vertices itself.
  3. You are using pre-transformed positions, which means your data must be in screen space coordinates already (-1 to 1 along the X and Y axis). If your vertices have positions outside that range, they are way outside of the visible area of the screen. You should not, in general, use pre-transformed coordinates unless you understand their implications (the transformation pipeline will not run).

This isn't related directly to your problem, but it's also not good to rely on the ability to iterate the object table and call Dispose on all it's members. That functionality will be going away in v2 of SlimDX and it just encourages sloppy reference handling.

\$\endgroup\$
1
  • \$\begingroup\$ I scaled the data down so they match the range from -1 to 1 but this still outputs strange output. \$\endgroup\$ Aug 7, 2011 at 20:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .