Presentation is loading. Please wait.

Presentation is loading. Please wait.

Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division.

Similar presentations


Presentation on theme: "Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division."— Presentation transcript:

1 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Real-Time Helper-Joint System Implementation and Application of the GDC2011 | Jubok Kim, Choong-Hyo Kim

2 Jubok Kim Technical Director of “Mabinogi 2” Worked as a game programmer for 10 years in Nexon Choong-Hyo Kim Technical Art Director of “Mabinogi 2” Worked as a 3D artist for 10 years in Nexon

3 Nexon? Nexon is the first Korean online game company to direct its attention to the overseas market A Casual MMORPG MapleStory Cartoon Rendered-MMORPG, Mabinogi MMO-Action Vindictus (Mabinogi:Heroes in Korea)

4 Mabinogi 2 “Mabinogi 2” is the third one of the series and the official sequel to “Mabinogi.” We are aiming very high quality bar of graphics and animation unlike its casual-looking prequel

5 What we’re gonna talk. 1. Real-time helper-joint system Problem definition Study about helper-joints Replication of helper-joints 2. Layering the helper-joints Concept / Authoring conventions Implementation 3. Optimization 4. Conclusion Pros/Cons/Considerations **Annotation slides

6 Problem Definition

7 OK… Error Candy Wrap Candy-wrap problem Basic bone structure + Skinning = Twisted wrist You will see problems like this even at shoulder or other complex joint

8 Twist bone is a solution Measure and calculate the average angle between forearm and wrist to get the rotation of the twist bone

9 Helper-Joint A joint which measures the movement of other joints and settle the movement of itself Many DCC tools provide features which are actually helper-joints Popular controllers in 3DS MAX : Position Constraint Look At Constraint Orientation Constraint…

10 So, what’s the problem left? All we’ve got helper-joints in our DCC tools already.

11 Hard to modify behavior All animation asset should re-baked if you change behavior of a helper-joint It becomes more and more painful to modify behavior of the skeletal structure in the later stage of development because animation asset size is growing bigger and bigger

12 Procedural Motion Helper-joint does not react properly to procedural animation like ragdoll or full-body IK

13 Customization Hard to author and modify costumes if you need to bake all the animation for them. (Most of typical MMOGs have rich character customization feature) Costumes from Vindictus, Nexon

14 We needed Helper-Joint which does not require pre-baked animation Let’s call it “Real-Time Helper-Joint”

15 Expected effects Expected effects with Real-Time Helper-Joint system are… Flexibility of the content creation pipeline Smaller package size to distribute Proper reaction to procedural animation Low cost for authoring various and complex costumes

16 Problems predicted We predicted some problems before beginning to build the system Inexperience Hardness of replication Performance Issue Artists were not experienced to the concept of Helper-Joint. We did not use Helper-Joint for the previous game, and materials are very rare in Korea. We did not know even if it was possible to replicate arbitrary Helper-Joint in DCC tool. There was a high chance of failing if we try to replicate too complex and unpredictable Helper-Joint. Even if we succeeded to replicate Helper-Joint in the game code, can we get enough performance to process many characters in the crowd scene of an MMOG?

17 Solving Strategy We prepared clear strategies for each predicted problems before starting to build the system for Inexperience for Hardness of replication for Performance Issue Technical artist decided to study about human skin changes rather than muscle movement, because Helper-Joint is about skinning Technical artist and programmer reached an agreement to maintain the list of Helper-Joints of 3DS MAX compact by choosing easy and intuitive ones Programmer decided to adopt simple and rigid architecture to support multi- threading

18 Study about Helper-Joints

19 Usually hard part to set up Where do we need it? We collected cases which is hard to set up skinning without Helper-Joint first Shoulder Pelvis

20 Parts requiring twist bone Upper Am Forearm Thigh

21 Parts with caution for rubbery joint problem Elbow Ankle

22 Parts affect to attractive character silhouette Change of Knee & Bulge of Thigh Pectoral Muscles Scapula

23 Why shoulder? Focused on female shoulder Shoulder has 3 degrees of freedom Most skinning issues get resolved if we solve the issues around shoulder Female shoulder was the best study subject with all the reviewed cases

