From bd124c20e5cfb99d8457daf883579029afcf818e Mon Sep 17 00:00:00 2001 From: Sneha Belkhale Date: Sun, 10 Mar 2019 22:29:19 +0700 Subject: [PATCH 1/5] adding example for using IK with threejs skinned meshes, including an axis conversion utility to convert models to +Z Forward coordinate system --- examples/assets/rigTest-02.fbx | 797 +++++++++++++++++++++++++++++++++ examples/index.html | 3 +- examples/scripts/AxisUtils.js | 83 ++++ examples/skinned-mesh.html | 116 +++++ 4 files changed, 998 insertions(+), 1 deletion(-) create mode 100644 examples/assets/rigTest-02.fbx create mode 100644 examples/scripts/AxisUtils.js create mode 100644 examples/skinned-mesh.html diff --git a/examples/assets/rigTest-02.fbx b/examples/assets/rigTest-02.fbx new file mode 100644 index 0000000..70a8d20 --- /dev/null +++ b/examples/assets/rigTest-02.fbx @@ -0,0 +1,797 @@ +; FBX 7.5.0 project file +; Copyright (C) 1997-2010 Autodesk Inc. and/or its licensors. +; All rights reserved. +; ---------------------------------------------------- + +FBXHeaderExtension: { + FBXHeaderVersion: 1003 + FBXVersion: 7500 + CreationTimeStamp: { + Version: 1000 + Year: 2019 + Month: 3 + Day: 9 + Hour: 21 + Minute: 5 + Second: 47 + Millisecond: 0 + } + Creator: "FBX SDK/FBX Plugins version 2016.1" + SceneInfo: "SceneInfo::GlobalInfo", "UserData" { + Type: "UserData" + Version: 100 + MetaData: { + Version: 100 + Title: "" + Subject: "" + Author: "" + Keywords: "" + Revision: "" + Comment: "" + } + Properties70: { + P: "DocumentUrl", "KString", "Url", "", "/Users/kif/pr/hong-kong-rat/export/rigTest-02.fbx" + P: "SrcDocumentUrl", "KString", "Url", "", "/Users/kif/pr/hong-kong-rat/export/rigTest-02.fbx" + P: "Original", "Compound", "", "" + P: "Original|ApplicationVendor", "KString", "", "", "" + P: "Original|ApplicationName", "KString", "", "", "" + P: "Original|ApplicationVersion", "KString", "", "", "" + P: "Original|DateTime_GMT", "DateTime", "", "", "" + P: "Original|FileName", "KString", "", "", "" + P: "LastSaved", "Compound", "", "" + P: "LastSaved|ApplicationVendor", "KString", "", "", "" + P: "LastSaved|ApplicationName", "KString", "", "", "" + P: "LastSaved|ApplicationVersion", "KString", "", "", "" + P: "LastSaved|DateTime_GMT", "DateTime", "", "", "" + } + } +} +GlobalSettings: { + Version: 1000 + Properties70: { + P: "UpAxis", "int", "Integer", "",1 + P: "UpAxisSign", "int", "Integer", "",1 + P: "FrontAxis", "int", "Integer", "",2 + P: "FrontAxisSign", "int", "Integer", "",1 + P: "CoordAxis", "int", "Integer", "",0 + P: "CoordAxisSign", "int", "Integer", "",1 + P: "OriginalUpAxis", "int", "Integer", "",-1 + P: "OriginalUpAxisSign", "int", "Integer", "",1 + P: "UnitScaleFactor", "double", "Number", "",1 + P: "OriginalUnitScaleFactor", "double", "Number", "",1 + P: "AmbientColor", "ColorRGB", "Color", "",0,0,0 + P: "DefaultCamera", "KString", "", "", "Producer Perspective" + P: "TimeMode", "enum", "", "",11 + P: "TimeProtocol", "enum", "", "",2 + P: "SnapOnFrameMode", "enum", "", "",0 + P: "TimeSpanStart", "KTime", "Time", "",46186158000 + P: "TimeSpanStop", "KTime", "Time", "",11084677920000 + P: "CustomFrameRate", "double", "Number", "",-1 + P: "TimeMarker", "Compound", "", "" + P: "CurrentTimeMarker", "int", "Integer", "",-1 + } +} + +; Documents Description +;------------------------------------------------------------------ + +Documents: { + Count: 1 + Document: 140452948174272, "", "Scene" { + Properties70: { + P: "SourceObject", "object", "", "" + P: "ActiveAnimStackName", "KString", "", "", "" + } + RootNode: 0 + } +} + +; Document References +;------------------------------------------------------------------ + +References: { +} + +; Object definitions +;------------------------------------------------------------------ + +Definitions: { + Version: 100 + Count: 49 + ObjectType: "GlobalSettings" { + Count: 1 + } + ObjectType: "Model" { + Count: 8 + PropertyTemplate: "FbxNode" { + Properties70: { + P: "QuaternionInterpolate", "enum", "", "",0 + P: "RotationOffset", "Vector3D", "Vector", "",0,0,0 + P: "RotationPivot", "Vector3D", "Vector", "",0,0,0 + P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0 + P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0 + P: "TranslationActive", "bool", "", "",0 + P: "TranslationMin", "Vector3D", "Vector", "",0,0,0 + P: "TranslationMax", "Vector3D", "Vector", "",0,0,0 + P: "TranslationMinX", "bool", "", "",0 + P: "TranslationMinY", "bool", "", "",0 + P: "TranslationMinZ", "bool", "", "",0 + P: "TranslationMaxX", "bool", "", "",0 + P: "TranslationMaxY", "bool", "", "",0 + P: "TranslationMaxZ", "bool", "", "",0 + P: "RotationOrder", "enum", "", "",0 + P: "RotationSpaceForLimitOnly", "bool", "", "",0 + P: "RotationStiffnessX", "double", "Number", "",0 + P: "RotationStiffnessY", "double", "Number", "",0 + P: "RotationStiffnessZ", "double", "Number", "",0 + P: "AxisLen", "double", "Number", "",10 + P: "PreRotation", "Vector3D", "Vector", "",0,0,0 + P: "PostRotation", "Vector3D", "Vector", "",0,0,0 + P: "RotationActive", "bool", "", "",0 + P: "RotationMin", "Vector3D", "Vector", "",0,0,0 + P: "RotationMax", "Vector3D", "Vector", "",0,0,0 + P: "RotationMinX", "bool", "", "",0 + P: "RotationMinY", "bool", "", "",0 + P: "RotationMinZ", "bool", "", "",0 + P: "RotationMaxX", "bool", "", "",0 + P: "RotationMaxY", "bool", "", "",0 + P: "RotationMaxZ", "bool", "", "",0 + P: "InheritType", "enum", "", "",0 + P: "ScalingActive", "bool", "", "",0 + P: "ScalingMin", "Vector3D", "Vector", "",0,0,0 + P: "ScalingMax", "Vector3D", "Vector", "",1,1,1 + P: "ScalingMinX", "bool", "", "",0 + P: "ScalingMinY", "bool", "", "",0 + P: "ScalingMinZ", "bool", "", "",0 + P: "ScalingMaxX", "bool", "", "",0 + P: "ScalingMaxY", "bool", "", "",0 + P: "ScalingMaxZ", "bool", "", "",0 + P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0 + P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0 + P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1 + P: "MinDampRangeX", "double", "Number", "",0 + P: "MinDampRangeY", "double", "Number", "",0 + P: "MinDampRangeZ", "double", "Number", "",0 + P: "MaxDampRangeX", "double", "Number", "",0 + P: "MaxDampRangeY", "double", "Number", "",0 + P: "MaxDampRangeZ", "double", "Number", "",0 + P: "MinDampStrengthX", "double", "Number", "",0 + P: "MinDampStrengthY", "double", "Number", "",0 + P: "MinDampStrengthZ", "double", "Number", "",0 + P: "MaxDampStrengthX", "double", "Number", "",0 + P: "MaxDampStrengthY", "double", "Number", "",0 + P: "MaxDampStrengthZ", "double", "Number", "",0 + P: "PreferedAngleX", "double", "Number", "",0 + P: "PreferedAngleY", "double", "Number", "",0 + P: "PreferedAngleZ", "double", "Number", "",0 + P: "LookAtProperty", "object", "", "" + P: "UpVectorProperty", "object", "", "" + P: "Show", "bool", "", "",1 + P: "NegativePercentShapeSupport", "bool", "", "",1 + P: "DefaultAttributeIndex", "int", "Integer", "",-1 + P: "Freeze", "bool", "", "",0 + P: "LODBox", "bool", "", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0 + P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + P: "Visibility", "Visibility", "", "A",1 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",1 + } + } + } + ObjectType: "NodeAttribute" { + Count: 7 + PropertyTemplate: "FbxNull" { + Properties70: { + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "Size", "double", "Number", "",100 + P: "Look", "enum", "", "",1 + } + } + } + ObjectType: "Geometry" { + Count: 1 + PropertyTemplate: "FbxMesh" { + Properties70: { + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "BBoxMin", "Vector3D", "Vector", "",0,0,0 + P: "BBoxMax", "Vector3D", "Vector", "",0,0,0 + P: "Primary Visibility", "bool", "", "",1 + P: "Casts Shadows", "bool", "", "",1 + P: "Receive Shadows", "bool", "", "",1 + } + } + } + ObjectType: "AnimationStack" { + Count: 1 + PropertyTemplate: "FbxAnimStack" { + Properties70: { + P: "Description", "KString", "", "", "" + P: "LocalStart", "KTime", "Time", "",0 + P: "LocalStop", "KTime", "Time", "",0 + P: "ReferenceStart", "KTime", "Time", "",0 + P: "ReferenceStop", "KTime", "Time", "",0 + } + } + } + ObjectType: "AnimationLayer" { + Count: 1 + PropertyTemplate: "FbxAnimLayer" { + Properties70: { + P: "Weight", "Number", "", "A",100 + P: "Mute", "bool", "", "",0 + P: "Solo", "bool", "", "",0 + P: "Lock", "bool", "", "",0 + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "BlendMode", "enum", "", "",0 + P: "RotationAccumulationMode", "enum", "", "",0 + P: "ScaleAccumulationMode", "enum", "", "",0 + P: "BlendModeBypass", "ULongLong", "", "",0 + } + } + } + ObjectType: "AnimationCurveNode" { + Count: 6 + PropertyTemplate: "FbxAnimCurveNode" { + Properties70: { + P: "d", "Compound", "", "" + } + } + } + ObjectType: "AnimationCurve" { + Count: 18 + } + ObjectType: "Deformer" { + Count: 5 + } + ObjectType: "Pose" { + Count: 1 + } +} + +; Object properties +;------------------------------------------------------------------ + +Objects: { + NodeAttribute: 140452949221776, "NodeAttribute::world_root", "Null" { + Properties70: { + P: "Look", "enum", "", "",0 + } + TypeFlags: "Null" + } + Geometry: 140452938475904, "Geometry::mesh", "Mesh" { + Vertices: *156 { + a: -0.5,0,-0.5,0.5,0,-0.5,0.5,0,0.5,-0.5,0,0.5,-0.5,1,-0.5,0.5,1,-0.5,0.5,1,0.5,-0.5,1,0.5,0.5,5.31280088424683,-0.5,0.5,5.31280088424683,0.5,-0.5,5.31280088424683,0.5,-0.5,5.31280088424683,-0.5,-0.5,0,1.57523274421692,-0.5,1,1.57523274421692,0.5,1,1.57523274421692,0.5,0,1.57523274421692,0.5,1.4312801361084,0.5,-0.5,1.4312801361084,0.5,0.5,1.8625602722168,0.5,-0.5,1.8625602722168,0.5,0.5,2.2938404083252,0.5,-0.5,2.2938404083252,0.5,0.5,2.72512054443359,0.5,-0.5,2.72512054443359,0.5,0.5,3.15640068054199,0.5,-0.5,3.15640068054199,0.5,0.5,3.58768081665039,0.5,-0.5,3.58768081665039,0.5,0.5,4.01896095275879,0.5,-0.5,4.01896095275879,0.5,0.5,4.45024108886719,0.5,-0.5,4.45024108886719,0.5,0.5,4.88152122497559,0.5,-0.5,4.88152122497559,0.5,-0.5,1.4312801361084,-0.5,-0.5,1.8625602722168,-0.5,-0.5,2.2938404083252,-0.5,-0.5,2.72512054443359,-0.5,-0.5,3.15640068054199,-0.5,-0.5,3.58768081665039,-0.5,-0.5,4.01896095275879,-0.5,-0.5,4.45024108886719,-0.5,-0.5,4.88152122497559,-0.5,0.5,1.4312801361084,-0.5,0.5,1.8625602722168,-0.5,0.5,2.2938404083252,-0.5,0.5,2.72512054443359,-0.5,0.5,3.15640068054199,-0.5,0.5,3.58768081665039,-0.5,0.5,4.01896095275879,-0.5,0.5,4.45024108886719,-0.5,0.5,4.88152122497559,-0.5 + } + PolygonVertexIndex: *200 { + a: 0,4,5,-2,1,5,6,-3,3,7,4,-1,3,0,1,-3,11,10,9,-9,15,14,13,-13,12,13,7,-4,13,14,6,-8,14,15,2,-7,15,12,3,-3,17,7,6,-17,19,17,16,-19,21,19,18,-21,23,21,20,-23,25,23,22,-25,27,25,24,-27,29,27,26,-29,31,29,28,-31,33,31,30,-33,32,9,10,-34,34,4,7,-18,35,34,17,-20,36,35,19,-22,37,36,21,-24,38,37,23,-26,39,38,25,-28,40,39,27,-30,41,40,29,-32,42,41,31,-34,33,10,11,-43,43,5,4,-35,44,43,34,-36,45,44,35,-37,46,45,36,-38,47,46,37,-39,48,47,38,-40,49,48,39,-41,50,49,40,-42,51,50,41,-43,42,11,8,-52,16,6,5,-44,18,16,43,-45,20,18,44,-46,22,20,45,-47,24,22,46,-48,26,24,47,-49,28,26,48,-50,30,28,49,-51,32,30,50,-52,51,8,9,-33 + } + GeometryVersion: 124 + LayerElementNormal: 0 { + Version: 102 + Name: "N" + MappingInformationType: "ByPolygon" + ReferenceInformationType: "Direct" + Normals: *150 { + a: 0,0,-1,1,0,0,-1,0,0,0,-1,0,0,1,0,0,0,1,-1,0,0,0,1,0,1,0,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0.999999940395355,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-0.999999940395355,0,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-0.999999940395355,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0.999999940395355,0,0 + } + NormalsW: *50 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + } + LayerElementColor: 0 { + Version: 101 + Name: "Cd" + MappingInformationType: "ByPolygon" + ReferenceInformationType: "Direct" + Colors: *200 { + a: 0,0,1,1,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,0,0,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0.999999940395355,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,0.999999940395355,0,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0.999999940395355,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,0.999999940395355,0,0,1 + } + } + LayerElementUserData: 0 { + Version: 101 + Name: "UserDataLayer0" + MappingInformationType: "AllSame" + ReferenceInformationType: "Direct" + UserDataId: 0 + UserDataArray: { + UserDataType: "Float" + UserDataName: "pCaptFrame" + UserData: *1 { + a: 1 + } + } + } + Layer: 0 { + Version: 100 + LayerElement: { + Type: "LayerElementNormal" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementColor" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementUserData" + TypedIndex: 0 + } + } + } + NodeAttribute: 140452990134800, "NodeAttribute::root", "Null" { + TypeFlags: "Null" + } + NodeAttribute: 140452948093120, "NodeAttribute::bone1", "Root" { + TypeFlags: "Null", "Skeleton", "Root" + } + NodeAttribute: 140452995892016, "NodeAttribute::bone2", "LimbNode" { + TypeFlags: "Skeleton" + } + NodeAttribute: 140452943145568, "NodeAttribute::bone3", "LimbNode" { + TypeFlags: "Skeleton" + } + NodeAttribute: 140452971465152, "NodeAttribute::bone4", "LimbNode" { + TypeFlags: "Skeleton" + } + NodeAttribute: 140452943150464, "NodeAttribute::bone4_end_effector", "LimbNode" { + TypeFlags: "Skeleton" + } + Model: 140452983256576, "Model::world_root", "Null" { + Version: 232 + Properties70: { + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452743942656, "Model::mesh", "Mesh" { + Version: 232 + Properties70: { + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452979594240, "Model::root", "Null" { + Version: 232 + Properties70: { + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",0,6.01473474502563,-2.01256275177002 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452977363456, "Model::bone1", "Root" { + Version: 232 + Properties70: { + P: "RotationOrder", "enum", "", "",5 + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Rotation", "Lcl Rotation", "", "A",18.7982009690537,179.999994844218,179.999997878857 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,0.999999984776801,0.999999984776801 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452938210304, "Model::bone2", "LimbNode" { + Version: 232 + Properties70: { + P: "RotationOrder", "enum", "", "",5 + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",1.94610103268145e-07,-7.26192777023016e-08,-2.16268591847666 + P: "Lcl Rotation", "Lcl Rotation", "", "A",71.793087442368,5.03067387413121e-06,3.06003502936578e-06 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1.00000002332392,1.00000002332392 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452743804416, "Model::bone3", "LimbNode" { + Version: 232 + Properties70: { + P: "RotationOrder", "enum", "", "",5 + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",1.87368184203859e-07,2.09820120652959e-09,-2.32034163016393 + P: "Lcl Rotation", "Lcl Rotation", "", "A",-0.591288411422127,3.56750512476097e-07,-5.19875344178686e-08 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,0.999999991899276,0.999999991899275 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452743952896, "Model::bone4", "LimbNode" { + Version: 232 + Properties70: { + P: "RotationOrder", "enum", "", "",5 + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",2.20850378457253e-07,9.65026669685898e-15,-2.52623376250266 + P: "Lcl Rotation", "Lcl Rotation", "", "A",-79.9999999999998,1.5274149840555e-07,-7.65627377633972e-06 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Model: 140452477256704, "Model::bone4_end_effector", "LimbNode" { + Version: 232 + Properties70: { + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",0,0,-1.58039259910583 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",0 + } + Shading: Y + Culling: "CullingOff" + } + Pose: 140452950974480, "Pose::mesh", "BindPose" { + Type: "BindPose" + Version: 100 + NbPoseNodes: 8 + PoseNode: { + Node: 0 + Matrix: *16 { + a: 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 + } + } + PoseNode: { + Node: 140452983256576 + Matrix: *16 { + a: 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 + } + } + PoseNode: { + Node: 140452979594240 + Matrix: *16 { + a: 1,0,0,0,0,1,0,0,0,0,1,0,0,6.01473474502563,-2.01256275177002,1 + } + } + PoseNode: { + Node: 140452977363456 + Matrix: *16 { + a: 0.999999999999995,6.0496832380289e-09,9.71149705978034e-08,0,3.70209268081306e-08,-0.946659364062731,-0.322235966313427,0,8.99853735227214e-08,0.322235966313429,-0.946659364062726,0,0,6.01473474502563,-2.01256275177002,1 + } + } + PoseNode: { + Node: 140452938210304 + Matrix: *16 { + a: 0.999999999999996,-8.11963089024008e-08,4.28040363489265e-08,0,4.36396819992639e-08,0.0103197465123925,-0.999946758099334,0,8.07502577434883e-08,0.999946758099332,0.010319746512396,0,3.93259424429822e-16,5.31783962699858,0.0347641478830692,1 + } + } + PoseNode: { + Node: 140452743804416 + Matrix: *16 { + a: 0.999999999999995,-8.74227800037247e-08,4.37113900018624e-08,0,4.37113900018622e-08,-3.82333054105288e-15,-0.999999999999999,0,8.74227800037248e-08,0.999999999999996,1.73472347597681e-18,0,2.64697796016969e-23,2.99762153625488,0.0108188083395362,1 + } + } + PoseNode: { + Node: 140452743952896 + Matrix: *16 { + a: 0.999999999999997,4.37113900018624e-08,6.95408492289436e-08,0,5.51229575107911e-08,-0.984807753012207,-0.173648177666927,0,6.08939642546049e-08,0.17364817766693,-0.984807753012206,0,1.05879118406788e-22,0.471387773752213,0.0108188083395362,1 + } + } + PoseNode: { + Node: 140452743942656 + Matrix: *16 { + a: 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 + } + } + } + Deformer: 140452471472848, "Deformer::", "Skin" { + Version: 101 + Link_DeformAcuracy: 50 + SkinningType: "Linear" + } + Deformer: 140452471525344, "SubDeformer::", "Cluster" { + Version: 100 + UserData: "", "" + Indexes: *13 { + a: 0,1,2,3,6,7,12,13,14,15,16,17,19 + } + Weights: *13 { + a: 0.5,0.5,1,1,0.548556864261627,0.55434787273407,1,0.969225466251373,0.982765972614288,1,0.138466164469719,0.170758664608002,0.000712834880687296 + } + Transform: *16 { + a: 0.999999998839336,4.37113906782531e-08,6.95408451888335e-08,0,4.37113906782533e-08,-0.999999998839336,-1.91361979417845e-16,0,6.95408475568158e-08,3.23108914511496e-15,-0.999999964787662,0,-2.13573558154686e-08,0.471387784535811,0.0108187352988623,1 + } + TransformLink: *16 { + a: 1.00000000116066,4.37113907797215e-08,6.95408477182424e-08,0,4.37113907797215e-08,-1.00000000116066,3.23108915261537e-15,0,6.95408500862249e-08,-1.91361992894451e-16,-1.00000003521233,0,-3.86791549552278e-15,0.471387785082934,0.0108187356798152,1 + } + } + Deformer: 140452950967024, "SubDeformer::", "Cluster" { + Version: 100 + UserData: "", "" + Indexes: *30 { + a: 0,1,4,5,6,7,13,14,16,17,18,19,20,21,22,23,24,25,34,35,36,37,38,39,43,44,45,46,47,48 + } + Weights: *30 { + a: 0.5,0.5,1,1,0.45144310593605,0.44565212726593,0.0307745542377234,0.0172340553253889,0.86153382062912,0.829241335391998,1,0.999287188053131,0.97689950466156,1,0.772360384464264,0.786726415157318,0.346185028553009,0.245670050382614,1,1,1,0.8277547955513,0.326833069324493,0.00109193765092641,1,1,1,0.810589969158173,0.307815670967102,0.0233198832720518 + } + Transform: *16 { + a: 0.999999956623377,4.37113899031381e-08,8.7422774421285e-08,0,-8.74227744212851e-08,-3.76013880759974e-15,0.999999956623377,0,4.37113899031385e-08,-0.999999956623377,6.12323373013156e-17,0,2.61587477298034e-07,0.0108188075825666,-2.99762159437064,1 + } + TransformLink: *16 { + a: 1.00000004337662,-8.74227820054941e-08,4.37113936952432e-08,0,4.37113936952433e-08,-3.760139133804e-15,-1.00000004337662,0,8.74227820054943e-08,1.00000004337662,6.12323426134196e-17,0,2.53772585703869e-14,2.99762172439735,0.0108188080518387,1 + } + } + Deformer: 140452950969024, "SubDeformer::", "Cluster" { + Version: 100 + UserData: "", "" + Indexes: *29 { + a: 8,9,10,11,20,22,23,24,25,26,27,28,29,30,31,32,33,37,38,39,40,41,42,46,47,48,49,50,51 + } + Weights: *29 { + a: 1,1,1,1,0.0231005121022463,0.227639615535736,0.213273569941521,0.653814911842346,0.754329979419708,1,1,1,1,1,1,1,1,0.172245234251022,0.673166930675507,0.998908042907715,1,1,1,0.189410045742989,0.692184329032898,0.976680099964142,1,1,1 + } + Transform: *16 { + a: 1.00000002827856,4.36396805435813e-08,8.07502608579233e-08,0,-8.11963154536138e-08,0.0103197467666703,0.999946727630989,0,4.28040353490156e-08,-0.999946757701318,0.0103197464563392,0,4.30300924480118e-07,-0.0201164550288303,-5.31791504889334,1 + } + TransformLink: *16 { + a: 0.999999971721429,-8.1196310861383e-08,4.28040329281421e-08,0,4.36396798710629e-08,0.0103197466076357,-0.999946742291431,0,8.07502644701366e-08,0.999946772361755,0.0103197469179739,0,9.97363296108912e-15,5.31783958555348,0.0347641537633147,1 + } + } + Deformer: 140452950970800, "SubDeformer::", "Cluster" { + Version: 100 + UserData: "", "" + Transform: *16 { + a: 0.999999971554217,3.7020922183626e-08,8.99853641396799e-08,0,6.04968210640078e-09,-0.94665936965189,0.322235982215133,0,9.71149574613748e-08,-0.322235973727771,-0.946659394585907,0,1.59062720802321e-07,5.0453850316211,-3.84337541171404,1 + } + TransformLink: *16 { + a: 1.00000002844577,6.04968245057664e-09,9.7114962986396e-08,0,3.70209227421236e-08,-0.946659383933193,-0.322235978589023,0,8.99853607569578e-08,0.322235970101666,-0.946659358999169,0,-1.18393856313585e-14,6.01473488999875,-2.01256272062356,1 + } + } + AnimationStack: 140452966180704, "AnimStack::Main", "" { + } + AnimationLayer: 140452990124048, "AnimLayer::Base Layer", "" { + } + AnimationCurveNode: 140452990122608, "AnimCurveNode::T", "" { + Properties70: { + P: "d|X", "Number", "", "A",0 + P: "d|Y", "Number", "", "A",0 + P: "d|Z", "Number", "", "A",0 + } + } + AnimationCurveNode: 140452471430048, "AnimCurveNode::R", "" { + Properties70: { + P: "d|X", "Number", "", "A",0 + P: "d|Y", "Number", "", "A",0 + P: "d|Z", "Number", "", "A",0 + } + } + AnimationCurveNode: 140452471485664, "AnimCurveNode::S", "" { + Properties70: { + P: "d|X", "Number", "", "A",1 + P: "d|Y", "Number", "", "A",1 + P: "d|Z", "Number", "", "A",1 + } + } + AnimationCurveNode: 140452951998048, "AnimCurveNode::T", "" { + Properties70: { + P: "d|X", "Number", "", "A",0 + P: "d|Y", "Number", "", "A",0 + P: "d|Z", "Number", "", "A",0 + } + } + AnimationCurveNode: 140452471579968, "AnimCurveNode::R", "" { + Properties70: { + P: "d|X", "Number", "", "A",0 + P: "d|Y", "Number", "", "A",0 + P: "d|Z", "Number", "", "A",0 + } + } + AnimationCurveNode: 140452951074128, "AnimCurveNode::S", "" { + Properties70: { + P: "d|X", "Number", "", "A",1 + P: "d|Y", "Number", "", "A",1 + P: "d|Z", "Number", "", "A",1 + } + } +} + +; Object connections +;------------------------------------------------------------------ + +Connections: { + + ;Model::world_root, Model::RootNode + C: "OO",140452983256576,0 + + ;NodeAttribute::world_root, Model::world_root + C: "OO",140452949221776,140452983256576 + + ;Model::mesh, Model::world_root + C: "OO",140452743942656,140452983256576 + + ;Model::root, Model::world_root + C: "OO",140452979594240,140452983256576 + + ;AnimCurveNode::T, Model::world_root + C: "OP",140452990122608,140452983256576, "Lcl Translation" + + ;AnimCurveNode::R, Model::world_root + C: "OP",140452471430048,140452983256576, "Lcl Rotation" + + ;AnimCurveNode::S, Model::world_root + C: "OP",140452471485664,140452983256576, "Lcl Scaling" + + ;Geometry::mesh, Model::mesh + C: "OO",140452938475904,140452743942656 + + ;AnimCurveNode::T, Model::mesh + C: "OP",140452951998048,140452743942656, "Lcl Translation" + + ;AnimCurveNode::R, Model::mesh + C: "OP",140452471579968,140452743942656, "Lcl Rotation" + + ;AnimCurveNode::S, Model::mesh + C: "OP",140452951074128,140452743942656, "Lcl Scaling" + + ;Deformer::, Geometry::mesh + C: "OO",140452471472848,140452938475904 + + ;NodeAttribute::root, Model::root + C: "OO",140452990134800,140452979594240 + + ;Model::bone1, Model::root + C: "OO",140452977363456,140452979594240 + + ;NodeAttribute::bone1, Model::bone1 + C: "OO",140452948093120,140452977363456 + + ;Model::bone2, Model::bone1 + C: "OO",140452938210304,140452977363456 + + ;NodeAttribute::bone2, Model::bone2 + C: "OO",140452995892016,140452938210304 + + ;Model::bone3, Model::bone2 + C: "OO",140452743804416,140452938210304 + + ;NodeAttribute::bone3, Model::bone3 + C: "OO",140452943145568,140452743804416 + + ;Model::bone4, Model::bone3 + C: "OO",140452743952896,140452743804416 + + ;NodeAttribute::bone4, Model::bone4 + C: "OO",140452971465152,140452743952896 + + ;Model::bone4_end_effector, Model::bone4 + C: "OO",140452477256704,140452743952896 + + ;NodeAttribute::bone4_end_effector, Model::bone4_end_effector + C: "OO",140452943150464,140452477256704 + + ;AnimLayer::Base Layer, AnimStack::Main + C: "OO",140452990124048,140452966180704 + + ;AnimCurveNode::T, AnimLayer::Base Layer + C: "OO",140452990122608,140452990124048 + + ;AnimCurveNode::R, AnimLayer::Base Layer + C: "OO",140452471430048,140452990124048 + + ;AnimCurveNode::S, AnimLayer::Base Layer + C: "OO",140452471485664,140452990124048 + + ;AnimCurveNode::T, AnimLayer::Base Layer + C: "OO",140452951998048,140452990124048 + + ;AnimCurveNode::R, AnimLayer::Base Layer + C: "OO",140452471579968,140452990124048 + + ;AnimCurveNode::S, AnimLayer::Base Layer + C: "OO",140452951074128,140452990124048 + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452938572272,140452990122608, "d|X" + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452938452832,140452990122608, "d|Y" + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452938470048,140452990122608, "d|Z" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452471438496,140452471430048, "d|X" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452471411520,140452471430048, "d|Y" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452951081840,140452471430048, "d|Z" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452471481808,140452471485664, "d|X" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452471528768,140452471485664, "d|Y" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452471473392,140452471485664, "d|Z" + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452951084448,140452951998048, "d|X" + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452471480016,140452951998048, "d|Y" + + ;AnimCurve::, AnimCurveNode::T + C: "OP",140452471433280,140452951998048, "d|Z" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452951765216,140452471579968, "d|X" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452951072016,140452471579968, "d|Y" + + ;AnimCurve::, AnimCurveNode::R + C: "OP",140452951073104,140452471579968, "d|Z" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452951079264,140452951074128, "d|X" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452951080592,140452951074128, "d|Y" + + ;AnimCurve::, AnimCurveNode::S + C: "OP",140452951077040,140452951074128, "d|Z" + + ;SubDeformer::, Deformer:: + C: "OO",140452471525344,140452471472848 + + ;SubDeformer::, Deformer:: + C: "OO",140452950967024,140452471472848 + + ;SubDeformer::, Deformer:: + C: "OO",140452950969024,140452471472848 + + ;SubDeformer::, Deformer:: + C: "OO",140452950970800,140452471472848 + + ;Model::bone4, SubDeformer:: + C: "OO",140452743952896,140452471525344 + + ;Model::bone3, SubDeformer:: + C: "OO",140452743804416,140452950967024 + + ;Model::bone2, SubDeformer:: + C: "OO",140452938210304,140452950969024 + + ;Model::bone1, SubDeformer:: + C: "OO",140452977363456,140452950970800 +} +;Takes section +;---------------------------------------------------- + +Takes: { + Current: "" + Take: "Main" { + FileName: "Main.tak" + LocalTime: 0,46186158000 + ReferenceTime: 0,46186158000 + } +} diff --git a/examples/index.html b/examples/index.html index 0df140e..d5b1c6f 100644 --- a/examples/index.html +++ b/examples/index.html @@ -247,7 +247,8 @@

THREE.IK

"single-effector", "single-target", "multi-effector", - "readme-example" + "readme-example", + "skinned-mesh" ] }; function extractQuery() { diff --git a/examples/scripts/AxisUtils.js b/examples/scripts/AxisUtils.js new file mode 100644 index 0000000..d15d990 --- /dev/null +++ b/examples/scripts/AxisUtils.js @@ -0,0 +1,83 @@ +/** + * @author snayss -- https://codercat.tk + * + * Helper utility to iterate through a THREE.Bone heirarchy from a model + * created in an external software and set each bone +Z Forward vector to + * face the child bone. + * + **/ + +const t = new THREE.Vector3(); +const q = new THREE.Quaternion(); +const p = new THREE.Plane(); +const FORWARD = new THREE.Vector3(0,0,1); +var RESETQUAT = new THREE.Quaternion(); + +/** +* Takes in a rootBone and recursively traverses the bone heirarchy, +* setting each bone's +Z axis to face it's child bones. The IK system follows this +* convention, so this step is necessary to update the bindings of a skinned mesh. +* +* Must rebind the model to it's skeleton after this function. +* +* @param {THREE.BONE} rootBone +*/ + +function setZForward(rootBone) { + var worldPos = {}; + getOriginalWorldPositions(rootBone, worldPos); + updateTransformations(rootBone, worldPos); +} + +function updateTransformations(parentBone, worldPos) { + + var averagedDir = new THREE.Vector3(); + parentBone.children.forEach((childBone) => { + //average the child bone world pos + var childBonePosWorld = worldPos[childBone.id]; + averagedDir.add(childBonePosWorld); + }); + + averagedDir.multiplyScalar(1/(parentBone.children.length)); + + //set quaternion + parentBone.quaternion.copy(RESETQUAT); + parentBone.updateMatrixWorld(); + //get the child bone position in local coordinates + var childBoneDir = parentBone.worldToLocal(averagedDir).normalize(); + //set the direction to child bone to the forward direction + var quat = getAlignmentQuaternion(FORWARD, childBoneDir); + if (quat) { + //rotate parent bone towards child bone + parentBone.quaternion.premultiply(quat); + parentBone.updateMatrixWorld(); + //set child bone position relative to the new parent matrix. + parentBone.children.forEach((childBone) => { + var childBonePosWorld = worldPos[childBone.id].clone(); + parentBone.worldToLocal(childBonePosWorld); + childBone.position.copy(childBonePosWorld); + }); + } + + parentBone.children.forEach((childBone) => { + updateTransformations(childBone, worldPos); + }) +} + +function getAlignmentQuaternion(fromDir, toDir) { + const adjustAxis = t.crossVectors(fromDir, toDir).normalize(); + const adjustAngle = fromDir.angleTo(toDir); + if (adjustAngle) { + const adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); + return adjustQuat; + } + return null; +} + +function getOriginalWorldPositions(rootBone, worldPos) { + rootBone.children.forEach((child) => { + var childWorldPos = child.getWorldPosition(new THREE.Vector3()); + worldPos[child.id] = childWorldPos; + getOriginalWorldPositions(child, worldPos); + }) +} diff --git a/examples/skinned-mesh.html b/examples/skinned-mesh.html new file mode 100644 index 0000000..002f052 --- /dev/null +++ b/examples/skinned-mesh.html @@ -0,0 +1,116 @@ + + + + + THREE.IK - skinned mesh test + + + + + + +
+ skinned mesh example by codercat +
+ + + + + + + + + + + + From 5acacd9c4ab557f9fa6c74d1c377fe1d235ff150 Mon Sep 17 00:00:00 2001 From: Sneha Belkhale Date: Mon, 11 Mar 2019 19:37:38 +0700 Subject: [PATCH 2/5] hinge constraints with example - needs more testing --- build/three-ik.js | 77 +++++++++++++++++++- build/three-ik.module.js | 80 +++++++++++++++++++-- examples/hinge-constraints.html | 124 ++++++++++++++++++++++++++++++++ examples/index.html | 3 +- examples/scripts/AxisUtils.js | 102 +++++++++++++++++--------- src/IKHingeConstraint.js | 66 +++++++++++++++++ src/IKJoint.js | 15 ++-- src/index.js | 4 +- src/utils.js | 59 ++++++++++++++- 9 files changed, 483 insertions(+), 47 deletions(-) create mode 100644 examples/hinge-constraints.html create mode 100644 src/IKHingeConstraint.js diff --git a/build/three-ik.js b/build/three-ik.js index 5af851c..062d562 100644 --- a/build/three-ik.js +++ b/build/three-ik.js @@ -8,6 +8,9 @@ var t1 = new three.Vector3(); var t2 = new three.Vector3(); var t3 = new three.Vector3(); var m1 = new three.Matrix4(); +var t = new three.Vector3(); +var q = new three.Quaternion(); +var p = new three.Plane(); function getWorldPosition(object, target) { return target.setFromMatrixPosition(object.matrixWorld); } @@ -47,7 +50,7 @@ function setQuaternionFromDirection(direction, up, target) { var el = m1.elements; z.copy(direction); x.crossVectors(up, z); - if (x.lengthSq() === 0) { + if (x.lengthSq() == 0) { if (Math.abs(up.z) === 1) { z.x += 0.0001; } else { @@ -71,6 +74,28 @@ function transformPoint(vector, matrix, target) { var w = vector.x * e[3] + vector.y * e[7] + vector.z * e[11] + e[15]; target.set(x / w, y / w, z / w); } +function getAlignmentQuaternion(fromDir, toDir) { + var adjustAxis = t.crossVectors(fromDir, toDir).normalize(); + var adjustAngle = fromDir.angleTo(toDir); + if (adjustAngle > 0.01 && adjustAngle < 3.14) { + var adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); + return adjustQuat; + } + return null; +} +function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { + p.normal = normal; + var projectedVec = p.projectPoint(toDir, new three.Vector3()).normalize(); + var quat = getAlignmentQuaternion(fromDir, projectedVec); + return quat; +} +function rotateOnAxis(bone, direction, axis) { + var forward = new three.Vector3(0, 0, 1).applyQuaternion(bone.quaternion); + var q = getAlignmentQuaternionOnPlane(direction, forward, axis); + if (q) { + bone.quaternion.premultiply(q); + } +} var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; @@ -372,6 +397,8 @@ var IKJoint = function () { this._isSubBase = false; this._subBasePositions = null; this.isIKJoint = true; + this._originalUp = new three.Vector3(0, 1, 0); + this._originalUp.applyQuaternion(this.bone.quaternion).normalize(); this._updateWorldPosition(); } createClass(IKJoint, [{ @@ -499,7 +526,11 @@ var IKJoint = function () { this.bone.position.copy(position); this._updateMatrixWorld(); this._worldToLocalDirection(direction); - setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); + if (this.constraints[0] && this.constraints[0].type === "hinge") { + rotateOnAxis(this.bone, direction, this.constraints[0].axis); + } else { + setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); + } } else { this.bone.position.copy(position); } @@ -847,6 +878,46 @@ var IK = function () { return IK; }(); +var Z_AXIS$1 = new three.Vector3(0, 0, -1); +var X_AXIS = new three.Vector3(1, 0, 0); +var t1$1 = new three.Vector3(); +var t2$1 = new three.Vector3(); +var t3$1 = new three.Vector3(); +var t4 = new three.Vector3(); +var RAD2DEG$1 = three.Math.RAD2DEG; +var IKHingeConstraint = function () { + function IKHingeConstraint(angle, axis) { + classCallCheck(this, IKHingeConstraint); + this.axis = axis; + this.angle = angle; + this.type = "hinge"; + this.rotationPlane = new three.Plane(this.axis); + } + createClass(IKHingeConstraint, [{ + key: '_apply', + value: function _apply(joint) { + var direction = new three.Vector3().copy(joint._getDirection()); + var parentDirection = joint._localToWorldDirection(t1$1.copy(Z_AXIS$1)).normalize(); + var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(this.axis)).normalize(); + this.rotationPlane.normal = rotationPlaneNormal; + var projectedDir = this.rotationPlane.projectPoint(direction, new three.Vector3()); + var parentDirectionProjected = this.rotationPlane.projectPoint(parentDirection, t3$1); + var currentAngle = projectedDir.angleTo(parentDirectionProjected) * RAD2DEG$1; + var cross = t4.crossVectors(projectedDir, parentDirectionProjected); + if (cross.dot(rotationPlaneNormal) > 0) { + currentAngle += 180; + } + if (currentAngle > this.angle) { + parentDirectionProjected.applyAxisAngle(rotationPlaneNormal, this.angle / RAD2DEG$1); + joint._setDirection(parentDirectionProjected); + } else { + joint._setDirection(projectedDir); + } + } + }]); + return IKHingeConstraint; +}(); + var BoneHelper = function (_Object3D) { inherits(BoneHelper, _Object3D); function BoneHelper(height, boneSize, axesSize) { @@ -1169,6 +1240,7 @@ if (typeof window !== 'undefined' && _typeof(window.THREE) === 'object') { window.THREE.IKChain = IKChain; window.THREE.IKJoint = IKJoint; window.THREE.IKBallConstraint = IKBallConstraint; + window.THREE.IKHingeConstraint = IKHingeConstraint; window.THREE.IKHelper = IKHelper; } @@ -1176,6 +1248,7 @@ exports.IK = IK; exports.IKChain = IKChain; exports.IKJoint = IKJoint; exports.IKBallConstraint = IKBallConstraint; +exports.IKHingeConstraint = IKHingeConstraint; exports.IKHelper = IKHelper; Object.defineProperty(exports, '__esModule', { value: true }); diff --git a/build/three-ik.module.js b/build/three-ik.module.js index 837d259..8a42ff7 100644 --- a/build/three-ik.module.js +++ b/build/three-ik.module.js @@ -1,9 +1,12 @@ -import { AxesHelper, Color, ConeBufferGeometry, Math as Math$1, Matrix4, Mesh, MeshBasicMaterial, Object3D, Vector3 } from 'three'; +import { AxesHelper, Color, ConeBufferGeometry, Math as Math$1, Matrix4, Mesh, MeshBasicMaterial, Object3D, Plane, Quaternion, Vector3 } from 'three'; var t1 = new Vector3(); var t2 = new Vector3(); var t3 = new Vector3(); var m1 = new Matrix4(); +var t = new Vector3(); +var q = new Quaternion(); +var p = new Plane(); function getWorldPosition(object, target) { return target.setFromMatrixPosition(object.matrixWorld); } @@ -43,7 +46,7 @@ function setQuaternionFromDirection(direction, up, target) { var el = m1.elements; z.copy(direction); x.crossVectors(up, z); - if (x.lengthSq() === 0) { + if (x.lengthSq() == 0) { if (Math.abs(up.z) === 1) { z.x += 0.0001; } else { @@ -67,6 +70,28 @@ function transformPoint(vector, matrix, target) { var w = vector.x * e[3] + vector.y * e[7] + vector.z * e[11] + e[15]; target.set(x / w, y / w, z / w); } +function getAlignmentQuaternion(fromDir, toDir) { + var adjustAxis = t.crossVectors(fromDir, toDir).normalize(); + var adjustAngle = fromDir.angleTo(toDir); + if (adjustAngle > 0.01 && adjustAngle < 3.14) { + var adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); + return adjustQuat; + } + return null; +} +function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { + p.normal = normal; + var projectedVec = p.projectPoint(toDir, new Vector3()).normalize(); + var quat = getAlignmentQuaternion(fromDir, projectedVec); + return quat; +} +function rotateOnAxis(bone, direction, axis) { + var forward = new Vector3(0, 0, 1).applyQuaternion(bone.quaternion); + var q = getAlignmentQuaternionOnPlane(direction, forward, axis); + if (q) { + bone.quaternion.premultiply(q); + } +} var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; @@ -368,6 +393,8 @@ var IKJoint = function () { this._isSubBase = false; this._subBasePositions = null; this.isIKJoint = true; + this._originalUp = new Vector3(0, 1, 0); + this._originalUp.applyQuaternion(this.bone.quaternion).normalize(); this._updateWorldPosition(); } createClass(IKJoint, [{ @@ -495,7 +522,11 @@ var IKJoint = function () { this.bone.position.copy(position); this._updateMatrixWorld(); this._worldToLocalDirection(direction); - setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); + if (this.constraints[0] && this.constraints[0].type === "hinge") { + rotateOnAxis(this.bone, direction, this.constraints[0].axis); + } else { + setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); + } } else { this.bone.position.copy(position); } @@ -843,6 +874,46 @@ var IK = function () { return IK; }(); +var Z_AXIS$1 = new Vector3(0, 0, -1); +var X_AXIS = new Vector3(1, 0, 0); +var t1$1 = new Vector3(); +var t2$1 = new Vector3(); +var t3$1 = new Vector3(); +var t4 = new Vector3(); +var RAD2DEG$1 = Math$1.RAD2DEG; +var IKHingeConstraint = function () { + function IKHingeConstraint(angle, axis) { + classCallCheck(this, IKHingeConstraint); + this.axis = axis; + this.angle = angle; + this.type = "hinge"; + this.rotationPlane = new Plane(this.axis); + } + createClass(IKHingeConstraint, [{ + key: '_apply', + value: function _apply(joint) { + var direction = new Vector3().copy(joint._getDirection()); + var parentDirection = joint._localToWorldDirection(t1$1.copy(Z_AXIS$1)).normalize(); + var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(this.axis)).normalize(); + this.rotationPlane.normal = rotationPlaneNormal; + var projectedDir = this.rotationPlane.projectPoint(direction, new Vector3()); + var parentDirectionProjected = this.rotationPlane.projectPoint(parentDirection, t3$1); + var currentAngle = projectedDir.angleTo(parentDirectionProjected) * RAD2DEG$1; + var cross = t4.crossVectors(projectedDir, parentDirectionProjected); + if (cross.dot(rotationPlaneNormal) > 0) { + currentAngle += 180; + } + if (currentAngle > this.angle) { + parentDirectionProjected.applyAxisAngle(rotationPlaneNormal, this.angle / RAD2DEG$1); + joint._setDirection(parentDirectionProjected); + } else { + joint._setDirection(projectedDir); + } + } + }]); + return IKHingeConstraint; +}(); + var BoneHelper = function (_Object3D) { inherits(BoneHelper, _Object3D); function BoneHelper(height, boneSize, axesSize) { @@ -1165,7 +1236,8 @@ if (typeof window !== 'undefined' && _typeof(window.THREE) === 'object') { window.THREE.IKChain = IKChain; window.THREE.IKJoint = IKJoint; window.THREE.IKBallConstraint = IKBallConstraint; + window.THREE.IKHingeConstraint = IKHingeConstraint; window.THREE.IKHelper = IKHelper; } -export { IK, IKChain, IKJoint, IKBallConstraint, IKHelper }; +export { IK, IKChain, IKJoint, IKBallConstraint, IKHingeConstraint, IKHelper }; diff --git a/examples/hinge-constraints.html b/examples/hinge-constraints.html new file mode 100644 index 0000000..0d5551e --- /dev/null +++ b/examples/hinge-constraints.html @@ -0,0 +1,124 @@ + + + + + THREE.IK - hinge constraint test + + + + + + + + + + + + + + + + + + diff --git a/examples/index.html b/examples/index.html index d5b1c6f..576ecde 100644 --- a/examples/index.html +++ b/examples/index.html @@ -248,7 +248,8 @@

THREE.IK

"single-target", "multi-effector", "readme-example", - "skinned-mesh" + "skinned-mesh", + "hinge-constraints" ] }; function extractQuery() { diff --git a/examples/scripts/AxisUtils.js b/examples/scripts/AxisUtils.js index d15d990..5123f31 100644 --- a/examples/scripts/AxisUtils.js +++ b/examples/scripts/AxisUtils.js @@ -10,6 +10,10 @@ const t = new THREE.Vector3(); const q = new THREE.Quaternion(); const p = new THREE.Plane(); +const t1 = new THREE.Vector3(); +const t2 = new THREE.Vector3(); +const t3 = new THREE.Vector3(); +const m1 = new THREE.Matrix4(); const FORWARD = new THREE.Vector3(0,0,1); var RESETQUAT = new THREE.Quaternion(); @@ -23,18 +27,17 @@ var RESETQUAT = new THREE.Quaternion(); * @param {THREE.BONE} rootBone */ -function setZForward(rootBone) { - var worldPos = {}; - getOriginalWorldPositions(rootBone, worldPos); - updateTransformations(rootBone, worldPos); +function setZForward(rootBone, scene) { + var worldPos = {} + getOriginalWorldPositions(rootBone, worldPos) + updateTransformations(rootBone, worldPos, scene); } -function updateTransformations(parentBone, worldPos) { - +function updateTransformations(parentBone, worldPos, scene) { var averagedDir = new THREE.Vector3(); parentBone.children.forEach((childBone) => { //average the child bone world pos - var childBonePosWorld = worldPos[childBone.id]; + var childBonePosWorld = worldPos[childBone.id][0]; averagedDir.add(childBonePosWorld); }); @@ -43,41 +46,76 @@ function updateTransformations(parentBone, worldPos) { //set quaternion parentBone.quaternion.copy(RESETQUAT); parentBone.updateMatrixWorld(); + //get the child bone position in local coordinates - var childBoneDir = parentBone.worldToLocal(averagedDir).normalize(); - //set the direction to child bone to the forward direction - var quat = getAlignmentQuaternion(FORWARD, childBoneDir); - if (quat) { - //rotate parent bone towards child bone - parentBone.quaternion.premultiply(quat); - parentBone.updateMatrixWorld(); - //set child bone position relative to the new parent matrix. - parentBone.children.forEach((childBone) => { - var childBonePosWorld = worldPos[childBone.id].clone(); - parentBone.worldToLocal(childBonePosWorld); - childBone.position.copy(childBonePosWorld); - }); - } + var childBoneDir = parentBone.worldToLocal(averagedDir.clone()).normalize(); + + //preserve the original up vectors + var up = _worldToLocalDirection(worldPos[parentBone.id][1].clone(), parentBone) + setQuaternionFromDirection(childBoneDir, up, parentBone.quaternion) + parentBone.updateMatrixWorld(); + //set child bone position relative to the new parent matrix. parentBone.children.forEach((childBone) => { - updateTransformations(childBone, worldPos); + var childBonePosWorld = worldPos[childBone.id][0].clone(); + parentBone.worldToLocal(childBonePosWorld); + childBone.position.copy(childBonePosWorld); + }); + + parentBone.children.forEach((childBone) => { + updateTransformations(childBone, worldPos, scene); }) } -function getAlignmentQuaternion(fromDir, toDir) { - const adjustAxis = t.crossVectors(fromDir, toDir).normalize(); - const adjustAngle = fromDir.angleTo(toDir); - if (adjustAngle) { - const adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); - return adjustQuat; +//borrowing this from utils.js , not sure how to import it +function setQuaternionFromDirection(direction, up, target) { + const x = t1; + const y = t2; + const z = t3; + const m = m1; + const el = m1.elements; + + z.copy(direction); + x.crossVectors(up, z); + + if (x.lengthSq() === 0) { + // parallel + if (Math.abs(up.z) === 1) { + z.x += 0.0001; + } else { + z.z += 0.0001; + } + z.normalize(); + x.crossVectors(up, z); } - return null; + + x.normalize(); + y.crossVectors(z, x); + + el[ 0 ] = x.x; el[ 4 ] = y.x; el[ 8 ] = z.x; + el[ 1 ] = x.y; el[ 5 ] = y.y; el[ 9 ] = z.y; + el[ 2 ] = x.z; el[ 6 ] = y.z; el[ 10 ] = z.z; + + target.setFromRotationMatrix(m); } function getOriginalWorldPositions(rootBone, worldPos) { + var rootBoneWorldPos = rootBone.getWorldPosition(new THREE.Vector3()) + var rootBoneUpDir = _localToWorldDirection(Y_AXIS.clone(), rootBone); + worldPos[rootBone.id] = [rootBoneWorldPos, rootBoneUpDir]; rootBone.children.forEach((child) => { - var childWorldPos = child.getWorldPosition(new THREE.Vector3()); - worldPos[child.id] = childWorldPos; - getOriginalWorldPositions(child, worldPos); + getOriginalWorldPositions(child, worldPos) }) } + +function _worldToLocalDirection(direction, sparent) { + const inverseParent = new THREE.Matrix4().getInverse(sparent.matrixWorld); + direction.transformDirection(inverseParent); + return direction; +} + +function _localToWorldDirection(direction, sparent) { + const parent = sparent.matrixWorld; + direction.transformDirection(parent); + return direction; +} diff --git a/src/IKHingeConstraint.js b/src/IKHingeConstraint.js new file mode 100644 index 0000000..06e7d35 --- /dev/null +++ b/src/IKHingeConstraint.js @@ -0,0 +1,66 @@ +import { Quaternion, Matrix4, Vector3, Plane, Math as ThreeMath } from 'three'; +import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection } from './utils.js'; + +const Z_AXIS = new Vector3(0, 0, -1); +const X_AXIS = new Vector3(1, 0, 0); + +const t1 = new Vector3(); +const t2 = new Vector3(); +const t3 = new Vector3(); +const t4 = new Vector3(); + +const { DEG2RAD, RAD2DEG } = ThreeMath; + +/** + * A class for a constraint. + */ +class IKHingeConstraint { + /** + * Pass in an angle value in degrees, and axis of rotation. + * Axis of rotation must be in local coordinates. + * + * @param {number} angle + * @param {Vector3} axis + */ + constructor(angle, axis) { + this.axis = axis; + this.angle = angle; + this.type = "hinge" + this.rotationPlane = new Plane(this.axis); + } + + /** + * Applies a hinge constraint to passed in IKJoint. The direction will always be updated + * with this constraint, because it will always be projected onto the rotation plane. + * Additionally, an angle constraint will be applied if necessary. + * + * @param {IKJoint} joint + * @private + */ + _apply(joint) { + // Get direction of joint and parent in world space + const direction = new Vector3().copy(joint._getDirection()); + const parentDirection = joint._localToWorldDirection(t1.copy(Z_AXIS)).normalize(); + const rotationPlaneNormal = joint._localToWorldDirection(t2.copy(this.axis)).normalize(); + this.rotationPlane.normal = rotationPlaneNormal; + var projectedDir = this.rotationPlane.projectPoint(direction, new Vector3()) + + var parentDirectionProjected = this.rotationPlane.projectPoint(parentDirection, t3) + var currentAngle = projectedDir.angleTo(parentDirectionProjected) * RAD2DEG; + + //apply adjustment to angle if it is "negative" + var cross = t4.crossVectors(projectedDir, parentDirectionProjected); + if(cross.dot(rotationPlaneNormal) > 0){ + currentAngle += 180; + } + + if(currentAngle > this.angle){ + parentDirectionProjected.applyAxisAngle(rotationPlaneNormal, this.angle/RAD2DEG); + joint._setDirection(parentDirectionProjected); + } else { + joint._setDirection(projectedDir); + } + } +} + +export default IKHingeConstraint; diff --git a/src/IKJoint.js b/src/IKJoint.js index 438b824..87c3860 100644 --- a/src/IKJoint.js +++ b/src/IKJoint.js @@ -1,5 +1,5 @@ import { Quaternion, Matrix4, Vector3 } from 'three'; -import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection } from './utils.js'; +import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection, rotateOnAxis } from './utils.js'; import IKBallConstraint from './IKBallConstraint.js'; const Y_AXIS = new Vector3(0, 1, 0); @@ -27,6 +27,8 @@ class IKJoint { this._subBasePositions = null; this.isIKJoint = true; + this._originalUp = new Vector3(0,1,0); + this._originalUp.applyQuaternion(this.bone.quaternion).normalize(); this._updateWorldPosition(); } @@ -174,12 +176,15 @@ class IKJoint { let inverseParent = new Matrix4().getInverse(this.bone.parent.matrixWorld); transformPoint(position, inverseParent, position); this.bone.position.copy(position); - this._updateMatrixWorld(); - this._worldToLocalDirection(direction); - setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); - + if (this.constraints[0] && this.constraints[0].type === "hinge") { + rotateOnAxis(this.bone, direction, this.constraints[0].axis); + } else { + //TODO: make a flag to not use original up if you are not using hinge constraints + //with predefined axis that need to stay constant. + setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); + } } else { this.bone.position.copy(position); } diff --git a/src/index.js b/src/index.js index 3971ad5..a963627 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ import IK from './IK.js'; import IKChain from './IKChain.js'; import IKJoint from './IKJoint.js'; import IKBallConstraint from './IKBallConstraint.js'; +import IKHingeConstraint from './IKHingeConstraint.js'; import IKHelper from './IKHelper.js'; // If this is being included via script tag and using THREE @@ -12,9 +13,10 @@ if (typeof window !== 'undefined' && typeof window.THREE === 'object') { window.THREE.IKChain = IKChain; window.THREE.IKJoint = IKJoint; window.THREE.IKBallConstraint = IKBallConstraint; + window.THREE.IKHingeConstraint = IKHingeConstraint; window.THREE.IKHelper = IKHelper; } export { - IK, IKChain, IKJoint, IKBallConstraint, IKHelper + IK, IKChain, IKJoint, IKBallConstraint, IKHingeConstraint, IKHelper }; diff --git a/src/utils.js b/src/utils.js index 9a5e337..ecb3f2c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -import { Vector3, Matrix4 } from 'three'; +import { Vector3, Matrix4, Quaternion, Plane } from 'three'; /** * A collection of utilities. @@ -9,6 +9,9 @@ const t1 = new Vector3(); const t2 = new Vector3(); const t3 = new Vector3(); const m1 = new Matrix4(); +const t = new Vector3(); +const q = new Quaternion(); +const p = new Plane(); /** * Returns the world position of object and sets @@ -71,7 +74,8 @@ export function setQuaternionFromDirection(direction, up, target) { z.copy(direction); x.crossVectors(up, z); - if (x.lengthSq() === 0) { + + if (x.lengthSq() == 0) { // parallel if (Math.abs(up.z) === 1) { z.x += 0.0001; @@ -117,3 +121,54 @@ export function transformPoint(vector, matrix, target) { const w = (vector.x * e[3]) + (vector.y * e[7]) + (vector.z * e[11]) + e[15]; target.set(x / w, y / w, z / w); }; + +/** + * Aligns two vectors about their cross product. Returns a quaternion that will + * map the fromDir vector to the toDir vector. + * + * @param {THREE.Vector3} fromDir + * @param {THREE.Vector3} toDir + * @return {THREE.Quaternion | null} + */ +export function getAlignmentQuaternion(fromDir, toDir) { + const adjustAxis = t.crossVectors(fromDir, toDir).normalize(); + const adjustAngle = fromDir.angleTo(toDir); + + if (adjustAngle > 0.01 && adjustAngle < 3.14) { + const adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); + return adjustQuat; + } + return null; +} + +/** + * Aligns two vectors, but ensuring that the result is on a plane oriented by the normal param. + * Returns a quaternion that will map the fromDir vector to the toDir vector. + * + * @param {THREE.Vector3} fromDir + * @param {THREE.Vector3} toDir + * @param {THREE.Vector3} normal + * @return {THREE.Vector3 | normal} + */ +export function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { + p.normal = normal; + const projectedVec = p.projectPoint(toDir, new Vector3()).normalize(); + const quat = getAlignmentQuaternion(fromDir, projectedVec); + return quat; +} + +/** + * Takes a three.js bone and rotates it on a (hinge) axis to the final + * direction. + * + * @param {THREE.BONE} bone + * @param {THREE.Vector3} direction + * @param {THREE.Vector3} axis + */ +export function rotateOnAxis(bone, direction, axis) { + var forward = new Vector3(0,0,1).applyQuaternion(bone.quaternion); + var q = getAlignmentQuaternionOnPlane(direction,forward,axis); + if(q){ + bone.quaternion.premultiply(q) + } +} From 2e1bb87960f374db6bb0650c000131701fd052c4 Mon Sep 17 00:00:00 2001 From: Sneha Belkhale Date: Mon, 11 Mar 2019 22:50:25 +0700 Subject: [PATCH 3/5] hinge constraint now uses initial bone positions to set rotation axis --- build/three-ik.js | 42 +++++++-------------------------- build/three-ik.module.js | 42 +++++++-------------------------- examples/hinge-constraints.html | 6 ++--- examples/scripts/AxisUtils.js | 18 +++++++------- src/IKChain.js | 7 ++++-- src/IKHingeConstraint.js | 13 ++++------ src/IKJoint.js | 9 ++----- 7 files changed, 41 insertions(+), 96 deletions(-) diff --git a/build/three-ik.js b/build/three-ik.js index 062d562..04c51e3 100644 --- a/build/three-ik.js +++ b/build/three-ik.js @@ -74,28 +74,6 @@ function transformPoint(vector, matrix, target) { var w = vector.x * e[3] + vector.y * e[7] + vector.z * e[11] + e[15]; target.set(x / w, y / w, z / w); } -function getAlignmentQuaternion(fromDir, toDir) { - var adjustAxis = t.crossVectors(fromDir, toDir).normalize(); - var adjustAngle = fromDir.angleTo(toDir); - if (adjustAngle > 0.01 && adjustAngle < 3.14) { - var adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); - return adjustQuat; - } - return null; -} -function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { - p.normal = normal; - var projectedVec = p.projectPoint(toDir, new three.Vector3()).normalize(); - var quat = getAlignmentQuaternion(fromDir, projectedVec); - return quat; -} -function rotateOnAxis(bone, direction, axis) { - var forward = new three.Vector3(0, 0, 1).applyQuaternion(bone.quaternion); - var q = getAlignmentQuaternionOnPlane(direction, forward, axis); - if (q) { - bone.quaternion.premultiply(q); - } -} var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; @@ -392,6 +370,7 @@ var IKJoint = function () { this.bone = bone; this.distance = 0; this._originalDirection = new three.Vector3(); + this._originalHinge = new three.Vector3(); this._direction = new three.Vector3(); this._worldPosition = new three.Vector3(); this._isSubBase = false; @@ -526,11 +505,7 @@ var IKJoint = function () { this.bone.position.copy(position); this._updateMatrixWorld(); this._worldToLocalDirection(direction); - if (this.constraints[0] && this.constraints[0].type === "hinge") { - rotateOnAxis(this.bone, direction, this.constraints[0].axis); - } else { - setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); - } + setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); } else { this.bone.position.copy(position); } @@ -584,6 +559,7 @@ var IKChain = function () { } else { var previousJoint = this.joints[this.joints.length - 2]; + var previousPreviousJoint = this.joints[this.joints.length - 3]; previousJoint._updateMatrixWorld(); previousJoint._updateWorldPosition(); joint._updateWorldPosition(); @@ -595,7 +571,9 @@ var IKChain = function () { joint._updateWorldPosition(); var direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new three.Vector3().copy(direction); - joint._originalDirection = new three.Vector3().copy(direction); + if (previousPreviousJoint) { + previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); + } this.totalLengths += distance; } if (target) { @@ -886,19 +864,17 @@ var t3$1 = new three.Vector3(); var t4 = new three.Vector3(); var RAD2DEG$1 = three.Math.RAD2DEG; var IKHingeConstraint = function () { - function IKHingeConstraint(angle, axis) { + function IKHingeConstraint(angle) { classCallCheck(this, IKHingeConstraint); - this.axis = axis; this.angle = angle; - this.type = "hinge"; - this.rotationPlane = new three.Plane(this.axis); + this.rotationPlane = new three.Plane(); } createClass(IKHingeConstraint, [{ key: '_apply', value: function _apply(joint) { var direction = new three.Vector3().copy(joint._getDirection()); var parentDirection = joint._localToWorldDirection(t1$1.copy(Z_AXIS$1)).normalize(); - var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(this.axis)).normalize(); + var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(joint._originalHinge)).normalize(); this.rotationPlane.normal = rotationPlaneNormal; var projectedDir = this.rotationPlane.projectPoint(direction, new three.Vector3()); var parentDirectionProjected = this.rotationPlane.projectPoint(parentDirection, t3$1); diff --git a/build/three-ik.module.js b/build/three-ik.module.js index 8a42ff7..b8805c0 100644 --- a/build/three-ik.module.js +++ b/build/three-ik.module.js @@ -70,28 +70,6 @@ function transformPoint(vector, matrix, target) { var w = vector.x * e[3] + vector.y * e[7] + vector.z * e[11] + e[15]; target.set(x / w, y / w, z / w); } -function getAlignmentQuaternion(fromDir, toDir) { - var adjustAxis = t.crossVectors(fromDir, toDir).normalize(); - var adjustAngle = fromDir.angleTo(toDir); - if (adjustAngle > 0.01 && adjustAngle < 3.14) { - var adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); - return adjustQuat; - } - return null; -} -function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { - p.normal = normal; - var projectedVec = p.projectPoint(toDir, new Vector3()).normalize(); - var quat = getAlignmentQuaternion(fromDir, projectedVec); - return quat; -} -function rotateOnAxis(bone, direction, axis) { - var forward = new Vector3(0, 0, 1).applyQuaternion(bone.quaternion); - var q = getAlignmentQuaternionOnPlane(direction, forward, axis); - if (q) { - bone.quaternion.premultiply(q); - } -} var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; @@ -388,6 +366,7 @@ var IKJoint = function () { this.bone = bone; this.distance = 0; this._originalDirection = new Vector3(); + this._originalHinge = new Vector3(); this._direction = new Vector3(); this._worldPosition = new Vector3(); this._isSubBase = false; @@ -522,11 +501,7 @@ var IKJoint = function () { this.bone.position.copy(position); this._updateMatrixWorld(); this._worldToLocalDirection(direction); - if (this.constraints[0] && this.constraints[0].type === "hinge") { - rotateOnAxis(this.bone, direction, this.constraints[0].axis); - } else { - setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); - } + setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); } else { this.bone.position.copy(position); } @@ -580,6 +555,7 @@ var IKChain = function () { } else { var previousJoint = this.joints[this.joints.length - 2]; + var previousPreviousJoint = this.joints[this.joints.length - 3]; previousJoint._updateMatrixWorld(); previousJoint._updateWorldPosition(); joint._updateWorldPosition(); @@ -591,7 +567,9 @@ var IKChain = function () { joint._updateWorldPosition(); var direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new Vector3().copy(direction); - joint._originalDirection = new Vector3().copy(direction); + if (previousPreviousJoint) { + previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); + } this.totalLengths += distance; } if (target) { @@ -882,19 +860,17 @@ var t3$1 = new Vector3(); var t4 = new Vector3(); var RAD2DEG$1 = Math$1.RAD2DEG; var IKHingeConstraint = function () { - function IKHingeConstraint(angle, axis) { + function IKHingeConstraint(angle) { classCallCheck(this, IKHingeConstraint); - this.axis = axis; this.angle = angle; - this.type = "hinge"; - this.rotationPlane = new Plane(this.axis); + this.rotationPlane = new Plane(); } createClass(IKHingeConstraint, [{ key: '_apply', value: function _apply(joint) { var direction = new Vector3().copy(joint._getDirection()); var parentDirection = joint._localToWorldDirection(t1$1.copy(Z_AXIS$1)).normalize(); - var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(this.axis)).normalize(); + var rotationPlaneNormal = joint._localToWorldDirection(t2$1.copy(joint._originalHinge)).normalize(); this.rotationPlane.normal = rotationPlaneNormal; var projectedDir = this.rotationPlane.projectPoint(direction, new Vector3()); var parentDirectionProjected = this.rotationPlane.projectPoint(parentDirection, t3$1); diff --git a/examples/hinge-constraints.html b/examples/hinge-constraints.html index 0d5551e..8f610c0 100644 --- a/examples/hinge-constraints.html +++ b/examples/hinge-constraints.html @@ -86,9 +86,9 @@ var currentBone = boneGroup; var constraintArray = [ new IK.IKBallConstraint(90), - new IK.IKHingeConstraint(180, new THREE.Vector3(-1,0,0)), - new IK.IKHingeConstraint(180, new THREE.Vector3(-1,0,0)), - new IK.IKHingeConstraint(100, new THREE.Vector3(-1,0,0)), + new IK.IKHingeConstraint(180), + new IK.IKHingeConstraint(180), + new IK.IKHingeConstraint(100), ]; for (let i = 0; i < 5; i++) { diff --git a/examples/scripts/AxisUtils.js b/examples/scripts/AxisUtils.js index 5123f31..487b5eb 100644 --- a/examples/scripts/AxisUtils.js +++ b/examples/scripts/AxisUtils.js @@ -50,9 +50,8 @@ function updateTransformations(parentBone, worldPos, scene) { //get the child bone position in local coordinates var childBoneDir = parentBone.worldToLocal(averagedDir.clone()).normalize(); - //preserve the original up vectors - var up = _worldToLocalDirection(worldPos[parentBone.id][1].clone(), parentBone) - setQuaternionFromDirection(childBoneDir, up, parentBone.quaternion) + //set direction to face child + setQuaternionFromDirection(childBoneDir, Y_AXIS, parentBone.quaternion) parentBone.updateMatrixWorld(); //set child bone position relative to the new parent matrix. @@ -101,21 +100,20 @@ function setQuaternionFromDirection(direction, up, target) { function getOriginalWorldPositions(rootBone, worldPos) { var rootBoneWorldPos = rootBone.getWorldPosition(new THREE.Vector3()) - var rootBoneUpDir = _localToWorldDirection(Y_AXIS.clone(), rootBone); - worldPos[rootBone.id] = [rootBoneWorldPos, rootBoneUpDir]; + worldPos[rootBone.id] = [rootBoneWorldPos]; rootBone.children.forEach((child) => { getOriginalWorldPositions(child, worldPos) }) } -function _worldToLocalDirection(direction, sparent) { - const inverseParent = new THREE.Matrix4().getInverse(sparent.matrixWorld); +function _worldToLocalDirection(direction, parent) { + const inverseParent = new THREE.Matrix4().getInverse(parent.matrixWorld); direction.transformDirection(inverseParent); return direction; } -function _localToWorldDirection(direction, sparent) { - const parent = sparent.matrixWorld; - direction.transformDirection(parent); +function _localToWorldDirection(direction, parent) { + const parentMat = parent.matrixWorld; + direction.transformDirection(parentMat); return direction; } diff --git a/src/IKChain.js b/src/IKChain.js index dd34dfc..309cd82 100644 --- a/src/IKChain.js +++ b/src/IKChain.js @@ -60,6 +60,8 @@ class IKChain { // and update the total length. else { const previousJoint = this.joints[this.joints.length - 2]; + const previousPreviousJoint = this.joints[this.joints.length - 3]; + previousJoint._updateMatrixWorld(); previousJoint._updateWorldPosition(); joint._updateWorldPosition(); @@ -73,8 +75,9 @@ class IKChain { joint._updateWorldPosition(); const direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new Vector3().copy(direction); - joint._originalDirection = new Vector3().copy(direction); - + if(previousPreviousJoint){ + previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); + } this.totalLengths += distance; } diff --git a/src/IKHingeConstraint.js b/src/IKHingeConstraint.js index 06e7d35..a8d1a69 100644 --- a/src/IKHingeConstraint.js +++ b/src/IKHingeConstraint.js @@ -16,17 +16,14 @@ const { DEG2RAD, RAD2DEG } = ThreeMath; */ class IKHingeConstraint { /** - * Pass in an angle value in degrees, and axis of rotation. - * Axis of rotation must be in local coordinates. + * Pass in an angle value in degrees, + * Axis of rotation for the constraint is calculated from initial bone positions. * * @param {number} angle - * @param {Vector3} axis */ - constructor(angle, axis) { - this.axis = axis; + constructor(angle) { this.angle = angle; - this.type = "hinge" - this.rotationPlane = new Plane(this.axis); + this.rotationPlane = new Plane(); } /** @@ -41,7 +38,7 @@ class IKHingeConstraint { // Get direction of joint and parent in world space const direction = new Vector3().copy(joint._getDirection()); const parentDirection = joint._localToWorldDirection(t1.copy(Z_AXIS)).normalize(); - const rotationPlaneNormal = joint._localToWorldDirection(t2.copy(this.axis)).normalize(); + const rotationPlaneNormal = joint._localToWorldDirection(t2.copy(joint._originalHinge)).normalize(); this.rotationPlane.normal = rotationPlaneNormal; var projectedDir = this.rotationPlane.projectPoint(direction, new Vector3()) diff --git a/src/IKJoint.js b/src/IKJoint.js index 87c3860..d7590c0 100644 --- a/src/IKJoint.js +++ b/src/IKJoint.js @@ -21,6 +21,7 @@ class IKJoint { this.distance = 0; this._originalDirection = new Vector3(); + this._originalHinge = new Vector3(); this._direction = new Vector3(); this._worldPosition = new Vector3(); this._isSubBase = false; @@ -178,13 +179,7 @@ class IKJoint { this.bone.position.copy(position); this._updateMatrixWorld(); this._worldToLocalDirection(direction); - if (this.constraints[0] && this.constraints[0].type === "hinge") { - rotateOnAxis(this.bone, direction, this.constraints[0].axis); - } else { - //TODO: make a flag to not use original up if you are not using hinge constraints - //with predefined axis that need to stay constant. - setQuaternionFromDirection(direction, this._originalUp, this.bone.quaternion); - } + setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); } else { this.bone.position.copy(position); } From 913eb2106b93be78f0c0b144e02e1d7c57983c80 Mon Sep 17 00:00:00 2001 From: Sneha Belkhale Date: Wed, 13 Mar 2019 21:42:10 +0530 Subject: [PATCH 4/5] cleaning up example / removing unused functions --- build/three-ik.js | 4 +-- build/three-ik.module.js | 6 ++-- examples/hinge-constraints.html | 3 +- src/IKChain.js | 2 ++ src/IKHingeConstraint.js | 3 +- src/IKJoint.js | 5 ++- src/utils.js | 56 +-------------------------------- 7 files changed, 13 insertions(+), 66 deletions(-) diff --git a/build/three-ik.js b/build/three-ik.js index 04c51e3..44e6050 100644 --- a/build/three-ik.js +++ b/build/three-ik.js @@ -8,9 +8,6 @@ var t1 = new three.Vector3(); var t2 = new three.Vector3(); var t3 = new three.Vector3(); var m1 = new three.Matrix4(); -var t = new three.Vector3(); -var q = new three.Quaternion(); -var p = new three.Plane(); function getWorldPosition(object, target) { return target.setFromMatrixPosition(object.matrixWorld); } @@ -571,6 +568,7 @@ var IKChain = function () { joint._updateWorldPosition(); var direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new three.Vector3().copy(direction); + joint._originalDirection = new three.Vector3().copy(direction); if (previousPreviousJoint) { previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); } diff --git a/build/three-ik.module.js b/build/three-ik.module.js index b8805c0..70ffb2a 100644 --- a/build/three-ik.module.js +++ b/build/three-ik.module.js @@ -1,12 +1,9 @@ -import { AxesHelper, Color, ConeBufferGeometry, Math as Math$1, Matrix4, Mesh, MeshBasicMaterial, Object3D, Plane, Quaternion, Vector3 } from 'three'; +import { AxesHelper, Color, ConeBufferGeometry, Math as Math$1, Matrix4, Mesh, MeshBasicMaterial, Object3D, Plane, Vector3 } from 'three'; var t1 = new Vector3(); var t2 = new Vector3(); var t3 = new Vector3(); var m1 = new Matrix4(); -var t = new Vector3(); -var q = new Quaternion(); -var p = new Plane(); function getWorldPosition(object, target) { return target.setFromMatrixPosition(object.matrixWorld); } @@ -567,6 +564,7 @@ var IKChain = function () { joint._updateWorldPosition(); var direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new Vector3().copy(direction); + joint._originalDirection = new Vector3().copy(direction); if (previousPreviousJoint) { previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); } diff --git a/examples/hinge-constraints.html b/examples/hinge-constraints.html index 8f610c0..9f60ed7 100644 --- a/examples/hinge-constraints.html +++ b/examples/hinge-constraints.html @@ -111,7 +111,8 @@ update() { if(this.target){ - this.target.position.y = 2+4*Math.sin(0.003*performance.now()) + this.target.position.y = 2 + 2 * Math.sin(0.003 * performance.now()) + this.target.position.x = 1 + 2 * Math.sin(0.003 * performance.now()) } } }; diff --git a/src/IKChain.js b/src/IKChain.js index 309cd82..a5069cd 100644 --- a/src/IKChain.js +++ b/src/IKChain.js @@ -75,6 +75,8 @@ class IKChain { joint._updateWorldPosition(); const direction = previousJoint._getWorldDirection(joint); previousJoint._originalDirection = new Vector3().copy(direction); + joint._originalDirection = new Vector3().copy(direction); + if(previousPreviousJoint){ previousJoint._originalHinge = previousJoint._worldToLocalDirection(previousJoint._originalDirection.clone().cross(previousPreviousJoint._originalDirection).normalize()); } diff --git a/src/IKHingeConstraint.js b/src/IKHingeConstraint.js index a8d1a69..79bd469 100644 --- a/src/IKHingeConstraint.js +++ b/src/IKHingeConstraint.js @@ -1,5 +1,4 @@ -import { Quaternion, Matrix4, Vector3, Plane, Math as ThreeMath } from 'three'; -import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection } from './utils.js'; +import { Quaternion, Vector3, Plane, Math as ThreeMath } from 'three'; const Z_AXIS = new Vector3(0, 0, -1); const X_AXIS = new Vector3(1, 0, 0); diff --git a/src/IKJoint.js b/src/IKJoint.js index d7590c0..afe5fd5 100644 --- a/src/IKJoint.js +++ b/src/IKJoint.js @@ -1,5 +1,5 @@ import { Quaternion, Matrix4, Vector3 } from 'three'; -import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection, rotateOnAxis } from './utils.js'; +import { transformPoint, getCentroid, getWorldPosition, setQuaternionFromDirection } from './utils.js'; import IKBallConstraint from './IKBallConstraint.js'; const Y_AXIS = new Vector3(0, 1, 0); @@ -177,9 +177,12 @@ class IKJoint { let inverseParent = new Matrix4().getInverse(this.bone.parent.matrixWorld); transformPoint(position, inverseParent, position); this.bone.position.copy(position); + this._updateMatrixWorld(); + this._worldToLocalDirection(direction); setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); + } else { this.bone.position.copy(position); } diff --git a/src/utils.js b/src/utils.js index ecb3f2c..a8c5256 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -import { Vector3, Matrix4, Quaternion, Plane } from 'three'; +import { Vector3, Matrix4 } from 'three'; /** * A collection of utilities. @@ -9,9 +9,6 @@ const t1 = new Vector3(); const t2 = new Vector3(); const t3 = new Vector3(); const m1 = new Matrix4(); -const t = new Vector3(); -const q = new Quaternion(); -const p = new Plane(); /** * Returns the world position of object and sets @@ -121,54 +118,3 @@ export function transformPoint(vector, matrix, target) { const w = (vector.x * e[3]) + (vector.y * e[7]) + (vector.z * e[11]) + e[15]; target.set(x / w, y / w, z / w); }; - -/** - * Aligns two vectors about their cross product. Returns a quaternion that will - * map the fromDir vector to the toDir vector. - * - * @param {THREE.Vector3} fromDir - * @param {THREE.Vector3} toDir - * @return {THREE.Quaternion | null} - */ -export function getAlignmentQuaternion(fromDir, toDir) { - const adjustAxis = t.crossVectors(fromDir, toDir).normalize(); - const adjustAngle = fromDir.angleTo(toDir); - - if (adjustAngle > 0.01 && adjustAngle < 3.14) { - const adjustQuat = q.setFromAxisAngle(adjustAxis, adjustAngle); - return adjustQuat; - } - return null; -} - -/** - * Aligns two vectors, but ensuring that the result is on a plane oriented by the normal param. - * Returns a quaternion that will map the fromDir vector to the toDir vector. - * - * @param {THREE.Vector3} fromDir - * @param {THREE.Vector3} toDir - * @param {THREE.Vector3} normal - * @return {THREE.Vector3 | normal} - */ -export function getAlignmentQuaternionOnPlane(toDir, fromDir, normal) { - p.normal = normal; - const projectedVec = p.projectPoint(toDir, new Vector3()).normalize(); - const quat = getAlignmentQuaternion(fromDir, projectedVec); - return quat; -} - -/** - * Takes a three.js bone and rotates it on a (hinge) axis to the final - * direction. - * - * @param {THREE.BONE} bone - * @param {THREE.Vector3} direction - * @param {THREE.Vector3} axis - */ -export function rotateOnAxis(bone, direction, axis) { - var forward = new Vector3(0,0,1).applyQuaternion(bone.quaternion); - var q = getAlignmentQuaternionOnPlane(direction,forward,axis); - if(q){ - bone.quaternion.premultiply(q) - } -} From e613326d0dc94474c7816a2c779f480e60f4a326 Mon Sep 17 00:00:00 2001 From: Sneha Belkhale Date: Wed, 13 Mar 2019 21:45:20 +0530 Subject: [PATCH 5/5] minor diffs --- src/IKJoint.js | 1 - src/utils.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/IKJoint.js b/src/IKJoint.js index afe5fd5..7ed3b8c 100644 --- a/src/IKJoint.js +++ b/src/IKJoint.js @@ -182,7 +182,6 @@ class IKJoint { this._worldToLocalDirection(direction); setQuaternionFromDirection(direction, Y_AXIS, this.bone.quaternion); - } else { this.bone.position.copy(position); } diff --git a/src/utils.js b/src/utils.js index a8c5256..c40c069 100644 --- a/src/utils.js +++ b/src/utils.js @@ -72,7 +72,7 @@ export function setQuaternionFromDirection(direction, up, target) { x.crossVectors(up, z); - if (x.lengthSq() == 0) { + if (x.lengthSq() === 0) { // parallel if (Math.abs(up.z) === 1) { z.x += 0.0001;