
Advertisement
Blogs
Sound Design: How I made The Jet Pack sound for the game Exocorps
The 3D book
A childrens' story adventure book with a 3D fantasy world to enter on the right page.
... Currently 3DFormat Reader consists of a C++ Windows program: Two "child" windows in a main window frame. Two windows: a text2D rendering window and a 3Drendering window. The text2D window, as its' name implies, displays text and 2D graphics; it is programmed using Microsoft's DirectWrite text formatting API and Microsoft's Direct2D API for 2D graphics. The 3Drendering window uses the OpenGL API. A 3DEBook page is formatted in one of two possible modes: DW_MODE or GL_MODE. In GL_MODE both windows are shown; the text2D rendering window is on the left and the 3D OpenGL window is on the right. In DW_MODE, only the text2D rendering window is shown, the OpenGL window is hidden (Logically it is still there, it has just been given zero width). The 3DFormat Reader reads text files, which consists of the text of the book, control character for the formatting of text, (bold, underline, ...), display of tables, loading of images(.jpg .png ...), and control of 2D and 3D routines. 3DReader programming is based on a ModelViewController (MVC) architecture. The MVC design is modular: The Controller component handles user input from the operating system , the Model component processes the input, and the View component sends output back to the user on the display. Typical ParentChild windows programs have multiple "call back" window procedures(winProcs): One for the parent window and one for child window. The MVC model, simplifies message routing by using a callback window procedure which receives Windows messages for the main window, the text2D window and the OGL window.
A sample MVC program by Song Ho Ahn was used as a template for the 3DEReader. Rushed for time now, so a hasty sign off and thanks for reading.  8  21 18 I spent the last few days working on procedural mesh generation. First looking to find a bit of code to do what I had in mind. Which begs the question: What
did I have in mind? I just wanted a cube mesh generator such that... Requirements Input: An integer n = units from origin to cube face. Output: The vertices for a unit cube centered on the origin. 8n² triangles per cube face. 3 times 8n² verts in clockwise winding order (from the outside of the cube) ready for the rendering pipeline. Screenshot of some cubes generated with the procedural cube mesh generator. That was about it for the output requirements. I did not want to hand code even a single vertex and did not want to load a mesh file. I was sure the
code was out there somewhere, but was not finding it. So, a bit reluctantly at first, I started coding the mesh generator. I started enjoying creating this thing and stopped searching for the "outtheresomewhere" code; although still curious how others did this. Analysis First question: How do we number the verts? It would be great to conceive of some concise algorithm to put out the cube face verts all in clockwise
order for the outside faces of the cube directly. That seemed beyond me so I plodded along step by step. I decided to just use a simple nested loop to generate the cube face verts and number them in the order they were produced. The hope(and the presumption) was: The loop code was in some order, running thru the x y and z coordinates in order, from n to +n, therefore the output would be a recognizable pattern. The simple nested loop vert generator did not let us down: It gave us a recognizable pattern, at least for this face. It turned out (as expected now) that all six faces have similar recognizable patterns. Plotting the first row or two of verts you can easily see how to run the rest of the pattern. Plot of the first(of six) cube faces verts output by the vert generator: Input of n: There are (2n+1)² verts per cube face, or 25 verts for n = 2. This is looking at the x = n face from the outside of the cube. To simplify the math it helps to define s = 2n. Then there are (s + 1)² verts, or 25 for s = 4 s² cells on the face, or 16 for 4 = 2. We are going divide each cell into 2 triangles, so there are 2s² triangles per face, or 32 for s = 4. Second question: What pattern for the triangles? How to number the 2s² = 32 triangles? What we want in the end is a bit of code such that... for triangles T[0] thru T[2s²1] or T[0] thru T[31]( for n = 4), we have T[N] = f0(N), f1(N), f2(N). Where f0(N) gives the first vertex of T[N] as a function of N. and f1 and f2 give the second and third verts, all in CW winding order looking into the cube of course. Here the choice is a bit arbitrary, but it would seem to make things easier if we can manage to have the order of triangles follow the order of verts to a degree. Numbering the triangles. And now the problem becomes: Look at the triangle vert list, T0  T8...T31 in the image, and try to discern some pattern leading us to the sought after functions f0(N), f1(N), f2(N) where N is the number of the triangle, 0 thru 2s²1. This really is the holy grail of this whole effort; then we have T[N] = f0(N), f1(N), f2(N) and that list of verts can be sent directly to the rendering pipeline. Of course we want these functions to work for all six faces and all 12s² triangles to cover the cube. But first let's see if we can just do this one face, 0 thru 2s²1.. Thru a bit of trial and error the 32 triangles(T0  T31) were ordered as shown. Now we have an ordered list of the triangles and the
verts from our loop. T0 = 0 5 6
T1 = 6 1 0
T2 = 1 6 7
T3 = 7 2 1
T4 = 2 7 8
T5 = 8 3 2
T6 = 3 8 9
T7 = 9 4 3
T8 = 5 10 11 ... T30 T31. If we can find a pattern in the verts on the right side
of this list; we can implement it in an algorithm and
the rest is just coding. Pattern recognition: It appears T2 = T0 with 1 added to each component
T3 = T1 with 1 added to each component In general T[N+2] = T[N] with 1 added to each component, until we come to T8 at least. Also it is hard to recognize a relation between the even and odd triangles,To see what is happening here it helps to look at an image of the generalized case where n can take on any integer value n > 0. Looking for patterns in this generalized(for any n) vert plot we see... We have defined s = 2n. The 4 corner coordinates(+n,+n) of the x =  n cube face, one at each corner (+n,+n). There are (s+1)² verts/face numbered (0 thru (s+1)² 1). There are 2s² triangles/face numbered (0 thru 2s² 1). They are indicated in red. It's not as bad as it looks iff you break it down. Let's look at the even triangles only and just the 0th vert of these triangles. For any row we see the number of that first vert of the even triangles just increases by one going down the row. We can even try a relation such as T[N].0 = N/2. Here T[N].0 denotes the 0th vert of th Nth triangle. Which works until we have to jump to the next row. Every time we jump a row we T[N+1].0 = T[N].0 + 2 for the first triangle in the higher row. So we need a corrective term to the T[N].0 = N/2 relation that adds 1 every time we jump a row. We can use computer integer division to generate such a term and N/2s is such a term. It only changes value when we jump rows and we get our first function ... f0(N) = N/2 + N/2s. (even triangles) Remember the integer division will discard any remainder from the terms and check this works for the entire cube face, but only for the even triangles. What about the odd triangles? Going back to the triangle vs vert list for the specific case n = 2, s = 4 for the first row; we see for the odd triangles T[N].0 = T[N1].0 + s + 2. And adding this term, s + 2 to the formula for the even triangle 0th vert we get f0[N] for the odd triangles. f0(N) = N/2 + N/2s + s + 2. (odd triangles) Continuing this somewhat tedious analysis for the remaining functions f1(N), f2(N) we eventually have these relations for the x = n cube face triangles. for N = 0 thru N = 2s²  1. defining m = N/2 + N/2s. T[N] = m, m + s + 1, m + s + 2 T[N] = f0(N), f1(N), f2(N). (even N) T[N] = m + s + 2, m + 1, m T[N] = f0'(N), f1'(N), f2'(N) (odd N) So it turns out we have two sets of functions for the verts, fn(N) for the even triangles and fn'(N) for the odd. To recap here; we now have formulae for all the T[N] verts as functions of N and the input parameter n: Input: An integer n = units from origin to cube face. But this is only for the first face x = n, we have five more faces to determine. So the question is: Do these formulae work for the other faces? And the answer is no they do not, but going through a similar analysis for the remaining face gives similar T[N] = f0(N), f1(N), f2(N) for them. There is still the choice of how to number the remaining triangles and verts on the remaining five faces, and the f0(N), f1(N), f2(N) will depend on the somewhat arbitrary choice of how we do the numbering. For the particular choice of a numbering scheme I ended up making, it became clear how to determine the f0(N), f1(N), f2(N) for the remaining faces. It required making generalized vert plots for the remaining five face similar to the previous image. Then these relation emerged... For face x = n T[N] N(0 thru 2²1) we have the f0(N), f1(N), f2(N), even and odd For face x = n T[N] N(2s² thru 4s²1) add (s+1)² to the x=n face components and reverse the winding order For face y = n T[N] N(4s² thru 6s²1) add 2(s+1)² to the x=n face components and reverse the winding order For face y = n T[N] N(6s² thru 8s²1) add 3(s+1)² to the x=n face components For face z = n T[N] N(8s²0 thru 10s²1) add 4(s+1)² to the x=n face components For face z = n T[N] N(10s²0 thru 12s²1) add 5(s+1)² to the x=n face components and reverse the winding order And these are enough to allow us to write explicit expressions for all 12n² triangles for all 6 faces T[N] and what remains to be done is to implement these expression in code. Which turned out to be a much simpler task than finding the f0(N), f1(N), f2(N) and resulted in a surprisingly short bit of code. Implementation I have attempted to make this C++ snippet of code as generic as possible and have removed any devplatform specific #includes and the like. GLM, a C++ mathematics library for graphics developed by Christophe Riccio is used. It is a header only library. https://github.com/gtruc/glm/releases/download/0.9.9.0/glm0.9.9.0.zip That is the only outside dependency. // Procedural cube face verticies generator #include <vector> #include <glm/gtc/matrix_transform.hpp> struct Triangle { glm::vec3 vert[3]; // the three verts of the triangle }; /* std::vector<Triangle> cube_Faces(int n) Input: integer 'n'; the units from origin to cube face. Output: vector<Triangle> glTriangle; container for the 12*(2*n)² triangles covering the 6 cube faces. */ std::vector<Triangle> cube_Faces(int n){ size_t number_of_triangles(12*(2*n )*(2*n)); size_t number_of_face_verts(6*(2*n +1 )*(2*n+1)); std::vector<glm::vec3> face_verts(number_of_face_verts); std::vector<Triangle> glTriangle(number_of_triangles); // Generate the 6*(2n +1 )² face verts  int l(0); for(int i = 0; i < 6; i++){ for(int j = n; j <= n; j++){ for(int k = n; k <= n; k++){ // Below "ifS" strip out all interior cube verts. if( i == 0){ // do yz faces face_verts[l].x = (float)(n); //x face_verts[l].y = (float)j; //y face_verts[l].z = (float)k;}//z if( i == 1){ // do yz faces face_verts[l].x = (float)(n); //x face_verts[l].y = (float)j; //y face_verts[l].z = (float)k;}//z if( i == 2){ // do zx faces face_verts[l].x = (float)j; //x face_verts[l].y = (float)(n); //y face_verts[l].z = (float)k;}//z if( i == 3){ // do zx faces face_verts[l].x = (float)j; //x face_verts[l].y = (float)(n); //y face_verts[l].z = (float)k;}//z if( i == 4){ // do xy faces face_verts[l].x = (float)j; //x face_verts[l].y = (float)k; //y face_verts[l].z = (float)(n);}//z if( i == 5){ // do xy faces face_verts[l].x = (float)j; //x face_verts[l].y = (float)k; //y face_verts[l].z = (float)(n);}//z l++; } } } // Generate the 12*(2*n)² triangles from the face verts  int s = 2*n; int q = 2*s*s; int a = (s+1)*(s+1); int f(0); int r(0); int h(0); for( int N=0; N < number_of_triangles; ){ // triangles already in CW winding if( N < q  N < 5*q && N > 3*q  1 ){ // do the even indicies f= q*(N/q); r = a*(N/q); h = (Nf)/2 + (Nf)/(2*s) + r; glTriangle[N].vert[0] = face_verts[h]; glTriangle[N].vert[1] = face_verts[s + 1 + h]; glTriangle[N].vert[2] = face_verts[s + 2 + h]; N++; f= q*(N/q); r = a*(N/q); h = (Nf)/2 + (Nf)/(2*s) + r; // do the odd indicies glTriangle[N].vert[0] = face_verts[s + 2 + h]; glTriangle[N].vert[1] = face_verts[ 1 + h]; glTriangle[N].vert[2] = face_verts[h]; N++; f= q*(N/q); r = a*(N/q); h = (Nf)/2 + (Nf)/(2*s) + r; } // triangles needing reverse order for CW winding if( N > 5*q  1  N < 3*q && N > q  1 ){ // do the even indicies glTriangle[N].vert[0] = face_verts[s + 2 + h]; glTriangle[N].vert[1] = face_verts[s + 1 + h]; glTriangle[N].vert[2] = face_verts[h]; N++; f= q*(N/q); r = a*(N/q); h = (Nf)/2 + (Nf)/(2*s) + r; // do the odd indicies glTriangle[N].vert[0] = face_verts[h]; glTriangle[N].vert[1] = face_verts[1 + h]; glTriangle[N].vert[2] = face_verts[s + 2 + h]; N++; f= q*(N/q); r = a*(N/q); h = (Nf)/2 + (Nf)/(2*s) + r; } } // Normalize the cube to side = 1  for(int i = 0; i < number_of_triangles; i++){ glTriangle[i].vert[0].x = glTriangle[i].vert[0].x/(2.0*(float)n); glTriangle[i].vert[0].y = glTriangle[i].vert[0].y/(2.0*(float)n); glTriangle[i].vert[0].z = glTriangle[i].vert[0].z/(2.0*(float)n); glTriangle[i].vert[1].x = glTriangle[i].vert[1].x/(2.0*(float)n); glTriangle[i].vert[1].y = glTriangle[i].vert[1].y/(2.0*(float)n); glTriangle[i].vert[1].z = glTriangle[i].vert[1].z/(2.0*(float)n); glTriangle[i].vert[2].x = glTriangle[i].vert[2].x/(2.0*(float)n); glTriangle[i].vert[2].y = glTriangle[i].vert[2].y/(2.0*(float)n); glTriangle[i].vert[2].z = glTriangle[i].vert[2].z/(2.0*(float)n); }; return glTriangle; } The rendering was done using OpenGl. // OGL render call to the cube mesh generator  PSUEDOCODE int n(2); int cube_triangle_Count = (12*(2*n)*(2*n)); std::vector<Triangle> cube_Triangles(cube_triangle_Count); cube_Triangles = cube_Faces(n); glBindBuffer(GL_ARRAY_BUFFER, uiVBO[0]); glBufferData(GL_ARRAY_BUFFER, cube_Triangles.size()*sizeof(Triangle), &cube_Triangles[0], GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), 0); glEnableVertexAttribArray(0); glDrawArray(GL_TRIANGLES,0,3*cube_triangle_Count); This just gets the position attribute of the cube face triangle verts; for the color and other attributes there are a couple of options: Use separate GL_ARRAY_BUFFERS for the color and other attributes. Or add attributes to the Triangle struct... struct Triangle { glm::vec3 vert[3]; // the three verts of the triangle attribute1; attribute2; ... }; Screenshot of the spherified cube. What's next? Now that we have the cube mesh what we can do with with it practically unlimited. The first thing I did was turn it into a sphere. Playing with tesselating the cube or sphere or stellating it with different patterns; might do. Ended up trying a few matrix transformations on the cube mesh. These are shown in the image below. These shapes are result short bits of code like the code for the column shape below. //Column for(int i = 0; i < number_of_triangles; i++){ for(int j = 0; j < 3; j++){ if( glTriangle[i].vert[j].y < 0.5f && glTriangle[i].vert[j].y > 0.5f ){ float length_of_v = sqrt((glTriangle[i].vert[j].x * glTriangle[i].vert[j].x) + (glTriangle[i].vert[j].z * glTriangle[i].vert[j].z)); glTriangle[i].vert[j].x = 0.5f*glTriangle[i].vert[j].x/length_of_v; glTriangle[i].vert[j].z = 0.5f*glTriangle[i].vert[j].z/length_of_v; } } } Doing this; the blacksmith at his forge analogy soon presents. The mesh is the ingot, hammer matrices stretch, round and bend it against the fixed geometry of the anvil  coordinate system. I am the smith.
Tetrahedron The tetrahedron is the platonic solid with the least number of faces(4), edges(6), and verts(4). In antiquity it was associated with the element of fire due to its' sharp vertices. The algorithm for the tetrahedron mesh was developed in a similar way to the cube, but here it seemed simpler to get a routine for just one face  an equilateral triangle  and use matrix translations and rotations to form the complete tetrahedron. So more like origami or tinsmithing than blacksmithing. Procedural tetrahedron screenshot. The n = 4 and the general case To get an routine for the general case, n an integer > 0, a bit of what I think is known as mathematical induction was used. PSUEDOCODE Algorithm to generate equilateral triangle face with unit side composed of n² "subtriangle" in the xy plane. std::vector<Triangle> equilateral(int n){ std::vector<Triangle> tri_Angle(n²); // Create the seed triangle in xy plane . // This is triangle "0" in the image above. // This is in the xy(z=0) plane so all the // tri_Angle.vert[0 thrue n 1 ].z = 0. // We just work with the x and y verts. tri_Angle[all].vert[all].z = 0; // The seed triangle tri_Angle[0].vert[0].x = 0; tri_Angle[0].vert[0].y = 0; tri_Angle[0].vert[1].x = 1/2n; tri_Angle[0].vert[1].y = sin(π/3)/n; tri_Angle[0].vert[2].x = 1/n; tri_Angle[0].vert[2].y = 0; // Build the equilateral triangle face. int count(0); for(int row = 0; row < n; row++){ count = 0; Spin = glmRotateMatrix( π/3, zaxis ); // The magic happens here! for(int i = 2*n*row  row*row; i < 2*n*row  row*row + 2*n  2*row  1; i++) { if (count % 2 == 0 ) // Triangle is even in the row  just translate { // more magic. x_Lat = glm_Matrix((count + row)/2n, row*sin(π/3)/n, 0.0f); tri_Angle[i].vert[0] = x_Lat* tri_Angle[0].vert[0]; tri_Angle[i].vert[1] = x_Lat* tri_Angle[0].vert[1]; } else // Triangle is odd in the row  rotate then translate { //and more magic. x_Lat = glm_Matrix((count + row + 1)/2n, row*sin(π/3)/n, 0.0f); tri_Angle[i].vert[0] = x_Lat*Spin*tri_Angle[0].vert[0]; tri_Angle[i].vert[1] = x_Lat*Spin*tri_Angle[0].vert[1]; } } count++; } return tri_Angle; } This is the psuedocode version of the routine which generates the verts for the n² triangles in a face. Getting this algorithm was a bit of a brain drain but looking for patterns in the image of the face allowed it to happen. We use a "seed" triangle, which is triangle 0 on the lower left of the figure. The verts of this one triangle are input; the rest of the n² triangles verts are generated by translating and rotating this seed triangle. Notice: There are n rows, every row has 2 less triangles than the row below. If we number the triangles from 0 to 2n  2*row  2, where the rows run 0 to n; the even triangles just need to be translated ... in the x direction by (count + row)/2n where count = their position in the row 0 to 2n  2*row  2. in the y direction by row*height. height = height of seed triangle. The odd triangles need to be rotated pi/3 = 60 degrees around the z axis then translated ... in the x direction by (count + row + 1)/2n where count = their position in the row 0 to 2n  2*row  2. in the y direction by row*height. height = height of seed triangle. Now we have a single face for the tetrahedron, to join the four faces together we need the angle between the faces called the dihedral angle. Dihedral Angle Each of the five platonic solids has a characteristic called the dihedral angle. This is the angle between the faces. For the cube it is 90 degrees or pi/2 radians. For the tetrahedron it is 70.528779° = arccos(1/3) = atan(2*sqrt(2)); The tetrahedron, with just four faces, is the simplest of the platonic solids. The simplest way I can think of to build it: Start with the four face stacked one on another, edges aligned. Imagine the top three faces each hinged to the bottom face along one edge. Then rotate each face around then hinged edge by arccos(1/3), the dihedral angle. That is the method of the bit of code shown below. vector<Triangle> tetrahedron(int N){ std::vector<Triangle> tetra(4n²); tetra[all].vert[all].z = 0; // The seed triangle tetra[0].vert[0].x = 0; tetra[0].vert[0].y = 0; tetra[0].vert[1].x = 1/2n; tetra[0].vert[1].y = sin(π/3)/n; tetra[0].vert[2].x = 1/n; tetra[0].vert[2].y = 0; //  The first face  // generate the first equilateral triangle face with unit side // composed of n² "subtriangle" in the xy(z=0) plane. int count(0); for(int row = 0; row < n; row++) { count = 0; Spin = glmRotateMatrix( π/3, zaxis ); for(int i = 2*n*row  row*row; i < 2*n*row  row*row + 2*n  2*row  1; i++) { if (count % 2 == 0 ) // Triangle is even in the row  just translate { x_Lat = glm_Matrix((count + row)/2n, row*sin(π/3)/n, 0.0f); tetra[i].vert[0] = x_Lat* tetra[0].vert[0]; tetra[i].vert[1] = x_Lat* tetra[0].vert[1]; } else // Triangle is odd in the row  rotate then translate { x_Lat = glm_Matrix((count + row + 1)/2n, row*sin(π/3)/n, 0.0f); tetra[i].vert[0] = x_Lat*Spin*tetra[0].vert[0]; tetra[i].vert[1] = x_Lat*Spin*tetra[0].vert[1]; } } count++; } //  The second face  // generate the second equilateral face from the first // by rotating around the X axis by the dihedral angle. float tetra_Dihedral = atan(2*sqrt(2)); Spin = glmRotateMatrix( tetra_Dihedral, xaxis ); //just rotate for(int i = 0; i < n²; i++) { for(int j = 0; j < 3; j++) { tetra[n² + i].vert[j] = Spin*tetra[i].vert[j]; } } //The rotation gives CCW verts so need need to make them CW again for(int i = n²; i < 2n²; i++) { swap(tetra[i].vert[0]  with  tetra[i].vert[2]; } //  The third face  // For the second face we rotated the first triangle around its' // base on the X  axis. For the third face we rotate the first // triangle around its' edge along the vector ( 0.5, 0.866025, 0.0 ). Spin = glmRotateMatrix( tetra_Dihedral ,glm::vec3(0.5f,0.866025f,0.0f)); for(int i = 0; i < n²; i++) { for(int j = 0; j < 3; j++) { tetra[2n² + i].vert[j] = Spin*tetra[i].vert[j]; } } //need to make it CW again for(int i = 2n²; i < 3n²; i++) { swap(tetra[i].vert[0]  with  tetra[i].vert[2]; } //  The forth face  // For the forth face we first translate the original face along the // X axis so it right edge vector (0.5f, 0.866025f, 0.0f) passes thru the origin. // Then we rotate the first triangle around the that vector by the dihedral angle. x_Lat = glm::translate( glm::vec3(1.0f, 0.0f, 0.0f)); Spin = glmRotateMatrix( tetra_Dihedral, glm::vec3(0.5f,0.866025f,0.0f)); for(int i = 0; i < n²; i++) { for(int j = 0; j < 3; j++) { tetra[3n² + i].vert[j] = Spin*x_Lat*tetra[i].vert[j]; } } //need to make it CW again for(int i = 3n²; i < 4n²; i++) { swap(tetra[i].vert[0]  with  tetra[i].vert[2]; } // We now have the complete tetrahedron, tetra(4n²), but its' base // is not horizontal so let's make is so. // put the base in the xz plane // rotate 90  dihedral angle around X axis. Spin = glm::rotate( tetra_Dihedral  half_PI, xaxis); for(int i = 0; i < 4n²; i++) { for(int j = 0; j < 3; j++) { tetra[i].vert[j] = Spin*tetra[i].vert[j]; } } // We now have the complete tetrahedron, tetra(4n²), sitting with its' // base on the xz(y=0) plane, let's put its' center at the origin. // For this we need another Platonic Solid attribute: The radius of // the tetrahedrons circumscribed sphere which is sqrt(3/8). So the // center of the tet is this vertical distance down from its' apex. // To put the center at the origin we need to translate down this // distance along the Y axis. We need also to xlat along the Z axis // by 1/2(sqrt(3)) = 0.28867; the distance from the center of a face // to the center of a side. // Finally we need to center along the X axis( xlat .5) x_Lat = glm::translate( glm::vec3(0.5f, sqrt(3/8), sqrt(3)/2); for(int i = 0; i < 4n²; i++) { for(int j = 0; j < 3; j++) { tetra[i].vert[j] = x_Lat*tetra[i].vert[j]; } } return tetra; } Notes: Oops: Left out std::vector<Triangle> tri_Angles(4*n*n); Should be the first line of the function body! Corrections to the corrections: First line of function definition vector<Triangle> tetrahedron(int N){ should be vector<Triangle> tetrahedron(int n){ Those last two for loops could and probably should be combined to do a translate*rotate*triangle in one statement, but I have not tried it. All distances are for a tetrahedron with unit side. The sign of the dihedral angle in the rotations was usually determined by trial and error. I.e.; I tried one sign, compiled the code and rendered the tet. If it was wrong I just reversed the sign. The end result is a tetrahedron with its' center at the origin, its' base in the xz plane, and one edge parallel to the X axis. Of the five platonic solids; three (tetrahedron, octahedron, icosahedron) are composed of equilateral triangle faces. One of square faces (cube). And one of pentagon faces (dodecahedron). Two tetrahedrons fit nicely in a cube. 111618: Corrections to code blocks for equilateral triangle and tetrahedron. 111818: More corrections.
Join my Ludum Dare team!
Gameplay Xilvan Design games for 20182019!
Major refactoring and script progress report
Y'all ever just get wayyyy to distracted time n time again?
13 RONIN  DevLog #7  Fight or flight!
This is how I build the decision tree var decisionTree = DecisionTreeBuilder .Begin( "Reacting", state => state.Player.IsReacting() ? "Elapsed" : "Opp new action") .AddNode( "Elapsed", state => state.Player.IsTimeToReact(state.GameTime.TotalGameTime) ? "Facing" : "Done 1") .AddNode( "Facing", state => { return state.Player.IsFacingOpponent() ? "Reachable 1" : "Reachable 2"; }) .AddNode( "Reachable 1", state => state.Player.IsWithinReach(state.Opponent) ? "Opp attacking" : "Opp approaching") .AddNode( "Opp attacking", state => state.Player.ActionToReactUpon is AttackAction ? "Defend" : "Attack 1") .AddLeaf( "Defend", state => { state.Player.ResetReaction(); state.Player.ParryHeadCut(); }) .AddLeaf( "Attack 1", state => { state.Player.ResetReaction(); state.Player.HeadCut(); }) .Parent() .AddNode( "Opp approaching", state => state.Opponent.IsAdvancing() && state.Opponent.IsFacingOpponent() ? "Idle 1" : "Advance 1") . . . .Build(); AddNode will create and append a new node to the current nodes’ children and then go into the newly created node and make it current. AddLeaf will create and append a new leaf, but not go into it. Parent will go to the parent node and make it current. Build will return the newly composed tree. The choice to use strings for names makes it easy to follow the code but also makes it easy switching between the diagram and the code. The Parent, and Nameproperties together with the GetFullName method make nice tools while debugging. Player In my game I have a HumanPlayerclass and a ComputerPlayerclass, both implementing an abstract class Player. The main difference between the Human and the ComputerPlayerclass is how the Updatemethod is implemented. The HumanPlayerclass is using input from keyboard and gamepad to control the player character while the ComputerPlayer is using the decision tree. The code for using the tree looks like this: var leaf = _decisionTree.Evaluate(state);
leaf.Action(state); Nice, isn’t it?
Happy coding!
jan.
【ＳＴＵＦＦ】
GameDev Challenge  Frogger  Part 4 (Final)
10hz Smoothness
Planet generation in real time.....
Right at the end we call "m_pUniverse>Run();". This actually starts the build thread. What it does is continuously look at the position of the god object which we have attached to the camera above in the code, and build the planet with the appropriate LOD based on it's proximity to the various terrain chunks.........Let's not bore you with more text or boring pictures. Instead we will bore you with a boring video:
As you can see it generates terrain reasonably fast. But there is still a lot more we can do. First off we should eliminate the backside of the planet. Note that as we descend towards the planet the backside becomes bigger and bigger as the horizon becomes closer and closer to the camera. This is one advantage of a spherical world. Second we can add a lot more threads. In general we try to cache as much data as possible. What we can still do is pregenerate our octree at one level down using a fractal function pipeline. In general most the CPU time is spent in the fractal data generation, so it makes sense to put add more threading there. Fortunately this is one of the easier places we can use threading. For our next entry we hope to go all the way down to the surface and include some nominal shading.
Game Events based on Physics Interactions
Oh, and the explosion is from here: https://mbtskoudsalg.com/explore/explosionspritesheetpng/#gal_post_3691_explosionspritesheetpng3.png, after reading the site terms I decided it was safe to use.
The Final Form
I nearly broke everything I'm sure of it
Making an arcade racer like CTR
I'm going for basic.
Adding Code to the Questionnaire, Elevator, and Key Cards
Escape Pod Model
Battle Gem Ponies DevLog #196 (Showcase Dreams)
Serious planning has begun, so far it's only been preemptive measures and bookmarking things to come back to later. Now I'm getting down the fine details of everything I need to know, do, study, and purchase in order to make the most out of this Magfest trip.
More on yotesgames.com
Getting Back In To It Card Game Day 2
I Am Overburdened, saving my soul
Certain item skills with persisting effects (e.g.: Fear, Cripple) could get stuck or be triggered on recently summoned/resurrected monsters in rare cases. These were not visible, but could affect how well you did, so my apologies for the occasional unfair (de)buffs.
The positioning of hallucinations from toxicity effects (e.g.: Toad monsters, poison in potion) were not correctly randomized. These hallucinated monsters were always placed in the upperleft quarter of the map, breaking the illusion. Now this is fixed.
A while ago I added itemslot notifications to item pickups and chests when standing nearby. It did not occur to me though, that with this modification the treasury pickups and chests triggered unnecessary ones too. Oops, fixed!
In balance With this update balance tweaks found their way into the game too. There were still some complaints regarding this aspect, so I made normal mode a tad bit harder and nightmare difficulty a tiny bit more forgiving. These are not substantial changes, only slightly affecting the pickup and chest spawn rates. Another extra is “near death” detection. Simply put if you end up in a really bad situation at some point, the game tries not to punish you even harder on the next dungeon level These changes are subtle, so they keep the game challenging, but they were introduced to make it more fair at the same time. Boss confusion Some people found the boss level corpses confusing. I don’t know if the new version will work out better (this is not the first time I change this ), but now the number of corpses (below the one serving as the story hint) are tied to the unlock progress of the boss entry in the monster book. I know some other unlocks still need more and better hints too. I’m going to work on these problems in the near future… Am I still alive? Yes, pretty much. Due to personal and financial reasons I had to put fulltime game development on hold for a long while, but I’m back now and I will be working on my projects in the upcoming months. Before I vanished from the face of the INTERNETZ I teased possible ports and a bigger content update for I Am Overburdened. These are not forgotten and progressing well In a week or two I will reveal more details about my plans. Until then, have a funfilled time.
Thanks for reading and take care!

Advertisement