24 Focused on female shoulder Why female? Expression of subtle silhouette of a female character is much more harder than tough expression of muscles of a male character Female shoulder was the best study subject with all the reviewed cases

25 More early decisions We made some more decisions before start to set up female shoulder 3DS MAX native features only Maintenance cost of the system might grow bigger, if we develop our own Helper-Joint system (what if there is 3DS MAX upgrade?)

26 More early decisions We made some more decisions before start to set up female shoulder Focus on movement of skin only The system would be an over-engineered one if we have studied and replicated the movement of muscles

27 Rotation around the longitudinal axis Markers on my body skin Little rotation around the deltoid muscle Rotation limits

28 The arm rotation makes Smoothe curve. Little rotation around the deltoid muscle. Rotation with the arm forward

29 Changes along the curve on the deltoid.

30 Rotation with the arm upward Less deformation at the root of the upper arm. Noticeable crease of the skin surface Silhouette from the deltoid, triceps, and the latissimus. Bulging deltoid

31 Basic Components Basic components to settle movement of a Helper-Joint Animation of the base framework Nodes for Measurement Controllers for intermediate calculation

32 No detail for the rig Will not talk about it right now. There’s No Fancy technique. Keep it simple.

33 Final rig Preview with 3ds max

34 Demonstration Video 1 This demonstration shows the our final rig with 3DS MAX native Helper-Joints

35

36 PositionConstraint LookAtConstraint OrientationConstraint ListController (S/R/T) ReactionController (S/R/T/Float) ExpressionController (S/R/T/Float) WireParameter we decided not to use WireParameter later, because it is too slow and hard to modify ExposeTM Final list to replicate We determined the minimal list of Helper-Joints to replicate by the study

37 Replication of Helper-Joints

38 PositionConstraint Let’s start with the simplest Helper-Joint in the list mentioned before Simply adds and calculates the average of position targets multiplied by weight

39 Preparation Technical artist part Sample Scene Technical artist created a scene 2~3 bones using position constraints and export its setting as an XML file by MAXScript

40 Preparation Programmer part Interface Definition Programmer defined the simple interface for Helper-Joint and integrate it into the existing animation system struct HelperJointResult { enum Type { Scale, Rotation, Position, }; Type type; Vector4 result; }; class IHelperJoint { virtual void Process(CurrentPose& pose) = 0; virtual const HelperJointResult& GetResult() const = 0; };

41 Pseudo-code Really easy one, huh? class PositionConstraint : public IHelperJoint { virtual void Process(const CurrentPose& pose) { Calculate the matrix T which transforms a ‘world space coordinate’ into a ‘parent space coordinate’ Calculate the average position P of the positions of target bones multiplied by weight in the world space Transform P by T and store it } virtual const HelperJointResult& GetResult() const { return P; } };

42 Pseudo-code Really easy one, huh? class PositionConstraint : public IHelperJoint { virtual void Process(const CurrentPose& pose) { Calculate the matrix T which transforms a ‘world space coordinate’ into a ‘parent space coordinate’ Calculate the average position P of the positions of target bones multiplied by weight in the world space Transform P by T and store it } virtual const HelperJointResult& GetResult() const { return P; } };

43 Life isn’t that simple… 3DS MAX calculates and stores the differences between bind pose and the weighted average of target positions in the bind pose, and add them to later calculation results if ‘Keep Initial Offset’ option is checked Bone position from PositionAtConstraint Actual bind position Position adjusted by ‘Keep initial offset’ option

