8. Geometry and Texture Creation
In this section, we will learn how to create new geometry and textures from scratch and apply textures to a mesh.
- Geometry Creation
- Texture Creation
- Applying textures to a mesh
Starting the Web Viewer with an Empty Model
The meshes in landinggear.scs, which is the model we used in Section 6. Interacting with the Model Tree, do not have texture coordinates so we will create our own geometry to apply textures to. Remove the landinggear model from the viewer parameters. It should now look like this:
Creating the Cube Mesh Geometry
Now we will create the geometry of a cube to apply textures to. Add the createCubeMeshData function below to the material.js file we created in Section 7. Color and Transparency:
The function createCubeMeshData takes a position and size, and creates a MeshData object. The MeshData object stores the positions of the faces that make up the cube, the normal for the faces, and the uv texture mapping data.
The cube is made up of 6 sides, each side consisting of 2 triangles to make a square. p0 – p7 are the 8 vertices of the cube that we use to make each triangle. For readability, we first create an array called triangles which list each point, before adding the x, y, z values into an array called positions.
The uv values correspond to the vertices that make up each triangle. For each vertex, there is a corresponding 2d coordinate. Ex (p0 -> (0,0), p2 -> (1,1), p1 -> (1,0), etc).
The normals correspond to the direction of the face of the cube that the vertices are on. For example, the first face of the cube is in the Z plane, and it's normal is (0,0,-1). Each vertex will have a corresponding normal.
The face winding depends on the order of the vertices that make up the triangles. In this example, the triangles are created using a counter-clockwise face winding.
Create a Cube
Now, let's add the following code to material.js:
The function createCube will create a new MeshInstanceData and render it to the scene. Let's breakdown what this function is doing step-by-step.
First, we will call createCubeMeshData which returns a MeshData object. Then, we will use the MeshData object to create a mesh, which will return a meshId. Once we have a meshId, we will use it to create a MeshInstanceData object.
When we create our MeshInstanceData object, we will specify a mesh id, identity matrix, instance name, and face color.
The createMeshInstance function takes a MeshInstanceData object as a parameter. We will call the function with our MeshInstanceData object to create an instance of the cube in the scene.
The MeshInstanceData constructor takes the following optional parameters:
- meshId: meshId of the mesh to instantiate
- matrix: a matrix that will be applied to this instance
- instanceName: a name that will be visible when querying the model hierarchy
- faceColor: the color for faces of this instance
- lineColor: the color for lines of this instance
- pointColor: the color for points of this instance
- creationFlags: additional options that can be used to alter the behavior of this instance
Creating Multiple Cubes
Now that we have functions to create the geometry, we need to call them. In the HTML page, after we start the HOOPS Web Viewer, we add the following code to the modelStructureReady callback. When the callback function is triggered, it will be safe to create the geometry. The following code will create four cubes in red, green, blue, and yellow colors. It will then set the view orientation to ISO, which will change the camera to see all the geometry we created.
Running the Application
Load the application. You should now see 4 cubes.
Before we apply any textures, we first need to load the images. We will add the loadImage() function to the same JS file to load an image.
The loadImage function makes an XMLHttpRequest to fetch the data for an image in an array buffer. The contents of the array buffer are then used to create a Uint8Array as part of an ImageOptions object, which also contains the image format. The ImageOptions object is then used as a parameter for the createImage() function, which returns an ImageId.
The returned ImageId can then be used to apply a texture to a part.
Now we will add the setNodeTexture() function to the same JS file and use it to set the texture on a node.
The setNodeTexture() function takes a NodeId and an ImageId and calls the setNodesTexture() function which takes an array of type NodeId and a TextureOptions object. There are two options we will set, imageId and modifiers.
We will need some images to load as textures. Create an images folder at the top level of your application's directory. Copy the images from the package at web_viewer\examples\images\textures to the new images folder. There should be three textures: bricks.png, nebula.png, and textured.png.
Creating the UI
Add the following HTML inside at the end of the buttons div after the viewer. We will use these images to set a texture on a selected node when the image is clicked.
Connect UI to Texture Logic
Now, we will add code to load the images and apply a texture when one of the images is clicked. Add the following function to the top of the HTML script above window.onload. We will then call this function from the modelStructureReady callback:
Initialize the Textures
At the end of the modelStructureReady callback, call the initializeTextures function. This will load the images and prepare the HTML images for activating the textures when clicked.
Loading the Application
Load the application. Select a cube, then an image, and the selected texture will be applied.