コード例 #1
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_spawn_and_delete_one_object(self, client_type):
        """
        Ask Clerk to spawn one object.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Constants and parameters for this test.
        objID, templateID = 1, '_templateEmpty'

        # Spawn a new object from templateID. The new object must have objID=1.
        init = {'templateID': templateID,
                'rbs': {'position': (0, 0, 0)}}
        ret = client.spawn([init])
        assert ret.ok and ret.data == [objID]

        # Attempt to spawn a non-existing template.
        assert not client.spawn([{'templateID': 'blah'}]).ok

        # Send invalid data to 'spawn'.
        assert not client.spawn([{'blah': 'blah'}]).ok

        # Exactly one object must exist at this point.
        ret = client.getAllObjectIDs()
        assert (ret.ok, ret.data) == (True, [objID])

        # Attempt to delete a non-existing object. This must silently fail.
        assert client.removeObject(100).ok
        ret = client.getAllObjectIDs()
        assert (ret.ok, ret.data) == (True, [objID])

        # Delete an existing object.
        assert client.removeObject(objID).ok
        ret = client.getAllObjectIDs()
        assert (ret.ok, ret.data) == (True, [])
コード例 #2
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_collada_model(self, client_type):
        """
        Add a template based on a Collada model, spawn it, and query its
        geometry.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Add a valid template with Collada data and verify the upload worked.
        temp = getTemplate('foo', fragments={'f_dae': getFragDae()})
        assert client.addTemplates([temp]).ok

        # Spawn the template.
        ret = client.spawn([{'templateID': temp.aid}])
        assert ret.ok
        objID = ret.data[0]

        # Query and the geometry.
        ret = client.getFragments([objID])
        assert ret.ok

        # Verify it has the correct type ('DAE') and address.
        ret = ret.data[objID]
        assert ret['f_dae']['fragtype'] == 'DAE'
        assert ret['f_dae']['url_frag'] == (
            config.url_instances + '/' + str(objID) + '/f_dae')
コード例 #3
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_get_template(self, client_type):
        """
        Spawn some default templates and query their template IDs.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Parameters and constants for this test.
        objID_1, objID_2 = 1, 2
        templateID_0 = '_templateEmpty'
        templateID_1 = '_templateBox'

        # Spawn a new object. Its ID must be 1.
        new_objs = [
            {'templateID': templateID_0, 'rbs': {'position': [0, 0, 0]}},
            {'templateID': templateID_1, 'rbs': {'position': [0, 0, 0]}},
        ]
        ret = client.spawn(new_objs)
        assert ret.ok and ret.data == [objID_1, objID_2]

        # Retrieve template of first object.
        ret = client.getTemplateID(objID_1)
        assert ret.ok and (ret.data == templateID_0)

        # Retrieve template of second object.
        ret = client.getTemplateID(objID_2)
        assert ret.ok and (ret.data == templateID_1)

        # Attempt to retrieve a non-existing object.
        assert not client.getTemplateID(100).ok
コード例 #4
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_setFragments_dae(self, client_type):
        """
        Spawn a new object and modify its geometry at runtime.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Get a Collada fragment.
        f_dae = {'f_dae': getFragDae()}

        # Add a new template and spawn it.
        temp = getTemplate('t1', fragments=f_dae)
        assert client.addTemplates([temp]).ok

        new_obj = {'templateID': temp.aid,
                   'rbs': {'position': (1, 1, 1), 'velocityLin': (-1, -1, -1)}}
        ret = client.spawn([new_obj])
        objID = ret.data[0]
        assert ret.ok and ret.data == [objID]
        del temp, new_obj, ret

        # Query the body states to obtain the 'version' value.
        ret = client.getRigidBodies(objID)
        assert ret.ok
        version = ret.data[objID]['rbs'].version

        # Fetch-, modify-, update- and verify the geometry.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['f_dae']['fragtype'] == 'DAE'

        # Change the geometry for fragment 'f_dae' to a RAW type.
        assert client.setFragments({objID: {'f_dae': getFragRaw()._asdict()}}).ok

        # Ensure the fragment is now indeed of type 'RAW'.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['f_dae']['fragtype'] == 'RAW'

        # Ensure 'version' is different as well.
        ret = client.getRigidBodies(objID)
        assert ret.ok and (ret.data[objID]['rbs'].version != version)

        # Change the fragment geometry once more.
        version = ret.data[objID]['rbs'].version
        assert client.setFragments({objID: {'f_dae': getFragDae()._asdict()}}).ok

        # Ensure it now has type 'DAE' again.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['f_dae']['fragtype'] == 'DAE'

        # Ensure 'version' is different as well.
        ret = client.getRigidBodies(objID)
        assert ret.ok and (ret.data[objID]['rbs'].version != version)
コード例 #5
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_add_get_remove_constraints(self, client_type):
        """
        Create some bodies. Then add/query/remove constraints.

        This test only verifies that the Igor interface works. It does *not*
        verify that the objects are really linked in the actual simulation.
        """
        # Reset the constraint database.
        igor = azrael.igor.Igor()
        assert igor.reset().ok

        # Get the client for this test.
        client = self.clients[client_type]

        # Spawn the two bodies.
        pos_1, pos_2, pos_3 = [-2, 0, 0], [2, 0, 0], [6, 0, 0]
        new_objs = [
            {'templateID': '_templateSphere', 'rbs': {'position': pos_1}},
            {'templateID': '_templateSphere', 'rbs': {'position': pos_2}},
            {'templateID': '_templateSphere', 'rbs': {'position': pos_3}}
        ]
        id_1, id_2, id_3 = 1, 2, 3
        assert client.spawn(new_objs) == (True, None, [id_1, id_2, id_3])

        # Define the constraints.
        con_1 = getP2P(rb_a=id_1, rb_b=id_2, pivot_a=pos_2, pivot_b=pos_1)
        con_2 = get6DofSpring2(rb_a=id_2, rb_b=id_3)

        # Verify that no constraints are currently active.
        assert client.getConstraints(None) == (True, None, [])
        assert client.getConstraints([id_1]) == (True, None, [])

        # Add both constraints and verify they are returned correctly.
        assert client.addConstraints([con_1, con_2]) == (True, None, 2)
        ret = client.getConstraints(None)
        assert ret.ok and (sorted(ret.data) == sorted([con_1, con_2]))

        ret = client.getConstraints([id_2])
        assert ret.ok and (sorted(ret.data) == sorted([con_1, con_2]))

        assert client.getConstraints([id_1]) == (True, None, [con_1])
        assert client.getConstraints([id_3]) == (True, None, [con_2])

        # Remove the second constraint and verify the remaining constraint is
        # returned correctly.
        assert client.deleteConstraints([con_2]) == (True, None, 1)
        assert client.getConstraints(None) == (True, None, [con_1])
        assert client.getConstraints([id_1]) == (True, None, [con_1])
        assert client.getConstraints([id_2]) == (True, None, [con_1])
        assert client.getConstraints([id_3]) == (True, None, [])
