TheNerd's picture

Total NOOB....

Hello all,

Can someone point me to a tutorial that would show me how to use the latest version of OpenTK to display basic primitives (sphere, Cube, cylinder, etc) ??? In the old OpenTK.Compatibility I know those functions existed in the Glu class, but as far as I know that namespace is supposed to be deprecated... so how do you do the same thing in the new one??????

Thanks!!!!


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
hack2root's picture

MainWindow.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
 
namespace OpenTKTryout {
    class MainWindow : GameWindow {
 
        protected override void OnLoad(EventArgs e) {
            GL.ClearColor(Color.MidnightBlue);
        }
 
        protected override void OnRenderFrame(FrameEventArgs e) {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
 
            GL.Color3(Color.Red);
            GL.Begin(BeginMode.Triangles);						// Drawing Using TrianGL.es
            GL.Vertex3(0.0f, 0.2f, 0.0f);				// Top
            GL.Vertex3(-0.2f, -0.2f, 0.0f);				// Bottom Left
            GL.Vertex3(0.2f, -0.2f, 0.0f);				// Bottom Right
            GL.End();
 
            SwapBuffers();
        }
    }
}

Program.cs

using System;
using OpenTK;
 
namespace OpenTKTryout {
    class Program {
        [STAThread]
        static void Main() {
            using (var mainWindow = new MainWindow()) {
                mainWindow.Title = "OpenTK Test !!!";
                mainWindow.Run();
            }
        }
    }
}
TheNerd's picture

Ah so you are saying I need to find the mathematical formulas and build them myself because there's no longer any GLUT emulation in OpenTK?

It'd be nice to be able to do something like GL.Sphere(Radius, VectorOfLocation) and have that return the vertices or a vertice array.
hmm maybe I should build and contribute :)

Hortus Longus's picture

Hi.

TheNerd wrote:

Ah so you are saying I need to find the mathematical formulas and build them myself because there's no longer any GLUT emulation in OpenTK?

Look in your OpenTK-Folder(bin) and you will see: OpenTK.Compatibility.dll is part of OpenTK until now. :-)

TheNerd wrote:

but as far as I know that namespace is supposed to be deprecated... so how do you do the same thing in the new one?

Yes, about years. So do it as before, until a successor is to come (on the way).

TheNerd wrote:

It'd be nice to be able to do something like GL.Sphere(Radius, VectorOfLocation) and have that return the vertices or a vertice array.
hmm maybe I should build and contribute :)

You should do it. ;-)

TheNerd's picture

OK so I ported over some code I found floating out there to do a hemisphere (works well for sky domes!) with texturing. This will also work to build a complete sphere, although I am not sure if its inefficient or not. This was ported directly from http://www.swiftless.com/tutorials/opengl/sphere.html

First, a struct:

 public  struct VERTICES
    {
        //The x position of our current vertex
        public float X;
        //The Y position of our current vertex
        public float Y;
        //The Z position of our current vertex
        public float Z;
 
        //The U(x) texture coordinate of the current vertex
        public float U;
        //The V(x) texture coordinate of the current vertex
        public float V;
    };

