def addLineOfCubes(client, objName: str, numCubes: int): """ Spawn a single body with ``numCubes`` fragments. The body will also be tagged with `objName`. """ # Get the mesh and collision shape for a single cube. vert, csmetabox = demolib.cubeGeometry(0.5, 0.5, 0.5) # Create a fragment template (convenience only for the loop below). ref_frag = demolib.getFragMetaRaw(vert=vert, uv=[], rgb=[]) # Create the fragments and line them up in a line along the x-axis. frags = {} centre = (numCubes - 1) / 2 for idx in range(numCubes): frag_name = str(idx) frag_pos = [(idx - centre) * 2, 0, 0] frags[frag_name] = ref_frag._replace(position=frag_pos) # Rigid body description (defines its physics, not its appearance). body = demolib.getRigidBody(cshapes={'cs': csmetabox}) # Define the object template (physics and appearance) and upload to Azrael. template = Template('PlotObject', body, frags, {}, {}) ret = client.addTemplates([template]) # Spawn one instance. templates = [{'templateID': 'PlotObject', 'rbs': {'position': [0, 0, 0]}}] ret = client.spawn(templates) assert ret.ok objID = ret.data[0] # Tag the object. assert client.setObjectTags({objID: objName})
def _buildTemplate(name, imass, hlen, lf, rf): """ Return a complete template for a cube object. This is a helper function only. """ # Geometry and collision shape for platform. vert, cshapes = demolib.cubeGeometry(hlen, hlen, hlen) # Rigid body data for platforms (defines their physics, not appearance). body = demolib.getRigidBody( cshapes={'cs': cshapes}, linFactor=lf, rotFactor=rf, imass=imass, ) # Geometry for the platforms (defines their appearance, not physics). fm = demolib.getFragMetaRaw( vert=vert, uv=[], rgb=[], scale=1, pos=(0, 0, 0), rot=(0, 0, 0, 1) ) frags = {'frag_1': fm} # Define the platform template and upload it to Azrael. return Template(name, body, frags, {}, {})
def _buildTemplate(name, imass, hlen, lf, rf): """ Return a complete template for a cube object. This is a helper function only. """ # Geometry and collision shape for platform. vert, cshapes = demolib.cubeGeometry(hlen, hlen, hlen) # Rigid body data for platforms (defines their physics, not appearance). body = demolib.getRigidBody( cshapes={'cs': cshapes}, linFactor=lf, rotFactor=rf, imass=imass, ) # Geometry for the platforms (defines their appearance, not physics). fm = demolib.getFragMetaRaw(vert=vert, uv=[], rgb=[], scale=1, pos=(0, 0, 0), rot=(0, 0, 0, 1)) frags = {'frag_1': fm} # Define the platform template and upload it to Azrael. return Template(name, body, frags, {}, {})
def getSmallAsteroidTemplate(hlen): """ Return the template for a small asteroid. A small asteroid is merely a cube with half length ``hlen``. """ vert, cshapes = demolib.cubeGeometry(hlen, hlen, hlen) # Define the geometry of the Asteroids. fm = demolib.getFragMetaRaw( vert=vert, uv=[], rgb=[], scale=1, pos=(0, 0, 0), rot=(0, 0, 0, 1) ) frags = {'frag_1': fm} # Define the physics parameters for the Asteroids. body = demolib.getRigidBody( imass=80, inertia=[1, 1, 1], cshapes={'cs': cshapes}, ) return Template('Asteroid_small', body, frags, {}, {})
def getLargeAsteroidTemplate(hlen): """ Return the template for a large asteroid. A large asteroids consists of multiple 6 patched together cubes. Each cube has a half length of ``hlen``. """ def _randomise(scale, pos): pos = scale * np.random.uniform(-1, 1, 3) + pos return pos.tolist() # Geometry and collision shape for a single cube. vert, csmetabox = demolib.cubeGeometry(hlen, hlen, hlen) # Define the positions of the individual cubes/fragments in that will # constitute the Asteroid. The positions are slightly randomised to make the # Asteroids somewhat irregular. ofs = 1.5 * hlen noise = 0.6 frag_src = { 'up': _randomise(noise, [0, ofs, 0]), 'down': _randomise(noise, [0, -ofs, 0]), 'left': _randomise(noise, [ofs, 0, 0]), 'right': _randomise(noise, [-ofs, 0, 0]), 'front': _randomise(noise, [0, 0, ofs]), 'back': _randomise(noise, [0, 0, -ofs]), } del ofs # Patch together the fragments that will constitute the geometry of a # single large Asteroid. While at it, also create the individual collision # shapes for each fragment (not part of the geometry but needed # afterwards). frags, cshapes = {}, {} for name, pos in frag_src.items(): frags[name] = demolib.getFragMetaRaw( vert=vert, uv=[], rgb=[], scale=1, pos=pos, rot=[0, 0, 0, 1], ) # Collision shape for this fragment. cshapes[name] = csmetabox._replace(position=pos) del name, pos del frag_src # Specify the physics of the overall bodies. body = demolib.getRigidBody( imass=0.01, inertia=[3, 3, 3], cshapes=cshapes, ) # Return the completed Template. return Template('Asteroid_large', body, frags, {}, {})
def addMolecule(): # Connect to Azrael. client = azrael.clerk.Clerk() # The molecule will consist of several cubes. vert, csmetabox = demolib.cubeGeometry(0.5, 0.5, 0.5) # The molecule object. ofs = 2 frag_src = { 'up': [0, ofs, 0], 'down': [0, -ofs, 0], 'left': [ofs, 0, 0], 'right': [-ofs, 0, 0], 'front': [0, 0, ofs], 'back': [0, 0, -ofs], 'center': [0, 0, 0], } del ofs # frag_src = {'down': frag_src['down']} # frag_src = {'center': frag_src['center']} frags, cshapes = {}, {} for name, pos in frag_src.items(): frags[name] = demolib.getFragMetaRaw( vert=vert, uv=[], rgb=[], scale=1, pos=pos, rot=[0, 0, 0, 1], ) cshapes[name] = csmetabox._replace(position=pos) del name, pos del frag_src # Rigid body data parameters (defines its physics, not appearance). Specify # the centre of mass (com) and principal axis rotation (paxis) for the # inertia values. body = demolib.getRigidBody( imass=0.1, com=[0, -1, 0], inertia=[1, 2, 3], paxis=[0, 0, 0, 1], cshapes=cshapes, ) # Define the object template and upload it to Azrael. template = Template('molecule', body, frags, {}, {}) ret = client.addTemplates([template]) # Spawn one molecule. templates = [{'templateID': 'molecule', 'rbs': {'position': [0, 0.5, 0]}}] objID = client.spawn(templates)
def addPlatforms(): # Connect to Azrael. client = azrael.clerk.Clerk() # Geometry and collision shape for platform. vert, cshapes = demolib.cubeGeometry(2, 0.1, 2) # Rigid body data for platforms (defines their physics, not appearance). body = demolib.getRigidBody( cshapes={'cs': cshapes}, linFactor=[0, 0, 0], rotFactor=[0, 0, 0] ) # Geometry for the platforms (defines their appearance, not physics). fm = demolib.getFragMetaRaw( vert=vert, uv=[], rgb=[], scale=1, pos=(0, 0, 0), rot=(0, 0, 0, 1) ) frags = {'frag_1': fm} # Define the platform template and upload it to Azrael. template = Template('platform', body, frags, {}, {}) ret = client.addTemplates([template]) # Spawn several platforms at different positions. Their positions are # supposed to create the impression of a stairway. The values assume the # camera is at [0, 0, 10] and points in -z direction. platforms = [] for ii in range(5): pos = (-10 + ii * 5, -ii * 2, -20) platforms.append( { 'templateID': 'platform', 'rbs': { 'position': pos, 'velocityLin': (0, 0, 0), 'scale': 1, 'imass': 20 } } ) platformIDs = client.spawn(platforms) # Tag all platforms with a custom string. cmd = {platformID: 'Platform' for platformID in platformIDs.data} assert client.setObjectTags(cmd)
def addPlatforms(): # Connect to Azrael. client = azrael.clerk.Clerk() # Geometry and collision shape for platform. vert, cshapes = demolib.cubeGeometry(2, 0.1, 2) # Rigid body data for platforms (defines their physics, not appearance). body = demolib.getRigidBody(cshapes={'cs': cshapes}, linFactor=[0, 0, 0], rotFactor=[0, 0, 0]) # Geometry for the platforms (defines their appearance, not physics). fm = demolib.getFragMetaRaw(vert=vert, uv=[], rgb=[], scale=1, pos=(0, 0, 0), rot=(0, 0, 0, 1)) frags = {'frag_1': fm} # Define the platform template and upload it to Azrael. template = Template('platform', body, frags, {}, {}) ret = client.addTemplates([template]) # Spawn several platforms at different positions. Their positions are # supposed to create the impression of a stairway. The values assume the # camera is at [0, 0, 10] and points in -z direction. platforms = [] for ii in range(5): pos = (-10 + ii * 5, -ii * 2, -20) platforms.append({ 'templateID': 'platform', 'rbs': { 'position': pos, 'velocityLin': (0, 0, 0), 'scale': 1, 'imass': 20 } }) platformIDs = client.spawn(platforms) # Tag all platforms with a custom string. cmd = {platformID: 'Platform' for platformID in platformIDs.data} assert client.setObjectTags(cmd)
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 = pyazrael.AzraelClient() # Geometry and collision shape for cube. vert, cs = demolib.cubeGeometry() # Assign the UV coordinates. Each vertex needs a coordinate pair. That # means each triangle needs 6 coordinates. And the cube has 12 triangles. 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() # ---------------------------------------------------------------------- # Define a cube with boosters and factories. # ---------------------------------------------------------------------- # Two boosters, one left, one right. Both point in the same direction. boosters = { '0': aztypes.Booster(position=[+0.05, 0, 0], direction=[0, 0, 1], force=0), '1': aztypes.Booster(position=[-0.05, 0, 0], direction=[0, 0, 1], force=0) } # ---------------------------------------------------------------------- # 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) curUV = uv except FileNotFoundError: texture_errors += 1 rgb = curUV = np.array([]) # Create the template. tID = ('BoosterCube_{}'.format(ii)) frags = {'frag_1': demolib.getFragMetaRaw(vert, curUV, rgb), 'frag_2': demolib.getFragMetaRaw(vert, curUV, rgb)} body = demolib.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)) # ---------------------------------------------------------------------- # 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 # Since the first four cubes will be chained together we need at least four # of them! assert len(allObjs) >= 4 allObjs = [] pos_0 = [2, 0, -10] pos_1 = [-2, 0, -10] pos_2 = [-6, 0, -10] pos_3 = [-10, 0, -10] allObjs.append({'templateID': tID_cube[0], 'rbs': {'position': pos_0}}) allObjs.append({'templateID': tID_cube[1], 'rbs': {'position': pos_1}}) allObjs.append({'templateID': tID_cube[2], 'rbs': {'position': pos_2}}) allObjs.append({'templateID': tID_cube[3], 'rbs': {'position': pos_3}}) # The first object cannot move (only rotate). It serves as an anchor for # the connected bodies. allObjs[0]['rbs']['linFactor'] = [0, 0, 0] allObjs[0]['rbs']['rotFactor'] = [1, 1, 1] # Add a small damping factor to all bodies to avoid them moving around # perpetually. for oo in allObjs[1:]: oo['rbs']['linFactor'] = [0.9, 0.9, 0.9] oo['rbs']['rotFactor'] = [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 objIDs = 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', objIDs[0], objIDs[1], p2p_0), ConstraintMeta('', 'p2p', objIDs[1], objIDs[2], p2p_1), ConstraintMeta('', '6DOFSPRING2', objIDs[2], objIDs[3], dof), ] assert client.addConstraints(constraints) == ( True, None, [True] * len(constraints))
def addTexturedCubeTemplates(numCols, numRows, numLayers): # Get a Client instance. client = pyazrael.AzraelClient() # Geometry and collision shape for cube. vert, cs = demolib.cubeGeometry() # Assign the UV coordinates. Each vertex needs a coordinate pair. That # means each triangle needs 6 coordinates. And the cube has 12 triangles. 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 a 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) # ---------------------------------------------------------------------- # Create templates for the factory output. # ---------------------------------------------------------------------- tID_1 = 'Product1' tID_2 = 'Product2' frags_1 = {'frag_1': demolib.getFragMetaRaw(0.75 * vert, uv, rgb)} frags_2 = {'frag_1': demolib.getFragMetaRaw(0.24 * vert, uv, rgb)} body = demolib.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': aztypes.Booster(position=[+0.05, 0, 0], direction=[0, 0, 1], force=0), '1': aztypes.Booster(position=[-0.05, 0, 0], direction=[0, 0, 1], force=0) } # Two factories, one left one right. They will eject the new objects # forwards and backwards, respectively. factories = { '0': aztypes.Factory(position=[+1.5, 0, 0], direction=[+1, 0, 0], templateID=tID_1, exit_speed=[0.1, 1]), '1': aztypes.Factory(position=[-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': demolib.getFragMetaRaw(vert, uv, rgb)} body = demolib.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) curUV = uv except FileNotFoundError: texture_errors += 1 rgb = curUV = np.array([]) # Create the template. tID = ('BoosterCube_{}'.format(ii)) frags = {'frag_1': demolib.getFragMetaRaw(vert, curUV, rgb), 'frag_2': demolib.getFragMetaRaw(vert, curUV, rgb)} body = demolib.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 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 = pyazrael.AzraelClient() # Geometry and collision shape for cube. vert, cs = demolib.cubeGeometry() # Assign the UV coordinates. Each vertex needs a coordinate pair. That # means each triangle needs 6 coordinates. And the cube has 12 triangles. 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() # ---------------------------------------------------------------------- # Define a cube with boosters and factories. # ---------------------------------------------------------------------- # Two boosters, one left, one right. Both point in the same direction. boosters = { '0': aztypes.Booster(position=[+0.05, 0, 0], direction=[0, 0, 1], force=0), '1': aztypes.Booster(position=[-0.05, 0, 0], direction=[0, 0, 1], force=0) } # ---------------------------------------------------------------------- # 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) curUV = uv except FileNotFoundError: texture_errors += 1 rgb = curUV = np.array([]) # Create the template. tID = ('BoosterCube_{}'.format(ii)) frags = { 'frag_1': demolib.getFragMetaRaw(vert, curUV, rgb), 'frag_2': demolib.getFragMetaRaw(vert, curUV, rgb) } body = demolib.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)) # ---------------------------------------------------------------------- # 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 # Since the first four cubes will be chained together we need at least four # of them! assert len(allObjs) >= 4 allObjs = [] pos_0 = [2, 0, -10] pos_1 = [-2, 0, -10] pos_2 = [-6, 0, -10] pos_3 = [-10, 0, -10] allObjs.append({'templateID': tID_cube[0], 'rbs': {'position': pos_0}}) allObjs.append({'templateID': tID_cube[1], 'rbs': {'position': pos_1}}) allObjs.append({'templateID': tID_cube[2], 'rbs': {'position': pos_2}}) allObjs.append({'templateID': tID_cube[3], 'rbs': {'position': pos_3}}) # The first object cannot move (only rotate). It serves as an anchor for # the connected bodies. allObjs[0]['rbs']['linFactor'] = [0, 0, 0] allObjs[0]['rbs']['rotFactor'] = [1, 1, 1] # Add a small damping factor to all bodies to avoid them moving around # perpetually. for oo in allObjs[1:]: oo['rbs']['linFactor'] = [0.9, 0.9, 0.9] oo['rbs']['rotFactor'] = [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 objIDs = 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', objIDs[0], objIDs[1], p2p_0), ConstraintMeta('', 'p2p', objIDs[1], objIDs[2], p2p_1), ConstraintMeta('', '6DOFSPRING2', objIDs[2], objIDs[3], dof), ] assert client.addConstraints(constraints) == (True, None, [True] * len(constraints))