242-515 agd: 10. meshes11 objective o to describe how shape meshes and models can be used, with...
TRANSCRIPT
242-515 AGD: 10. Meshes 11
• Objectiveo to describe how shape meshes and models
can be used, with emphasis on the OBJ and MTL 3D formats
Animation and Games
Development242-515, Semester 1, 2014-2015
10. Meshes and Models
242-515 AGD: 10. Meshes 22
1. Triangle Meshes2. Three Custom Meshes3. Phong and Gouraud Shading4. jME Built-in Shapes5. Using Models6. Loading Models into a Grid7. Using the Chase Camera8. The Wavefront (.obj) Format9. OBJ Cube Example10.Other Geometric Models
Overview
242-515 AGD: 10. Meshes 33
• Modern hardware is optimized for triangle meshes: o polygon meshes where every polygon is a triangle
• There are three basic types of information in a triangle mesh:1. Vertices2. Edges3. Faces
1. Triangle Meshes
vertex
edgeface
242-515 AGD: 10. Meshes 44
1. Vertices. Each triangle has exactly three vertices. A vertex may be shared by many triangles.
2. Edges. An edge connects two vertices. Each triangle has three edges. Usually, each edge is shared by two faces.
3. Faces. The surfaces of the triangles. Usually stored as a list of three vertices, or a list of three edges.
Triangle Mesh Information
242-515 AGD: 10. Meshes 66
• An indexed triangle mesh consists of a list of vertices, and a list of triangles indicies.
Indexed Triangle Mesh
+X
+Z
+Y
(1,0,1)
(1,0,-1)
(-1,0,1)
(-1,0,-1)
(0,2,0)
0 1
2
3
4
242-515 AGD: 10. Meshes 77
• Each triangle is represented using three indices. • The index order is specified in terms of the front
face of the triangle. • The index order follows the right-hand rule:
o the front faces the direction of the thumbo the vertices indicies are listed in the counterclockwise
order of the fingers
Index Order and Front Faces
242-515 AGD: 10. Meshes 88
• The triangle on the front of the pyramid is facing along the +z axis.
• This means that the triangle's indicies will be defined counter-clockwise as {0, 1, 4)o {1,4,0} or (4,0,1} also okay
Example
+X
+Y
0 1
4
242-515 AGD: 10. Meshes 99
jME Code Fragments// Vertex positions in space Vector3f [] verts = new Vector3f[5]; verts[0] = new Vector3f(-1, 0, 1); verts[1] = new Vector3f(1, 0, 1); verts[2] = new Vector3f(1, 0, -1); verts[3] = new Vector3f(-1, 0, -1); verts[4] = new Vector3f(0, 2, 0);
// the order in which mesh should be constructed from triangles int [] indexes = {0,3,1, 1,3,2, 2,3,4, 4,3,0, 0,1,4, 4,1,2};
PyramidMesh.java
242-515 AGD: 10. Meshes 1010
// mesh creationMesh mesh = new Mesh();
// Setting mesh buffers // the 2nd parameter is the number of components in each value mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(verts)); mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes)); mesh.updateBound();
242-515 AGD: 10. Meshes 1111
// mesh is displayed as a white wireframe Geometry geom = new Geometry("pyramid", mesh); Material matWF = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); matWF.setColor("Color", ColorRGBA.White); geom.setMaterial(matWF); rootNode.attachChild(geom);
RenderState rs = matWF.getAdditionalRenderState(); rs.setWireframe(true); // activate wireframe view rs.setFaceCullMode(RenderState.FaceCullMode.Off);
cam.setLocation(new Vector3f(0.5f, 3.5f, 4)); cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y); // look at the origin with +y-axis being up
242-515 AGD: 10. Meshes 1313
2. Three Custom Meshes
solid blue
coloredverticies
lit with adirectionallight
CustomMeshes.java
242-515 AGD: 10. Meshes 1515
Mesh mesh = new Mesh();
// Vertex positions in space Vector3f[] verts = new Vector3f[4]; verts[0] = new Vector3f(0,0,0); // can use any ordering verts[1] = new Vector3f(2,0,0); verts[2] = new Vector3f(0,2,0); verts[3] = new Vector3f(2,2,0);
// the order in which mesh should be constructed from triangles int[] indexes = {2,0,1, 1,3,2};
// setting the buffersmesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(verts)); mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes)); mesh.updateBound();
Create the Mesh
242-515 AGD: 10. Meshes 1616
Geometry geom = new Geometry("solid", mesh); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Blue); geom.setMaterial(mat); geom.setLocalTranslation(-2, -3, 0); rootNode.attachChild(geom);
A Solid Blue Mesh
242-515 AGD: 10. Meshes 1717
Mesh colMesh = mesh.clone();
Geometry colGeom = new Geometry ("colored", colMesh); Material colMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); colMat.setBoolean("VertexColor", true);
// each vertex requires 4 color values (R G B A) // in the same ordering as the vertices array, verts[] float[] colorArray = {
1f, 0f, 0f, 1f, // red
0.8f, 0.2f, 1f, 1f, // purple
0.8f, 0.2f, 1f, 1f, 0f, 0f, 1f, 1f }; // blue
// Set the color buffer colMesh.setBuffer(Type.Color, 4, colorArray); colGeom.setMaterial(colMat);
:
Coloured Verticies
0 1
2 3
242-515 AGD: 10. Meshes 1818
// Set the color buffer colMesh.setBuffer(Type.Color, 4, colorArray); colGeom.setMaterial(colMat);
// move mesh so it doesn't overlap with the first one colGeom.setLocalTranslation(2, -3, 0); rootNode.attachChild(colGeom);
242-515 AGD: 10. Meshes 1919
Mesh litMesh = mesh.clone(); Geometry litGeom = new Geometry ("lit", litMesh);
Material litMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); litMat.setBoolean("UseMaterialColors", true); litMat.setColor("Diffuse", ColorRGBA.Orange); // reflection litMat.setColor("Specular", ColorRGBA.White); litMat.setFloat("Shininess", 12); litGeom.setMaterial(litMat);
:
Lit Mesh, Reflecting Orange
242-515 AGD: 10. Meshes 2020
// verticies require normals for Phong lighting float[] normals = {0,0,1, 0,0,1, 0,0,1, 0,0,1}; // all of them point along +z axis litMesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); litGeom.setLocalTranslation(-2, 1, 0); rootNode.attachChild(litGeom);
// add a white light to make the lit object visible DirectionalLight dl = new DirectionalLight(); dl.setDirection(new Vector3f(1, 0, -2).normalizeLocal()); dl.setColor(ColorRGBA.White);
rootNode.addLight(dl);
242-515 AGD: 10. Meshes 2121
Vertex Normals Diagram
+X
+Z
+Y
0 1
2 3
(0,0,0)
(0,2,0)
(2,0,0)
(2,2,0)
directionallight
242-515 AGD: 10. Meshes 2222
• Phong shading interpolates normals between all the shape verticies. It uses these normals to calculate the shading for every pixelo many normals are generatedo this is the default shading technique in jME
• Gouraud shading calculates the shading for each vertex, and interpolates those shading values across every pixelo this requires less computation since only vertex normals
are needed
3.3. Phong and Gouraud Shading
242-515 AGD: 10. Meshes 2323
• The black arrows are the vertex normals.• The blue arrows are the interpolated surface
normals.• These extra normals will make the hexagon look
smoother on screen.
Phong Shading in Action
242-515 AGD: 10. Meshes 2525
4. jME Built-in Shapes
Dome (the cone and pyramid are special cases)
Torus PQTorus Surface(a NURB)
242-515 AGD: 10. Meshes 2626
// Sphere mesh = new Sphere(16, 16, 1.0f); // Cylinder mesh = new Cylinder(20, 50, 1, 2, true); :
Geometry geom = new Geometry("Shape", mesh); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Blue); geom.setMaterial(mat);rootNode.attachChild(geom);
RenderState rs = mat.getAdditionalRenderState(); rs.setWireframe(true); // activate wireframe view rs.setFaceCullMode(RenderState.FaceCullMode.Off);
Viewing Wireframe ShapesWireShape.java
242-515 AGD: 10. Meshes 2727
Dome, Cone, Pyramid
Dome mesh = new Dome(Vector3f.ZERO, 32, 32, 1f, false); // center, planes, radialSamples, radius, insideView
242-515 AGD: 10. Meshes 2828
Dome mesh = new Dome(Vector3f.ZERO, 2, 32, 1f, false); // cone: planes == 2 and radialSamples > 4
242-515 AGD: 10. Meshes 2929
Dome mesh = new Dome(Vector3f.ZERO, 2, 4, 1f, false); // pyramid: planes == 2 and radialSamples == 4
242-515 AGD: 10. Meshes 3030
• For more information see http://en.wikipedia.org/wiki/Torus_knot
Knotted Doughnuts (braids)
PQTorus mesh = new PQTorus(2, -3, 2, 1, 64, 64); // trefoil// (2,−3) torus knot
242-515 AGD: 10. Meshes 3131
PQTorus mesh = new PQTorus(3, -8, 2, 0.5f, 64, 64); // Flower torus, (3, -8) torus knot
242-515 AGD: 10. Meshes 3232
• jME's built-in shapes come with pre-calculated normals, which can be displayed visually with the ShowNormals.j3md material definition.o a color gradient is calculated from the model's surface
normals. o no need for lights in the scene
Shapes and Normals
242-515 AGD: 10. Meshes 3333
public void simpleInitApp() { Cylinder mesh = new Cylinder(20, 50, 1, 2, true); Geometry geom = new Geometry("Shape", mesh); Material mat = new Material(assetManager,
"Common/MatDefs/Misc/ShowNormals.j3md"); geom.setMaterial(mat); geom.rotate(FastMath.HALF_PI, 0, 0); rootNode.attachChild(geom);}
Viewing a Cylinder's Normals
242-515 AGD: 10. Meshes 3434
• There are many free 3D models available onlineo e.g. at http://archive3d.net/,
http://sketchup.google.com/3dwarehouse/o see the list of websites at
http://www.hongkiat.com/blog/ 60-excellent-free-3d-model-websites/
• There are many 3D model formats:o e.g. 3D Studio (.3ds), Blender (.blend), Wavefront (.obj)o see the list at
http://edutechwiki.unige.ch/en/3D_file_format
5. Using Models
242-515 AGD: 10. Meshes 3535
• Blender (http://www.blender.org/)o extremely powerful, free, open source 3D modellero a confusing GUI which keeps being changedo use it to import models, and export them in a different
format
• 3DViewer (http://www.xs4all.nl/~karelse/3DViewer/)o very simple, free 3D viewer which is useful for checking
models
3D Tools
242-515 AGD: 10. Meshes 3636
• jME can load models in two formats:o Wavefront .obj formato Ogre XML
• I'll use .obj since it's a simple format, and is supported by most (all) 3D modeling tools.o the format is text-based, and is easy to edit manually
(see later)
• Blender can export Ogre XML files, but it requires the installation of a plugino details at http://jmonkeyengine.org/wiki/doku.php/
jme3:advanced:3d_models
3D Formats in jME
242-515 AGD: 10. Meshes 3737
• jME can also save Spatial nodes as .j3o binary files, which can be loaded later.o details at http://jmonkeyengine.org/wiki/doku.php/
jme3:advanced:save_and_load
• .j3o files can be viewed and edited by the jME SDK
242-515 AGD: 10. Meshes 4040
// globals private static final float STEP = 0.1f; // space between grid lines private static final float WIDTH = 1.0f; // extent along +axis of grid and box
public void simpleInitApp() { addZone(1.2f,3.2f); // (Y,Z) camera position
// load the model Spatial model = assetManager.loadModel( "Models/Teapot/Teapot.obj"); rootNode.attachChild(model); } // end of simpleInitApp()
Partial CodeGridZoneLoad.java
242-515 AGD: 10. Meshes 4141
private void addZone(float y, float z) { viewPort.setBackgroundColor(ColorRGBA.DarkGray);
cam.setLocation(new Vector3f(0,y,z)); cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y); // look at the origin with +y-axis being up
// grid-based floor int numLines = (int)((WIDTH/STEP)*2 + 1); addWireShape(new Grid(numLines, numLines, STEP), ColorRGBA.White).center();
// axes arrows addArrow(Vector3f.UNIT_X, ColorRGBA.Red); addArrow(Vector3f.UNIT_Y, ColorRGBA.Green); addArrow(Vector3f.UNIT_Z, ColorRGBA.Blue);
addBox(WIDTH); // wireframe box :
242-515 AGD: 10. Meshes 4242
// white light aimed down in front-left quadrant DirectionalLight dl1 = new DirectionalLight(); dl1.setDirection(new Vector3f(-1, -1, 1).normalizeLocal()); dl1.setColor( ColorRGBA.White); rootNode.addLight(dl1);
// white light aimed down in back-right quadrant DirectionalLight dl2 = new DirectionalLight(); dl2.setDirection(new Vector3f(1, -1, -1).normalizeLocal()); dl2.setColor( ColorRGBA.White); rootNode.addLight(dl2);
} // end of addZone()
xz
y
xz
y
242-515 AGD: 10. Meshes 4343
private void addArrow(Vector3f dir, ColorRGBA color) { Arrow arrow = new Arrow(dir); arrow.setLineWidth(3); // make arrow thicker addWireShape(arrow, color); } // end of addArrow()
private void addBox(float size) // a wireframe box { Geometry g = addWireShape( new WireBox(size, size/2, size), ColorRGBA.Yellow); g.setLocalTranslation( new Vector3f(0, size/2, 0)); }
242-515 AGD: 10. Meshes 4444
private Geometry addWireShape(Mesh shape, ColorRGBA color) // add a colored wireframe version of shape to the scene { Geometry g = new Geometry("shape", shape); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.getAdditionalRenderState().setWireframe(true); mat.setColor("Color", color); g.setMaterial(mat); rootNode.attachChild(g); return g; } // end of addWireShape()
242-515 AGD: 10. Meshes 4545
• The imported shape may be too large, translated a long way from the origin, or rotated incorrectly.
• In that case, add code to simpleInitApp() like:
o model.scale(0.01f); // 100 times smaller
o model.rotate(0, (float)Math.toRadians(90), 0); // rotate 90 degrees around the y-axis
o model.setLocalTranslation( new Vector3f(-2, 0, 0)); // move 2 units left along the x-axis
Adjusting the Model
242-515 AGD: 10. Meshes 4646
• The loaded bus.obj model is too large:o Spatial model = assetManager.loadModel( "models/bus.obj");
o bus.obj is in the local models/ directory
• I worked out the size problem by using the camera keys and mouse to move about:o the WASDQZ keys to zoom in/out
and translate left/right/up/down.o the mouse to rotate left/right/up/down
Bus Example
242-515 AGD: 10. Meshes 4747
• Original viewof the bus:
• After zooming out a lot (it's a seat inside thebus):
242-515 AGD: 10. Meshes 4949
• Modified jME code:Spatial model = assetManager.loadModel("models/bus.obj");model.scale(0.005f); model.setLocalTranslation( new Vector3f(0, 0.2f, 0));
200x smallerthen movedup 0.2f units:
242-515 AGD: 10. Meshes 5050
Spatial model = assetManager.loadModel( "Models/HoverTank/Tank2.mesh.xml");model.scale(0.1f);model.rotate(0, -(float)Math.toRadians(90), 0);
HoverTank
10x smallerthen rotated-90 degreesaround they-axis:
242-515 AGD: 10. Meshes 5151
• A drawback of the default camera (a FlyByCamera object) is that rotation is around the camera's position.
• For model viewing, it is often better to rotate around the model.
• This is possible by using a ChaseCamera object insteado this kind of camera can rotate around a specified Spatialo it will also automatically follow the Spatal if it moves (i.e.
it chases the model)
7. Using the Chase Camera
242-515 AGD: 10. Meshes 5252
Rotating Around the HoverTank
ViewHoverTank.java
rotate left and up by dragging the mouse
242-515 AGD: 10. Meshes 5353
public void simpleInitApp() { Node tank = (Node) assetManager.loadModel( "Models/HoverTank/Tank2.mesh.xml"); rootNode.attachChild(tank);
// replace FlyByCamera by ChaseCamera flyCam.setEnabled(false); ChaseCamera chaseCam = new ChaseCamera(cam, tank, inputManager); // camera follows the tank and // can turn around it by dragging the mouse chaseCam.setSmoothMotion(true); chaseCam.setMaxDistance(100000); chaseCam.setMinVerticalRotation(-FastMath.PI / 2); :
Partial Code
242-515 AGD: 10. Meshes 5454
// white light from above Vector3f lightDir = new Vector3f(-1, -1, 1).normalize(); DirectionalLight dl1 = new DirectionalLight(); dl1.setColor(ColorRGBA.White); dl1.setDirection(lightDir); rootNode.addLight(dl1);
// orange light from below Vector3f lightDir2 = new Vector3f(1, 1, -1).normalize(); DirectionalLight dl2 = new DirectionalLight(); dl2.setColor(ColorRGBA.Orange); dl2.setDirection(lightDir2); rootNode.addLight(dl2);
} // end of simpleInitApp()
242-515 AGD: 10. Meshes 5555
• Most models are made up of triangles. • Each triangle is called a face.
• Each face is defined using verticeso a face may also have normals and texture coordinates
• Faces can be grouped together, and different groups can be assigned materials made from ambient, diffuse, and specular colors, and textures.
• • The material information is stored in a separate
MTL text file.
8. The Wavefront (.obj) Format
242-515 AGD: 10. Meshes 5656
• There are three types of basic OBJ statements: o for defining shapeo for groupingo for materials (most of these operations are located in
the separate .mtl file)
242-515 AGD: 10. Meshes 5757
• Vertex position:v float float float
• each "v" line has an index, starting from 1
• A normal:vn float float float
• each "vn" line has an index, starting from 1
• 2D or 3D texture coordinate:vt float float [float]
• each "vt" line has an index, startiong from 1
Defining Shape
242-515 AGD: 10. Meshes 5858
• A face (in terms of vertex indicies)f int int int
• A face (uses vertex and texture indicies)f int/int int/int int/int
• A face (uses vertex, texture and normal indicies):f int/int/int int/int/int int/int/int
242-515 AGD: 10. Meshes 5959
• g name • Faces ("f" lines) defined after a "g" line will belong
to the group called “name”.
• Named groups are a useful way of referring to a collection of faces for assigning materials or textures.
Grouping
242-515 AGD: 10. Meshes 6060
• mltlib filename• The MTL file named in the “mltlib” statement will
contain material definitions used in the rest of the OBJ file.
• usemtl name • All subsequent faces after this line will be
rendered with the named material, until the next “usemtl” line.
Material Use Statements
242-515 AGD: 10. Meshes 6161
• Ambient RGB color:Ka r g b
• Diffuse RGB:Kd r g b
• Specular color: Ks r g b
• Transparency:d alpha Tr alpha
The MTL File Format
242-515 AGD: 10. Meshes 6262
• Shininess:Ns s
• Illumination mode:illum n
• If n == 1 then no specular highlights are used• If n == 2 then use “Ks” value for the highlights• if n == 0 then all lighting is disabled
• Texture filename:map_Ka filename
242-515 AGD: 10. Meshes 6363
• A cube was exported from Blender as the OBJ file blenderCube.objo a MTL file was created automatically, called
blenderCube.mtl
9. OBJ Cube Example
in Blender
242-515 AGD: 10. Meshes 6464
# Blender v2.62 (sub 0) OBJ File: ''# www.blender.orgmtllib blenderCube.mtlo Cubev 1.000000 -1.000000 -1.000000 // 1v 1.000000 -1.000000 1.000000 // 2v -1.000000 -1.000000 1.000000 // 3v -1.000000 -1.000000 -1.000000 // 4v 1.000000 1.000000 -0.999999 // 5v 0.999999 1.000000 1.000001 // 6v -1.000000 1.000000 1.000000 // 7v -1.000000 1.000000 -1.000000 // 8
vn 0.000000 -1.000000 0.000000 // 1vn 0.000000 1.000000 0.000000 // 2vn 1.000000 0.000000 0.000000 // 3vn -0.000000 -0.000000 1.000000 // 4vn -1.000000 -0.000000 -0.000000 // 5vn 0.000000 0.000000 -1.000000 // 6g Cube_Cube_Materialusemtl Material :
The OBJ file
only 6 normals(1 per face)
blenderCube.obj
242-515 AGD: 10. Meshes 6565
s offf 1//1 2//1 3//1f 1//1 3//1 4//1f 5//2 8//2 7//2f 5//2 7//2 6//2f 1//3 5//3 6//3f 1//3 6//3 2//3f 2//4 6//4 7//4f 2//4 7//4 3//4f 3//5 7//5 8//5f 3//5 8//5 4//5f 5//6 1//6 4//6f 5//6 4//6 8//6
each face is defined using3 vertices and 1 normal;no tex coords used
12 faces (even though thecube only has 6 sides)
242-515 AGD: 10. Meshes 6666
4
OBJ Cube Diagram
+X
+Z
+Y
(1,-1,1)
(1,-1,-1)
(-1,1,1)
1
23
5
(-1,-1,1)
(1,1,-1)
67
(-1,1,-1)8
53
2
6
4
1
242-515 AGD: 10. Meshes 6767
A Cube Side Has 2 Faces
+X
+Z
+Y
(1,-1,1)
(1,1,1)(-1,1,1)
23(-1,-1,1)
67
4
f 2//4 6//4 7//4f 2//4 7//4 3//4
uses the right hand ruleto define the front face
242-515 AGD: 10. Meshes 6868
# Blender MTL File: ''# Material Count: 1newmtl MaterialNs 96.078431Ka 0.000000 0.000000 0.000000 // no ambientKd 0.640000 0.640000 0.640000 // grayKs 0.500000 0.500000 0.500000Ni 1.000000 // no refractiond 1.000000 // no transparencyillum 2 // use Ks for highlights
The MTL File blenderCube.mtl
242-515 AGD: 10. Meshes 6969
Spatial model = assetManager.loadModel("models/blenderCube.obj");model.scale(0.5f);
Loaded by jME
Cube's sidesare scaled to0.5 units
GridZoneLoad.java
242-515 AGD: 10. Meshes 7070
• It's easy to modify the MTL file:Kd 0.54 0.89 0.63 // jade (see Part 9, sec 6.1)
Modifying the Material
Cube's diffusereflection isnow jade
242-515 AGD: 10. Meshes 7171
• The OBJ format is fully explained at:o http://local.wasp.uwa.edu.au/~pbourke/dataformats/obj/
• The MTL format:o http://local.wasp.uwa.edu.au/~pbourke/dataformats/mtl/
Full OBJ and MTL Details
242-515 AGD: 10. Meshes 7272
• The use of meshes composed of verticies, faces, edges, and normals is the standard way of creating models in computer graphics.
• There are other approaches, such as:o Constructive Solid Geometry (CSG)o Metaballs
10. Other Geometric Models
242-515 AGD: 10. Meshes 7373
• CSG builds a shape using Boolean operators (union, difference, intersection) on primitive shapes (cubes, spheres, etc)o available in the Unreal engine
Constructive Solid Geometry (CSG)
union:cube + sphere
difference: cube - sphere
intersection: cube ∩ sphere