Write a 3D Soft Engine from Scratch: Part 3

Written by admin. Posted in HTML5

Tagged: , , , , , , ,

Published on February 02, 2014 with No Comments

This entry is part 3 of 7 in the series Write a 3D Soft Engine from Scratch

In the previous tutorial, we learned how to draw lines & triangles and we really started to see the 3D side our meshes thanks to this wireframe rendering. But we’ve only displayed a cube… And even a simple cube already has 12 faces! Are we going to be forced to handle ourselves all the faces for more complex objects this way? Hopefully not.

3D modelers help the collaboration between 3D designers and developers. The designer can use its favorite tools to build his scenes or meshes (3D Studio Max, Maya, Blender, etc.). Then he will export his work into a file that will be loaded by the developers. The developers will finally push the meshes into his real time 3D engine. There are several file formats available on the market to serialize the job done by the artists. In our case, we’re going to use JSON. Indeed, David Catuhe has done an export library for Blender that output a .babylon file using JSON. We’re then going to see how to parse that file and display the meshes in our lovely soft engine.

Blender is a free 3D modeler you can download here: http://www.blender.org/download/get-blender/

You can write plug-ins in Python. That’s what we’ve done for the exporter.

By following this tutorial series, you will be able to have such a result:

And you’ll see that you’ve already done most of the job in the 2 previous tutorials to do that.

Install the Babylon exporter and generate your own scene with Blender

Once you’ll have installed Blender, please download our Babylon exporter from here: io_export_babylon.py

Copy this file into the scriptaddons directory where you’ve installed Blender (for instance “C:Program FilesBlender FoundationBlender2.67scriptsaddons” in my specific case).

You need to active our plug-in in the user preferences. Go to “File” –> “User Preferences” and the “Addons” tab. Search for “babylon” and activate it by checking the case.

image

Do whatever you want with Blender. If you’re like me, really bad at building 3D meshes, here is a cool option that will impress your friends during geeks parties: “Add” –> “Mesh” –> “Monkey”:

image

You should then obtain a screen like that:

image

Last step is to export it into the .babylon file format (our JSON file). “File” –> “Export” –> “Babylon.js

image

Name the file “monkey.babylon”.

Note: this monkey is named Suzanne and is very well-known in the 3D/gaming community. By knowing her, you’re now a proud member of this cool community! Welcome onboard! ;)

Loading the exported JSON file and displaying its meshes

As I was telling you at the beginning of this article, we’ve already built all the needed logic to display more complex meshes like Suzanne. We’ve got our Face, Mesh & Vertex logic. This is all we need for now.

In the JSON format, the Babylon exporter is adding more details than we currently need. For instance, it also adds potential details about the textures, the lights, etc. That’s why, we’re going to parse the file and jump directly to areas we’re only interesting in: the vertices and the faces’ indices to build our wireframe rendering.

Note: for C# developers, you need to install Json.NET from Newtonsoft via nuGet like we’ve done in the first tutorial to add SharpDX. Indeed, JSON parsing is not natively supported in .NET like inside a browser using JavaScript.

Let’s start by adding the loading logic inside the Device object:

<span style="color: green;">// Loading the JSON file in an asynchronous manner
</span><span style="color: blue;">public async </span><span style="color: rgb(43, 145, 175);">Task</span><span style="color: black;">&lt;</span><span style="color: rgb(43, 145, 175);">Mesh</span><span style="color: black;">[]&gt; LoadJSONFileAsync(</span><span style="color: blue;">string </span><span style="color: black;">fileName)
   </span><span style="color: blue;">var </span><span style="color: black;">meshes = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">List</span><span style="color: black;">&lt;</span><span style="color: rgb(43, 145, 175);">Mesh</span><span style="color: black;">&gt;();
   </span><span style="color: blue;">var </span><span style="color: black;">file = </span><span style="color: blue;">await </span><span style="color: black;">Windows.ApplicationModel.</span><span style="color: rgb(43, 145, 175);">Package</span><span style="color: black;">.Current.InstalledLocation.GetFileAsync(fileName);
   </span><span style="color: blue;">var </span><span style="color: black;">data = </span><span style="color: blue;">await </span><span style="color: black;">Windows.Storage.</span><span style="color: rgb(43, 145, 175);">FileIO</span><span style="color: black;">.ReadTextAsync(file);
   </span><span style="color: blue;">dynamic </span><span style="color: black;">jsonObject = Newtonsoft.Json.</span><span style="color: rgb(43, 145, 175);">JsonConvert</span><span style="color: black;">.DeserializeObject(data);
    </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">meshIndex = 0; meshIndex &lt; jsonObject.meshes.Count; meshIndex++)
   
       </span><span style="color: blue;">var </span><span style="color: black;">verticesArray = jsonObject.meshesmeshIndex.vertices;
       </span><span style="color: green;">// Faces
       </span><span style="color: blue;">var </span><span style="color: black;">indicesArray = jsonObject.meshesmeshIndex.indices;
        </span><span style="color: blue;">var </span><span style="color: black;">uvCount = jsonObject.meshesmeshIndex.uvCount.Value;
       </span><span style="color: blue;">var </span><span style="color: black;">verticesStep = 1;
        </span><span style="color: green;">// Depending of the number of texture's coordinates per vertex
       // we're jumping in the vertices array  by 6, 8 &amp; 10 windows frame
       </span><span style="color: blue;">switch </span><span style="color: black;">((</span><span style="color: blue;">int</span><span style="color: black;">)uvCount)
       
           </span><span style="color: blue;">case </span><span style="color: black;">0:
               verticesStep = 6;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">1:
               verticesStep = 8;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">2:
               verticesStep = 10;
               </span><span style="color: blue;">break</span><span style="color: black;">;
       
        </span><span style="color: green;">// the number of interesting vertices information for us
       </span><span style="color: blue;">var </span><span style="color: black;">verticesCount = verticesArray.Count / verticesStep;
       </span><span style="color: green;">// number of faces is logically the size of the array divided by 3 (A, B, C)
       </span><span style="color: blue;">var </span><span style="color: black;">facesCount = indicesArray.Count / 3;
       </span><span style="color: blue;">var </span><span style="color: black;">mesh = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Mesh</span><span style="color: black;">(jsonObject.meshesmeshIndex.name.Value, verticesCount, facesCount);
        </span><span style="color: green;">// Filling the Vertices array of our mesh first
       </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; verticesCount; index++)
       
           </span><span style="color: blue;">var </span><span style="color: black;">x = (</span><span style="color: blue;">float</span><span style="color: black;">)verticesArrayindex * verticesStep.Value;
           </span><span style="color: blue;">var </span><span style="color: black;">y = (</span><span style="color: blue;">float</span><span style="color: black;">)verticesArrayindex * verticesStep + 1.Value;
           </span><span style="color: blue;">var </span><span style="color: black;">z = (</span><span style="color: blue;">float</span><span style="color: black;">)verticesArrayindex * verticesStep + 2.Value;
           mesh.Verticesindex = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Vector3</span><span style="color: black;">(x, y, z);
       
        </span><span style="color: green;">// Then filling the Faces array
       </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; facesCount; index++)
       
           </span><span style="color: blue;">var </span><span style="color: black;">a = (</span><span style="color: blue;">int</span><span style="color: black;">)indicesArrayindex * 3.Value;
           </span><span style="color: blue;">var </span><span style="color: black;">b = (</span><span style="color: blue;">int</span><span style="color: black;">)indicesArrayindex * 3 + 1.Value;
           </span><span style="color: blue;">var </span><span style="color: black;">c = (</span><span style="color: blue;">int</span><span style="color: black;">)indicesArrayindex * 3 + 2.Value;
           mesh.Facesindex = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Face </span><span style="color: black;"> A = a, B = b, C = c ;
       }
        </span><span style="color: green;">// Getting the position you've set in Blender
       </span><span style="color: blue;">var </span><span style="color: black;">position = jsonObject.meshesmeshIndex.position;
       mesh.Position = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Vector3</span><span style="color: black;">((</span><span style="color: blue;">float</span><span style="color: black;">)position0.Value, (</span><span style="color: blue;">float</span><span style="color: black;">)position1.Value, (</span><span style="color: blue;">float</span><span style="color: black;">)position2.Value);
       meshes.Add(mesh);
   }
   </span><span style="color: blue;">return </span><span style="color: black;">meshes.ToArray();
