コード例 #1
0
def createTemplate():
    # Create the vertices for a unit cube.
    vert = defineCube()

    # Define initial fragment size and position relative to rigid body.
    scale = 1
    pos, rot = (0, 0, 0), (0, 0, 0, 1)

    # Define the one and only geometry fragment for this template.
    data_raw = FragRaw(vert, [], [])
    frags = {'frag_foo': FragMeta('raw', scale, pos, rot, data_raw)}
    del scale, pos, rot

    # We will need that collision shape to construct the rigid body below.
    cs_sphere = CollShapeMeta(cstype='Sphere',
                              position=(0, 0, 0),
                              rotation=(0, 0, 0, 1),
                              csdata=CollShapeSphere(radius=1))

    # Create the rigid body.
    body = azrael.aztypes.RigidBodyData(
        scale=1,
        imass=1,
        restitution=0.9,
        rotation=(0, 0, 0, 1),
        position=(0, 0, 0),
        velocityLin=(0, 0, 0),
        velocityRot=(0, 0, 0),
        cshapes={'foo_sphere': cs_sphere},
        linFactor=(1, 1, 1),
        rotFactor=(1, 1, 1),
        version=0)

    # Compile and return the template.
    return Template('my_first_template', body, frags, {}, {})
コード例 #2
0
ファイル: demolib.py プロジェクト: QuantumUniverse777/azrael
def cubeGeometry(hlen_x=1.0, hlen_y=1.0, hlen_z=1.0):
    """
    Return the vertices and collision shape for a Box.

    The parameters ``hlen_*`` are the half lengths of the box in the respective
    dimension.
    """
    # 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
    ])

    # Scale the x/y/z dimensions.
    vert[0::3] *= hlen_x
    vert[1::3] *= hlen_y
    vert[2::3] *= hlen_z

    # Convenience.
    box = CollShapeBox(hlen_x, hlen_y, hlen_z)
    cs = CollShapeMeta('box', (0, 0, 0), (0, 0, 0, 1), box)
    return vert, cs
コード例 #3
0
def createTemplate():
    # Create the vertices for a unit cube.
    vert = defineCube()

    # Define initial fragment size and position relative to rigid body.
    scale = 1
    pos, rot = (0, 0, 0), (0, 0, 0, 1)

    # Define the one and only geometry fragment for this template.
    data_raw = FragRaw(vert, [], [])
    frags = {
        'body': FragMeta('raw', scale, pos, rot, data_raw),
        'satellite': FragMeta('raw', scale, pos, rot, data_raw),
    }
    del scale, pos, rot

    # We will need that collision shape to construct the rigid body below.
    cs_sphere = CollShapeMeta(cstype='Sphere',
                              position=(0, 0, 0),
                              rotation=(0, 0, 0, 1),
                              csdata=CollShapeSphere(radius=1))

    # Create the rigid body.
    body = RigidBodyData(scale=1,
                         imass=1,
                         restitution=0.9,
                         rotation=(0, 0, 0, 1),
                         position=(0, 0, 0),
                         velocityLin=(0, 0, 0),
                         velocityRot=(0, 0, 0),
                         cshapes={'foo_sphere': cs_sphere},
                         linFactor=(1, 1, 1),
                         rotFactor=(1, 1, 1),
                         version=0)

    # Define a booster
    booster = Booster(
        position=[0, 0, 0],  # Booster is located here and...
        direction=[1, 0, 0],  # points in this direction.
        force=0  # Initial force.
    )
    boosters = {'booster_foo': booster}

    return Template('my_first_template', body, frags, boosters, {})
