• Aucun résultat trouvé

Building a Mallet with Two Cylinders

Dans le document OpenGL ES 2 for Android (Page 163-168)

}

This is just a holder class so that we can return both the vertex data and the draw list in a single object. Now we just need to define build():

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/ObjectBuilder.java private GeneratedData build() {

return new GeneratedData(vertexData, drawList);

}

That’s everything we need for createPuck() to work. Let’s take a quick moment to review the flow:

• First we call the static method createPuck() from outside the class. This method creates a new ObjectBuilder with the right array size to hold all of the data for the puck. It also creates a display list so that we can draw the puck later on.

• Inside createPuck(), we call appendCircle() and appendOpenCylinder() to generate the top and sides of the puck. Each method adds its data to vertexData and a draw command to drawList.

• Finally, we call build() to return the generated data.

Building a Mallet with Two Cylinders

We can now use what we’ve learned to build a mallet. A mallet can be built out of two cylinders, so building a mallet is almost like building two pucks of different sizes. We’ll define the mallet a certain way, as seen in Figure 44, Mallet Definition, on page 152.

The handle height will be about 75 percent of the overall height, and the base height will be 25 percent of the overall height. We can also say that the han-dle’s width is about one-third the overall width. With these definitions in place, we’ll be able to calculate where to place the two cylinders that make up the mallet.

When writing out these definitions, it sometimes helps to take a sheet of paper and draw the object out and then plot where everything is relative to the Adding an Object Builder

151

Figure 44—Mallet Definition

center and sides of the objects. To create our mallet, we’ll need to figure out the y position for each cylinder top as well as the center position for each cylinder.

Let’s add a new method called createMallet() just after createPuck(). We’ll start off with the following code:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/ObjectBuilder.java static GeneratedData createMallet(

Point center, float radius, float height, int numPoints) { int size = sizeOfCircleInVertices(numPoints) * 2

+ sizeOfOpenCylinderInVertices(numPoints) * 2;

ObjectBuilder builder = new ObjectBuilder(size);

// First, generate the mallet base.

float baseHeight = height * 0.25f;

Circle baseCircle = new Circle(

center.translateY(-baseHeight), radius);

Cylinder baseCylinder = new Cylinder(

baseCircle.center.translateY(-baseHeight / 2f), radius, baseHeight);

builder.appendCircle(baseCircle, numPoints);

builder.appendOpenCylinder(baseCylinder, numPoints);

We create a new ObjectBuilder of the proper size, and then we generate the base of the mallet. This code is quite similar to what we did in createPuck().

Chapter 8. Building Simple Objects

152

When you optimize imports, Eclipse might give you the choice to import com.airhockey.android.util.Geometry.Point, which is our Point class, or android.Graphics.Point, which is a different Point class in the Android SDK. You’ll want to make sure to select ourPoint class.

Let’s add the following code to generate the handle:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/ObjectBuilder.java float handleHeight = height * 0.75f;

float handleRadius = radius / 3f;

Circle handleCircle = new Circle(

center.translateY(height * 0.5f), handleRadius);

Cylinder handleCylinder = new Cylinder(

handleCircle.center.translateY(-handleHeight / 2f), handleRadius, handleHeight);

builder.appendCircle(handleCircle, numPoints);

builder.appendOpenCylinder(handleCylinder, numPoints);

We follow the same steps as before but with different coordinates and different sizes. Let’s add a call to build() to complete the method:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/ObjectBuilder.java return builder.build();

}

That’s it for our ObjectBuilder class! We can now generate pucks and mallets;

and when we want to draw them, all we need to do is bind the vertex data to OpenGL and call object.draw().

8.4 Updating Our Objects

Now that we have an object builder, we’ll need to update our Mallet class since we’re no longer drawing it as a point. We’ll also need to add a new Puck class.

Let’s start with the puck. Create a new class in the same package called Puck, and add the following code to the class:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/Puck.java private static final int POSITION_COMPONENT_COUNT = 3;

public final float radius, height;

private final VertexArray vertexArray;

