[Bf-blender-cvs] [b9f422c4be9] blender-v2.83-release: Tests: add physics tests cloth and softybody

Himanshi Kalra noreply at git.blender.org
Tue Apr 28 13:00:34 CEST 2020


Commit: b9f422c4be9c27bb47e5305bad2c91901719100e
Author: Himanshi Kalra
Date:   Mon Apr 27 16:47:07 2020 +0200
Branches: blender-v2.83-release
https://developer.blender.org/rBb9f422c4be9c27bb47e5305bad2c91901719100e

Tests: add physics tests cloth and softybody

This uses the same framework as automated modifier tests. It adds a physics
modifier, bakes and compares vertex coordinates on the end frame.

Differential Revision: https://developer.blender.org/D7017

===================================================================

M	tests/python/CMakeLists.txt
M	tests/python/modules/mesh_test.py
A	tests/python/physics_cloth.py
A	tests/python/physics_softbody.py

===================================================================

diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index db5d5dcf73b..2d6dc4af40e 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -188,6 +188,22 @@ add_blender_test(
   --run-all-tests
 )
 
+add_blender_test(
+  physics_cloth
+  ${TEST_SRC_DIR}/physics/cloth_test.blend
+  --python ${TEST_PYTHON_DIR}/physics_cloth.py
+  --
+  --run-all-tests
+)
+
+add_blender_test(
+  physics_softbody
+  ${TEST_SRC_DIR}/physics/softbody_test.blend
+  --python ${TEST_PYTHON_DIR}/physics_softbody.py
+  --
+  --run-all-tests
+)
+
 add_blender_test(
   constraints
   --python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py
diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py
index f188d998884..af0e78257d5 100644
--- a/tests/python/modules/mesh_test.py
+++ b/tests/python/modules/mesh_test.py
@@ -73,6 +73,28 @@ class ModifierSpec:
                " with parameters: " + str(self.modifier_parameters)
 
 
+class PhysicsSpec:
+    """
+    Holds one Physics modifier and its parameters.
+    """
+
+    def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int):
+        """
+        Constructs a physics spec.
+        :param modifier_name: str - name of object modifier, e.g. "Cloth"
+        :param modifier_type: str - type of object modifier, e.g. "CLOTH"
+        :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
+        :param frame_end:int - the last frame of the simulation at which it is baked
+        """
+        self.modifier_name = modifier_name
+        self.modifier_type = modifier_type
+        self.modifier_parameters = modifier_parameters
+        self.frame_end = frame_end
+
+    def __str__(self):
+        return "Physics Modifier: " + self.modifier_name + " of type " + self.modifier_type + \
+               " with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end)
+
 class OperatorSpec:
     """
     Holds one operator and its parameters.
@@ -105,7 +127,7 @@ class MeshTest:
     the public method run_test().
     """
 
-    def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False):
+    def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False, threshold=None):
         """
         Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name
         or test_object_name don't exist.
@@ -125,6 +147,7 @@ class MeshTest:
                                         type(operation)))
         self.operations_stack = operations_stack
         self.apply_modifier = apply_modifiers
+        self.threshold = threshold
 
         self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
         self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
@@ -235,6 +258,49 @@ class MeshTest:
         if self.apply_modifier:
             bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name)
 
+
+    def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end):
+        for scene in bpy.data.scenes:
+            for modifier in obj.modifiers:
+                if modifier.type == test_mod_type:
+                    obj.modifiers[test_mod_name].point_cache.frame_end = frame_end
+                    override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache}
+                    bpy.ops.ptcache.bake(override, bake=True)
+                    break
+
+    def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec):
+        """
+        Apply Physics settings to test objects.
+        """
+        scene = bpy.context.scene
+        scene.frame_set(1)
+        modifier = test_object.modifiers.new(physics_spec.modifier_name,
+                                             physics_spec.modifier_type)
+        physics_setting = modifier.settings
+        if self.verbose:
+            print("Created modifier '{}' of type '{}'.".
+                  format(physics_spec.modifier_name, physics_spec.modifier_type))
+
+
+        for param_name in physics_spec.modifier_parameters:
+            try:
+                setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name])
+                if self.verbose:
+                    print("\t set parameter '{}' with value '{}'".
+                          format(param_name, physics_spec.modifier_parameters[param_name]))
+            except AttributeError:
+                # Clean up first
+                bpy.ops.object.delete()
+                raise AttributeError("Modifier '{}' has no parameter named '{}'".
+                                     format(physics_spec.modifier_type, param_name))
+
+        scene.frame_set(physics_spec.frame_end + 1)
+
+        self._bake_current_simulation(test_object, physics_spec.modifier_type, physics_spec.modifier_name, physics_spec.frame_end)
+        if self.apply_modifier:
+            bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name)
+
+
     def _apply_operator(self, test_object, operator: OperatorSpec):
         """
         Apply operator on test object.