コード例 #6
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_spawn_and_get_state_variables(self, client_type):
        """
        Spawn a new object and query its state variables.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Constants and parameters for this test.
        templateID, objID_1 = '_templateEmpty', 1

        # Query the state variables for a non existing object.
        objID = 100
        assert client.getRigidBodies(objID) == (True, None, {objID: None})
        del objID

        # Instruct Clerk to spawn a new object. Its objID must be '1'.
        pos, vlin = (0, 1, 2), (-3, 4, -5)
        body = getRigidBody(position=pos, velocityLin=vlin)
        init = {
            'templateID': templateID,
            'rbs': {'position': body.position,
                    'velocityLin': body.velocityLin},
        }
        ret = client.spawn([init])
        assert ret.ok and ret.data == [objID_1]

        # The body parameters of the new object must match the inital state
        # (plus the tweaks provided to the spawn command).
        ret = client.getRigidBodies(objID_1)
        assert ret.ok and (set(ret.data.keys()) == {1})
        assert ret.data[objID_1]['rbs'].position == pos
        assert ret.data[objID_1]['rbs'].velocityLin == vlin

        # Same test but this time get all of them.
        assert client.getRigidBodies(None) == ret

        # Query just the state variables instead of the entire rigid body.
        assert client.getObjectStates(None) == client.getObjectStates([objID_1])
        ret = client.getObjectStates([objID_1])
        assert ret.ok
        r = ret.data[objID_1]
        assert set(r.keys()) == set(['rbs', 'frag'])
        r = ret.data[objID_1]['rbs']
        assert r['position'] == list(pos)
        assert r['velocityLin'] == list(vlin)
コード例 #7
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_remove_fragments(self, client_type):
        """
        Remove a fragment. This test is basically the integration test for
        'test_dibbler.test_updateFragments_partial'.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Convenience.
        objID = 1

        # The original template has the following three fragments:
        frags_orig = {
            'fname_1': getFragRaw(),
            'fname_2': getFragDae(),
            'fname_3': getFragRaw(),
        }
        t1 = getTemplate('t1', fragments=frags_orig)

        # Add a new template and spawn it.
        assert client.addTemplates([t1]).ok
        new_obj = {'templateID': t1.aid,
                   'rbs': {'position': (1, 1, 1), 'velocityLin': (-1, -1, -1)}}
        assert client.spawn([new_obj]) == (True, None, [objID])

        # Query the fragment geometries and Body State to verify that both
        # report three fragments.
        ret = client.getFragments([objID])
        assert ret.ok and len(ret.data[objID]) == 3
        ret = client.getObjectStates(objID)
        assert ret.ok and len(ret.data[objID]['frag']) == 3

        # Update the fragments as follows: keep the first intact, remove the
        # second, and modify the third one.
        frags_new = {
            'fname_2': getFragNone()._asdict(),
            'fname_3': getFragDae()._asdict()
        }
        assert client.setFragments({objID: frags_new}).ok

        # After the last update there must now only be two fragments.
        ret = client.getFragments([objID])
        assert ret.ok and len(ret.data[objID]) == 2
        ret = client.getObjectStates(objID)
        assert ret.ok and len(ret.data[objID]['frag']) == 2
コード例 #8
0
ファイル: demo_collada.py プロジェクト: daviddeng/azrael
def spawnColladaModel(scale, fname):
    """
    Import the collada file ``fname`` as a new template and spawn it.
    """
    # Get a Client instance.
    client = azrael.client.Client()

    # Collada format: a .dae file plus a list of textures in jpg or png format.
    b = os.path.dirname(__file__)
    b = os.path.join(b, '..', 'azrael', 'test')
    dae_file = open(b + '/cube.dae', 'rb').read()
    dae_rgb1 = open(b + '/rgb1.png', 'rb').read()
    dae_rgb2 = open(b + '/rgb2.jpg', 'rb').read()
    f_dae = FragDae(dae=dae_file,
                    rgb={'rgb1.png': dae_rgb1,
                         'rgb2.jpg': dae_rgb2})
    del b

    # Put both fragments into a valid list of FragMetas.
    frags = [FragMeta('f_dae', 'dae', f_dae)]

    temp = Template('Collada', [4, 1, 1, 1], frags, [], [])
    assert client.addTemplates([temp]).ok
    print('done')

    # Spawn the template near the center.
    print('  Spawning object... ', end='', flush=True)
    pos, orient = [0, 0, -10], [0, 1, 0, 0]
    d = {'scale': scale,
         'imass': 0.1,
         'position': pos,
         'rotation': orient,
         'axesLockLin': [1, 1, 1],
         'axesLockRot': [1, 1, 1],
         'template': temp.aid}
    ret = client.spawn([d])
    objID = ret.data[0]
    print('done (ID=<{}>)'.format(objID))

    return objID