コード例 #4
0
ファイル: demolib.py プロジェクト: QuantumUniverse777/azrael
def loadSphere(radius: float=1.0):
    """
    Return the collision shape and vertices for a sphere with ``radius``.

    Args:
        radius (float): sphere radius (must be >= 0.01).

    Returns:
        (vert, colshapes): the vertices and collision shape for the sphere.
    """
    assert radius >= 0.01
    p = os.path.dirname(os.path.abspath(__file__))
    fname = os.path.join(p, 'models', 'sphere', 'sphere.obj')
    vert, uv, rgb = loadModel(fname)
    vert *= radius
    cshapes = CollShapeMeta(cstype='Sphere',
                            position=(0, 0, 0),
                            rotation=(0, 0, 0, 1),
                            csdata=CollShapeSphere(radius=radius))
    return vert, cshapes
コード例 #5
0
ファイル: demolib.py プロジェクト: QuantumUniverse777/azrael
def getRigidBody(scale: (int, float)=1,
                 imass: (int, float)=1,
                 restitution: (int, float)=0.9,
                 com: (tuple, list)=(0, 0, 0),
                 inertia: (tuple, list)=(1, 1, 1),
                 paxis: (tuple, list)=(0, 0, 0, 1),
                 rotation: (tuple, list)=(0, 0, 0, 1),
                 position: (tuple, list, np.ndarray)=(0, 0, 0),
                 velocityLin: (tuple, list, np.ndarray)=(0, 0, 0),
                 velocityRot: (tuple, list, np.ndarray)=(0, 0, 0),
                 cshapes: dict=None,
                 linFactor: (tuple, list, np.ndarray)=(1, 1, 1),
                 rotFactor: (tuple, list, np.ndarray)=(1, 1, 1),
                 version: int=0):
    if cshapes is None:
        cshapes = CollShapeMeta(cstype='Sphere',
                                position=(0, 0, 0),
                                rotation=(0, 0, 0, 1),
                                csdata=CollShapeSphere(radius=1))
        cshapes = {'Sphere': cshapes}
    return aztypes.RigidBodyData(
        scale, imass, restitution, com, inertia, paxis,
        rotation, position, velocityLin, velocityRot, cshapes,
        linFactor, rotFactor, version)
コード例 #6
0
ファイル: test.py プロジェクト: QuantumUniverse777/azrael
def getCSBox(pos=[0, 0, 0], rot=[0, 0, 0, 1], dim=[1, 1, 1]):
    """
    Convenience function: return collision shape for Box.
    """
    return CollShapeMeta('box', pos, rot, CollShapeBox(*dim))
コード例 #7
0
ファイル: test.py プロジェクト: QuantumUniverse777/azrael
def getCSEmpty(pos=[0, 0, 0], rot=[0, 0, 0, 1]):
    """
    Convenience function to construct an Empty shape.
    """
    return CollShapeMeta('empty', pos, rot, CollShapeEmpty())
コード例 #8
0
ファイル: test.py プロジェクト: QuantumUniverse777/azrael
def getCSPlane(pos=[0, 0, 0], rot=[0, 0, 0, 1], normal=[0, 0, 1], ofs=0):
    """
    Convenience function: return collision shape for a Plane in the x/y
    dimension.
    """
    return CollShapeMeta('plane', pos, rot, CollShapePlane(normal, ofs))
コード例 #9
0
ファイル: test.py プロジェクト: QuantumUniverse777/azrael
def getCSSphere(pos=[0, 0, 0], rot=[0, 0, 0, 1], radius=1):
    """
    Convenience function: return collision shape for Sphere.
    """
    return CollShapeMeta('sphere', pos, rot, CollShapeSphere(radius))