</span>

<span style="color: green;">// Loading the JSON file in an asynchronous manner and
/ calling back with the function passed providing the array of meshes loaded
</span><span style="color: blue;">public </span><span style="color: black;">LoadJSONFileAsync(fileName: </span><span style="color: blue;">string</span><span style="color: black;">, callback: (result: Mesh[]) =&gt; </span><span style="color: blue;">any</span><span style="color: black;">): </span><span style="color: blue;">void </span><span style="color: black;">
   </span><span style="color: blue;">var </span><span style="color: black;">jsonObject = ;
   </span><span style="color: blue;">var </span><span style="color: black;">xmlhttp = </span><span style="color: blue;">new </span><span style="color: black;">XMLHttpRequest();
   xmlhttp.open(</span><span style="color: rgb(163, 21, 21);">"GET"</span><span style="color: black;">, fileName, </span><span style="color: blue;">true</span><span style="color: black;">);
   </span><span style="color: blue;">var </span><span style="color: black;">that = </span><span style="color: blue;">this</span><span style="color: black;">;
   xmlhttp.onreadystatechange = </span><span style="color: blue;">function </span><span style="color: black;">() 
       </span><span style="color: blue;">if </span><span style="color: black;">(xmlhttp.readyState == 4 &amp;&amp; xmlhttp.status == 200) 
           jsonObject = JSON.parse(xmlhttp.responseText);
           callback(that.CreateMeshesFromJSON(jsonObject));
       
   };
   xmlhttp.send(</span><span style="color: blue;">null</span><span style="color: black;">);
</span><span style="color: blue;">private </span><span style="color: black;">CreateMeshesFromJSON(jsonObject): Mesh[] 
   </span><span style="color: blue;">var </span><span style="color: black;">meshes: Mesh[] = [];
   </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">meshIndex = 0; meshIndex &lt; jsonObject.meshes.length; meshIndex++) 
       </span><span style="color: blue;">var </span><span style="color: black;">verticesArray: </span><span style="color: blue;">number</span><span style="color: black;">[] = jsonObject.meshesmeshIndex.vertices;
       </span><span style="color: green;">// Faces
       </span><span style="color: blue;">var </span><span style="color: black;">indicesArray: </span><span style="color: blue;">number</span><span style="color: black;">[] = jsonObject.meshesmeshIndex.indices;
        </span><span style="color: blue;">var </span><span style="color: black;">uvCount: </span><span style="color: blue;">number </span><span style="color: black;">= jsonObject.meshesmeshIndex.uvCount;
       </span><span style="color: blue;">var </span><span style="color: black;">verticesStep = 1;
        </span><span style="color: green;">// Depending of the number of texture's coordinates per vertex
       // we're jumping in the vertices array  by 6, 8 &amp; 10 windows frame
       </span><span style="color: blue;">switch </span><span style="color: black;">(uvCount) 
           </span><span style="color: blue;">case </span><span style="color: black;">0:
               verticesStep = 6;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">1:
               verticesStep = 8;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">2:
               verticesStep = 10;
               </span><span style="color: blue;">break</span><span style="color: black;">;
       
        </span><span style="color: green;">// the number of interesting vertices information for us
       </span><span style="color: blue;">var </span><span style="color: black;">verticesCount = verticesArray.length / verticesStep;
       </span><span style="color: green;">// number of faces is logically the size of the array divided by 3 (A, B, C)
       </span><span style="color: blue;">var </span><span style="color: black;">facesCount = indicesArray.length / 3;
       </span><span style="color: blue;">var </span><span style="color: black;">mesh = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Mesh(jsonObject.meshesmeshIndex.name, verticesCount, facesCount);
               
       </span><span style="color: green;">// Filling the Vertices array of our mesh first
       </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; verticesCount; index++) 
           </span><span style="color: blue;">var </span><span style="color: black;">x = verticesArrayindex * verticesStep;
           </span><span style="color: blue;">var </span><span style="color: black;">y = verticesArrayindex * verticesStep + 1;
           </span><span style="color: blue;">var </span><span style="color: black;">z = verticesArrayindex * verticesStep + 2;
           mesh.Verticesindex = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(x, y, z);
       
               
       </span><span style="color: green;">// Then filling the Faces array
       </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; facesCount; index++) 
           </span><span style="color: blue;">var </span><span style="color: black;">a = indicesArrayindex * 3;
           </span><span style="color: blue;">var </span><span style="color: black;">b = indicesArrayindex * 3 + 1;
           </span><span style="color: blue;">var </span><span style="color: black;">c = indicesArrayindex * 3 + 2;
           mesh.Facesindex = 
               A: a,
               B: b,
               C: c
           ;
       }
               
       </span><span style="color: green;">// Getting the position you've set in Blender
       </span><span style="color: blue;">var </span><span style="color: black;">position = jsonObject.meshesmeshIndex.position;
       mesh.Position = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(position0, position1, position2);
       meshes.push(mesh);
   }
   </span><span style="color: blue;">return </span><span style="color: black;">meshes;