44 Introducing preprocess step Preprocess() function has been added to IHelperJoint interface to handle the ‘Keep Initial Offset’ option class PositionConstraint : public IHelperJoint { virtual void Preprocess(const BindPose& bindPose) { if ‘keep initial offset’ option is cheked { Run Process() with bind pose and store the result in P_base Calculate the differences of position between P_base and actual bind pose and store the results in Offset } virtual void Process(const CurrentPose& pose) { Calculate the matrix T which transform a ‘world space coordinate’ into a ‘parent space coordinate’ Calculate the averaged position P of the positions of target bones multiplied by weight in the world space Transform P by T and store it if ‘keep initial offset’ option is cheked Add the preprocessed Offset to P }

45 Detailed implementation note of other Helper-Joints will be added in appendix Replicate the remainder It is straightforward to replicate except non-intuitive options like ‘Keep Initial Offset’

46 Cool, it’s all xxxxing done All replications are done, but this is too easy, and bad premonition always proved right… PositionConstraint LookAtConstraint OrientationConstraint ListController (S/R/T) ReactionController (S/R/T/Float) ExpressionController (S/R/T/Float) WireParameter ExposeTM

47 Unexpected problem The order of evaluation was not a straightforward one In general animation calculation process, if you calculate the world transform in order, accurate result will be guaranteed

48 Unfortunately, this simple and easy solution does not work with Helper-Joint A Helper-Joint can refer to the bone which is not its own ancestor LookAt Constraint LookAt Target

49 A naïve solution? The order of evaluation can be calculated by traversing Helper-Joints in postorder To traverse Helper-Joints, we should figure out their dependency first /* Let’s add a function to the IHelperJoint interface to return the list of bone indices which the Helper-Joint referred to */ class LookAtConstraint { virtual void GetDependency( vector & dependency) { Add indices of look-at targets to dependency Add index of upnode to dependency } /* Other functions */ }

50 is not a rigid solution… We have a lot of Helper-Joints to implement, so more solid and human error proof solution is required /* What if there is more complicated Helper-Joint like an expression controller? */ /* Furthermore, how do I validate and convince the dependency list, and evaluate that order is correct? */ class ReactorExpressionBinary { void GetDependancy(std::vector & dependancies); }; class ReactorExpressionID { void GetDependancy(std::vector & dependancies); }; class ReactorExpressionFunction { void GetDependancy(std::vector & dependancies); }; class ReactorExpressionConstant { void GetDependancy(std::vector & dependancies); };

51 Concrete solution Use evaluation step itself as dependency traversal step PositionConstraint LookAtConstraint OrientationConstraint PositionReactionController RotationReactionController ScaleReactionConstraint FloatWireConstraint IHelperJointPoseSource GetWorldTranslation(int boneIndex) GetWorldRotation(int boneIndex) GetWorldScale(int boneIndex) GetLocalTranslation(int boneIndex) GetLocalRotation(int boneIndex) GetLocalScale(int boneIndex) GetWorldTransform(int boneIndex) … Each implementation of Helper-Joint is modified not to access the animation pose array directly. Instead, Helper-Joint uses IHelperJointPoseSource whenever it requires pose of other bones

52 A special implementation of IHelperJointPoseSource - EvaluationOrderBuilder is used when order of evaluation is calculated. Assumes that bones with index less than 11 are already evaluated bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …10

53 Bone 9, 10 are already evaulated, so they can be referenced without problem. bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …10

54 Bone 12 is not traversed yet. So evaluation of bone 11 is holded, and evaluation of bone 12 begins instead. bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …10

55 If a bone is calculated by pre-baked animation, then it can be referenced anytime. Let’s assume bone 24 is settled by pre-baked animation. Bone 24 is assumed to be traversed now, and added to the traversal order list. bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …1024

56 Bone 12 is evaluated, so add it to the traversal list. bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …102412

57 Now bone 11 can reference bone 12 safely. bone11 PositionConstraint 9, 10 OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ … EvaluationOrderBuilder : public IHelperJointPoseSource

58 Bone 11 is evaluated now, so add it to the list too Bone 12 is already evaluated, so evaluate bone 13 next. bone11 PositionConstraint 9, 10 EvaluationOrderBuilder : public IHelperJointPoseSource OrientationConstraint 12 ScaleXYZ bone12 PositionXYZ LookAtConstraint 24 ScaleXYZ …

59 Demonstration Video 2 This demonstration shows the basic implementation result. (Shoulder, Knee, reaction to IK)

60

61 Layering Helper-Joints

62 Customization Let’s extend the realtime Helper-Joint system to character customization MMOGs have many kinds of costumes Too Big to bake them out Single base animation Many of them require its own animation. And costumes can be changed at any time. We can’t export all the costume animations. …for all the costumes.

63 Most pre-baked animation contain poses for this layer Body Layer Contains Helper-Joint settings of shoulder, knee and others Helper-Joint for body Face and separate pose clips Face & Hands Contains Helper-Joint settings of costumes Helper-Joint for costume Layering skeletal structure We introduced the concept of layer of the skeletal structure Pre-baked Procedural

64 Base Muscle Face Hand Long Skirt Short Skirt Mantle Sets of various frameworks Nude body Layering example Basic concept for framework layering

65 Base Muscle Face Hand Long Skirt Short Skirt Mantle Sets of various frameworks Face Mesh Layering example Basic concept for framework layering

66 Base Muscle Face Hand Long Skirt Short Skirt Mantle Sets of various frameworks Robe Mesh Layering example Basic concept for framework layering

67 Base Muscle Face Hand Long Skirt Mantle Sets of various frameworks Short Skirt Layering example Basic concept for framework layering Short Skirt

68 Authoring convention We used a naming convention to determine the layer where a bone is contained and what the bone is for _+~_+~ 0Base 1Face 1Hand 1Muscle 2Costume 2Tool {B} : Baked {D} : Deformable _0Base{B}#Spine1 _ _ 0Base {B} # # Spine1 Skin WeightLayerPre-baked/RuntimeName

69 Authoring convention We used a naming convention to determine the layer where a bone is contained and what the bone is for _+~_+~ 0Base 1Face 1Hand 1Muscle 2Costume 2Tool {B} : Baked {D} : Deformable _0Base{B}#Spine1 _ _ 0Base {B} # # Spine1 Skin WeightLayerPre-baked/RuntimeName

70 Authoring convention We used a naming convention to determine the layer where a bone is contained and what the bone is for _+~_+~ 0Base 1Face 1Hand 1Muscle 2Costume 2Tool {B} : Baked {D} : Deformable _0Base{B}#Spine1 _ _ 0Base {B} # # Spine1 Skin WeightLayerPre-baked/RuntimeName

71 Authoring convention We used a naming convention to determine the layer where a bone is contained and what the bone is for _+~_+~ 0Base 1Face 1Hand 1Muscle 2Costume 2Tool {B} : Baked {D} : Deformable _0Base{B}#Spine1 _ _ 0Base {B} # # Spine1 Skin WeightLayerPre-baked/RuntimeName

72 Authoring convention A layer should not be cyclic, or bi-directional (Frm Layer 1) _Layer0{B}#BBB _Layer1{B}#CCC (Frm Layer 0) _Layer0{B}#BBB _Layer0{B}#AAA _Layer0{B}#BBB _Layer1{B}#CCC _Layer0{B}#AAA (Layer 0 + Layer 1) _Layer0{B}#BBB _Layer1{B}#CCC _Layer0{B}#AAA (Animation Data)

73 Export and import Exporting from and importing back into the DCC tool is done layer by layer AAA.Layer0.Framework AAA.Layer1.Framework AAA.Layer2.Framework AAA.Mesh AAA.Layer0.Animation AAA.Layer2.HelperJointSettin g AAA.Layer1.HelperJointSettin g AAA.max Pre-baked Layer “I’ll use these layers.”

74 Framework template We maintained a list of typical framework settings as templates Predefined framework templates Popular fantasy costumes like skirts, and robes Delivered to the outsourcing company Low cost for modification Behavior of the procedural layers can be changed at any time with low cost Just re-export the helper-joint setting for the layer Great benefit to quality control

75 Framework class is introduced A Framework instance maintains current stack of layers and bind poses Implementation Animation pose player and Helper-Joint processor should be modified to handle framework layers AnimationPose class is introduced A AnimationPose instance contains current bone poses and flows through each animation module.

76 One animation player for one layer There can be multiple instances of animation players but there is one AnimationPose instance only. Each animation player matches the bone name to the framework to get the indices to fill the final pose array in AnimationPose instance One Helper-Joint processor for one layer There can be multiple instances of Helper-Joint processors and works similar to an animation player. Actually, the animation player and the Helper-Joint processor are implemented as a node in the general module based on the animation system AnimationPlayer AdvanceProcess or ILayerPlayer 0Logic 1Base 2Hand Pose SRT (Layer0) SRT Bind pose SRT (Layer1) SRT Bind pose HelperJointProc HelperJointLayerPro c Pose SRT (Layer0) SRT Bind Pose SRT (Layer1) SRT SRT (Layer3) SRT

77 Demonstration Video 3 This demonstration shows several costumes which have their own Helper-Joint setting

78

79 Optimization

80 Unexpected problem again Not like that we expected, multi-threading was an easy part Multithreading was an easy part World transform calculation Data parallel approach and Intel TBB did all the work Thanks, Intel! Performance loss on scale calculation

81 World transform calculation Usually, multiplying local transform with parent’s world transform occurs once for a bone in a frame When the calculation result of a Helper-Joint is applied… then all world transform of entire sub-tree are invalidated The Helper-Joint evaluation process updates local poses of bones almost randomly To make things worse, implementation of Helper- Joints require world transform of other bones in many cases

82 Typical character framework contains 100~200 bones So performance hit will be drastic if you re-calculate world transform of all bones whenever world transform of a bone is referenced

83 Local pose of bone 8 is updated, then reset the dirty mask of the bone and its descendants If a world transform for a bone is calculated, we would set the dirty mask for the bone If local bone pose of a bone is updated, we would reset the dirty mask of the bone and its descendants

84 Dirty mask This performance problem can be simply solved if each entry in the world transform array has dirty mask Ancestor of bone 8 is 0 only Decendants of bone 8 is 9~14 All entries in the world transform array have dirty mask All bones have list of their own ancestors and descendants. These lists are maintained by Framework instance

85 When we need world transform of bone 12, then update ancestors of bone 12 and bone 12 itself only If world transform of a bone is referred when its dirty mask is reset, we would check and calculate world transforms of its ancestors and the bone itself It can be done recursively without list of ancestors and descendants, but there is performance hit by function call in that case

86 Improved, but not enough The result of performance measurement showed that much more time consumed than expected time from the complexity of Helper-Joint calculation code L2 cache miss was the cause Fine measurement revealed that L2 cache miss is the cause Helper-Joint is evaluated in random order, and references to world transform of other bones occur in random and sporadic manner

87 Access should be gathered Accesses to memory should be predicted and gathered Calculating the order of reference Ready world transforms in advance The order of referencing to world transform is calculated in similar way to the way order of evaluation of Helper-Joint is calculated Prefetch worked well Calculate world transform in the order before evaluating a Helper-Joint so the world transform can be loaded in L2 cache many L2 cache hot spots have been removed by this approach by combination with some prefetch instructions

88 Calculate inverse transform of a matrix is expensive. Performance loss on scale Scale transform is just one multiplication step and does not impact the performance in usual animation system, but again, it is a different story from the real-time Helper- Joint system Many Helper-Joint requires inverse matrix of world transform of arbitrary bone Getting SRT from a matrix is expensive too

89 But non-trivial scale is rare If scale is trivial, inverse transform calculation becomes getting transpose Getting SRT from a matrix becomes much simpler too R -1 = R T SRT(M) = { (1, 1, 1), QuaternionFromMatrix(M), (M[3][1], M[3][2], M[3][3] }

90 Getting SRT from a world transform is replaced by matrix into quaternion conversion if scale mask is not set. Matrix inversion is replaced by matrix transpose if scale mask is not set. Scale mask Scale mask which is similar to world transform dirty mask introduced into AnimationPose and array of world transform We would set scale mask for the bone if local pose of a bone contains non-trivial scale

91 Result is good Performance gain comes from the fact that animation containing non-trivial scale is very rare Further optimization is possible for the cases with non-trivial scale. For example, code calculating x component transformed by inverse of parent’s world transform can be reduced into one line.

92 Performance chart Here goes actual performance chart for current implementation HelperJoint Total Clocks No.of Calls Time per Call (ms) Avg count in one char Time per one char (ms)

93 Conclusion

94 pros. Animation asset maintenance became easier Polishing the behaviors of Helper-Joint became easier Authoring high quality costumes became easier Variance in monsters can be introduced easily with additional Helper-Joint layer Package size to distribute got reduced

95 cons. Riggers should get more familiar to the concept of real-time Helper-Joint system Relation between costume and Helper-Joint layer introduces another complexity in asset definition management Performance hit is not ignorable

96 Considerations Do enough research before you begin to implement. Avoid over design Make the best use of native features of DCC tools Maintenance will be hard if there are too many in-house tools Runtime Helper-Joint system has a very different runtime execution path Integrating it into the existing system needs careful consideration

97 Considerations Set a standard of expression and performance which artist and programmer both can reach an agreement

98 Q&AQ&A Jubok Kim | twitter.com/eiaserinnys (Korean) Choong-Hyo Kim | twitter.com/siyoskii (Korean)

99 Thank you

100 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Replicating 3DS MAX Native Controllers LookAtConstraint | OrientationConstraint | ReactionController | ExpressionController&FloatWire | XYZ&ListControllers ADDITIONAL NOTE

101 Point the weighted avrage position of Bone01 and Bone02, and the ratio is 50:50 Look At Up Node The upward direction to Bone02 is used as upward direction of the bone Make it look at targets LookAtConstraint first calculates the weighted average of look-at targets, then makes the constrainted bone to point the position

102 void LookAtConstraint::Process(/* Arguments omitted */) { Calculate the transform T doing [World space]→[Parent space] Tranform the positon of constrainted bone into its parental space with T Calculated the weighted average position of targets P Transform the P with T, and calculate the forward vector F Get the upward vector U by transform the upward target with T Calculate the result matrix from F and U, and convert it into quaternion } Relatively easy to replicate LookAtConstraint works in a very clear and intuitive manner, and the first version of replication is also simple

103 void LookAtConstraint::Process(/* Arguments omitted */) { Calculate the transform T doing [World space]→[Parent space] Tranform the positon of constrainted bone into its parental space with T Calculated the weighted average position of targets P Transform the P with T, and calculate the forward vector F Get the upward vector U by transform the upward target with T Calculate the result matrix from F and U, and convert it into quaternion } Prepare the basis to look at LookAtConstraint acually works in the world space, but results in local space It is favorable to do the calcuation with the inverse of parent’s world transform

104 void LookAtConstraint::Process(/* Arguments omitted */) { Calculate the transform T doing [World space]→[Parent space] Tranform the positon of constrainted bone into its parental space with T Calculated the weighted average position of targets P Transform the P with T, and calculate the forward vector F Get the upward vector U by transform the upward target with T Calculate the result matrix from F and U, and convert it into quaternion } Finding the forward vector Calculate the weighted average position of targets, and transform it with the basis transform T to find the forward vector

105 void LookAtConstraint::Process(/* Arguments omitted */) { Calculate the transform T doing [World space]→[Parent space] Tranform the positon of constrainted bone into its parental space with T Calculated the weighted average position of targets P Transform the P with T, and calculate the forward vector F Get the upward vector U by transform the upward target with T Calculate the result matrix from F and U, and convert it into quaternion } Finding the upward vector Simple and straight?

106 If the “world checkbox” is checked, an identity transform is used as the upnode target If the value of “Upnode Control” is “LookAt”, the constrainted bone points the upnode If the value is “Axis Alignment”, one axis of upnode is taken as the upward vector The “Source Axis”XYZ detemines which axis will be considered upward, and the “aligned to Upnode Axis” determines which axis will be taken as the upward vector when the value of “Upnode control” is “Axis Alignment” …takes a lot of care There are so many options for upnode control

107 void LookAtConstraint::Process(/* Arguments omitted */) { Calculate the transform T doing [World space]→[Parent space] Tranform the positon of constrainted bone into its parental space with T Calculated the weighted average position of targets P Transform the P with T, and calculate the forward vector F Get the upward vector U by transform the upward target with T Calculate the result matrix from F and U, and convert it into quaternion } Finding the result rotation We’ve get the forward and upward vectors, so cross them to get the result rotation Does this concludes the replication of LookAtConstraint?

108 Some options left in rollout Wow, the “Keep Initial Offset” checkbox AGAIN!

109 The result direction of the constrainted bone by the LookAtConstraint in the bind pose The actual direction of the constrainted bone in the bind pose The bone which is constrainted by LookAtConstraint The intermediate direction calculated by LookAtConstraint only The final direction which is the sum of the intermediate result and the difference preserved by “Keep Initial Offset” option Preserving the difference The “Keep Initial Offset” option of LookAtConstraint works similar to the same option of PositionConstaint

110 void LookAtConstraint::Preprocess() { Preserve the difference of rotation between the bind pose and the result with Process() only when “Keep Initial Offset” is checked } void LookAtConstraint::Process(/* Arguments Omitted */) { /* Ommitted */ Add the preserved rotation O to F when the “Keep Initial Offset” is checked Calculate the result matrix from F and U, and convert it into quaternion } …concludes the replication The final replication which calculates and preserves the difference by “Keep Initial Offset”

111 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Replicating 3DS MAX Native Controllers LookAtConstraint | OrientationConstraint | ReactionController | ExpressionController&FloatWire | XYZ&ListControllers ADDITIONAL NOTE

112 The constrainted bone takes the average rotation of Bone01 and Bone02 Takes the average rotation OrientationConstraint calculates the weighted average of rotations of targets

113 void OrientationConstraint::Process(/* Omitted */) { Get local rotations of targets Calculate the weighted average of the rotations } The simplest one ever? It even looks like that it is simpler than PositionConstraint

114 void OrientationConstraint::Preprocess() { Preserve the difference of rotation D between the bind pose and the result with Process() only when “Keep Initial Offset” is checked } void OrientationConstraint::Process(/* Omitted */) { if (“Transform rule” is “Local to Local”) Preserve the local rotations of targets else // “World to World” Transform the rotation of targets to the parental space and preserve them Calculate the weighted average of preserved rotations Add preserved difference D to the average when “Keep initial offset” is checked Preserved the result } No, it is not a simple one It has the “Keep initial offset” checkbox too, and a very complex option which controls blend method

115 void OrientationConstraint::Preprocess() { Preserve the difference of rotation D between the bind pose and the result with Process() only when “Keep Initial Offset” is checked } void OrientationConstraint::Process(/* Omitted */) { if (“Transform rule” is “Local to Local”) Preserve the local rotations of targets else // “World to World” Transform the rotation of targets to the parental space and preserve them Calculate the weighted average of preserved rotations Add preserved difference D to the average when “Keep initial offset” is checked Preserved the result } Usual “Keep Initial Offset” This option works in the same manner to LookAtConstraint

116 void OrientationConstraint::Preprocess() { Preserve the difference of rotation D between the bind pose and the result with Process() only when “Keep Initial Offset” is checked } void OrientationConstraint::Process(/* Omitted */) { if (“Transform rule” is “Local to Local”) Preserve the local rotations of targets else // “World to World” Transform the rotation of targets to the parental space and preserve them Calculate the weighted average of preserved rotations Add preserved difference D to the average when “Keep initial offset” is checked Preserved the result } Strange “Transform Rules” This option betrays the usual concept of animation blending

117 A bone which is constrainted by OrientationConstraint with “World→World” transform rule Parent Child 15 degree rotated (15 degree in world) 15 degree rotated (30 degree in world) “Local→Local” case ( ) / 2 = 15 World→World case ( ) / 2 = 22.5 degree Blending what you “see” This option is needed because target bones generally have different parents, so blended result can be very different to the result which an artist expects

118 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Replicating 3DS MAX Native Controllers LookAtConstraint | OrientationConstraint | ReactionController | ExpressionController&FloatWire | XYZ&ListControllers ADDITIONAL NOTE

119 When the local Z rotation in euler angle of +1Muscle{D}#R_Knee_B_Dummy is degree, the Z rotation value of _1Muscle{D}#R_Leg1_Bulge This UI is VEEEEEEERY unintuitive DO SOMETHING, AUTODESK! is Input-Graph-Output All *ReactionManagers calculate the output value through the plotted graph

120 Local Rotation X/Y/Z World Rotation X/Y/Z Local Position X/Y/Z World Position X/Y/Z Distance Too many input types! You need to standarize the way to retrieve all the possible inputs

121 PositionEvaluator RotationEvaluator DistanceEvaluator Abstract the input at first The main function of the *ReactionManager is the “float →Graph →result” transformation

122 const float PositionEvaluator::Process(/* Omitted */) { if (demands the position in local space) if (relative to the parent bone) return (local translation)[channel] else if (relative to a reference bone) transform into the space of the reference bone return (transformed translation)[channel] else // reference is undefined return (world translation)[channel] else return (world translation)[channel] } PositionEvaluator This class handles input types “Local Position X/Y/Z” and “World Position X/Y/Z”

123 const float PositionEvaluator::Process(/* Omitted */) { if (demands the position in local space) if (relative to the parent bone) return (local translation)[channel] else if (relative to a reference bone) transform into the space of the reference bone return (transformed translation)[channel] else // reference is undefined return (world translation)[channel] else return (world translation)[channel] } Relative to a reference? Relative to the parent bone, and world space is very intuitive But the correct replication requires translation relative to the reference bone

124 …comes from helper-bone Helper-bone rollout have many options which are hard to implement You should agree upon the options which would be supported by the replication

125 const float RotationEvaluator::Process(/* Omitted */) { if (demands the rotation in local space) if (relative to the parent bone) return (local rotation)[channel] else if (relative to a reference bone) transform into the space of the reference bone return (transformed rotation)[channel] else // reference is undefined, then returns in world space return (world rotation)[channel] else return (world rotation)[channel] } RotationEvaluator This class handles input types “Local Rotation X/Y/Z” and “World Rotation X/Y/Z”

126 Quaternion→EulerXYZ It can be derived from a conversion of a quaternion to a matrix and decomposition of a matrix to euler angles

127 const float DistanceEvaluator::Process(/* Omitted */) { if (the reference bone is valid) returns the distance between the base bone and the reference bone else return the distance between the base bone and the world origin } DistanceEvaluator This handles input type “Distance”

128 const ReactorResult ReactionControllerBase::Process(/* Omitted */) { Find the largest master/slave index whose master value is not larger than the given master value in the sorted master/slave table Interpolate the reation and the next to it returns the result } Graph lookup is easy It can be implemented in the same way as a key frame animation lookup routine

129 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Replicating 3DS MAX Native Controllers LookAtConstraint | OrientationConstraint | ReactionController | ExpressionController&FloatWire | XYZ&ListControllers ADDITIONAL NOTE

130 Uses a raw expression Both FloatWire and *ExpressionControl need parsing an expression

131 Interpreting the given expression every frame would hurt runtime performance, so you should build a parser for it But explaining how to build a parser is beyond the scope, so I will skip this part Replication is simple …except you need to implement an expression parser

132 RotationEvaluator, PositionEvaluator can be used in these controllers again Reuses the implementations The implementations of ID of the expression are almost same as handlers of input types of *ReactionManager

133 Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division in NEXON Corp.). M 2 team Director is Kim, Dong-Gun | Project M 2 is produced by Kim, Dong-Gun GT-R team, Engine Development Team for Project M 2 and more. GT-R Team Technical Director is Jeon, Hyeong-Kyu Replicating 3DS MAX Native Controllers LookAtConstraint | OrientationConstraint | ReactionController | ExpressionController&FloatWire | XYZ&ListControllers ADDITIONAL NOTE

134 Each XYZ channel can have an independent float controller Group of float controllers We replicated 3 types of XYZ controllers - PositionXYZ, EulerXYZ, and ScaleXYZ

135 void EulerXYZ::Process(/* Omitted */) { if (is it dynamically changing?) preserve the default value foreach (controllers associated with X/Y/Z channel) result[channel] = calculate the float controller if (the controller is FloatReactionControl) convert result[channel] to radian convert the result euler angles into a quaternion else preserve the default value } Need an explaination? In special case, it performs radian to degree conversion

136 Group of controllers We replicated 3 types of lists – PositionList, RotationList, and ScaleList

137 void RotationList::Process(/* Omitted */) { foreach (controllers in the list) [current] = result of the controller if (controller is a *Constraint) [result] = Slerp([result], [current], [weight]) else [result] = [result] * [current] } Actually, only LookAtConstraint and OrientationConstraint requires this Need an explaination? (2) In special case, the calculation process changes!

138 EoD Jubok Kim | twitter.com/eiaserinnys (Korean) Choong-Hyo Kim | twitter.com/siyoskii (Korean)


Download ppt "Ⓒ 2011 NEXON Corporation & devCAT Studio. All Rights Reserved M 2 team, Game Development Team for Project M 2 in longCAT (The 3 rd New Development Division."

Similar presentations


Ads by Google