コード例 #10
0
    def _compileCollisionShape(self, rbState: _RigidBodyData):
        """
        Return the correct Bullet collision shape based on ``rbState``.

        Bullet does *not* use inertia tensors. It also does not have (much of)
        a concept of centre of mass. Instead it *assumes* that the principal
        axis of inertia of the (unrotated) collision shape is *literally* the
        x/y/z axis of the world coordinate system. It *assumes* further that
        the centre of mass is at the center of that collision shape.

        This is fine for axis symmetric objects (eg spheres, boxes, capsules),
        but  not much else. We can get around this restriction with a compound
        shape. The basic idea is that with a compound shape bullet does not
        care about the child shapes, only the compound shape. We may thus apply
        a particular transform (translate/rotate) to all child shapes and the
        inverse transform to the compound shape. If we do this correctly we can
        make the position of the compound shape coincide with its centre of
        mass, and have its principal axis aligned with x/y/z in world
        coordinates. This is explained next.

        Before we start recall that the centre of mass and axis of inertia for
        the overall rigid body were specified by the user (available in the
        `rbState` argument). Azrael *never* computes or modifies inertia
        tensors or centre of masses - the user has to do that explicitly.

        Now, to get around the aforementioned problem: first we put the
        collision shape(s) into a compound shape. However, we do not put them
        at their absolute positions but at their respective position/rotation
        relative to the centre of mass and principal axis orientation.

        The compound shape's centre of mass thus coincides with the position of
        the compound shape. The compound's principal axis of inertia also
        coincides with the x/y/z axis of the world coordinates. Together this
        satisfies Bullet's assumption stated in the second paragraph of this
        doc string.

        The position/rotation of the child shapes in world coordinates is still
        *wrong*, however, because their position/rotation was relative to
        centre of mass and principal axis. To correct it we move and rotate the
        compound shape in the exact opposite way.

        The net effect is that the child shapes are now in the correct location
        (as we want it) *and* the centre of mass coincides with the position of
        the compound shape (as Bullet wants it) *and* the principal axis of the
        compound shape aligns with the x/y/z axis of the world coordinate
        system (as Bullet wants it as well).

        Remarks:
          * The mass and inertia of the child shapes is irrelevant; Bullet
            only looks at the mass/inertia of the compound.
          * Bullet does not compute the mass/inertia of the compound based on
            the children; it offers convenience methods for doing so, but the
            user must explicitly use them.
          * The one and only purpose of the child shapes inside a compound is
            to define where (and how) the rigid body can be "collided with".

        :param _RigidBodyData rbState: meta data to describe the body.
        :return: compound shape with all the individual shapes.
        :rtype: ``CompoundShape``
        """
        # Create the compound shape that will hold all other shapes.
        compound = azBullet.CompoundShape()

        # Compute the inverse paComT.
        paxis = Quaternion(*rbState.paxis).normalized()

        # Determine the transform with respect to the principal axis
        # orientation and the centre of mass.
        i_paComT = Transform(paxis, Vec3(*rbState.com)).inverse()

        # Create the collision shapes one by one.
        scale = rbState.scale
        for cs in rbState.cshapes.values():
            # Convert the input data (usually a list of values) to a
            # proper CollShapeMeta tuple (sanity checks included).
            cs = CollShapeMeta(*cs)

            # Instantiate the specified collision shape.
            cstype = cs.cstype.upper()
            if cstype == 'SPHERE':
                sphere = CollShapeSphere(*cs.csdata)
                child = azBullet.SphereShape(scale * sphere.radius)
            elif cstype == 'BOX':
                box = CollShapeBox(*cs.csdata)
                hl = Vec3(scale * box.x, scale * box.y, scale * box.z)
                child = azBullet.BoxShape(hl)
            elif cstype == 'EMPTY':
                child = azBullet.EmptyShape()
            elif cstype == 'PLANE':
                # Planes are always static.
                rbState_mass = 0
                plane = CollShapePlane(*cs.csdata)
                normal = Vec3(*plane.normal)
                child = azBullet.StaticPlaneShape(normal, plane.ofs)
            else:
                child = azBullet.EmptyShape()
                msg = 'Unrecognised collision shape <{}>'.format(cstype)
                self.logit.warning(msg)

            # Specify the position/rotation of the child shape. Then adjust
            # them to be relative to the principal axis orientation and centre
            # of mass location (setRigidBodyData will compensate for this with
            # the inverse transform on the compound shape).
            t = Transform(Quaternion(*cs.rotation), Vec3(*cs.position))
            t = i_paComT * t

            # Add the child with the correct transform.
            compound.addChildShape(t, child)

        return compound