</span>

<span style="color: green;">// Loading the JSON file in an asynchronous manner and
/ calling back with the function passed providing the array of meshes loaded
</span><span style="color: black;">Device.prototype.LoadJSONFileAsync = </span><span style="color: blue;">function </span><span style="color: black;">(fileName, callback) 
   </span><span style="color: blue;">var </span><span style="color: black;">jsonObject = ;
   </span><span style="color: blue;">var </span><span style="color: black;">xmlhttp = </span><span style="color: blue;">new </span><span style="color: black;">XMLHttpRequest();
   xmlhttp.open(</span><span style="color: rgb(163, 21, 21);">"GET"</span><span style="color: black;">, fileName, </span><span style="color: blue;">true</span><span style="color: black;">);
   </span><span style="color: blue;">var </span><span style="color: black;">that = </span><span style="color: blue;">this</span><span style="color: black;">;
   xmlhttp.onreadystatechange = </span><span style="color: blue;">function </span><span style="color: black;">() 
       </span><span style="color: blue;">if</span><span style="color: black;">(xmlhttp.readyState == 4 &amp;&amp; xmlhttp.status == 200) 
           jsonObject = JSON.parse(xmlhttp.responseText);
           callback(that.CreateMeshesFromJSON(jsonObject));
       
   };
   xmlhttp.send(</span><span style="color: blue;">null</span><span style="color: black;">);
;
evice.prototype.CreateMeshesFromJSON = </span><span style="color: blue;">function </span><span style="color: black;">(jsonObject) 
   </span><span style="color: blue;">var </span><span style="color: black;">meshes = [];
   </span><span style="color: blue;">for</span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">meshIndex = 0; meshIndex &lt; jsonObject.meshes.length; meshIndex++) 
       </span><span style="color: blue;">var </span><span style="color: black;">verticesArray = jsonObject.meshesmeshIndex.vertices;
       </span><span style="color: green;">// Faces
       </span><span style="color: blue;">var </span><span style="color: black;">indicesArray = jsonObject.meshesmeshIndex.indices;
        </span><span style="color: blue;">var </span><span style="color: black;">uvCount = jsonObject.meshesmeshIndex.uvCount;
       </span><span style="color: blue;">var </span><span style="color: black;">verticesStep = 1;
        </span><span style="color: green;">// Depending of the number of texture's coordinates per vertex
       // we're jumping in the vertices array  by 6, 8 &amp; 10 windows frame
       </span><span style="color: blue;">switch</span><span style="color: black;">(uvCount) 
           </span><span style="color: blue;">case </span><span style="color: black;">0:
               verticesStep = 6;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">1:
               verticesStep = 8;
               </span><span style="color: blue;">break</span><span style="color: black;">;
           </span><span style="color: blue;">case </span><span style="color: black;">2:
               verticesStep = 10;
               </span><span style="color: blue;">break</span><span style="color: black;">;
       
        </span><span style="color: green;">// the number of interesting vertices information for us
       </span><span style="color: blue;">var </span><span style="color: black;">verticesCount = verticesArray.length / verticesStep;
       </span><span style="color: green;">// number of faces is logically the size of the array divided by 3 (A, B, C)
       </span><span style="color: blue;">var </span><span style="color: black;">facesCount = indicesArray.length / 3;
       </span><span style="color: blue;">var </span><span style="color: black;">mesh = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Mesh(jsonObject.meshesmeshIndex.name, verticesCount, facesCount);
        </span><span style="color: green;">// Filling the Vertices array of our mesh first
       </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; verticesCount; index++) 
           </span><span style="color: blue;">var </span><span style="color: black;">x = verticesArrayindex * verticesStep;
           </span><span style="color: blue;">var </span><span style="color: black;">y = verticesArrayindex * verticesStep + 1;
           </span><span style="color: blue;">var </span><span style="color: black;">z = verticesArrayindex * verticesStep + 2;
           mesh.Verticesindex = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(x, y, z);
       
        </span><span style="color: green;">// Then filling the Faces array
       </span><span style="color: blue;">for</span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">index = 0; index &lt; facesCount; index++) 
           </span><span style="color: blue;">var </span><span style="color: black;">a = indicesArrayindex * 3;
           </span><span style="color: blue;">var </span><span style="color: black;">b = indicesArrayindex * 3 + 1;
           </span><span style="color: blue;">var </span><span style="color: black;">c = indicesArrayindex * 3 + 2;
           mesh.Facesindex = 
               A: a,
               B: b,
               C: c
           ;
       }
        </span><span style="color: green;">// Getting the position you've set in Blender
       </span><span style="color: blue;">var </span><span style="color: black;">position = jsonObject.meshesmeshIndex.position;
       mesh.Position = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(position0, position1, position2);
       meshes.push(mesh);
   }
   </span><span style="color: blue;">return </span><span style="color: black;">meshes;
