1. Trang chủ
  2. » Công Nghệ Thông Tin

3D Graphics with OpenGL ES and M3G- P40 doc

10 129 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 171,94 KB

Nội dung

374 ANIMATION IN M3G CHAPTER 16 than 180 ◦ in 3D space. In practice, this means that the dot product of any two adjacent quaternion keyframes must be positive. If this is not the case with your source data, you can enforce it by adding extra keyframes for segments that rotate 180 ◦ or more in 3D space. Note that each AnimationTrack object only defines the type of property to be ani- mated, without specifying an object. An AnimationTrack can therefore be associ- ated with multiple objects to animate the same property, in the same way, for each one. Similarly, a KeyframeSequence can be associated with multiple AnimationTrack objects, and hence multiple animation targets of possibly different types. The only restric- tion is that the keyframe types and animated properties must be compatible. This makes it possible to share the keyframe data between multiple objects, while being able to control the animation of each one individually. 16.3 TIMING AND SPEED: AnimationController In addition to a KeyframeSequence and an AnimationTrack, each individual animation needs an AnimationController to “drive” the animation. The controller is attached to each AnimationTrack object to define the speed and timing of that particular animation. Again, a single controller can be attached to multiple animation tracks, and in noninteractive animation, a single controller will often handle the entire scene. However, using multiple controllers will give you the degree of flexibility you want for playing back multiple animations simultaneously. For example, assuming one object with two animation tracks, motionTrack and rotationTrack, it makes sense to control both using a single controller: myObject.addAnimationTrack(motionTrack); myObject.addAnimationTrack(rotationTrack); AnimationController control = new AnimationController(); control.setActiveInterval(10000, 25000); control.setPosition(0.f, 10000); control.setSpeed(0.5f); motionTrack.setController(control); rotationTrack.setController(control); Assuming milliseconds for the time unit, this would begin the animation of your object at ten seconds into the animation, animate it at half speed for fifteen seconds, and then stop. The animation would start playing from the beginning of your keyframe sequences. World time and sequence time BeforewelookatAnimationController in more detail, we must be more spe- cific about time as it applies to M3G animation. With KeyframeSequence,weare SECTION 16.3 TIMING AND SPEED: AnimationController 375 talking in terms of sequence time: time 0 (zero) is the start of our keyframe sequence, and all keyframes in the sequence are defined relative to that. What you want to feed to the animation system from the application, however, is usually world time: that can be time passed since the start of your application, game session, or composite animation, or it may be the time of day if you so desire. Individual animation tracks may start and stop at arbitrary points in world time. It would often be impractical to build your keyframe sequences using world time, so AnimationController lets you easily map world time to the time of each keyframe sequence. AnimationController uses two pieces of data to convert between world time and sequence time: a reference point and relative speed. You set the reference point by calling setPosition(float sequenceTime, int worldTime). This is exactly equivalent to saying “I want my sequence time to be sequenceTime when the world clock reaches world- Time.” Often, your desired sequenceTime w ill be zero and you are just saying “I want my sequence to start at worldTime,” but M3G gives you a bit more flexibility in mapping the times. In addition to setting the position of your animation sequence, you may want to set the speed at which the sequence time passes relative to the world clock. Figure 16.3 sequence time world time reference point speed 5 2.0 speed 5 0.5 speed 5 1.0 Figure 16.3: Relationship of world time and sequence time with different speed settings. 376 ANIMATION IN M3G CHAPTER 16 illustrates the relationship of the reference times and speed. By default, sequence time and world time are synchronized, but you can speed up or slow down a particular animation, or specify different units of time for your keyframes. You do this by calling setSpeed(float speed, int worldTime). A speed of 0.5, for example, will make your sequence run at half the normal speed, whereas 2.0 makes it twice as fast. A speed of 0.001 would let you specify your keyframes as whole seconds if your world time is in milliseconds. Note the other parameter, worldTime. Why do you need to specifythat? That is the point in world time at which your sequence speed change occurs. Imagine you have been running a long animation sequence for a while, and suddenly want to speed it up for one reason or another. You will still want to continue the animation from the current position, so if you were to just change the speed factor, you would have to adjust the reference time point to avoid a sudden jump. Instead, setSpeed takes the current world time as input and automatically makes that the new reference point. The sequence time at the new refer- ence point is changed to match whatever it was for your animation before you decided to change the speed. As a result, you will only notice that your animation continues to run smoothly at the new speed. Weight and active interval In addition to the speed of a controller, you can also set its weight via setWeight (float weight). The interpolated animation data is then multiplied by weight prior to applying it to its animation target. This is useful for animation blending, which we will describe in more detail in Section 16.5.2. Performance tip: You can speed up the animation process by telling M3G to ignore any animations you do not need at any given moment. You can do that by either setting the weight of the respective controller to zero, or setting the controller of an animation track to NULL. Finally, each AnimationController has an active interval,setvia setActiveInterval(int start, int end). start and end are the world times at which the activity of this animation controller begins and ends, respectively. Each con- troller is only considered in animation calculations if the current world time falls within its active interval; otherwise, it is as if the animations controlled by that controller did not exist at all. pitfall: The animations controlled by an inactive controller (either zero weight or out- side of the active interval) are not updated at all. In other words, the animation targets are not reset to any default values when a controller becomes inactive. In particular, if you make a jump in your animation that lands the world time outside of the active interval of any controller, the values animated through that controller will retain the values they had before the jump. SECTION 16.4 ANIMATION EXECUTION 377 On a related note, an animated M3G scene will contain initial values for everything when loaded from an M3G file, but there is no direct way to reset those initial values. You must either reload the file, or have your animations active at time zero to set the correct values. 16.4 ANIMATION EXECUTION So, you have set up your KeyframeSequence, AnimationTrack, and AnimationController. You have added the track to an object—say, myMesh.How do you effect your animation to the mesh? You call animate(int worldTime), passing in the time currently displayed by the world clock you maintain in your application: static long startTime = System.currentTimeMillis(); myMesh.animate(System.currentTimeMillis() - startTime); You can call animate on myMesh ,orifmyMesh happens to be a part of myWorld, you can animate all of myWorld with a single animate call. myWorld.addChild(myMesh); myWorld.animate(System.currentTimeMillis() - startTime); Animation in M3G is always requested by the application. There are no timers or e vents involved unless you want to involve them. Neither does the animation engine have any state that you need to change to play, stop, or rewind your animations. All you do is pass in your desired world time to animate, and everything is updated to reflect that point in time. Let us look at an example on event-based animation. Assume an animation controller, actionController, controls a composite animation representing an action that a game entity would perform in response to some event. To trigger the animation upon receiving that event, we only need one line of code in addition to the previous example: actionController.setPosition(0.0f, eventTime); Here, eventTime is the world time of the event. When myWorld.animate() is next called, the animation controlled by actionController is automatically effected on the target objects. To re-trigger the animation in the future, another call to setPosition is sufficient. Performance tip: There is no way to read back the current state of a par- ticular animation in M3G, but you may want to know the phase of an animation in order to execute some corresponding action in the game logic. The duration of each animation could be encoded in the user data field of the animation controller, but you can also link that information directly to your animation: create an empty Group node 378 ANIMATION IN M3G CHAPTER 16 and attach an animation track to its alpha factor. You can then set the keyframe values so that the alpha factor will contain any desired data values, synchronized to the actual animation, which you can query directly. If you only need a binary state, you can use the node enable flags instead. Animation proceeds recursively: all objects reachable from the object being animated, according to the rules set forth in Section 13.4.1, are automatically animated as well. If you call animate on a World object, for example, everything in your World is animated; if you animate a Group, only objects within that group are touched. Note, however, that all referenced objects are animated—if your Group has a Mesh using a Texture2D via an Appearance, the transformation of the Texture2D will also update if it has an animation attached. Normally, you need not worry about this—M3G just handles it automatically for you, and the result is what you would expect in most cases. We mentioned at the beg inning of this chapter that animation in M3G is essentially just a way of quickly setting a number of parameters, and that is exactly what happens. When your animate function call returns, all the animated parameters will have their new values and the animation engine leaves the rest up to you. Normally, you will just proceed to Graphics3D.render and draw your scene, but you are free to modify anything between animation and rendering if you want to. One common step is to call align on your entire world or some specific nodes to compute any node alignments you have defined. However, you can do any other processing before rendering. You do not even have to render; you can just keep on animating if you have a special use case for that—for example, you could use the spline interpolation to generate vertex data by reading back the animated values after each animation call, then assigning the data to a vertex array. 16.5 ADVANCED ANIMATION We have now covered the basic setup and execution of keyframe animation in M3G. By now, you can get simple animations up and running to modify your object parameters. We can now look at how to make more complex animations involving animated meshes, blending between multiple animation sequences, and some useful animation features not explicitly described in the M3G specification. 16.5.1 DEFORMABLE MESHES In Section 4.2 we discussed how morphing and skinning can be used to bring polygon meshes to life. M3G has support for both of these techniques via the MorphingMesh and SkinnedMesh classes. The former allows morphing between sets of vertex attributes, and the latter implements skinning. SECTION 16.5 ADVANCED ANIMATION 379 MorphingMesh Creating a MorphingMesh is very similar to creating a regular Mesh. In fact, the only difference is that you need to pass in an additional targets par ameter: MorphingMesh(VertexBuffer base, VertexBuffer[] targets, IndexBuffer[] submeshes, Appearance[] appearances) The targets array is your set of morph targets. The base vertex buffer gives the vertices for your undeformed mesh, and each buffer in the targets array is one morphed version of the original mesh. The morph targets only need to contain the vertex properties that change. For example, if all of the morph targets share the same texture coordinates, those may be specified only in the base mesh. Mor ph targets cannot contain vertex attributes that are not present in the base mesh, and the set of attributes in each morph target must be the same. Refer to the M3G specification for more details. In the common case, you want to blend vertex positions and normals between multiple shapes, while retaining per-vertex colors or texture coordinates. This is illustrated in Figure 16.4, and easily achieved in code: 1-⌺w w 0 w 1 w 2 Figure 16.4: An example of morphing in M3G. The base mesh, top left, is modified by blending in positions and normals from three morph targets. See the code example in text. 380 ANIMATION IN M3G CHAPTER 16 VertexArray basePositions, baseNormals, colors, texCoords; VertexArray morphedPositions[3], morphedNormals[3]; IndexBuffer primitives; Appearance appearance; // Array initialization omitted // Initialize the base vertex buffer VertexBuffer baseVertices = new VertexBuffer(); baseMesh.setPositions(basePositions, 1.f, null); baseMesh.setNormals(baseNormals); baseMesh.setTexCoords(0, texCoords, 1/128.f, null); baseMesh.setColors(colors); // Initialize the morph target vertex buffers note that // only the morphed attributes, i.e., positions and normals, // are needed for these VertexBuffer morphedVertices[] = new VertexBuffer[3]; for(inti=0;i<3;++i) { morphedVertices[i] = new VertexBuffer(); morphedVertices[i].setPositions(morphedPositions[i], 1.f, null); morphedVertices[i].setNormals(morphedNormals[i]); } // Create the final mesh object MorphingMesh mesh = new MorphingMesh(baseVertices, morphedVertices, primitives, appearance); // Set to an even blend between the base mesh and each morph target float weights[3] = { 0.25f, 0.25f, 0.25f }; mesh.setWeights(weights); Once you have the MorphingMesh constructed, you can animate it via the morph target weights. You can either call setWeights(float[] weig hts) to set them directly, or animate the MORPH_WEIGHTS property of the mesh. The keyframes in that case will be vectors that have one floating-point weight corresponding to each morph target. However you apply the weights, each morph target will contribute to the final mesh shape according to its weight. The actual equation is M = B +  w i (T i − B ) = (1 −  w i )B +  w i T i , (16.1) that is, the difference between the base mesh B and each morph target T is weighted and added to the base mesh to produce the final shape M. Note that if the weights sum to SECTION 16.5 ADVANCED ANIMATION 381 one, the effect of the base mesh is canceled out and you are only blending between your morphed shapes. You are free to use any weights, though, including negative ones. An alternative way of thinking about this is that each morph target represents a single feature, and you can blend these features to alter your base mesh. This approach is often used to produce facial animation, with different expressions being blended in. pitfall: In morphing, it is your responsibility to make sure that the morphed vertex coor- dinates do not overflow the numeric range of your vertex buffer. M3G can handle very large intermediate results (from weighting each mor ph target), but the end result must still fit within the original range of either 8 or 16 bits. SkinnedMesh Now, let us build a complete animated character using skinning. Most of this will already be familiar, so we will begin with a practical example before introducing the new functions in detail. While the code is fairly straightforward, there is quite a bit of it, so we have split it into a couple of sections. The entire example is also available from the companion web site. Example: Building a Skinned Character Our skinned character is shown in Figure 16.5. First, we will construct the skinned mesh object with the various bones that we can control separately: pelvis (root node) left thigh right thigh torso left upper arm right upper arm left fore arm right fore arm neck head left shin right shin Figure 16.5: Our example of a skinned character. The rendered figure is shown on the left, and the skeleton group on the right. The illustration shows the origin (sphere) and primary axis (triangle) of each bone, while the arrows indicate parent-child relationships in the scene graph. The torso node is co-located with the root node, pelvis, emphasized. 382 ANIMATION IN M3G CHAPTER 16 private SkinnedMesh stickMan; private Group torso, neck, head; private Group leftThigh, rightThigh, leftShin, rightShin; private Group leftUpperArm, rightUpperArm, leftForeArm, rightForeArm; We have defined simple 2D vertex data along the outlines of our character. From this, we construct the vertex and index buffers as well as the actual mesh object in a way that is similar to past examples: static private byte vertices[] = { // Head and neck — 10, 127, 0, 10, 127, 0, — 25, 103, 0, 25, 103, 0, — 10, 80, 0, 10, 80, 0, — 15, 70, 0, 15, 70, 0, // Arms and torso — 120, 55, 0, — 120, 40, 0, — 80, 60, 0, — 80, 40, 0, — 40, 65, 0, — 40, 40, 0, 120, 55, 0, 120, 40, 0, 80, 60, 0, 80, 40, 0, 40, 65, 0, 40, 40, 0, — 20, 0, 0, 20, 0, 0, // Lower body and legs — 30, — 20, 0, 0, — 30, 0, 30, — 20, 0, — 30, — 60, 0, — 5, — 60, 0, — 30, — 120, 0, — 10, — 120, 0, 5, — 60, 0, 30, — 60, 0, 10, — 120, 0, 30, — 120, 0 }; static private int tristrips[] = { 1, 0, 3, 2, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 6, 20, 7, 21, 18, 19, 16, 17, 14, 15, 20, 22, 21, 23, 24, 23, 22, 26, 25, 28, 27, 24, 23, 30, 29, 32, 31 }; static private int striplens[]={8,16,5,6,6}; // Create the vertices and triangle strips for the mesh VertexArray pos = new VertexArray(vertices.length / 3, 3, 1); pos.set(0, vertices.length / 3, vertices); VertexBuffer vb = new VertexBuffer(); vb.setPositions(pos, 1.f, null); vb.setDefaultColor(0x000000); IndexBuffer ib = new TriangleStripArray(tristrips, striplens); stickMan = new SkinnedMesh(vb, ib, new Appearance(), new Group()); SECTION 16.5 ADVANCED ANIMATION 383 Connecting the Bones So far, our mesh is no different from regular Mesh class objects, except that there is an empty group to serve as a skeleton. Next, we will create the bones and connect them into a skeleton as shown in Figure 16.5, starting with the group we already inserted above: Group pelvis = stickMan.getSkeleton(); // Connect the torso, neck, and head torso = new Group(); pelvis.addChild(torso); neck = new Group(); torso.addChild(neck); neck.setTranslation(0.f, 60.f, 0.f); head = new Group(); neck.addChild(head); head.setTranslation(0.f, 20.f, 0.f); // Connect the arms to the torso leftUpperArm = new Group(); torso.addChild(leftUpperArm); leftUpperArm.setTranslation(30.f, 50.f, 0.f); leftUpperArm.setOrientation( — 90.f, 0.f, 0.f, 1.f); leftForeArm = new Group(); leftUpperArm.addChild(leftForeArm); leftForeArm.setTranslation(0.f, 50.f, 0.f); Note how the arms, for example, are offset inside the torso group. The translation of each bone determines the location of its hinge point (or igin) relative to the hinge point of its parent bone. We have also used a convention where the Y axis of each bone runs along the length of the bone, so we are rotating some of the bones. The character defined by the untransformed vertices is standing with arms stretched out to the sides, and our bones now match that rest pose. Attaching the Skin The final step in creating a skinned character is attaching flesh to the bones. We must tell M3G which vertices each bone should affect, and if multiple bones affect any single vertex, M3G will average their influences based on the weights assigned to each bone. You can use any integer values for the weights—in this example, we use a nominal scale of 0 to 100. We have also laid out the vertex data so that we can attach each bone to a group of vertices at once: . life. M3G has support for both of these techniques via the MorphingMesh and SkinnedMesh classes. The former allows morphing between sets of vertex attributes, and the latter implements skinning. SECTION. 1.f, null); morphedVertices[i].setNormals(morphedNormals[i]); } // Create the final mesh object MorphingMesh mesh = new MorphingMesh(baseVertices, morphedVertices, primitives, appearance); // Set. are rotating some of the bones. The character defined by the untransformed vertices is standing with arms stretched out to the sides, and our bones now match that rest pose. Attaching the Skin The

Ngày đăng: 03/07/2014, 11:20