Now, the method:

 /*
       * Now for the actual creation code. We are inputting R as the number of subdivisions,
            H as the translation on the horizontal axis, K as the translation on the vertical
            axis, and Z as the translation on the Z axis.
       */
      static VERTICES[] CreateSphere(float R, float H, float K, float Z) 
      {
      //Now are variables for this is as followed. n is the current vertex we are working
      //with. While a and b are used to control our loops.
      int n;
      float a;
      float b;
 
      int space = 10;
      int VertexCount = (90 / space) * (360 / space) * 4;
 
      VERTICES[] VERTEX = new VERTICES[VertexCount];
      //Set n to 0 to start off with the first vertex
      n = 0;
      //Assign our b loop to go through 90 degrees in intervals of our variable space
      for (b = 0; b <=(90.0 - space); b+=space){
 
     //   Assign our a loop to go through 360 degrees in intervals of our variable space
            for( a = 0; a <= 360 - space; a+=space){
                //Start editing our vertex.
               // I am calculating the X value here.
                VERTEX[n].X = R * (float)Math.Sin((a) / 180 * (float)Math.PI) * (float)Math.Sin((b) / 180 * (float)Math.PI) - H;
                //The Y value here.
                VERTEX[n].Y = R * (float)Math.Cos((a) / 180 * Math.PI) * (float)Math.Sin((b) / 180 * Math.PI) + K;
                //The Z value here.
                VERTEX[n].Z = R * (float)Math.Cos((b) / 180 * Math.PI) - Z;
               // Now I am calculating the texture coordinates. I have used (2*b) as the texture
               // is twice as wide as it is high. Hence 2:1. You can remove the (2*) if you wish
               // to use a texture with the same width and height, or increase it accordingly.
                VERTEX[n].V = (2 * b) / 360;
                VERTEX[n].U = (a) / 360;
               // Then start working with the next vertex
                n++;
 
               // Then we do the same calculations as before, only adding the space variable
               // to the b values.
                VERTEX[n].X = R * (float)Math.Sin((a) / 180 * Math.PI) * (float)Math.Sin((b + space) / 180 * Math.PI) - H;
                VERTEX[n].Y = R * (float)Math.Cos((a) / 180 * Math.PI) * (float)Math.Sin((b + space) / 180 * Math.PI) + K;
                VERTEX[n].Z = R * (float)Math.Cos((b + space) / 180 * Math.PI) - Z;
                VERTEX[n].V = (2 * (b + space)) / 360;
                VERTEX[n].U = (a) / 360;
                n++;
 
               // Then we do the same calculations as the first, only adding the space variable
               // to the a values.
                VERTEX[n].X = R * (float)Math.Sin((a + space) / 180 * Math.PI) * (float)Math.Sin((b) / 180 * Math.PI) - H;
                VERTEX[n].Y = R * (float)Math.Cos((a + space) / 180 * Math.PI) * (float)Math.Sin((b) / 180 * Math.PI) + K;
                VERTEX[n].Z = R * (float)Math.Cos((b) / 180 * Math.PI) - Z;
                VERTEX[n].V = (2 * b) / 360;
                VERTEX[n].U = (a + space) / 360;
                n++;
 
               // Then we do the same calculations as the first again, only adding the space variable
               // to both the b and the a values.
                VERTEX[n].X = R * (float)Math.Sin((a + space) / 180 * Math.PI) * (float)Math.Sin((b + space) / 180 * Math.PI) - H;
                VERTEX[n].Y = R * (float)Math.Cos((a + space) / 180 * Math.PI) * (float)Math.Sin((b + space) / 180 * Math.PI) + K;
                VERTEX[n].Z = R * (float)Math.Cos((b + space) / 180 * Math.PI) - Z;
                VERTEX[n].V = (2 * (b + space)) / 360;
                VERTEX[n].U = (a + space) / 360;
                n++;
 
                }
                }
 
        return VERTEX;
      }
 

And then to render it I do something like this (I colored the two different halves differently to illustrate each call):

GL.Begin(BeginMode.TriangleStrip);
 
            VERTICES[] myVerts = CreateSphere(4.0f, 0.0f, 0.0f, 0.0f);
            VERTICES[] myVerts2 = CreateSphere(-4.0f, 0.0f, 0.0f, 0.0f);
            for (int i = 0; i < myVerts.Length; i++)
            {
                GL.Color3(0.5f, 0.0f, 0.0f); GL.Vertex3(myVerts[i].X, myVerts[i].Y, myVerts[i].Z);
            }
 
            for (int i = 0; i < myVerts2.Length; i++)
            {
                GL.Color3(1.5f, 0.0f, 0.0f); GL.Vertex3(myVerts2[i].X, myVerts2[i].Y, myVerts2[i].Z);
            }
            GL.End();

how can I improve this?

the Fiddler's picture

Two enhancements I can think of:

  • Use radians throughout instead of converting to/from degrees. Small change but makes for more readable code.
  • Instead of issuing each vertex three times, you can do it once and generate a separate element array that shows how vertices are connected into triangles. Results in better performance if you use VBOs.