private final List<DrawCommand> drawList;

public Puck(float radius, float height, int numPointsAroundPuck) { GeneratedData generatedData = ObjectBuilder.createPuck(new Cylinder(

new Point(0f, 0f, 0f), radius, height), numPointsAroundPuck);

Updating Our Objects

153

this.radius = radius;

this.height = height;

vertexArray = new VertexArray(generatedData.vertexData);

drawList = generatedData.drawList;

}

When a new Puck is created, it will generate the object data, store the vertices in a native buffer with vertexArray, and store the draw list in drawList.

Let’s complete the class:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/Puck.java public void bindData(ColorShaderProgram colorProgram) {

vertexArray.setVertexAttribPointer(0,

colorProgram.getPositionAttributeLocation(), POSITION_COMPONENT_COUNT, 0);

}

public void draw() {

for (DrawCommand drawCommand : drawList) { drawCommand.draw();

} }

The first method, bindData() follows the same pattern that we also follow with Table and Mallet: it binds the vertex data to the attributes defined by the shader program. The second method, draw(), just goes through the display list created by ObjectBuilder.createPuck().

We’ll also need to update the Mallet class. Replace everything inside the class with the following code:

AirHockeyWithImprovedMallets/src/com/airhockey/android/objects/Mallet.java private static final int POSITION_COMPONENT_COUNT = 3;

public final float radius;

public final float height;

private final VertexArray vertexArray;

private final List<DrawCommand> drawList;

public Mallet(float radius, float height, int numPointsAroundMallet) { GeneratedData generatedData = ObjectBuilder.createMallet(new Point(0f,

0f, 0f), radius, height, numPointsAroundMallet);

this.radius = radius;

this.height = height;

vertexArray = new VertexArray(generatedData.vertexData);

drawList = generatedData.drawList;

}

Chapter 8. Building Simple Objects

154

public void bindData(ColorShaderProgram colorProgram) {

for (DrawCommand drawCommand : drawList) { drawCommand.draw();

} }

This follows the same pattern as for Puck.

8.5 Updating Shaders

We’ll also need to update our color shader. We defined our puck and mallet with a per-vertex position but not with a per-vertex color. Instead we’ll have to pass in the color as a uniform. The first thing we’ll do to make these changes is add a new constant to ShaderProgram:

AirHockeyWithImprovedMallets/src/com/airhockey/android/programs/ShaderProgram.java protected static final String U_COLOR = "u_Color";

The next step is to update ColorShaderProgram. Go ahead and remove all references to aColorLocation, including getColorAttributeLocation(), and then add the following uniform location definition:

AirHockeyWithImprovedMallets/src/com/airhockey/android/programs/ColorShaderProgram.java private final int uColorLocation;

Update the constructor to set the uniform location:

uColorLocation = glGetUniformLocation(program, U_COLOR);

To complete the changes, update setUniforms() as follows:

public void setUniforms(float[] matrix, float r, float g, float b) { glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);

glUniform4f(uColorLocation, r, g, b, 1f);

}

We’ll also need to update the actual shaders. Update the contents of simple_ver-tex_shader.glsl as follows:

gl_Position = u_Matrix * a_Position;

}

Updating Shaders

155

Update simple_fragment_shader.glsl as follows:

Our shaders should now be up-to-date.

8.6 Integrating Our Changes

The hardest part of this chapter is done. We learned how to build a puck and a mallet out of simple geometric shapes, and we’ve also updated our shaders to reflect the changes. All that’s left is to integrate the changes into AirHock-eyRenderer; at the same time, we’ll also learn how to add the concept of a camera by adding a view matrix.

So why would we want to add another matrix? When we first started our air hockey project, we originally didn’t use any matrices at all. We first added an orthographic matrix to adjust for the aspect ratio, and then we switched to a perspective matrix to get a 3D projection. We then added a model matrix to start moving stuff around. A view matrix is really just an extension of a model matrix; it’s used for the same purposes, but it applies equally to every object in the scene.

Dans le document OpenGL ES 2 for Android (Page 163-168)