;</span>

You will probably wonder why we’re jumping by 6, 8 & 10 in the vertices array to take our 3D coordinate (X, Y, Z) of our vertices. Again, this is because the Babylon exporter adds more details that we currently need for our wireframe rendering. That’s why, we’re filtering those details using this frame approach. This logic is specific to our file format. If you want to load the export from other (like the one from three.js), you’ll just have to identity where to retrieve the vertices and faces indices in another file format.

Note: to be able to load our .babylon files, TypeScript/JavaScript developers, you need to define a new MIME type “application/babylon” targeting the extension “.babylon”. In IIS, you need to declare it inside your web.config:

<span style="color: blue;">  &lt;</span><span style="color: rgb(163, 21, 21);">system.webServer</span><span style="color: blue;">&gt;
   &lt;</span><span style="color: rgb(163, 21, 21);">staticContent</span><span style="color: blue;">&gt;
     &lt;</span><span style="color: rgb(163, 21, 21);">mimeMap </span><span style="color: red;">fileExtension</span><span style="color: blue;">=</span><span style="color: black;">"</span><span style="color: blue;">.babylon</span><span style="color: black;">" </span><span style="color: red;">mimeType</span><span style="color: blue;">=</span><span style="color: black;">"</span><span style="color: blue;">application/babylon</span><span style="color: black;">" </span><span style="color: blue;">/&gt;
   &lt;/</span><span style="color: rgb(163, 21, 21);">staticContent</span><span style="color: blue;">&gt;
 &lt;/</span><span style="color: rgb(163, 21, 21);">system.webServer</span><span style="color: blue;">&gt;</span>

C# developers, you need to change the properties of the file you will include in the solution. Switch “Build Action” to “Content” and always copy to the output directory:

image

Otherwise, the file won’t be found.

Finally, we now need to update the equivalent of our main function to call this new LoadJSONFileAsync function instead of creating manually our cube. As we will also have potentially several meshes to animate, we need also to change the rotation values during each tick to every meshes loaded:

<span style="color: blue;">private </span><span style="color: rgb(43, 145, 175);">Device </span><span style="color: black;">device;
</span><span style="color: rgb(43, 145, 175);">Mesh</span><span style="color: black;">[] meshes;
</span><span style="color: rgb(43, 145, 175);">Camera </span><span style="color: black;">mera = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Camera</span><span style="color: black;">();
</span><span style="color: blue;">private async void </span><span style="color: black;">Page_Loaded(</span><span style="color: blue;">object </span><span style="color: black;">sender, </span><span style="color: rgb(43, 145, 175);">RoutedEventArgs </span><span style="color: black;">e)
   </span><span style="color: green;">// Choose the back buffer resolution here
   </span><span style="color: rgb(43, 145, 175);">WriteableBitmap </span><span style="color: black;">bmp = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">WriteableBitmap</span><span style="color: black;">(640, 480);
    </span><span style="color: green;">// Our Image XAML control
   </span><span style="color: black;">frontBuffer.Source = bmp;
           
   device = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Device</span><span style="color: black;">(bmp);
   meshes = </span><span style="color: blue;">await </span><span style="color: black;">device.LoadJSONFileAsync(</span><span style="color: rgb(163, 21, 21);">"monkey.babylon"</span><span style="color: black;">);
   mera.Position = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Vector3</span><span style="color: black;">(0, 0, 10.0f);
   mera.Target = </span><span style="color: rgb(43, 145, 175);">Vector3</span><span style="color: black;">.Zero;
    </span><span style="color: green;">// Registering to the XAML rendering loop
   </span><span style="color: rgb(43, 145, 175);">CompositionTarget</span><span style="color: black;">.Rendering += CompositionTarget_Rendering;
</span><span style="color: green;">// Rendering loop handler
</span><span style="color: blue;">void </span><span style="color: black;">CompositionTarget_Rendering(</span><span style="color: blue;">object </span><span style="color: black;">sender, </span><span style="color: blue;">object </span><span style="color: black;">e)
   device.Clear(0, 0, 0, 255);
    </span><span style="color: blue;">foreach </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">mesh </span><span style="color: blue;">in </span><span style="color: black;">meshes) 
       </span><span style="color: green;">// rotating slightly the meshes during each frame rendered
       </span><span style="color: black;">mesh.Rotation = </span><span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">Vector3</span><span style="color: black;">(mesh.Rotation.X + 0.01f, mesh.Rotation.Y + 0.01f, mesh.Rotation.Z);
   
    </span><span style="color: green;">// Doing the various matrix operations
   </span><span style="color: black;">device.Render(mera, meshes);
   </span><span style="color: green;">// Flushing the back buffer into the front buffer
   </span><span style="color: black;">device.Present();
</span>

<span style="color: green;">///&lt;reference path="SoftEngine.ts"/&gt;
</span><span style="color: blue;">var </span><span style="color: black;">canvas: HTMLCanvasElement; 
</span><span style="color: blue;">var </span><span style="color: black;">device: SoftEngine.Device; 
</span><span style="color: blue;">var </span><span style="color: black;">meshes: SoftEngine.Mesh[] = [];
</span><span style="color: blue;">var </span><span style="color: black;">mera: SoftEngine.Camera;
document.addEventListener(</span><span style="color: rgb(163, 21, 21);">"DOMContentLoaded"</span><span style="color: black;">, init, </span><span style="color: blue;">false</span><span style="color: black;">);
</span><span style="color: blue;">function </span><span style="color: black;">init() 
   canvas = &lt;HTMLCanvasElement&gt; document.getElementById(</span><span style="color: rgb(163, 21, 21);">"frontBuffer"</span><span style="color: black;">);
   mera = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Camera();
   device = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Device(canvas); 
    mera.Position = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(0, 0, 10);
   mera.Target = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(0, 0, 0);
    device.LoadJSONFileAsync(</span><span style="color: rgb(163, 21, 21);">"monkey.babylon"</span><span style="color: black;">, loadJSONCompleted)
</span><span style="color: blue;">function </span><span style="color: black;">loadJSONCompleted(meshesLoaded: SoftEngine.Mesh[]) 
   meshes = meshesLoaded;
   </span><span style="color: green;">// Calling the HTML5 rendering loop
   </span><span style="color: black;">requestAnimationFrame(drawingLoop);