The code below implements this for a hemisphere. It also supports ellipsoids, which is why it calculates normals separately:

        public struct VertexP3N3T2
        {
            public Vector3 Position;
            public Vector3 Normal;
            public Vector2 TexCoord;
        }
 
        static VertexP3N3T2[] CalculateVertices(float radius, float height, byte segments, byte rings)
        {
            var data = new VertexP3N3T2[segments * rings];
 
            int i = 0;
 
            for (double y = 0; y < rings; y++)
            {
                double phi = (y / (rings - 1)) * Math.PI / 2;
                for (double x = 0; x < segments; x++)  
                {
                    double theta = (x / (segments - 1)) * 2 * Math.PI;
 
                    Vector3 v = new Vector3()
                    {
                        X = (float)(radius * Math.Sin(phi) * Math.Cos(theta)),
                        Y = (float)(height * Math.Cos(phi)),
                        Z = (float)(radius * Math.Sin(phi) * Math.Sin(theta)),
                    };
                    Vector3 n = Vector3.Normalize(v);
                    Vector2 uv = new Vector2()
                    {
                        X = (float)(x / (segments - 1)),
                        Y = (float)(y / (rings - 1))
                    };
                    // Using data[i++] causes i to be incremented multiple times in Mono 2.2 (bug #479506).
                    data[i] = new VertexP3N3T2() { Position = v, Normal = n, TexCoord = uv };
                    i++;
                }
 
            }  
 
            return data;
        }
 
        static ushort[] CalculateElements(float radius, float height, byte segments, byte rings)
        {
            var num_vertices = segments * rings;
            var data = new ushort[num_vertices * 6];
 
            ushort i = 0;
 
            for (byte y = 0; y < rings - 1; y++)
            {
                for (byte x = 0; x < segments - 1; x++)
                {
                    data[i++] = (ushort)((y + 0) * segments + x);
                    data[i++] = (ushort)((y + 1) * segments + x);
                    data[i++] = (ushort)((y + 1) * segments + x + 1);
 
                    data[i++] = (ushort)((y + 1) * segments + x + 1);
                    data[i++] = (ushort)((y + 0) * segments + x + 1);
                    data[i++] = (ushort)((y + 0) * segments + x);
                }
            }
 
            // Verify that we don't access any vertices out of bounds:
            foreach (int index in data)
                if (index >= segments * rings)
                    throw new IndexOutOfRangeException();
 
            return data;
        }

You can render this trivially using immediate mode:

float radius = 100;
float height = 100;
byte segments = 10;
byte rings = 10;
var vertices = CalculateVertices(radius, height, segments, rings);
var elements = CalculateElements(radius, height, segments, rings);
 
GL.Begin(BeginMode.Triangles);
foreach (var element in elements)
{
    var vertex = vertices[element];
    GL.TexCoord2(vertex.TexCoord);
    GL.Normal3(vertex.Normal);
    GL.Vertex3(vertex.Position);
}
GL.End();

or you can load the data into VBOs and enjoy a couple orders of magnitude higher performance.

TheNerd's picture

Using Fiddler's example, I can generate a hemisphere perfectly (works great for a skydome!), but I can't figure out for the life of me how to generate a FULL Sphere from that. Any pointers?

TheNerd's picture

Nevermind, I figured it out :) Just change:

double phi = (y / (rings - 1)) * Math.PI / 2;

to

double phi = (y / (rings - 1)) * Math.PI;
MattCruikshank's picture

Thanks for this code!

One change I would make is that you are making a vertex for the 0th segment, and then doing it again for the last segment. This means that you're duplicating data that you don't need to.

If you change:

double theta = (x / (segments - 1)) * 2 * Math.PI;

To:

double theta = (x / (segments)) * 2 * Math.PI;

And change this:

for (byte x = 0; x < segments - 1; x++)

To this:

for (byte x = 0; x < segments; x++)

And also change each of these:

data[i++] = (ushort)((y + 0) * segments + x + 1);

Into this:

data[i++] = (ushort)((y + 0) * segments + (x + 1) % segments);

Then you won't duplicate data.

I would recommend testing this with a 4 segments. In your original code, you'd see a 3-sided figure. With my code, you should see a 4-sided figure. Note that with my code, you actually produce the same amount of vertex data, but "rings" more element data.

harish's picture

Hi,

Can any one please upload the entire code?

I'm getting a window without sphere.