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')
def spawnSpaceship(scale, fname): """ Define a sphere with three boosters and four fragments. The first fragment comprises the hull (ie. the sphere itself), whereas remaining three fragments reprsent the "flames" that come out of the boosters. """ # Get a Client instance. client = azrael.client.Client() # Load the model vert, uv, rgb = demolib.loadBoosterCubeBlender() frag_cube = FragRaw(vert, uv, rgb) del vert, uv, rgb # Attach six boosters, two for every axis. dir_x = np.array([1, 0, 0]) dir_y = np.array([0, 1, 0]) dir_z = np.array([0, 0, 1]) pos = (0, 0, 0) B = types.Booster boosters = { 'b_x': B(pos=pos, direction=(1, 0, 0), minval=0, maxval=10.0, force=0), 'b_y': B(pos=pos, direction=(0, 1, 0), minval=0, maxval=10.0, force=0), 'b_z': B(pos=pos, direction=(0, 0, 1), minval=0, maxval=10.0, force=0) } del dir_x, dir_y, dir_z, pos, B # Construct a Tetrahedron (triangular Pyramid). This is going to be the # (super simple) "flame" that comes out of the (still invisible) boosters. p = os.path.dirname(os.path.abspath(__file__)) p = os.path.join(p, '..', 'viewer', 'models') fname = os.path.join(p, 'sphere', 'sphere.obj') vert, uv, rgb = demolib.loadModel(fname) rgb = np.tile([0, 0, 0.8], len(vert) // 3) rgb += 0.2 * np.random.rand(len(rgb)) rgb = np.array(255 * rgb.clip(0, 1), np.uint8) frag_flame = FragRaw(vert, np.array([]), rgb) del p, fname, vert, uv, rgb # Add the template to Azrael. print(' Adding template to Azrael... ', end='', flush=True) tID = 'spaceship' cs = types.CollShapeBox(scale, scale, scale) cs = types.CollShapeMeta('box', (0, 0, 0), (0, 0, 0, 1), cs) body = demolib.getRigidBody(cshapes={'0': cs}) pos, rot = (0, 0, 0), (0, 0, 0, 1) frags = { 'frag_1': FragMeta('raw', scale, pos, rot, frag_cube), 'b_x': FragMeta('raw', 0, pos, rot, frag_flame), 'b_y': FragMeta('raw', 0, pos, rot, frag_flame), 'b_z': FragMeta('raw', 0, pos, rot, frag_flame), } temp = Template(tID, body, frags, boosters, {}) assert client.addTemplates([temp]).ok del cs, frags, temp, frag_cube, frag_flame, scale, pos, rot print('done')
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)
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
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
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']
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])]})
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)
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)
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
def addTexturedCubeTemplates(numCols, numRows, numLayers): # 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 = {"frag_1": getFragMeta("raw", FragRaw(0.75 * vert, uv, rgb))} frags_2 = {"frag_1": getFragMeta("raw", FragRaw(0.24 * vert, uv, rgb))} body = getRigidBody(cshapes={"0": cs}) t1 = Template(tID_1, body, frags_1, {}, {}) t2 = Template(tID_2, body, 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. boosters = { "0": types.Booster(pos=[+0.05, 0, 0], direction=[0, 0, 1], minval=0, maxval=10.0, force=0), "1": types.Booster(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. factories = { "0": types.Factory(pos=[+1.5, 0, 0], direction=[+1, 0, 0], templateID=tID_1, exit_speed=[0.1, 1]), "1": types.Factory(pos=[-1.5, 0, 0], direction=[-1, 0, 0], templateID=tID_2, exit_speed=[0.1, 1]), } # Add the template. tID_3 = "BoosterCube" frags = {"frag_1": getFragMeta("raw", FragRaw(vert, uv, rgb))} body = getRigidBody(cshapes={"0": cs}) t3 = Template(tID_3, body, frags, boosters, factories) 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 = { "frag_1": getFragMeta("raw", FragRaw(vert, curUV, rgb)), "frag_2": getFragMeta("raw", FragRaw(vert, curUV, rgb)), } body = getRigidBody(cshapes={"0": cs}) tmp = Template(tID, body, frags, boosters, {}) 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)) return tID_cube
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