@@ -171,6 +171,71 @@ namespace rlogic::internal
171171 EXPECT_NEAR (expectedMat2[i], mat2[i], 1e-4f ) << i;
172172 }
173173
174+ TEST_F (ASkinBinding, UpdatesBoundUniformOnNodeBindingChange)
175+ {
176+ // This is the same setup as regular tests, only recreated locally, since with the regular setup (by chance) the nodes are ordered differently.
177+ // If there is no binding dependency between node binding and skin binding, node binding created after skin binding might appear after skin binding
178+ // in update list, resulting in the changes to node binding not being propagated to skin binding and skin binding using old values during update.
179+ auto appearance = m_scene->createAppearance (createTestEffect (), " skinAppearance2" );
180+ ramses::UniformInput uniform;
181+ appearance->getEffect ().findUniformInput (" jointMat" , uniform);
182+ std::vector<ramses::Node*> jointNodes{ m_scene->createNode (), m_scene->createNode () };
183+ RamsesAppearanceBinding* appearanceBinding{ m_logicEngine.createRamsesAppearanceBinding (*appearance) };
184+ std::vector<const RamsesNodeBinding*> joints{ m_logicEngine.createRamsesNodeBinding (*jointNodes[0 ]), m_logicEngine.createRamsesNodeBinding (*jointNodes[1 ]) };
185+
186+ // add some transformations to the joints before calculating inverse mats and creating skin
187+ jointNodes[0 ]->setTranslation (1 .f , 2 .f , 3 .f );
188+ jointNodes[1 ]->setRotation (10 .f , 20 .f , 30 .f );
189+
190+ std::vector<matrix44f> inverseMats;
191+ inverseMats.resize (2u );
192+
193+
194+ float tempData[16 ]; // NOLINT(modernize-avoid-c-arrays) Ramses uses C array in matrix getters
195+ jointNodes[0 ]->getInverseModelMatrix (tempData);
196+ std::copy (std::begin (tempData), std::end (tempData), inverseMats[0 ].begin ());
197+ jointNodes[1 ]->getInverseModelMatrix (tempData);
198+ std::copy (std::begin (tempData), std::end (tempData), inverseMats[1 ].begin ());
199+
200+ auto skin = m_logicEngine.createSkinBinding (joints, inverseMats, *appearanceBinding, uniform, " skin2" );
201+
202+ jointNodes[0 ]->setRotation (1 .f , 2 .f , 3 .f );
203+ jointNodes[1 ]->setTranslation (-1 .f , -2 .f , -3 .f );
204+
205+ // The crutial part of this test is having this binding created after other logic nodes to make it appear last in node update topology.
206+ RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding (*jointNodes[0 ]);
207+ auto inputs = nodeBinding.getInputs ();
208+ inputs->getChild (" translation" )->set <vec3f>(vec3f{2 .1f , 2 .2f , 2 .3f });
209+
210+ EXPECT_TRUE (m_logicEngine.update ());
211+
212+ const matrix44f expectedMat1 = {
213+ 0 .998f , -0 .0523f , 0 .0349f , 0 .f ,
214+ 0 .0529f , 0 .9984f , -0 .0174f , 0 .f ,
215+ -0 .0339f , 0 .01925f , 0 .9992f , 0 .f ,
216+ 1 .0979f , 0 .19764f , -0 .69773f , 1 .f
217+ };
218+ const matrix44f expectedMat2 = {
219+ 1 .f , 0 .f , 0 .f , 0 .f ,
220+ 0 .f , 1 .f , 0 .f , 0 .f ,
221+ 0 .f , 0 .f , 1 .f , 0 .f ,
222+ -1 .f , -2 .f , -3 .f , 1 .f
223+ };
224+
225+ std::array<float , 32u > uniformData{};
226+ appearance->getInputValueMatrix44f (skin->getAppearanceUniformInput (), 2u , uniformData.data ());
227+ matrix44f mat1{};
228+ matrix44f mat2{};
229+ std::copy (uniformData.cbegin (), uniformData.cbegin () + 16u , mat1.begin ());
230+ std::copy (uniformData.cbegin () + 16u , uniformData.cend (), mat2.begin ());
231+
232+ for (size_t i = 0u ; i < 16 ; ++i)
233+ EXPECT_NEAR (expectedMat1[i], mat1[i], 1e-4f ) << i;
234+
235+ for (size_t i = 0u ; i < 16 ; ++i)
236+ EXPECT_NEAR (expectedMat2[i], mat2[i], 1e-4f ) << i;
237+ }
238+
174239 TEST_F (ASkinBinding, CalculatesSameValuesAfterLoadingFromFile)
175240 {
176241 WithTempDirectory tmpDir;
0 commit comments