From 7d18cd9903987cf36c1c03d20f86ccea8ca7cfe3 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Mon, 26 Aug 2024 15:30:02 -0400 Subject: [PATCH 1/3] feat: Support skeletal animation basics [flame_3d] --- .../shaders/spatial_material.shaderbundle | Bin 58920 -> 100768 bytes .../lib/src/graphics/graphics_device.dart | 5 ++ .../lib/src/graphics/joints_info.dart | 13 +++++ .../resources/material/spatial_material.dart | 26 +++++++++- .../flame_3d/lib/src/resources/mesh/mesh.dart | 3 +- .../lib/src/resources/mesh/surface.dart | 6 ++- .../lib/src/resources/mesh/vertex.dart | 29 ++++++++++- .../flame_3d/shaders/spatial_material.vert | 48 ++++++++++++++++-- 8 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 packages/flame_3d/lib/src/graphics/joints_info.dart diff --git a/packages/flame_3d/assets/shaders/spatial_material.shaderbundle b/packages/flame_3d/assets/shaders/spatial_material.shaderbundle index 016d3a9ed3328153593235dc3454d277d5a9a818..648c1383821f50baa464a28bda16076fcc195851 100644 GIT binary patch literal 100768 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 delta 4318 zcmd5yzf1Cw~c?8{k4<) zc;DaW`@GNZckaEMkx3W6u)6F?ce)KD!7z*^&8~ZcZCG4|MLi6|yH%Y94px zM|C07J|Q>x17`hv8Syt;Nef)*`a|aUe0h6awOQ3AQ|kj(?Fm;dst=iGy5v1OZ21fac}^qkI3xm$`bCAcdcDTl zhLu=rFKVm_3$|yOWuGXlJN>YCXhc1K(ui*VTVJQ>iMq}?bd!LO{MnT!>H_AsuKZ_9Sd6RN96&GW+%%wb z4?NMg85&o&Nxh`F?>LbmTUv{)+n4mM1~2he`DsQGUa=J1DZ|J!l8rR%zI#ibt8ST@ zhMV(cG!UH;jl3h@-0OF04vU0|HE_^9{(&MP@!oaUwWYBAb~3EHw(V=&xpT)>!$`$3 z2}svvQnAWa-5u3-onh3}7{>34!Eml&Tz^iN`}!l-?ZW zK7}3;A1VLxcKSTe3RsFiucjFvi9L1JJP1+e$g?NYOPEd1D?l;{>kJ4VPcP7_%J9jN zlj-d~Sct+qm$hhP71}UcIy$hao;88#*5r#0;u})LD>v9)$6nV7$}n614fS<2}8L(MRFI zeyqGd{RHmE`KmCD*)Yt&N;ar7p%5z%p`8iT*(urxgt4s^34CxbS| z&!(X^YGD6=f<1%1fI{r~ZVI{QxBm9Um|t+@B~FKd$#gJ?kY_^JoYg2!(qD|)u*7UdQ5*U3 z;$}x}Y|<*s4Y_;?!hFWl&n9;p?TN}c2MPf&h^X0=5{L~U>T0k@opA)mW>!MjNc#A0 zQ>TxI&4sXO^s%|ryHS_E#SmVSXRU6J-t31_2bS0^eiXIE-|WXxo4#3+V(Dern9q3n zc_=4>_7w2Owk(CP&I||-uZy<0{Z%M*k~*SW2BD45N~AJjIfQl!9~L42FJ}ez%%R=O zS&2RE6h7b2Sp^$%+EpPhX*G;=Q22OBt8fdB@frxhusCDAIjO^*BTb44cpZeXw37|K z4zxW%#VQEvCP0e%E!Vx~H0FrHgnL(A*Lm%*2#L>~fgBM?OzL;tJXwA@TMqXmILXsQ zUKVp6Lf(ZCBlX8u9bc}fHBVi2BGa)R>6hb|YZGsx+zYb1*Y9-XiX4$AKQ72{4sH<> zIcCPYpSx}luU1MLUVglCEcD`JIbP7}{9&`0CO_!gB9~s7D&LryE4v;Zl^d_*_f5ar zH}UzlZCiu(=3u40&>7AY)1AaD@l!Kc=`?1EmaJgq?7;qCzA=0L^ERGcjr$NfH{^^g z7un9^ERpV{t`HMs^@H^CxuK0^4U8>j#K!WteTCZ3wqLT_wzcltwtvO$)?JM|PvwY3 zlRVmv-SSv7t8dAHp1I;HO<5Bh&p=uIElt_uK~34{EmCHtd?h2j;W(=Jsv7_LJAN2~ zsxnZuAxhO!tc8HLcgOB#yS%K->D?@*Kb5rXZ$ACsNc$#BRJSXdz?8>QY+KO~+g5U` zcrONHTZ2U;w5$P~#+d(mXTLDssSV;ve8 zLWjcU=|{RnqeIWsqQ4I57PSrO7BzjTTGaXIdo5Day#JBwzJ@Oa8z1~tXcecqc*=(Z zYsCJP5C6DXWSh>cE#i>#w=JSTlsRXMMNLw4h$kNserIHvXy6bpUM@Pg+8rXx)PuV+ z_)Md^qd5>it#Zol%-FPU2fEMrj%V|ybaE2W`5uuJ=k5^^bcS<8N@6{No-(;4H2!6; zeWYm_{fD9@F@Ut76uqT#m5tAcT#@0l923jpX4pBoM&wMr0(NTtpV-OgyUg$HH1!)1 z8$2oayvJK+3@Nr!Ph#tR$_=`Tuw{pFwW`ZG{7B3vk!?G+{eu5>*l0SxXZX)cD%S6E zna> jointTransformsPerSurface = {}; + + /// Joints for the current surface + List jointTransforms = []; + + void setSurface(int surfaceIdx) { + jointTransforms = jointTransformsPerSurface[surfaceIdx] ?? []; + } +} \ No newline at end of file diff --git a/packages/flame_3d/lib/src/resources/material/spatial_material.dart b/packages/flame_3d/lib/src/resources/material/spatial_material.dart index dd12426b6bb..18953935357 100644 --- a/packages/flame_3d/lib/src/resources/material/spatial_material.dart +++ b/packages/flame_3d/lib/src/resources/material/spatial_material.dart @@ -16,7 +16,18 @@ class SpatialMaterial extends Material { vertexShader: Shader( _library['TextureVertex']!, slots: [ - UniformSlot.value('VertexInfo', {'model', 'view', 'projection'}), + UniformSlot.value('VertexInfo', { + 'model', + 'view', + 'projection', + }), + UniformSlot.value('JointMatrices', { + 'joint0', + 'joint1', + 'joint2', + 'joint3', + 'joint4', + }), ], ), fragmentShader: Shader( @@ -56,6 +67,7 @@ class SpatialMaterial extends Material { @override void bind(GraphicsDevice device) { _bindVertexInfo(device); + _bindJointMatrices(device); _bindMaterial(device); _bindCamera(device); } @@ -67,6 +79,18 @@ class SpatialMaterial extends Material { ..setMatrix4('VertexInfo.projection', device.projection); } + void _bindJointMatrices(GraphicsDevice device) { + final jointTransforms = device.jointsInfo.jointTransforms; + if (jointTransforms.length > 5) { + throw Exception( + 'At most 5 joints per surface, found ${jointTransforms.length}', + ); + } + for (final (idx, transform) in jointTransforms.indexed) { + vertexShader.setMatrix4('JointMatrices.joint$idx', transform); + } + } + void _bindMaterial(GraphicsDevice device) { _applyLights(device); fragmentShader diff --git a/packages/flame_3d/lib/src/resources/mesh/mesh.dart b/packages/flame_3d/lib/src/resources/mesh/mesh.dart index 765b03f5acb..c2fa8b42b13 100644 --- a/packages/flame_3d/lib/src/resources/mesh/mesh.dart +++ b/packages/flame_3d/lib/src/resources/mesh/mesh.dart @@ -26,7 +26,8 @@ class Mesh extends Resource { int get vertexCount => _surfaces.fold(0, (p, e) => p + e.vertexCount); void bind(GraphicsDevice device) { - for (final surface in _surfaces) { + for (final (idx, surface) in _surfaces.indexed) { + device.jointsInfo.setSurface(idx); device.bindSurface(surface); } } diff --git a/packages/flame_3d/lib/src/resources/mesh/surface.dart b/packages/flame_3d/lib/src/resources/mesh/surface.dart index 3b4594300d9..b561768b47a 100644 --- a/packages/flame_3d/lib/src/resources/mesh/surface.dart +++ b/packages/flame_3d/lib/src/resources/mesh/surface.dart @@ -18,6 +18,7 @@ class Surface extends Resource { required List vertices, required List indices, this.material, + this.jointMap, /** * If `true`, the normals will be calculated if they are not provided. */ @@ -34,15 +35,16 @@ class Surface extends Resource { _vertices = Float32List.fromList( normalizedVertices.fold([], (p, v) => p..addAll(v.storage)), ).buffer; - _vertexCount = _vertices.lengthInBytes ~/ (normalizedVertices.length * 9); + _vertexCount = normalizedVertices.length; _indices = Uint16List.fromList(indices).buffer; - _indexCount = _indices.lengthInBytes ~/ 2; + _indexCount = indices.length; _calculateAabb(normalizedVertices); } Material? material; + Map? jointMap; Aabb3 get aabb => _aabb; late Aabb3 _aabb; diff --git a/packages/flame_3d/lib/src/resources/mesh/vertex.dart b/packages/flame_3d/lib/src/resources/mesh/vertex.dart index c1fd8fac805..a11e8801fda 100644 --- a/packages/flame_3d/lib/src/resources/mesh/vertex.dart +++ b/packages/flame_3d/lib/src/resources/mesh/vertex.dart @@ -18,14 +18,20 @@ class Vertex { required Vector2 texCoord, this.color = const Color(0xFFFFFFFF), Vector3? normal, + Vector4? joints, + Vector4? weights, }) : position = position.immutable, texCoord = texCoord.immutable, normal = normal?.immutable, + joints = joints?.immutable, + weights = weights?.immutable, _storage = Float32List.fromList([ ...position.storage, // 1, 2, 3 ...texCoord.storage, // 4, 5 ...color.storage, // 6, 7, 8, 9 ...(normal ?? Vector3.zero()).storage, // 10, 11, 12 + ...(joints ?? Vector4.zero()).storage, // 13, 14, 15, 16 + ...(weights ?? Vector4.zero()).storage, // 17, 18, 19, 20 ]); Float32List get storage => _storage; @@ -40,6 +46,12 @@ class Vertex { /// The normal vector of the vertex. final ImmutableVector3? normal; + /// The joints of the vertex. + final ImmutableVector4? joints; + + /// The weights of the vertex. + final ImmutableVector4? weights; + /// The color on the vertex. final Color color; @@ -49,16 +61,27 @@ class Vertex { position == other.position && texCoord == other.texCoord && normal == other.normal && - color == other.color; + color == other.color && + joints == other.joints && + weights == other.weights; @override - int get hashCode => Object.hashAll([position, texCoord, normal, color]); + int get hashCode => Object.hashAll([ + position, + texCoord, + normal, + color, + joints, + weights, + ]); Vertex copyWith({ Vector3? position, Vector2? texCoord, Vector3? normal, Color? color, + Vector4? joints, + Vector4? weights, }) { // TODO(wolfenrain): optimize this. return Vertex( @@ -66,6 +89,8 @@ class Vertex { texCoord: texCoord ?? this.texCoord.mutable, normal: normal ?? this.normal?.mutable, color: color ?? this.color, + joints: joints ?? this.joints?.mutable, + weights: weights ?? this.weights?.mutable, ); } diff --git a/packages/flame_3d/shaders/spatial_material.vert b/packages/flame_3d/shaders/spatial_material.vert index 2aecb6f08e9..a602965d94a 100644 --- a/packages/flame_3d/shaders/spatial_material.vert +++ b/packages/flame_3d/shaders/spatial_material.vert @@ -4,6 +4,8 @@ in vec3 vertexPosition; in vec2 vertexTexCoord; in vec4 vertexColor; in vec3 vertexNormal; +in vec4 vertexJoints; +in vec4 vertexWeights; out vec2 fragTexCoord; out vec4 fragColor; @@ -16,18 +18,58 @@ uniform VertexInfo { mat4 projection; } vertex_info; +uniform JointMatrices { + mat4 joint0; + mat4 joint1; + mat4 joint2; + mat4 joint3; + mat4 joint4; +} joints; + +mat4 jointMat(float jointIndex) { + if (jointIndex == 0.0) { + return joints.joint0; + } else if (jointIndex == 1.0) { + return joints.joint1; + } else if (jointIndex == 2.0) { + return joints.joint2; + } else if (jointIndex == 3.0) { + return joints.joint3; + } else if (jointIndex == 4.0) { + return joints.joint4; + } else { + return mat4(0.0); + } +} + +mat4 computeSkinMatrix() { + if (vertexWeights.x == 0.0 && vertexWeights.y == 0.0 && vertexWeights.z == 0.0 && vertexWeights.w == 0.0) { + // no weights, skip skinning + return mat4(1.0); + } + + return vertexWeights.x * jointMat(vertexJoints.x) + + vertexWeights.y * jointMat(vertexJoints.y) + + vertexWeights.z * jointMat(vertexJoints.z) + + vertexWeights.w * jointMat(vertexJoints.w); +} + void main() { + mat4 skinMatrix = computeSkinMatrix(); + vec3 position = (skinMatrix * vec4(vertexPosition, 1.0)).xyz; + vec3 normal = normalize((skinMatrix * vec4(vertexNormal, 0.0)).xyz); + // Calculate the modelview projection matrix mat4 modelViewProjection = vertex_info.projection * vertex_info.view * vertex_info.model; // Transform the vertex position - gl_Position = modelViewProjection * vec4(vertexPosition, 1.0); + gl_Position = modelViewProjection * vec4(position, 1.0); // Pass the interpolated values to the fragment shader fragTexCoord = vertexTexCoord; fragColor = vertexColor; // Calculate the world-space position and normal - fragPosition = vec3(vertex_info.model * vec4(vertexPosition, 1.0)); - fragNormal = mat3(transpose(inverse(vertex_info.model))) * vertexNormal; + fragPosition = vec3(vertex_info.model * vec4(position, 1.0)); + fragNormal = mat3(transpose(inverse(vertex_info.model))) * normal; } From 415df1ab108cb88081754005c8f854abdcbebdff Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Wed, 28 Aug 2024 21:01:00 -0400 Subject: [PATCH 2/3] Support up to 16 joints --- .../shaders/spatial_material.shaderbundle | Bin 100768 -> 213816 bytes .../resources/material/spatial_material.dart | 17 +++++---- .../src/resources/shader/uniform_value.dart | 5 ++- packages/flame_3d/pubspec.yaml | 1 + .../flame_3d/shaders/spatial_material.vert | 33 ++++++++++++++++++ 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/packages/flame_3d/assets/shaders/spatial_material.shaderbundle b/packages/flame_3d/assets/shaders/spatial_material.shaderbundle index 648c1383821f50baa464a28bda16076fcc195851..841e7a8b93b0013d288de8be30058d1a3240463b 100644 GIT binary patch literal 213816 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 delta 12679 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 diff --git a/packages/flame_3d/lib/src/resources/material/spatial_material.dart b/packages/flame_3d/lib/src/resources/material/spatial_material.dart index 18953935357..dddb14c11b9 100644 --- a/packages/flame_3d/lib/src/resources/material/spatial_material.dart +++ b/packages/flame_3d/lib/src/resources/material/spatial_material.dart @@ -21,13 +21,10 @@ class SpatialMaterial extends Material { 'view', 'projection', }), - UniformSlot.value('JointMatrices', { - 'joint0', - 'joint1', - 'joint2', - 'joint3', - 'joint4', - }), + UniformSlot.value( + 'JointMatrices', + List.generate(_maxJoints, (idx) => 'joint$idx').toSet(), + ), ], ), fragmentShader: Shader( @@ -81,9 +78,9 @@ class SpatialMaterial extends Material { void _bindJointMatrices(GraphicsDevice device) { final jointTransforms = device.jointsInfo.jointTransforms; - if (jointTransforms.length > 5) { + if (jointTransforms.length > _maxJoints) { throw Exception( - 'At most 5 joints per surface, found ${jointTransforms.length}', + 'At most $_maxJoints joints per surface, found ${jointTransforms.length}', ); } for (final (idx, transform) in jointTransforms.indexed) { @@ -113,4 +110,6 @@ class SpatialMaterial extends Material { static final _library = gpu.ShaderLibrary.fromAsset( 'packages/flame_3d/assets/shaders/spatial_material.shaderbundle', )!; + + static const _maxJoints = 16; } diff --git a/packages/flame_3d/lib/src/resources/shader/uniform_value.dart b/packages/flame_3d/lib/src/resources/shader/uniform_value.dart index a138dfdf7f9..6b27f8d004c 100644 --- a/packages/flame_3d/lib/src/resources/shader/uniform_value.dart +++ b/packages/flame_3d/lib/src/resources/shader/uniform_value.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:flame_3d/graphics.dart'; import 'package:flame_3d/resources.dart'; +import 'package:ordered_set/comparing.dart'; /// {@template uniform_value} /// Instance of a uniform value. Represented by a [ByteBuffer]. @@ -21,7 +22,9 @@ class UniformValue extends UniformInstance { if (super.resource == null) { var previousIndex = -1; - final data = _storage.entries.fold>([], (p, e) { + final entries = _storage.entries.toList() + ..sort(Comparing.on((c) => c.key)); + final data = entries.fold>([], (p, e) { if (previousIndex + 1 != e.key) { final field = slot.fields.indexed.firstWhere((e) => e.$1 == previousIndex + 1); diff --git a/packages/flame_3d/pubspec.yaml b/packages/flame_3d/pubspec.yaml index 425399a95cd..4649e69d328 100644 --- a/packages/flame_3d/pubspec.yaml +++ b/packages/flame_3d/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: flutter_gpu: sdk: flutter meta: ^1.12.0 + ordered_set: ^6.0.1 vector_math: ^2.1.4 dev_dependencies: diff --git a/packages/flame_3d/shaders/spatial_material.vert b/packages/flame_3d/shaders/spatial_material.vert index a602965d94a..f7dfc0b4966 100644 --- a/packages/flame_3d/shaders/spatial_material.vert +++ b/packages/flame_3d/shaders/spatial_material.vert @@ -24,6 +24,17 @@ uniform JointMatrices { mat4 joint2; mat4 joint3; mat4 joint4; + mat4 joint5; + mat4 joint6; + mat4 joint7; + mat4 joint8; + mat4 joint9; + mat4 joint10; + mat4 joint11; + mat4 joint12; + mat4 joint13; + mat4 joint14; + mat4 joint15; } joints; mat4 jointMat(float jointIndex) { @@ -37,6 +48,28 @@ mat4 jointMat(float jointIndex) { return joints.joint3; } else if (jointIndex == 4.0) { return joints.joint4; + } else if (jointIndex == 5.0) { + return joints.joint5; + } else if (jointIndex == 6.0) { + return joints.joint6; + } else if (jointIndex == 7.0) { + return joints.joint7; + } else if (jointIndex == 8.0) { + return joints.joint8; + } else if (jointIndex == 9.0) { + return joints.joint9; + } else if (jointIndex == 10.0) { + return joints.joint10; + } else if (jointIndex == 11.0) { + return joints.joint11; + } else if (jointIndex == 12.0) { + return joints.joint12; + } else if (jointIndex == 13.0) { + return joints.joint13; + } else if (jointIndex == 14.0) { + return joints.joint14; + } else if (jointIndex == 15.0) { + return joints.joint15; } else { return mat4(0.0); } From 35498950ecb9c844c74127ce5b2dc3e69580528a Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Fri, 13 Sep 2024 14:40:29 -0400 Subject: [PATCH 3/3] Fix lint --- packages/flame_3d/lib/src/graphics/joints_info.dart | 2 +- .../flame_3d/lib/src/resources/material/spatial_material.dart | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/flame_3d/lib/src/graphics/joints_info.dart b/packages/flame_3d/lib/src/graphics/joints_info.dart index 5952d183f1d..c21204b211c 100644 --- a/packages/flame_3d/lib/src/graphics/joints_info.dart +++ b/packages/flame_3d/lib/src/graphics/joints_info.dart @@ -10,4 +10,4 @@ class JointsInfo { void setSurface(int surfaceIdx) { jointTransforms = jointTransformsPerSurface[surfaceIdx] ?? []; } -} \ No newline at end of file +} diff --git a/packages/flame_3d/lib/src/resources/material/spatial_material.dart b/packages/flame_3d/lib/src/resources/material/spatial_material.dart index dddb14c11b9..4b9ed6ef28c 100644 --- a/packages/flame_3d/lib/src/resources/material/spatial_material.dart +++ b/packages/flame_3d/lib/src/resources/material/spatial_material.dart @@ -80,7 +80,8 @@ class SpatialMaterial extends Material { final jointTransforms = device.jointsInfo.jointTransforms; if (jointTransforms.length > _maxJoints) { throw Exception( - 'At most $_maxJoints joints per surface, found ${jointTransforms.length}', + 'At most $_maxJoints joints per surface are supported;' + ' found ${jointTransforms.length}', ); } for (final (idx, transform) in jointTransforms.indexed) {