@@ -302,9 +368,12 @@ class MeshTest:
 
             elif isinstance(operation, OperatorSpec):
                 self._apply_operator(evaluated_test_object, operation)
+
+            elif isinstance(operation, PhysicsSpec):
+                self._apply_physics_settings(evaluated_test_object, operation)
             else:
-                raise ValueError("Expected operation of type {} or {}. Got {}".
-                                 format(type(ModifierSpec), type(OperatorSpec),
+                raise ValueError("Expected operation of type {} or {} or {}. Got {}".
+                                 format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec),
                                         type(operation)))
 
         # Compare resulting mesh with expected one.
@@ -312,7 +381,10 @@ class MeshTest:
             print("Comparing expected mesh with resulting mesh...")
         evaluated_test_mesh = evaluated_test_object.data
         expected_mesh = self.expected_object.data
-        compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh)
+        if self.threshold:
+            compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh, threshold=self.threshold)
+        else:
+            compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh)
         compare_success = (compare_result == 'Same')
 
         # Also check if invalid geometry (which is never expected) had to be corrected...
@@ -434,7 +506,7 @@ class ModifierTest:
     >>> modifiers_test.run_all_tests()
     """
 
-    def __init__(self, modifier_tests: list, apply_modifiers=False):
+    def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None):
         """
         Construct a modifier test.
         :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following
@@ -445,6 +517,7 @@ class ModifierTest:
         """
         self.modifier_tests = modifier_tests
         self.apply_modifiers = apply_modifiers
+        self.threshold = threshold
         self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
         self._failed_tests_list = []
 
@@ -461,7 +534,7 @@ class ModifierTest:
         expected_object_name = case[1]
         spec_list = case[2]
 
-        test = MeshTest(test_object_name, expected_object_name)
+        test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold)
         if self.apply_modifiers:
             test.apply_modifier = True
 
diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py
new file mode 100644
index 00000000000..835d0aaed60
--- /dev/null
+++ b/tests/python/physics_cloth.py
@@ -0,0 +1,56 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+import os
+import sys
+
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import ModifierTest, PhysicsSpec
+
+
+def main():
+    test = [
+        ["testCloth", "expectedCloth",
+         [PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]],
+    ]
+    cloth_test = ModifierTest(test, threshold=1e-3)
+
+    command = list(sys.argv)
+    for i, cmd in enumerate(command):
+        if cmd == "--run-all-tests":
+            cloth_test.apply_modifiers = True
+            cloth_test.run_all_tests()
+            break
+        elif cmd == "--run-test":
+            cloth_test.apply_modifiers = False
+            index = int(command[i + 1])
+            cloth_test.run_test(index)
+            break
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except:
+        import traceback
+        traceback.print_exc()
+        sys.exit(1)
diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py
new file mode 100644
index 00000000000..22e35a6cc76
--- /dev/null
+++ b/tests/python/physics_softbody.py
@@ -0,0 +1,56 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list