In the previous lessons, I mainly focused on teaching you how to create 3D objects by programming with OpenGL. With the advances in computer graphics and 3D scanning technologies, there are lots of already built 3D models available online. Instead of creating the 3D objects from scratch, it will be much easier to use existing 3D models to create a virtual environments. For example, it will be much easier to use an off-the-shelf computer graphics tool to draw and create a 3D object, and then load the objects into your Android app, rather than programming vertex by vertex in the Android program. In this lesson, I'm going to show you how to read and load 3D objects from files in common standard file formats. There's so much resources online. Here are some links to websites which provide free 3D models. You can download the model, 3D from the website, and you can print it out using a 3D printer, or visualize it using an off-the-shelf 3D viewing software. For example, the BU wheel at the bottom left was downloaded from the NASA website, and is a simplified model of the view of the Mars Rover Curiosity. The red complicated structure at the bottom right is a model of the CS-F1 out puting downloaded from the NIH website. One of the most commonly used 3D file formats is the stereolithography, STL file format. It is used and supported by most 3D printers. If you want to print your 3D design, you may have to export your design to the STL file format for printing. Is similar to a PDF file format for printing purposes. The STL file uses a standard triangle language, which means that a virtual 3D object is constructed by triangles or facets. Each object is defined with a vertices of the facets, the normal vectors of the facets, and the attribute. The attribute could be used to define the color of the object, but it's optional. STL file could be of either ASCII text file format or binary file format. In an ASCII STL file, you will see that the object is defined as a solid. In this example are defined an object or solid core triangle. It has a normal vector of 0 0 -1. So I first set the normal vector of the facets or triangle, then define the vertices of the triangle. As you can see, the vertexes are defined within the outer leaf section of the file. Each object or solid in the STL file could have multiple number of facets. For instance, for cube, we'll have 12 facets. This is because a cube has six faces and we need to define two triangles per face. To show you how these facets are defined, I've included the source code I used to create cube in the previous lesson on the right. In the previous lesson, I used an index buffer to define the triangles of each faces. The same method can be used to define the triangle or facets in the STL file. For example, the front face of the cube uses the vertex 0, 1, 2 to form the first triangle, and the first three vertices of the cube are used to define the first facets of the solid cube. The second triangle or the front face of the cube uses the vertex 0, 2, and 3, and the corresponding vertexes are used to define the second facets of the solid cube. Please note that the normal vectors of each facets has to be defined it, and the first two facets has the same normal vectors of 0 0 1. Let's put together a function to parse STL file. I'll start by defining the float list for vertexes and normal vectors, and the image list for the indexes. Then for each line or text in the STL file, I'll first trim the spaces and then split the text into an array of strings. If the line starts with the term facets I then check to see if it has to term normal. If so, I extract the X, Y, and Z value of a normal vectors using the parse float function, and then start processing the next line of texts. As before, I first trimmed a text and then split it into an array of strings. First check to see if the line start with the term outer. If so, I start processing the next three lines of text to extract the vertices of the facets. As you can see here, after parsing the string into floating point numbers for each of the vertex coordinates, I then calculate a mean, max, and min of the vertexes. This measurements are used to find the centroid and dimension of the object, and which will be used to translate and scale the object to fit their varying frustum on the screen. I then call the function FindPrev at vertex function to find the index for each vertex. So after obtaining all the vertexes, I then calculate the centroid of the object and then find the maximum extent of the object. I call the function float list to float array to convert a float list to then float away, and then translate and resize the object by subtracting each vertex with a centroid and scale it with the maximum extent. Therefore, the object will be moved to the origin with size minus 1-1. Please note that, I then call the float lists to float away, an integer list to int array to convert a float list and integer list to float array and integer array. The functions, FindPrev at vertex is defined to find the index of the vertex. Remember that the same vertex of a 3D cube is used multiple times to form the triangle facets of the cube, and vertex indices are used to remove redundant vertexes and simplify the joining of the 3D object. Therefore in this function, I first search for all the previously stored vertexes to see if the newly extracted vertex has been previously define it. If so, I would then return the index of that specific vertex. If it is not previously define it, I then add a new vertex into the vertexes list and also add the corresponding normal vector to normal list. The new index is then returned. Please note that each vertex has three values. Hence, the new vertex will be the total number of vertexes minus 3 then divide by 3. Then fold to floatArray and integerList to intArray. Functions are simply converting the list of float numbers and this integer numbers into a float array and integer array. They simply get each value in the list and store the value into the array variable. Apart from the text file format, STL could be of binary file format. Using the binary file format, the object's normal and vertexes data are stored in binary form. It significantly reduces the size of the file and also helps the float parsing program to be more efficient. In STL binding file format, the first 80 bytes are defined as header information which we can ignore. The next four bytes are used to define the number of triangles of vertices of the object. As a variable is a 32-bit integer in these four bytes sorted data. Afterwards, it follows the similar format as per the ASCII text file format, where each vertices or triangle of the object is defined one-by-one. The first term is the normal vector, then the three vertexes on the triangle followed by the attribute. Please note that the normal vector and a vertexes are of three dimensions, and each dimension is defined as a 32-bit floating point value. Hence, the normal vector takes up 12 bytes of data. On the other hand, the attribute value is only a 16-bit integer. Let's look into how to parse an STL binary file. Like before, I first define a vertexes as normal and indexList, and that starts from the byte 84 and find normal vertexes an attribute of each facets. The reason why I know the first 84 byte is that the first 80 bytes are the header information, and the following four bytes is for the total number of facets. As you can see, for each of the normal vector or vertex I used to function 42-bits to read four bytes of data from the binary array, and convert the data into 32-bit integer. Then the result would be passed to the function into 32-bits to a float to convert the 32-bit integer to a floating point number. After each 32-bits of four bytes of data it's read, the variable in N is then increments by four to point to the next variable. I also use the function read 16-bits to read the attribute variable from the binary array. After reading a normal vectors, vertexes and the attribute values, I check if the normal vector has define it. If not, I call the findNormals of triangle function to find a normal vector of the facets. I'll explained what the findNormal of triangle function does later. Similar to parsing the ASCII STL file, I calculate the centroid and the extent of the object by finding the maximum mean value of each dimension of each vertex. Then I call findPrev at vertex function to find the indexes of the vertex. Same as parsing the ASCII STL file, I then find the centroid and the extent of the object. The object is then translated to the origin and scale it to be within a 2 by 2 by 2 cube. The resulting vertices, normal vectors and indices are stored in the vertex buffer, normal buffer, and an index buffer. We have the vertex buffer, number buffer, index buffer define it. We can use the OpenGL functions discussed in the previous lessons to draw the 3D object. In the case of the STL file with no normal vectors defined, we have to find normal vectors or facets in order to display the object accurately with the shading and lighting effects. As I explained it in a previous lesson, the normal vector of a surface can be calculated by the cross product of two vectors of the surface. The unit normal vector can then be find by normalizing the normal vector with it's magnitude. So to calculate the unit normal vectors of a facet or triangle, I first define a function called GetVectorMagnitude3D, to calculate the magnitude of a 3D vector for normalizing the vector. Then I define a function called GetCrossProduct3D, which calculates the cross product of two vectors. The function normal of two vectors I designed it to calculate the unit normal vector by coupling that GetCrossProduct3D function to calculate the normal vector and normalize the vector with its magnitude which is calculated by the GetVectorMagnitude3D function. The function find normal of triangle is then defined to take the three vertices of a triangle as an input, and then return the unit normal vector of the triangle. The function basically finds the two vectors of the triangle and call the function FindNormalof2Vectors to calculate and return a unit normal vector. That's mentioned in the parsing STL binary file function, I use the function Int32bitsToFloat that convert a 32-bit integer to a floating point value. They read 32-bits and they 16-bits functions to read the 32-bit and 16-bit integer from a byte array respectively. The 32-bit floating point number is defined with the first bit as a sign, the next eight bits as the exponent, and the last three bits as a significand. The floating point value is then calculated by the sign times their significand times the two to the power of the exponent minus 150. In this example here, we can see that the 32-bits number is converted to 0.0390625. This is how the Int32bitsToFloat function works. For the 32-bits function, it basically converts default bytes of the binary array starting from the position index into a 32-bit integer. Same approach is used for the 16 bits function. In order to open the STL file in each other device, I need to modify the main activity Java class to enable the program to browse and open an STL file. So I first define a constant with 3D file request_code egual to 14, which defines the code for requesting the 3D file. Then the query function call VirtualObjectFileSearch to allow the user to the browse and select STL file. So it creates an intent and set the type to application and start the activity with 3D file request code. Then in the onActivityResult function, I edit the handle for the request. If the file would see that is a STL file and it's ending with the text STL, I would then call the getSTLFile function to read the STL file. In the onCreate function of the main activity, I added another line to code the VirtueObjectFileSearch function so that the app will first ask the user to select an STL file when it starts. Then the getSTLFile function, I basically opened the file and read the content of the file into a byte away fileData. I then check if the file is a binary or asking STL file by checking the byte 84 if it is an Alpha numeric value or not. If is an Alpha numeric value, it should then be an ASCII file. Otherwise, it should be a binary file. The reason why byte 84 is that the by 84 is the first byte of a facet in the binary STL file. I then parse fileData and string to the parseSTL function to process the file content. I first create a new Java class called STLObject, and then copy the vertex shader code onto this STLObject. To create the lighting effect, I copy and paste the source code for creating the effect on the vertex shader. Similar in the fragment shader, I use the same functions to create their lighting effect on the fragments. Then I create a variables for the buffers and also handles to the attribute or uniform variables. Then I'll add rays for the lighting effects. Then I add a new function called findPrev_addVertex. It basically search for the lists of vertexs to see if the new vertex is already in the list. If it is, it would return the index of the vertex, otherwise, it would add a new vertex to list and return the new index. Then I add the neural function called FloatList2floatarray, and IntegerList2intarray. It basically turn the floating point lists or integer lists into float array or integer array. Then I create a new function ParseSTLfilestring which takes a string as a parameter and then I create a list of variables for the vertices, normals, and indexes. Then I split the string into another string array called lines. I use a while loop to process the string line by line, and then I trimmed line to remove models tallying leading space. Then I check if the line starts with the word facet. Then I check if the next word is normal. If so, I extract the normal vectors from the string using the parseFloat function. I go to the next line, trim this string, and then check if it start with the word outer. Then I check the next three lines, trimmed the lines and check if they start with the word vertex. If so, I extract the vertices by using the path float functions. Then I calculate the mean, minimum, and maximum values of the vertices for finding the centroid of the object. Then I call it the find previous vertices function to check if the vertex has been fine, and then use it to add to the index list. After parsing all the lines, I then calculate the mean which is the centroid of the object and also the range which is size of the object. I then use the mean and the range to translate the object to the centroid by subtracting each vertices with the mean, and also scale the objects such that is within a two by two by two cube. By dividing the vertices to the maximum range, which is the size of the object. Then I use the float lists to float array, and they check the list into array to convert the list, the no vector list and index list to the normal buffer index buffer. Then, I add a new function called read32bits, which reads four bytes from the byte array to turn it into a 32 bits integer. Then another function called read16bits which do exactly the same but change only two bytes into an integer. Another function called Int32bitToFloat which turn our 32 bits integer into a floating point number. We a add the function GetVectorMagnitude3D which calculate the magnitude of a 3D vector. Another function called GetCrossProduct3D to calculate the cross product of two 3D vectors. Next, I add the function called FindNormalof2Vectors, it basically cover the unit normal vector from two vectors of a surface. Then, I add the function called FindNormalofTriangle, basically, taking the vertices of the triangle, and then calculate the unit normal vector. Then I add the new function ParseSTLBinary to read the STL binary file, it takes a byte array as a parameter. Like the ParseSTLFilestring, I first create a number of these variables for the vertices, normals, and indices, then I add a few floating point array for storing the vertices and normal vectors, then I start process the byte array. First, I skip the first 84 bytes which is the header of the file. Then the next three 32 bits variables on the normal vectors, and I use the function read32bits, and Integer32BitsToFloat, to convert the byte arrays into the 32-bit floating-point numbers. Next, will be the three vertices of the triangle. Then there's the 16 bits attribute variables. If the normal vector is not defined for the vertices, we can then calculate the normal vector by using the function FindNormalofTriangle. Then I update the meanx, and also the minimum and maximum values of the vertices. Then I use the function findPrev_addVertice to find the indices to the new three vertices. After reading all the vertices I've done, we use the same functions to calculate the mean or the centroid and range which is the size of the object to translate and scale the object like what we have done in the ParseSTLFilestring function. In the constructor of the STL object, I first check the string to see if it is have the value of binary if so, I'll use the ParseSTLBinary to read the vertices and normal vectors from the byte array. Otherwise, I'll use the ParseSTLFilestring function to process the string read from the file. Then I create the file buffers for the vertices, for indices, and the normal vectors. Then I set the initial values for the light effect and also start the shader programs, then set the handle to point to the attribute variables for the vertex, normal vectors, and the light thing effect. The draw function is exactly the same as before. Basically, just to pass all the vertices and lighting effects and normal vectors to the attribute or uniform variables in the shaders. Note that I set the value for the mColorHandle to a value 1, 0, 0, 1. Basically, it means that I want to draw the object in red. Then my renderer, I first comment out the object msphere and the mleftview and the mrightview, then add to a new variable STLObject, stl_file_string, and stl_file_data. Now, onSurfaceChanged function, I add the line of code to check if the variable stl_file_data, if it's equal to no, if not, then I create a new STL object by passing the variable stl_file_string and stl_file_data. Then, I add new function called ParseSTL string with parameters, file_str, and fileData, and use those to set the stl_file_string and stl_file_data variables. Now, in the onDrawFrame functions, I first comment out other functions for joining the stereo view. Then I add a function to check if the STL object is create a not is created, then I code a glViewport to set the viewport size, and initialize the mMVPMatrix, the mMVMatrix, mModelMatrix, and also set the mViewMatrix. Then I set the mProjectionMatrix and also the mModelMatrix, and those who I then use to calculate the mMVPMatrix. Then I call the stlobj.draw functions to draw the object on the screen. Then in the MainActivity object, I first create a new constant called a READ_3D_FILE_REQUEST code, and then add a function called VirtualObjectFileSearch which allow the user to search for STL file. Then in the onActivityResult function, I check the request code to see if it's equal to READ_3D_FILE_REQUEST, and then I check the file name to see if it's contain tag to the string.stl if so, then I call the function getSTLFile to read the file. Then in the onCreate functions, I then call the VirtualObjectFileSearch function to search for the STL file. Then, I create a function called getSTLFile, which takes the Uri or the file path as parameters, and then read the content of the file into a byte array. Then I check the 84-byte of the fileData to see if it's alpha numeric value, if is not, then it is a binary STL file. I set the variable STL equal to the text binary. If it's alpha numeric value, then I create a new string base on the fileData byte array. Then I call the parseSTL functions of the MyView object to pass the data read from the file. Then in MyView object, I add the function parseSTL, basically, takes two parameters, file_str and fileData, and pass it to the function, parseSTL in the My Renderer. When you run the program, you will see the list so far show and it'll allow you to select an STL file. But this time, I choose the binary STL file. Once a file is low, the 3D object is shown on the screen. Then next, I would like to open ASCII or text format of STL file, and I choose it to open the cube STL file. Once is low, you can see the cube is shown on the screen. As example, I download the STL file of the Mars rovers wheel, and I can use the app to open the STL file and show the wheel in this screen as a 3D object.