JustJim 132 Report post Posted August 27, 2014 (edited) Hey! While coding my way through my first little game, I came across fractal Terrain-Generation. Would be nice as I do not want to built many Planet-Models and Skulp them etc. (I wanna make em random). For this, it is prefered to use Icospheres as planets due to the static size of triangles. I started to look around for Tutorials and read throug the Theory of a Icosphere-Creator. I see it like this: Built a Icosphere with 12 Vertices and 20 Faces made by Indices. For Refining: Go through each Index, Calculate the Middle between Point 1 and 2, Point 2 and 3, Point 3 and 1 and add the resulting vertices with the middle-coords to the Vertex-Stack and get the Indices of the Vertices (called a,b and c). Make a new List of indices: Add Index of Point1, a, c Index of Point2, b, a Index of Point3, c, b a,b,c to the List After the whole for-loop replace indices-list with the new indices-list BUT: This won't work, for some reasons I do not understand! I tried to change order of the Indices as I came to the Idea, the vertices was drawn the wrong order but no good. This is what I wrote: List<VertexPositionColor> vertices = new List<VertexPositionColor>(); float t = (float)(1.0+Math.Sqrt(5.0f)) /2.0f; vertices.Add(new VertexPositionColor(new Vector3(-1, 0, t), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(1, 0, t), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(-1, 0, -t), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(1, 0, -t), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(0, t, 1), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(0, t, -1), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(0, -t, 1), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(0, -t, -1), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(t, 1, 0), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(-t, 1, 0), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(t, -1, 0), Color.Gray)); vertices.Add(new VertexPositionColor(new Vector3(-t, -1, 0), Color.Gray)); //short[] indices = new short[60]; List<short> indices = new List<short>(); indices.Add(0); indices.Add(6); indices.Add(1); indices.Add(0); indices.Add(11); indices.Add(6); indices.Add(1); indices.Add(4); indices.Add(0); indices.Add(1); indices.Add(8); indices.Add(4); indices.Add(1); indices.Add(10); indices.Add(8); indices.Add(2); indices.Add(5); indices.Add(3); indices.Add(2); indices.Add(9); indices.Add(5); indices.Add(2); indices.Add(11); indices.Add(9); indices.Add(3); indices.Add(7); indices.Add(2); indices.Add(3); indices.Add(10); indices.Add(7); indices.Add(4); indices.Add(8); indices.Add(5); indices.Add(4); indices.Add(9); indices.Add(0); indices.Add(5); indices.Add(8); indices.Add(3); indices.Add(5); indices.Add(9); indices.Add(4); indices.Add(6); indices.Add(10); indices.Add(1); indices.Add(6); indices.Add(11); indices.Add(7); indices.Add(7); indices.Add(10); indices.Add(6); indices.Add(7); indices.Add(11); indices.Add(2); indices.Add(8); indices.Add(10); indices.Add(3); indices.Add(9); indices.Add(11); indices.Add(0); List<short> indices2 = new List<short>(); for (int i = 0; i < indices.Count; i++) { Vector3 p1 = vertices[indices[i]].Position; short p1I = (short)i++; Vector3 p2 = vertices[indices[i]].Position; short p2I = (short)i++; Vector3 p3 = vertices[indices[i]].Position; short p3I = (short)i; short a = (short)vertices.Count; vertices.Add(new VertexPositionColor(new Vector3((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2, (p1.Z + p2.Z) / 2), Color.Black)); short b = (short)vertices.Count; vertices.Add(new VertexPositionColor(new Vector3((p2.X + p3.X) / 2, (p2.Y + p3.Y) / 2, (p2.Z + p3.Z) / 2), Color.Black)); short c = (short)vertices.Count; vertices.Add(new VertexPositionColor(new Vector3((p3.X + p1.X) / 2, (p3.Y + p1.Y) / 2, (p3.Z + p1.Z) / 2), Color.Black)); indices2.Add(indices[p1I]); indices2.Add(a); indices2.Add(c); indices2.Add(indices[p2I]); indices2.Add(b); indices2.Add(a); indices2.Add(indices[p3I]); indices2.Add(c); indices2.Add(b); indices2.Add(a); indices2.Add(b); indices2.Add(c); } indices = indices2; vertexList = new VertexPositionColor[vertices.Count]; for (int i = 0; i < vertices.Count; i++) { vertexList[i] = vertices[i]; } short[] indexList = new short[indices.Count]; for (int i = 0; i < indices.Count;i++ ) { indexList[i] = indices[i]; } vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor),12,BufferUsage.WriteOnly); vertexBuffer.SetData<VertexPositionColor>(vertexList); indexBuffer = new IndexBuffer(graphics.GraphicsDevice, typeof(short), indexList.Length, BufferUsage.WriteOnly); indexBuffer.SetData(indexList); Could anyone see through the code and show me my obvious error? Thanks! Edit: I tried it on a single Triangle. Result: It worked. But on the Ico, it won't :/ whats wrong with it? Edited August 27, 2014 by JustJim 0 Share this post Link to post Share on other sites
JustJim 132 Report post Posted August 27, 2014 Aight I got the Error. It was down in the SetVertexBuffer. There is a Magic "12" which i needed to replace. Also there was an Error in the Draw-Methode where I assigned the Buffer to the one that should be used, where I had to enter the Number of Vertices and Primitives, which I missed to do. BUT! I got another error, which makes me crazy! I made another for loop around the first one which smoothes the Ico. If i set it to 1 Iteration, everything is just fine, but with two, I run into an OutOfMemeoryException :/ Any Solution to that? 0 Share this post Link to post Share on other sites
JustJim 132 Report post Posted August 27, 2014 (edited) Well I actually just fixed this. Problem was: When i assigned indices = indices2 it was by reference making the whole thing infinite. Another Question When I starte the programm, it won't smoothin the ico rather than drawing the triangles on the surface of the bigger once, making the whole thing useless. how could i change this? Is Solved as I only add Normalized Vertices. Last Question: Lightning. I have this code in the Draw Section: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.DarkSlateBlue); DrawPrimitive(vertexList); base.Draw(gameTime); } protected void DrawPrimitive(VertexPositionColor[] vertices) { basicEffect.VertexColorEnabled = true; basicEffect.World = world; basicEffect.View = view; basicEffect.Projection = projection; GraphicsDevice.SetVertexBuffer(vertexBuffer); GraphicsDevice.Indices = indexBuffer; foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,0,vertexList.Length,0,indexList.Length/3); } } But it seems whenever I use something like basicEffect.LightingEnabled = true; I get a crash with following message: HRESULT: [0x80070057], Module: [General], ApiCode: [E_INVALIDARG/Invalid Arguments], Message: Wrong Parameter. Edited August 27, 2014 by JustJim 0 Share this post Link to post Share on other sites
Stefan Fischlschweiger 788 Report post Posted August 28, 2014 (edited) In VS go to Project -> <project name>-properties -> Debugging and check "Enable unmanaged code debugging" This will show you the underlying D3D error in the output window. I assume that the BasicEffect shader is expecting a parameter which is not being provided by your code/content. I had a similar problem but with TextureEnabled and missing texture coordinates Edited August 28, 2014 by Lordadmiral Drake 0 Share this post Link to post Share on other sites
JustJim 132 Report post Posted August 29, 2014 Yeah I searched that up. I guess it's due the fact that my Vertices offers no Normals, so there can't be any light calculation. How could be if there weren't Vectors that gives the direction in which the light reflects. Correct me if I'm wrong 0 Share this post Link to post Share on other sites
Stefan Fischlschweiger 788 Report post Posted August 29, 2014 (edited) On a quick visit to google I found this tutorial on generating Normals: http://www.riemers.net/eng/Tutorials/XNA/Csharp/ShortTuts/Normal_generation.php Maybe this helps you. I haven't tried it out because I dont need this functionality (Only using models myself) Edited August 29, 2014 by Lordadmiral Drake 0 Share this post Link to post Share on other sites
JustJim 132 Report post Posted August 29, 2014 (edited) Oh thanks for that link. I already found out how exactly to do it. I'll post it here, so anyone who needs that solution will find it. As I needed Normals along with Color and Position (XNA/Monogame only offers them with Position or PositionTexture but not color), I made an own structure: public struct VertexPositionColorNormal: IVertexType { public Vector3 Position; public Color Color; public Vector3 Normal; public VertexPositionColorNormal(Vector3 vector, Color color, Vector3 normal) { Position = vector; Color = color; Normal = normal; } public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration ( new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0), new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0) ); VertexDeclaration IVertexType.VertexDeclaration { get { return VertexPositionColorNormal.VertexDeclaration; } public float length() { return (float)Math.Sqrt(Position.X * Position.X + Position.Y * Position.Y + Position.Z * Position.Z) } To use it with VertexBuffer, I needed to make it inherit from IVertexType, otherwhise it won't work. Also needed is the VertexDeclaration along with it's get Methode, so it is readable and usable for this purpose. length() is not required but I needed it to test some cases with it. I chose to work with Lists for Vertices and Indices so I do not need to realloc memory all the time on arrays. As I needed normalized Vectors for the whole smoothing-process (in any other case there will only be the Ico with subdivided Triangles but no sphere at all) I also made a little function for Adding Vectors and not caring if they're already Normalized. protected List<VertexPositionColorNormal> AddNormalizedVertex(List<VertexPositionColorNormal> vertices, Vector3 vector, Color color, Vector3 Normal) { vector.Normalize(); vertices.Add(new VertexPositionColorNormal(vector, color, Normal)); return vertices; } This code takes the List as it is as a parameter to add the VertexPositionColorNormal and returns it (As I still didn't check if it's getting done by refference haha). Not much else to say here. I packed the whole creation-algorithm into the Initialize()-Methode. First of all, as I work with Lists, I needed to change them into Arrays at the end to get this whole thing to work, so there are member-variables in the gameclass along with the vertexBuffer and indexBuffer VertexBuffer vertexBuffer; IndexBuffer indexBuffer; VertexPositionColorNormal[] vertexList; int[] indexList; Don't mind the fact they're not private or protected as this was just a simple test. I WILL capsule the whole procedure in its own class along with its own world Matrix. Next (back in the Init()-Methode) I create a List for VertexPositionColorNormals and add the 12 Start-Vertices List<VertexPositionColorNormal> vertices = new List<VertexPositionColorNormal>(); vertices = AddNormalizedVertex(vertices, new Vector3(-1, 0, 1), Color.Gray,new Vector3(0,0,0)); vertices = AddNormalizedVertex(vertices, new Vector3(1, 0, 1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(-1, 0, -1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(1, 0, -1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(0, 1, 1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(0, 1, -1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(0, -1, 1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(0, -1, -1), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(1, 1, 0), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(-1, 1, 0), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(1, -1, 0), Color.Gray, new Vector3(0, 0, 0)); vertices = AddNormalizedVertex(vertices, new Vector3(-1, -1, 0), Color.Gray, new Vector3(0, 0, 0)); After that, I needed to give the indices. List<int> indices = new List<int>(); indices.Add(0); indices.Add(6); indices.Add(1); indices.Add(0); indices.Add(11); indices.Add(6); indices.Add(1); indices.Add(4); indices.Add(0); indices.Add(1); indices.Add(8); indices.Add(4); indices.Add(1); indices.Add(10); indices.Add(8); indices.Add(2); indices.Add(5); indices.Add(3); indices.Add(2); indices.Add(9); indices.Add(5); indices.Add(2); indices.Add(11); indices.Add(9); indices.Add(3); indices.Add(7); indices.Add(2); indices.Add(3); indices.Add(10); indices.Add(7); indices.Add(4); indices.Add(8); indices.Add(5); indices.Add(4); indices.Add(9); indices.Add(0); indices.Add(5); indices.Add(8); indices.Add(3); indices.Add(5); indices.Add(9); indices.Add(4); indices.Add(6); indices.Add(10); indices.Add(1); indices.Add(6); indices.Add(11); indices.Add(7); indices.Add(7); indices.Add(10); indices.Add(6); indices.Add(7); indices.Add(11); indices.Add(2); indices.Add(8); indices.Add(10); indices.Add(3); indices.Add(9); indices.Add(11); indices.Add(0); Okay. This alone will create the Ico at its Basic State, positioned at 0,0,0 with the radius of the Corners of 1 Now comes the refining (subdividing/smoothing, whatever you call it). Note that I, when I'll pack it into its own Class, I will make the Refining-Grade an choosable option, so there will not be number in the first for-loop anymore. This code is a little bit more, so I give the explaination the the comments: //First I need a new List to store the Vertices as I need them from the first List and do not want to overwrite them List<int> indices2 = new List<int>(); for (int j = 0; j <3 ; j++) //Number of Iteration for making it even smoother { for (int i = 0; i < indices.Count; i++) //Go through all Indices { //Here I get the Vertices responsible for ONE Triangle and store their index Vector3 p1 = vertices[indices[i]].Position; int p1I = (int)i++; Vector3 p2 = vertices[indices[i]].Position; int p2I = (int)i++; Vector3 p3 = vertices[indices[i]].Position; int p3I = (int)i; //Now I get the Index of the Vertex I want to add (as vertices.Count gives the actual Number, this will be //correct (counting from 1 to x+1 instead of 0 to x) //Next I need to get the middle between to vertices ( in this case from vertice p1 to p2) and add it as a new Vertex int a = (int)vertices.Count; vertices = AddNormalizedVertex(vertices, new Vector3((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2, (p1.Z + p2.Z) / 2), Color.Gray, new Vector3(0, 0, 0)); //Same here with p2 to p3 int b = (int)vertices.Count; vertices = AddNormalizedVertex(vertices, new Vector3((p2.X + p3.X) / 2, (p2.Y + p3.Y) / 2, (p2.Z + p3.Z) / 2), Color.Gray, new Vector3(0, 0, 0)); //And again with p1 to p3 int c = (int)vertices.Count; vertices = AddNormalizedVertex(vertices, new Vector3(((p1.X + p3.X) / 2), ((p1.Y + p3.Y) / 2), ((p1.Z + p3.Z) / 2)), Color.Gray, new Vector3(0, 0, 0)); //Now reassaign the Indices to make them right (carefull at the order) indices2.Add(indices[p1I]); indices2.Add(a); indices2.Add(c); indices2.Add(indices[p2I]); indices2.Add(b); indices2.Add(a); indices2.Add(indices[p3I]); indices2.Add(c); indices2.Add(b); indices2.Add(a); indices2.Add(b); indices2.Add(c); } //The first List now gets cleared and after that it gets all the indices from the second list so it stays up-to-date with //the correct order indices.Clear(); foreach (int index in indices2) { indices.Add(index); } } Aight. The lists now contains the vertices and the correct order of indices. Everything is fine till here. Now I need to convert them to the arrays that we already created at the start: vertexList = new VertexPositionColorNormal[vertices.Count]; for (int i = 0; i < vertices.Count; i++) { vertexList[i] = vertices[i]; } indexList = new int[indices.Count]; for (int i = 0; i < indices.Count; i++) { indexList[i] = indices[i]; } We still need to determine the Normals to make Lighting successfull for (int i = 0; i < indexList.Length; i++) { int index1 = indexList[i++]; int index2 = indexList[i++]; int index3 = indexList[i]; Vector3 side1 = vertexList[index1].Position - vertexList[index3].Position; Vector3 side2 = vertexList[index1].Position - vertexList[index2].Position; Vector3 normal = Vector3.Cross(side1, side2); vertexList[index1].Normal += normal; vertexList[index2].Normal += normal; vertexList[index3].Normal += normal; } for(int i = 0; i < vertxList.Length; i++) vertexList[i].Normal.Normalize(); What I've done here is to go through all indices. Every cycle I store three vertices that are responsible for a triangle and determine the Vector between them by substracting each two of them. I only needed to do this with two of them. For the Normal, I just build the Cross-Product and add them to all three Vertices. At the End they get Normalized so they all have the length of 1 The end of the story is to store them into the responsible buffers vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColorNormal), vertexList.Length, BufferUsage.WriteOnly); vertexBuffer.SetData<VertexPositionColorNormal>(vertexList); indexBuffer = new IndexBuffer(graphics.GraphicsDevice, typeof(int), indexList.Length, BufferUsage.WriteOnly); indexBuffer.SetData(indexList); For the drawing I made up an own function, so I can only draw this stuff with it. protected void DrawPrimitive(VertexPositionColorNormal[] vertices) { basicEffect.VertexColorEnabled = true; basicEffect.World = world; basicEffect.View = view; basicEffect.Projection = projection; GraphicsDevice.SetVertexBuffer(vertexBuffer); GraphicsDevice.Indices = indexBuffer; RasterizerState rasterizerState = new RasterizerState(); rasterizerState.CullMode = CullMode.None; GraphicsDevice.RasterizerState = rasterizerState; foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { basicEffect.LightingEnabled = true; basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1f, 0.8f, 0.5f); basicEffect.DirectionalLight0.Direction = new Vector3(1, 0.5f, 0); basicEffect.DirectionalLight0.Enabled = true; pass.Apply(); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexList.Length, 0, indexList.Length / 3); } } This is all what I've done. Next aim is to get random Terrain on it. After that I want to make the atmosphere (which will be the hard part as I can not use the normal shader-language cuz of Monogame uses MGFX-Files) Edited August 29, 2014 by JustJim 0 Share this post Link to post Share on other sites