コード例 #9
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_getAllObjectIDs(self, client_type):
        """
        Ensure the getAllObjectIDs command reaches Clerk.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Constants and parameters for this test.
        templateID, objID_1 = '_templateEmpty', 1

        # So far no objects have been spawned.
        ret = client.getAllObjectIDs()
        assert (ret.ok, ret.data) == (True, [])

        # Spawn a new object.
        init = {'templateID': templateID, 'rbs': {'position': (0, 0, 0)}}
        ret = client.spawn([init])
        assert ret.ok and ret.data == [objID_1]

        # The object list must now contain the ID of the just spawned object.
        ret = client.getAllObjectIDs()
        assert (ret.ok, ret.data) == (True, [objID_1])
コード例 #10
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_set_get_custom(self, client_type):
        """
        Spawn two objects and modify their custom fields, as well as set/query
        the custom field of a non-existing object.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Spawn two objects.
        id_1, id_2 = 1, 2
        init = {'templateID': '_templateSphere'}
        assert client.spawn([init, init]) == (True, None, [id_1, id_2])

        # Update the custom data for an existing- and a non-existing object.
        assert client.setCustomData({id_1: 'foo', 10: 'bar'}) == (True, None, [10])

        # Query two existing- and one non-existing object.
        ret = client.getCustomData([id_1, id_2, 10])
        assert ret.ok
        assert ret.data == ({id_1: 'foo', id_2: '', 10: None})

        # Query all at once.
        assert client.getCustomData(None) == client.getCustomData([id_1, id_2])
コード例 #11
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_update_FragmentStates(self, client_type):
        """
        Query and modify fragment states.
        Note that fragment states are updated via 'setFragments'.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Convenience.
        objID = 1

        # Add a new template and spawn it.
        temp = getTemplate('t1', fragments={'bar': getFragRaw()})
        assert client.addTemplates([temp]).ok

        new_obj = {'templateID': temp.aid,
                   'rbs': {'position': (1, 1, 1), 'velocityLin': (-1, -1, -1)}}
        ret = client.spawn([new_obj])
        assert ret.ok and ret.data == [objID]
        del temp, new_obj, ret

        # Query the Body State to get the Fragment States. Then verify the
        # Fragment State named 'bar'.
        ret = client.getObjectStates(objID)
        ref = {'bar': {'scale': 1, 'position': [0, 0, 0], 'rotation': [0, 0, 0, 1]}}
        assert ret.ok
        assert ret.data[objID]['frag'] == ref

        # Modify and update the fragment states in Azrael, then query and
        # verify it worked.
        newStates = {objID: {'bar': {'scale': 2.2, 'position': [1, 2, 3],
                                     'rotation': [1, 0, 0, 0]}}}
        assert client.setFragments(newStates).ok
        ret = client.getObjectStates(objID)
        assert ret.ok
        ret = ret.data[objID]['frag']['bar']
        assert ret == newStates[objID]['bar']
コード例 #12
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_setFragments_raw(self, client_type):
        """
        Spawn a new object and modify its geometry at runtime.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Convenience.
        objID = 1

        # Add a new template and spawn it.
        frag = {'bar': getFragRaw()}
        temp = getTemplate('t1', fragments=frag)
        assert client.addTemplates([temp]).ok

        new_obj = {'templateID': temp.aid,
                   'rbs': {'position': (1, 1, 1),
                           'velocityLin': (-1, -1, -1)}}
        ret = client.spawn([new_obj])
        assert ret.ok and ret.data == [objID]
        del temp, new_obj, ret

        # Query the SV to obtain the 'version' value.
        ret = client.getRigidBodies(objID)
        assert ret.ok
        version = ret.data[objID]['rbs'].version

        # Fetch-, modify-, update- and verify the geometry.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['bar']['fragtype'] == 'RAW'

        # Download the fragment.
        base_url = 'http://{ip}:{port}'.format(
            ip=config.addr_webserver, port=config.port_webserver)
        url = base_url + ret.data[objID]['bar']['url_frag'] + '/model.json'
        for ii in range(10):
            assert ii < 8
            try:
                tmp = urllib.request.urlopen(url).readall()
                break
            except urllib.request.URLError:
                time.sleep(0.2)
        tmp = json.loads(tmp.decode('utf8'))
        assert FragRaw(**tmp) == frag['bar'].fragdata

        # Change the fragment geometries.
        cmd = {objID: {k: v._asdict() for (k, v) in frag.items()}}
        assert client.setFragments(cmd).ok

        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['bar']['fragtype'] == 'RAW'

        # Download the fragment.
        url = base_url + ret.data[objID]['bar']['url_frag'] + '/model.json'
        tmp = urllib.request.urlopen(url).readall()
        tmp = json.loads(tmp.decode('utf8'))
        assert FragRaw(**tmp) == frag['bar'].fragdata

        # Ensure 'version' is different as well.
        ret = client.getRigidBodies(objID)
        assert ret.ok and (ret.data[objID]['rbs'].version != version)
コード例 #13
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_create_constraints_with_physics(self, client_type):
        """
        Spawn two rigid bodies and define a Point2Point constraint among them.
        Then apply a force onto one of them and verify the second one moves
        accordingly.
        """
        # Reset the constraint database.
        igor = azrael.igor.Igor()
        assert igor.reset().ok

        # Get the client for this test.
        client = self.clients[client_type]

        # Reset the database and instantiate a Leonard.
        leo = getLeonard(azrael.leonard.LeonardBullet)

        # Spawn two bodies.
        pos_a, pos_b = [-2, 0, 0], [2, 0, 0]
        new_objs = [
            {'templateID': '_templateSphere',
             'rbs': {'position': pos_a}},
            {'templateID': '_templateSphere',
             'rbs': {'position': pos_b}},
        ]
        id_1, id_2 = 1, 2
        assert client.spawn(new_objs) == (True, None, [id_1, id_2])

        # Verify the position of the bodies.
        ret = client.getObjectStates([id_1, id_2])
        assert ret.ok
        assert ret.data[id_1]['rbs']['position'] == pos_a
        assert ret.data[id_2]['rbs']['position'] == pos_b

        # Define- and add the constraints.
        con = [getP2P(rb_a=id_1, rb_b=id_2, pivot_a=pos_b, pivot_b=pos_a)]
        assert client.addConstraints(con) == (True, None, 1)

        # Apply a force that will pull the left object further to the left.
        # However, both objects must move the same distance in the same
        # direction because they are now linked together.
        assert client.setForce(id_1, [-10, 0, 0]).ok
        leo.processCommandsAndSync()
        leo.step(1.0, 60)

        # Query the object positions. Due to some database timings is sometimes
        # happen that the objects appear to not have moved. In that case retry
        # the query a few times before moving to the comparison.
        for ii in range(10):
            assert ii < 9

            # Query the objects and put their positions into convenience
            # variables.
            ret = client.getRigidBodies([id_1, id_2])
            pos_a2 = ret.data[id_1]['rbs'].position
            pos_b2 = ret.data[id_2]['rbs'].position

            # Exit this loop if both objects have moved.
            if (pos_a != pos_a2) and (pos_b != pos_b2):
                break
            time.sleep(0.1)

        # Verify that the objects have moved to the left and maintained their
        # distance.
        delta_a = np.array(pos_a2) - np.array(pos_a)
        delta_b = np.array(pos_b2) - np.array(pos_b)
        assert delta_a[0] < pos_a[0]
        assert np.allclose(delta_a, delta_b)