</span><span style="color: green;">// Rendering loop handler
</span><span style="color: blue;">function </span><span style="color: black;">drawingLoop() 
   device.clear();
    </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">i = 0; i &lt; meshes.length; i++) 
       </span><span style="color: green;">// rotating slightly the mesh during each frame rendered
       </span><span style="color: black;">meshesi.Rotation.x += 0.01;
       meshesi.Rotation.y += 0.01;
   
    </span><span style="color: green;">// Doing the various matrix operations
   </span><span style="color: black;">device.render(mera, meshes);
   </span><span style="color: green;">// Flushing the back buffer into the front buffer
   </span><span style="color: black;">device.present();
    </span><span style="color: green;">// Calling the HTML5 rendering loop recursively
   </span><span style="color: black;">requestAnimationFrame(drawingLoop);
</span>

<span style="color: blue;">var </span><span style="color: black;">canvas;
</span><span style="color: blue;">var </span><span style="color: black;">device;
</span><span style="color: blue;">var </span><span style="color: black;">meshes = [];
</span><span style="color: blue;">var </span><span style="color: black;">mera;
document.addEventListener(</span><span style="color: rgb(163, 21, 21);">"DOMContentLoaded"</span><span style="color: black;">, init, </span><span style="color: blue;">false</span><span style="color: black;">);
</span><span style="color: blue;">function </span><span style="color: black;">init() 
   canvas = document.getElementById(</span><span style="color: rgb(163, 21, 21);">"frontBuffer"</span><span style="color: black;">);
   mera = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Camera();
   device = </span><span style="color: blue;">new </span><span style="color: black;">SoftEngine.Device(canvas);
   mera.Position = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(0, 0, 10);
   mera.Target = </span><span style="color: blue;">new </span><span style="color: black;">BABYLON.Vector3(0, 0, 0);
   device.LoadJSONFileAsync(</span><span style="color: rgb(163, 21, 21);">"monkey.babylon"</span><span style="color: black;">, loadJSONCompleted);
</span><span style="color: blue;">function </span><span style="color: black;">loadJSONCompleted(meshesLoaded) 
   meshes = meshesLoaded;
   </span><span style="color: green;">// Calling the HTML5 rendering loop
   </span><span style="color: black;">requestAnimationFrame(drawingLoop);
</span><span style="color: green;">// Rendering loop handler
</span><span style="color: blue;">function </span><span style="color: black;">drawingLoop() 
   device.clear();
    </span><span style="color: blue;">for </span><span style="color: black;">(</span><span style="color: blue;">var </span><span style="color: black;">i = 0; i &lt; meshes.length; i++) 
       </span><span style="color: green;">// rotating slightly the mesh during each frame rendered
       </span><span style="color: black;">meshesi.Rotation.x += 0.01;
       meshesi.Rotation.y += 0.01;
   
    </span><span style="color: green;">// Doing the various matrix operations
   </span><span style="color: black;">device.render(mera, meshes);
   </span><span style="color: green;">// Flushing the back buffer into the front buffer
   </span><span style="color: black;">device.present();
    </span><span style="color: green;">// Calling the HTML5 rendering loop recursively
   </span><span style="color: black;">requestAnimationFrame(drawingLoop);
</span>

You should now have a 3D engine which is capable of loading meshes exported by Blender and animate them in a wireframe rendering mode! I don’t know about you, but I was very excited to reach this stage. :)

Again, you can download the solutions containing the source code:

C# : SoftEngineCSharpPart3.zip

- TypeScript : SoftEngineTSPart3.zip

- JavaScript : SoftEngineJSPart3.zip or simply right-click –> view source on the first embedded iframe

So, what’s next now? Well, we need to fill the triangles. This is called rasterization. We will also handle what we name a Z-Buffer to have a proper rendering. In the next tutorial, you’ll then learn how to obtain something like that:

image

We will fill the triangles with a random color. See you in the fourth tutorial.

Originally published: http://blogs.msdn.com/b/davrous/archive/2013/06/17/tutorial-part-3-learning-how-to-write-a-3d-soft-engine-in-c-ts-or-js-loading-meshes-exported-from-blender.aspx. Reprinted here with permission of the author.

The post Write a 3D Soft Engine from Scratch: Part 3 appeared first on .

Learn CSS | HTML5 | JavaScript | WordPress | Tutorials-Web Development | Reference | Books and More

No Comments

Comments for Write a 3D Soft Engine from Scratch: Part 3 are now closed.