コード例 #11
0
def addBoosterCubeTemplate(scale, vert, uv, rgb):
    # Get a Client instance.
    client = pyazrael.AzraelClient()

    # 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': aztypes.Booster(position=pos_left, direction=-dir_up, force=0),
        '1': aztypes.Booster(position=pos_center, direction=dir_forward, force=0),
        '2': aztypes.Booster(position=-pos_left, direction=dir_up, force=0),
        '3': aztypes.Booster(position=pos_center, direction=-dir_forward, 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': demolib.getFragMetaRaw(vert, uv, rgb),
        'b_left': demolib.getFragMetaRaw(vert_b, z, z),
        'b_right': demolib.getFragMetaRaw(vert_b, z, z),
    }

    body = demolib.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,
            'linFactor': [1, 1, 1],
            'rotFactor': [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': {'op': 'mod', 'scale': 0},
        'b_right': {'op': 'mod', 'scale': 0},
    }}
    assert client.setFragments(newStates).ok
コード例 #12
0
def computeAABBs(cshapes: dict):
    """
    Return a dictionary of AABBs that correspond to the ``cshapes``.

    This function returns an error as soon as it encounters an unknown
    collision shape.

    If ``cshapes`` contains a Plane shape then it must be the only collision
    shape (ie len(cshapes) == 1). Furthermore, planes must have default values
    for position and rotation.

    ..note:: The bounding boxes are large enough to accommodate all possible
             rotations of the collision shape. This makes them larger than
             necessary yet avoids recomputing them whenever the rotation of
             the body changes.

    :param dict[name: CollShapeMeta] cshapes: collision shapes
    :return: AABBs for the ``cshapes``.
    """
    # Convenience.
    s3 = np.sqrt(3.1)

    # Compute the AABBs for each shape.
    aabbs = {}
    try:
        # Wrap the inputs into CollShapeMeta structures.
        cshapes = {k: CollShapeMeta(*v) for (k, v) in cshapes.items()}
        if 'PLANE' in [_.cstype.upper() for _ in cshapes.values()]:
            if len(cshapes) > 1:
                msg = 'Plane must be the only collision shape'
                return RetVal(False, msg, None)

            name, cshape = cshapes.popitem()

            # Planes must have defaule values for position and rotation, or
            # Azrael considers them invalid.
            pos, rot = tuple(cshape.position), tuple(cshape.rotation)
            if (pos == (0, 0, 0)) and (rot == (0, 0, 0, 1)):
                aabbs[name] = (0, 0, 0, 0, 0, 0)
                return RetVal(True, None, aabbs)
            else:
                msg = 'Planes must have default position and rotation'
                return RetVal(False, msg, None)

        for name, cs in cshapes.items():
            # Move the origin of the collision shape according to its rotation.
            quat = util.Quaternion(*cs.rotation)
            pos = tuple(quat * cs.position)

            # Determine the AABBs based on the collision shape type.
            ctype = cs.cstype.upper()
            if ctype == 'SPHERE':
                # All half lengths have the same length (equal to radius).
                r = CollShapeSphere(*cs.csdata).radius
                aabbs[name] = pos + (r, r, r)
            elif ctype == 'BOX':
                # All AABBs half lengths are equal. The value equals the
                # largest extent times sqrt(3) to accommodate all possible
                # rotations.
                tmp = s3 * max(CollShapeBox(*cs.csdata))
                aabbs[name] = pos + (tmp, tmp, tmp)
            elif ctype == 'EMPTY':
                # Empty shapes do not have an AABB.
                continue
            else:
                # Error.
                msg = 'Unknown collision shape <{}>'.format(ctype)
                return RetVal(False, msg, None)
    except TypeError:
        # Error: probably because 'CollShapeMeta' or one of its sub-shapes,
        # could not be constructed.
        msg = 'Encountered invalid collision shape data'
        return RetVal(False, msg, None)

    return RetVal(True, None, aabbs)