コード例 #14
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_create_fetch_template(self, client_type):
        """
        Add a new object to the templateID DB and query it again.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Request an invalid ID.
        assert not client.getTemplates(['blah']).ok

        # Clerk has default objects. This one has an empty collision shape...
        name_1 = '_templateEmpty'
        ret = client.getTemplates([name_1])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_1]['template'].rbs.cshapes == {'csempty': getCSEmpty()}

        # ... this one is a sphere...
        name_2 = '_templateSphere'
        ret = client.getTemplates([name_2])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_2]['template'].rbs.cshapes == {'cssphere': getCSSphere()}

        # ... and this one is a box.
        name_3 = '_templateBox'
        ret = client.getTemplates([name_3])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_3]['template'].rbs.cshapes == {'csbox': getCSBox()}

        # Retrieve all three again but with a single call.
        ret = client.getTemplates([name_1, name_2, name_3])
        assert ret.ok
        assert set(ret.data.keys()) == set((name_1, name_2, name_3))
        assert ret.data[name_2]['template'].rbs.cshapes == {'cssphere': getCSSphere()}
        assert ret.data[name_3]['template'].rbs.cshapes == {'csbox': getCSBox()}
        assert ret.data[name_1]['template'].rbs.cshapes == {'csempty': getCSEmpty()}

        # Add a new object template.
        frag = {'bar': getFragRaw()}
        body = getRigidBody()
        temp_name = 't1'
        temp_orig = getTemplate(temp_name, rbs=body, fragments=frag)
        assert client.addTemplates([temp_orig]).ok

        # Fetch the just added template again and verify its content (skip the
        # geometry because it contains only meta information and will be
        # checked afterwards).
        ret = client.getTemplates([temp_name])
        assert ret.ok and (len(ret.data) == 1)
        temp_out = ret.data[temp_name]['template']
        assert temp_out.boosters == temp_orig.boosters
        assert temp_out.factories == temp_orig.factories
        assert temp_out.rbs == temp_orig.rbs

        # Fetch the geometry from the web server and verify it.
        ret = client.getTemplateGeometry(ret.data[temp_name])
        assert ret.ok
        assert ret.data['bar'] == frag['bar'].fragdata
        del ret, temp_out, temp_orig

        # Define a new object with two boosters and one factory unit.
        # The 'boosters' and 'factories' arguments are a list of named
        # tuples. Their first argument is the unit ID (Azrael does not
        # automatically assign any).
        boosters = {
            '0': types.Booster(pos=(0, 0, 0), direction=(0, 0, 1),
                               minval=0, maxval=0.5, force=0),
            '1': types.Booster(pos=(0, 0, 0), direction=(0, 0, 1),
                               minval=0, maxval=0.5, force=0),
        }
        factories = {
            '0': types.Factory(pos=(0, 0, 0), direction=(0, 0, 1),
                               templateID='_templateBox',
                               exit_speed=(0.1, 0.5))
        }

        # Attempt to query the geometry of a non-existing object.
        assert client.getFragments([1]) == (True, None, {1: None})

        # Define a new template, add it to Azrael, spawn it, and record its
        # object ID.
        body = getRigidBody(cshapes={'csbox': getCSBox()})
        temp = getTemplate('t2',
                           rbs=body,
                           fragments=frag,
                           boosters=boosters,
                           factories=factories)
        assert client.addTemplates([temp]).ok
        init = {'templateID': temp.aid,
                'rbs': {'position': (0, 0, 0)}}
        ret = client.spawn([init])
        assert ret.ok and len(ret.data) == 1
        objID = ret.data[0]

        # Retrieve- and verify the geometry of the just spawned object.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['bar']['fragtype'] == 'RAW'

        # Retrieve the entire template and verify the CS and geometry, and
        # number of boosters/factories.
        ret = client.getTemplates([temp.aid])
        assert ret.ok and (len(ret.data) == 1)
        t_data = ret.data[temp.aid]['template']
        assert t_data.rbs == body
        assert t_data.boosters == temp.boosters
        assert t_data.factories == temp.factories

        # Fetch the geometry from the Web server and verify it is correct.
        ret = client.getTemplateGeometry(ret.data[temp.aid])
        assert ret.ok
        assert ret.data['bar'] == frag['bar'].fragdata
コード例 #15
0
ファイル: test_client.py プロジェクト: daviddeng/azrael
    def test_controlParts(self, client_type):
        """
        Create a template with boosters and factories. Then send control
        commands to them and ensure the applied forces, torques, and
        spawned objects are correct.

        In this test the parent object moves and is oriented away from its
        default.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Reset the SV database and instantiate a Leonard.
        leo = getLeonard()

        # Parameters and constants for this test.
        objID_1 = 1
        pos_parent = [1, 2, 3]
        vel_parent = [4, 5, 6]

        # Part positions relative to parent.
        dir_0 = [0, 0, +2]
        dir_1 = [0, 0, -1]
        pos_0 = [0, 0, +3]
        pos_1 = [0, 0, -4]

        # Describes a rotation of 180 degrees around x-axis.
        orient_parent = [1, 0, 0, 0]

        # Part position in world coordinates if the parent is rotated by 180
        # degrees around the x-axis. The normalisation of the direction is
        # necessary because the parts will automatically normalise all
        # direction vectors, including dir_0 and dir_1 which are not unit
        # vectors.
        dir_0_out = -np.array(dir_0) / np.sum(abs(np.array(dir_0)))
        dir_1_out = -np.array(dir_1) / np.sum(abs(np.array(dir_1)))
        pos_0_out = -np.array(pos_0)
        pos_1_out = -np.array(pos_1)

        # ---------------------------------------------------------------------
        # Create a template with two factories and spawn it.
        # ---------------------------------------------------------------------

        # Define the parts.
        boosters = {
            '0': types.Booster(pos=pos_0, direction=dir_0,
                               minval=0, maxval=0.5, force=0),
            '1': types.Booster(pos=pos_1, direction=dir_1,
                               minval=0, maxval=1.0, force=0)
        }
        factories = {
            '0': types.Factory(pos=pos_0, direction=dir_0,
                               templateID='_templateBox',
                               exit_speed=[0.1, 0.5]),
            '1': types.Factory(pos=pos_1, direction=dir_1,
                               templateID='_templateSphere',
                               exit_speed=[1, 5])
        }

        # Define the template, add it to Azrael, and spawn an instance.
        temp = getTemplate('t1',
                           rbs=getRigidBody(),
                           boosters=boosters,
                           factories=factories)
        assert client.addTemplates([temp]).ok
        new_obj = {'templateID': temp.aid,
                   'rbs': {
                       'position': pos_parent,
                       'velocityLin': vel_parent,
                       'rotation': orient_parent}}
        ret = client.spawn([new_obj])
        assert ret.ok and (ret.data == [objID_1])
        del boosters, factories, temp, new_obj

        # ---------------------------------------------------------------------
        # Activate booster and factories and verify that the applied force and
        # torque is correct, as well as that the spawned objects have the
        # correct state variables attached to them.
        # ---------------------------------------------------------------------

        # Create the commands to let each factory spawn an object.
        exit_speed_0, exit_speed_1 = 0.2, 2
        forcemag_0, forcemag_1 = 0.2, 0.4
        cmd_b = {
            '0': types.CmdBooster(force=forcemag_0),
            '1': types.CmdBooster(force=forcemag_1),
        }
        cmd_f = {
            '0': types.CmdFactory(exit_speed=exit_speed_0),
            '1': types.CmdFactory(exit_speed=exit_speed_1),
        }

        # Send the commands and ascertain that the returned object IDs now
        # exist in the simulation. These IDs must be '2' and '3'.
        ret = client.controlParts(objID_1, cmd_b, cmd_f)
        id_2, id_3 = 2, 3
        assert (ret.ok, ret.data) == (True, [id_2, id_3])

        # Query the state variables of the objects spawned by the factories.
        ok, _, ret_SVs = client.getRigidBodies([id_2, id_3])
        assert (ok, len(ret_SVs)) == (True, 2)

        # Determine which body was spawned by which factory based on their
        # position. We do this by looking at their initial position which
        # *must* match one of the parents.
        body_2, body_3 = ret_SVs[id_2]['rbs'], ret_SVs[id_3]['rbs']
        if np.allclose(body_2.position, pos_1_out + pos_parent):
            body_2, body_3 = body_3, body_2

        # Verify the position and velocity of the spawned objects is correct.
        ac = np.allclose
        assert ac(body_2.velocityLin, exit_speed_0 * dir_0_out + vel_parent)
        assert ac(body_2.position, pos_0_out + pos_parent)
        assert ac(body_3.velocityLin, exit_speed_1 * dir_1_out + vel_parent)
        assert ac(body_3.position, pos_1_out + pos_parent)

        # Let Leonard sync its data and then verify it received the correct
        # total force and torque exerted by the boosters.
        leo.processCommandsAndSync()
        forcevec_0, forcevec_1 = forcemag_0 * dir_0_out, forcemag_1 * dir_1_out
        tot_force = forcevec_0 + forcevec_1
        tot_torque = (np.cross(pos_0_out, forcevec_0) +
                      np.cross(pos_1_out, forcevec_1))

        # Query the torque and force from Azrael and verify they are correct.
        leo_force, leo_torque = leo.totalForceAndTorque(objID_1)
        assert np.array_equal(leo_force, tot_force)
        assert np.array_equal(leo_torque, tot_torque)
