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

3D Graphics with OpenGL ES and M3G- P40 doc

10 129 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

For example, assuming one object with two animation tracks, motionTrack and rotationTrack, it makes sense to control both using a single controller: myObject.addAnimationTrackmotionTrack

Trang 1

ANIMATION IN M3G

than180◦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 rotate180◦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

Before we look at AnimationController in more detail, we must be more

spe-cific about time as it applies to M3G animation With KeyframeSequence, we are

Trang 2

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

AnimationControlleruses 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 will 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

Relationship of world time and sequence time with different speed settings.

Trang 3

ANIMATION IN M3G

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 specify that? 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, set via

setActiveInterval(intstart, 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

Trang 4

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, or if myMesh 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 events 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 setPositionis 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

Trang 5

ANIMATION IN M3G

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 beginning 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

Trang 6

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 parameter:

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 Morph 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:

F i g u r e 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.

Trang 7

ANIMATION IN M3G

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 (int i = 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[] weights) 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

Trang 8

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 morph 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

right thigh

torso left upper arm right upper arm

left fore arm right fore arm

neck head

left shin right shin

F i g u r e 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.

Trang 9

ANIMATION IN M3G

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

// Arms and torso

// Lower body and legs

};

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());

Trang 10

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 (origin) 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:

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