コード例 #16
0
ファイル: demo_default.py プロジェクト: daviddeng/azrael
def spawnCubes(numCols, numRows, numLayers, center=(0, 0, 0)):
    """
    Spawn multiple cubes in a regular grid.

    The number of cubes equals ``numCols`` * ``numRows`` * ``numLayers``. The
    center of this "prism" is at ``center``.

    Every cube has two boosters and two factories. The factories can themselves
    spawn more (purely passive) cubes.
    """
    tID_cube = addTexturedCubeTemplates(numCols, numRows, numLayers)

    # Get a Client instance.
    client = azrael.client.Client()

    # ----------------------------------------------------------------------
    # Spawn the differently textured cubes in a regular grid.
    # ----------------------------------------------------------------------
    allObjs = []
    cube_idx = 0
    cube_spacing = 0.1

    # Determine the template and position for every cube. The cubes are *not*
    # spawned in this loop, but afterwards.
    print("Compiling scene: ", end="", flush=True)
    t0 = time.time()
    for row in range(numRows):
        for col in range(numCols):
            for lay in range(numLayers):
                # Base position of cube.
                pos = np.array([col, row, lay], np.float64)

                # Add space in between cubes.
                pos *= -(2 + cube_spacing)

                # Correct the cube's position to ensure the center of the
                # grid coincides with the origin.
                pos[0] += (numCols // 2) * (1 + cube_spacing)
                pos[1] += (numRows // 2) * (1 + cube_spacing)
                pos[2] += (numLayers // 2) * (1 + cube_spacing)

                # Move the grid to position ``center``.
                pos += np.array(center)

                # Store the position and template for this cube.
                allObjs.append({"templateID": tID_cube[cube_idx], "rbs": {"position": pos.tolist()}})
                cube_idx += 1
                del pos
    print("{:,} objects ({:.1f}s)".format(len(allObjs), time.time() - t0))
    del cube_idx, cube_spacing, row, col, lay

    # Spawn the cubes from the templates at the just determined positions.
    print("Spawning {} objects: ".format(len(allObjs)), end="", flush=True)
    t0 = time.time()
    ret = client.spawn(allObjs)
    if not ret.ok:
        print("** Error:")
        print(ret)
        assert False
    print(" {:.1f}s".format(time.time() - t0))

    # Make 'frag_2' invisible by setting its scale to zero.
    for objID in ret.data:
        assert client.setFragments({objID: {"frag_2": {"scale": 0}}}).ok
        assert client.setCustomData({objID: "asteroid"}).ok
コード例 #17
0
ファイル: demo_chain.py プロジェクト: daviddeng/azrael
def spawnCubes(numCols, numRows, numLayers, center=(0, 0, 0)):
    """
    Spawn multiple cubes in a regular grid.

    The number of cubes equals ``numCols`` * ``numRows`` * ``numLayers``. The
    center of this "prism" is at ``center``.

    Every cube has two boosters and two factories. The factories can themselves
    spawn more (purely passive) cubes.
    """
    # Get a Client instance.
    client = azrael.client.Client()

    # Vertices that define a Cube.
    vert = 1 * np.array([
        -1.0, -1.0, -1.0,   -1.0, -1.0, +1.0,   -1.0, +1.0, +1.0,
        -1.0, -1.0, -1.0,   -1.0, +1.0, +1.0,   -1.0, +1.0, -1.0,
        +1.0, -1.0, -1.0,   +1.0, +1.0, +1.0,   +1.0, -1.0, +1.0,
        +1.0, -1.0, -1.0,   +1.0, +1.0, -1.0,   +1.0, +1.0, +1.0,
        +1.0, -1.0, +1.0,   -1.0, -1.0, -1.0,   +1.0, -1.0, -1.0,
        +1.0, -1.0, +1.0,   -1.0, -1.0, +1.0,   -1.0, -1.0, -1.0,
        +1.0, +1.0, +1.0,   +1.0, +1.0, -1.0,   -1.0, +1.0, -1.0,
        +1.0, +1.0, +1.0,   -1.0, +1.0, -1.0,   -1.0, +1.0, +1.0,
        +1.0, +1.0, -1.0,   -1.0, -1.0, -1.0,   -1.0, +1.0, -1.0,
        +1.0, +1.0, -1.0,   +1.0, -1.0, -1.0,   -1.0, -1.0, -1.0,
        -1.0, +1.0, +1.0,   -1.0, -1.0, +1.0,   +1.0, -1.0, +1.0,
        +1.0, +1.0, +1.0,   -1.0, +1.0, +1.0,   +1.0, -1.0, +1.0
    ])

    # Convenience.
    cs = CollShapeBox(1, 1, 1)
    cs = CollShapeMeta('', 'box', (0, 0, 0), (0, 0, 0, 1), cs)
    uv = np.array([], np.float64)
    rgb = np.array([], np.uint8)

    uv = np.zeros(12 * 6, np.float64)
    uv[0:6] = [0, 0, 1, 0, 1, 1]
    uv[6:12] = [0, 0, 1, 1, 0, 1]
    uv[12:18] = [1, 0, 0, 1, 0, 0]
    uv[18:24] = [1, 0, 1, 1, 0, 1]
    uv[24:30] = [0, 0, 1, 1, 0, 1]
    uv[30:36] = [0, 0, 1, 0, 1, 1]
    uv[36:42] = [1, 1, 1, 0, 0, 0]
    uv[42:48] = [1, 1, 0, 0, 0, 1]
    uv[48:54] = [0, 1, 1, 0, 1, 1]
    uv[54:60] = [0, 1, 0, 0, 1, 0]
    uv[60:66] = [0, 1, 0, 0, 1, 0]
    uv[66:72] = [1, 1, 0, 1, 1, 0]

    uv = np.array(uv, np.float64)

    # Compile the path to the texture file.
    path_base = os.path.dirname(os.path.abspath(__file__))
    path_base = os.path.join(path_base, '..', 'azrael', 'static', 'img')
    fname = os.path.join(path_base, 'texture_5.jpg')

    # Load the texture and convert it to flat vector because this is how OpenGL
    # will want it.
    img = PIL.Image.open(fname)
    img = np.array(img)
    rgb = np.rollaxis(np.flipud(img), 1).flatten()

    # # ----------------------------------------------------------------------
    # # Create templates for the factory output.
    # # ----------------------------------------------------------------------
    # tID_1 = 'Product1'
    # tID_2 = 'Product2'
    # frags_1 = [FragMeta('frag_1', 'raw', FragRaw(0.75 * vert, uv, rgb))]
    # frags_2 = [FragMeta('frag_1', 'raw', FragRaw(0.24 * vert, uv, rgb))]
    # t1 = Template(tID_1, [cs], frags_1, [], [])
    # t2 = Template(tID_2, [cs], frags_2, [], [])
    # assert client.addTemplates([t1, t2]).ok
    # del frags_1, frags_2, t1, t2

    # ----------------------------------------------------------------------
    # Define a cube with boosters and factories.
    # ----------------------------------------------------------------------
    # Two boosters, one left, one right. Both point in the same direction.
    b0 = types.Booster(partID='0', pos=[+0.05, 0, 0], direction=[0, 0, 1],
                       minval=0, maxval=10.0, force=0)
    b1 = types.Booster(partID='1', pos=[-0.05, 0, 0], direction=[0, 0, 1],
                       minval=0, maxval=10.0, force=0)

    # # Two factories, one left one right. They will eject the new objects
    # # forwards and backwards, respectively.
    # f0 = types.Factory(
    #     partID='0', pos=[+1.5, 0, 0], direction=[+1, 0, 0],
    #     templateID=tID_1, exit_speed=[0.1, 1])
    # f1 = types.Factory(
    #     partID='1', pos=[-1.5, 0, 0], direction=[-1, 0, 0],
    #     templateID=tID_2, exit_speed=[0.1, 1])

    # # Add the template.
    # tID_3 = 'BoosterCube'
    # frags = [FragMeta('frag_1', 'raw', FragRaw(vert, uv, rgb))]
    # t3 = Template(tID_3, [cs], frags, [b0, b1], [f0, f1])
    # assert client.addTemplates([t3]).ok
    # del frags, t3

    # ----------------------------------------------------------------------
    # Define more booster cubes, each with a different texture.
    # ----------------------------------------------------------------------
    tID_cube = {}
    templates = []
    texture_errors = 0
    for ii in range(numRows * numCols * numLayers):
        # File name of texture.
        fname = os.path.join(path_base, 'texture_{}.jpg'.format(ii + 1))

        # Load the texture image. If the image is unavailable do not endow the
        # cube with a texture.
        try:
            img = PIL.Image.open(fname)
            img = np.array(img)
            rgb = np.rollaxis(np.flipud(img), 1).flatten()
            curUV = uv
        except FileNotFoundError:
            texture_errors += 1
            rgb = curUV = np.array([])

        # Create the template.
        tID = ('BoosterCube_{}'.format(ii))
        frags = [FragMeta('frag_1', 'raw', FragRaw(vert, curUV, rgb)),
                 FragMeta('frag_2', 'raw', FragRaw(vert, curUV, rgb))]
        tmp = Template(tID, [cs], frags, [b0, b1], [])
        templates.append(tmp)

        # Add the templateID to a dictionary because we will need it in the
        # next step to spawn the templates.
        tID_cube[ii] = tID
        del frags, tmp, tID, fname

    if texture_errors > 0:
        print('Could not load texture for {} of the {} objects'
              .format(texture_errors, ii + 1))

    # Define all templates.
    print('Adding {} templates: '.format(ii + 1), end='', flush=True)
    t0 = time.time()
    assert client.addTemplates(templates).ok
    print('{:.1f}s'.format(time.time() - t0))

    # ----------------------------------------------------------------------
    # Spawn the differently textured cubes in a regular grid.
    # ----------------------------------------------------------------------
    allObjs = []
    cube_idx = 0
    cube_spacing = 0.0

    # Determine the template and position for every cube. The cubes are *not*
    # spawned in this loop, but afterwards.
    print('Compiling scene: ', end='', flush=True)
    t0 = time.time()
    for row in range(numRows):
        for col in range(numCols):
            for lay in range(numLayers):
                # Base position of cube.
                pos = np.array([col, row, lay], np.float64)

                # Add space in between cubes.
                pos *= -(4 + cube_spacing)

                # Correct the cube's position to ensure the center of the
                # grid coincides with the origin.
                pos[0] += (numCols // 2) * (1 + cube_spacing)
                pos[1] += (numRows // 2) * (1 + cube_spacing)
                pos[2] += (numLayers // 2) * (1 + cube_spacing)

                # Move the grid to position ``center``.
                pos += np.array(center)

                # Store the position and template for this cube.
                allObjs.append({'template': tID_cube[cube_idx],
                                'position': pos})
                cube_idx += 1
                del pos

    # fixme: this code assumes there are at least 4 cubes!
    if len(allObjs) < 4:
        print('fixme: start demo with at least 4 cubes!')
        sys.exit(1)
    allObjs = []
    pos_0 = [2, 0, 10]
    pos_1 = [-2, 0, 10]
    pos_2 = [-6, 0, 10]
    pos_3 = [-10, 0, 10]
    allObjs.append({'template': tID_cube[0], 'position': pos_0})
    allObjs.append({'template': tID_cube[1], 'position': pos_1})
    allObjs.append({'template': tID_cube[2], 'position': pos_2})
    allObjs.append({'template': tID_cube[3], 'position': pos_3})

    # The first object cannot move (only rotate). It serves as an anchor for
    # the connected bodies.
    allObjs[0]['axesLockLin'] = [0, 0, 0]
    allObjs[0]['axesLockRot'] = [1, 1, 1]

    # Add a small damping factor to all bodies to avoid them moving around
    # perpetually.
    for oo in allObjs[1:]:
        oo['axesLockLin'] = [0.9, 0.9, 0.9]
        oo['axesLockRot'] = [0.9, 0.9, 0.9]

    print('{:,} objects ({:.1f}s)'.format(len(allObjs), time.time() - t0))
    del cube_idx, cube_spacing, row, col, lay

    # Spawn the cubes from the templates at the just determined positions.
    print('Spawning {} objects: '.format(len(allObjs)), end='', flush=True)
    t0 = time.time()
    ret = client.spawn(allObjs)
    assert ret.ok
    ids = ret.data
    print('{:.1f}s'.format(time.time() - t0))

    # Define the constraints.
    p2p_0 = ConstraintP2P(pivot_a=[-2, 0, 0], pivot_b=[2, 0, 0])
    p2p_1 = ConstraintP2P(pivot_a=[-2, 0, 0], pivot_b=[2, 0, 0])
    p2p_2 = ConstraintP2P(pivot_a=[-2, 0, 0], pivot_b=[2, 0, 0])
    dof = Constraint6DofSpring2(
        frameInA=[0, 0, 0, 0, 0, 0, 1],
        frameInB=[0, 0, 0, 0, 0, 0, 1],
        stiffness=[2, 2, 2, 1, 1, 1],
        damping=[1, 1, 1, 1, 1, 1],
        equilibrium=[-2, -2, -2, 0, 0, 0],
        linLimitLo=[-4.5, -4.5, -4.5],
        linLimitHi=[4.5, 4.5, 4.5],
        rotLimitLo=[-0.1, -0.2, -0.3],
        rotLimitHi=[0.1, 0.2, 0.3],
        bounce=[1, 1.5, 2],
        enableSpring=[True, False, False, False, False, False])
    constraints = [
        ConstraintMeta('', 'p2p', ids[0], ids[1], p2p_0),
        ConstraintMeta('', 'p2p', ids[1], ids[2], p2p_1),
        ConstraintMeta('', '6DOFSPRING2', ids[2], ids[3], dof),
    ]
    assert client.addConstraints(constraints) == (True, None, 3)

    # Make 'frag_2' invisible by setting its scale to zero.
    for objID in ret.data:
        client.setFragmentStates({objID: [
            FragState('frag_2', 0, [0, 0, 0], [0, 0, 0, 1])]})
コード例 #18
0
ファイル: demo_pcb.py プロジェクト: daviddeng/azrael
def placeTarget(host, numTargets=1):
    """
    Spawn ``numTargets`` in the scene.

    The targets visually oscillate in size. They also have no collision
    shapes and are thus unaffected by physics (ie they cannot collide
    with anything).
    """
    # Connect to Azrael.
    client = azrael.client.Client(ip=host)

    # Spawn the target object from the 'BoosterCube_1' template (defined in
    # 'demo_boostercube' that must already be running at this point).
    init = []
    for ii in range(numTargets):
        tmp = {
            'templateID': 'BoosterCube_1',
            'rbs': {'imass': 0, 'position': (0, 0, 3 * ii)}
        }
        init.append(tmp)
    ret = client.spawn(init)

    # Check for errors and abort if there are any.
    if not ret.ok:
        print(ret)
        sys.exit(1)

    # Extract the IDs of the spawned target objects.
    targetIDs = ret.data
    print('Spawned {} targets'.format(len(targetIDs)))
    del init, ret

    # Replace the collision shape with an empty one to disable the physics for
    # those targets.
    cs = types.CollShapeEmpty()
    cs = types.CollShapeMeta('empty', (0, 0, 0), (0, 0, 0, 1), cs)
    cmd = {targetID: {'cshapes': {'cssphere': cs}} for targetID in targetIDs}
    assert client.setRigidBodies(cmd).ok
    del cs

    # Tag the object with target. This is necessary because the
    # `PyConBrisbaneClient.selectNewTarget` method will use to distinguish
    # targets from other objects.
    cmd = {targetID: 'Target' for targetID in targetIDs}
    assert client.setCustomData(cmd)

    # Create a random phase offset in the oscillation pattern (pure eye candy
    # to avoid all targets scale synchronously).
    phi = 2 * np.pi * np.random.rand(len(targetIDs))

    # Modify the scale of the target every 100ms.
    cnt = 0
    while True:
        time.sleep(0.1)

        # Compile the payload for the update command, then send it to Azrael.
        # The fragment names (eg 'frag_1') are hard coded in the Template
        # (don't worry about them, just accept that they exist).
        cmd = {}
        for idx, targetID in enumerate(targetIDs):
            # Compute the new scale value.
            scale = 1 + np.sin(2 * np.pi * 0.1 * cnt + phi[idx])
            scale *= 0.1

            tmp = {
                'frag_1': {'scale': scale},
                'frag_2': {'scale': scale},
            }
            cmd[targetID] = tmp
        assert client.setFragments(cmd).ok

        # Randomly update the target's position every 10s.
        if (cnt % 100) == 0:
            cmd = {}
            for targetID in targetIDs:
                pos = 15 * np.random.rand(3) - 10
                cmd[targetID] = {'position': pos.tolist()}
            assert client.setRigidBodies(cmd).ok
        cnt += 1
コード例 #19
0
ファイル: demo_default.py プロジェクト: daviddeng/azrael
def addBoosterCubeTemplate(scale, vert, uv, rgb):
    # Get a Client instance.
    client = azrael.client.Client()

    # Ensure the data has the correct format.
    vert = scale * np.array(vert)
    uv = np.array(uv, np.float32)
    rgb = np.array(rgb, np.uint8)
    print("done")

    # Attach four boosters: left (points down), front (points back), right
    # (points up), and back (point forward).
    dir_up = np.array([0, +1, 0])
    dir_forward = np.array([0, 0, -1])
    pos_left = np.array([-1.5, 0, 0])
    pos_center = np.zeros(3)

    boosters = {
        "0": types.Booster(pos=pos_left, direction=-dir_up, minval=0, maxval=10.0, force=0),
        "1": types.Booster(pos=pos_center, direction=dir_forward, minval=0, maxval=1000.0, force=0),
        "2": types.Booster(pos=-pos_left, direction=dir_up, minval=0, maxval=10.0, force=0),
        "3": types.Booster(pos=pos_center, direction=-dir_forward, minval=0, maxval=1000.0, force=0),
    }
    del dir_up, dir_forward, pos_left, pos_center

    # Construct a Tetrahedron (triangular Pyramid). This is going to be the
    # (super simple) "flame" that comes out of the (still invisible) boosters.
    y = 0.5 * np.arctan(np.pi / 6)
    a = (-0.5, -y, 1)
    b = (0.5, -y, 1)
    c = (0, 3 / 4 - y, 1)
    d = (0, 0, 0)
    vert_b = [(a + b + c) + (a + b + d) + (a + c + d) + (b + c + d)]
    vert_b = np.array(vert_b[0], np.float64)
    del a, b, c, d, y

    # Add the template to Azrael.
    print("  Adding template to Azrael... ", end="", flush=True)
    tID = "ground"
    cs = CollShapeBox(1, 1, 1)
    cs = CollShapeMeta("box", (0, 0, 0), (0, 0, 0, 1), cs)
    z = np.array([])
    frags = {
        "frag_1": getFragMeta("raw", FragRaw(vert, uv, rgb)),
        "b_left": getFragMeta("raw", FragRaw(vert_b, z, z)),
        "b_right": getFragMeta("raw", FragRaw(vert_b, z, z)),
    }

    body = getRigidBody()
    temp = Template(tID, body, frags, boosters, {})
    assert client.addTemplates([temp]).ok
    del cs, frags, temp, z
    print("done")

    # Spawn the template near the center.
    print("  Spawning object... ", end="", flush=True)
    pos, orient = [0, 0, -10], [0, 1, 0, 0]
    new_obj = {
        "templateID": tID,
        "rbs": {
            "scale": scale,
            "imass": 0.1,
            "position": pos,
            "rotation": orient,
            "axesLockLin": [1, 1, 1],
            "axesLockRot": [1, 1, 1],
        },
    }
    ret = client.spawn([new_obj])
    objID = ret.data[0]
    print("done (ID=<{}>)".format(objID))

    # Disable the booster fragments by settings their scale to Zero.
    newStates = {objID: {"b_left": {"scale": 0}, "b_right": {"scale": 0}}}
    assert client.setFragments(newStates).ok