Exemple #1
0
 def loadImageAsPlane(self, filepath, yresolution = 600):
     tex = loader.loadTexture(filepath) # @UndefinedVariable
     tex.setBorderColor(Vec4(0,0,0,0))
     tex.setWrapU(Texture.WMBorderColor)
     tex.setWrapV(Texture.WMBorderColor)
     cm = CardMaker(filepath + ' card')
     cm.setFrame(-tex.getOrigFileXSize(), tex.getOrigFileXSize(), -tex.getOrigFileYSize(), tex.getOrigFileYSize())
     card = NodePath(cm.generate())
     card.setTexture(tex)
     card.setScale(card.getScale()/ yresolution)
     card.flattenLight() # apply scale
     return card
Exemple #2
0
 def loadImageAsPlane(filepath, yresolution = 600):
     tex = loader.loadTexture(filepath)
     tex.setBorderColor(Vec4(0,0,0,0))
     tex.setWrapU(Texture.WMBorderColor)
     tex.setWrapV(Texture.WMBorderColor)
     cm = CardMaker(filepath + ' card')
     cm.setFrame(-tex.getOrigFileXSize(), tex.getOrigFileXSize(), -tex.getOrigFileYSize(), tex.getOrigFileYSize())
     card = NodePath(cm.generate())
     card.setTransparency(TransparencyAttrib.MAlpha)
     card.setTexture(tex)
     card.setZ(card.getZ()+0.018)
     card.setScale(card.getScale()/ yresolution)
     card.flattenLight() # apply scale
     return card
    def test_door_setup(self):
        parent_np = NodePath('parent_np')
        parent_np.setPosHpr(0, 10, .5, 180, 0, 0)

        door_origin = parent_np.attachNewNode('door_origin')
        door_origin.setPos(10, -25, .5)

        block = 4
        color = Vec4(.842, .167, .361, 1)

        # Set up door_np nodes
        door_np = NodePath('door_np')

        left_hole = door_np.attachNewNode('door_0_hole_left')
        right_hole = door_np.attachNewNode('door_0_hole_right')

        left_door = door_np.attachNewNode('door_0_left')
        right_door = door_np.attachNewNode('door_0_right')

        door_flat = door_np.attachNewNode('door_0_flat')
        door_trigger = door_np.attachNewNode('door_0_trigger')

        DNADoor.setupDoor(door_np, parent_np, door_origin, self.store, block, color)

        # Check if the nodes attributes and parents are correct
        self.assertEqual(door_np.getPos(door_origin), Point3(0, 0, 0))
        self.assertEqual(door_np.getHpr(door_origin), Point3(0, 0, 0))
        self.assertEqual(door_np.getScale(door_origin), Point3(1, 1, 1))

        def verify_color(color1, color2):
            self.assertAlmostEqual(color1.getX(), color2.getX(), places=4)
            self.assertAlmostEqual(color1.getY(), color2.getY(), places=4)
            self.assertAlmostEqual(color1.getZ(), color2.getZ(), places=4)
            self.assertAlmostEqual(color1.getW(), color2.getW(), places=4)

        verify_color(color, door_np.getColor())

        self.assertEqual(left_hole.getParent(), door_flat)
        self.assertEqual(left_hole.getName(), 'doorFrameHoleLeft')

        self.assertEqual(right_hole.getParent(), door_flat)
        self.assertEqual(right_hole.getName(), 'doorFrameHoleRight')

        self.assertEqual(left_door.getParent(), parent_np)
        self.assertEqual(left_door.getName(), 'leftDoor')
        verify_color(color, right_door.getColor())

        self.assertEqual(right_door.getParent(), parent_np)
        self.assertEqual(right_door.getName(), 'rightDoor')
        verify_color(color, right_door.getColor())

        self.assertEqual(door_trigger.getParent(), parent_np)
        self.assertEqual(door_trigger.getName(), 'door_trigger_%d' % block)

        store_np = self.store.getDoorPosHprFromBlockNumber(block)
        self.assertFalse(store_np.isEmpty())

        # Testing the pos is a pain because of decimal precision
        pos = store_np.getPos()
        self.assertAlmostEqual(pos.getX(), -10, places=2)
        self.assertAlmostEqual(pos.getY(), 35, places=2)
        self.assertAlmostEqual(pos.getZ(), 1, places=2)

        # Sometimes getH() returns -180 and others 180
        # Test the modulus (better than abs I guess)
        self.assertEqual(store_np.getH() % 180, 0)
Exemple #4
0
class Transform(Component):
    """Each game object has exactly one of these. A transform holds data
    about position, rotation, scale and parent relationship.
    
    In Panity this is a wrapper for a NodePath.
    """
    def __init__(self, game_object, name):
        # Component class sets self.game_object = game_object
        Component.__init__(self, game_object)
        self.node = NodePath(name)
        self.node.setPythonTag("transform", self)

    @classmethod
    def getClassSerializedProperties(cls):
        """Return all special property attributes in a dict. Only attributes
        derived from SerializedProperty are respected.

        On transform component this method always returns local position,
        -rotation and scale only.
        """
        d = {}
        d["local_position"] = Transform.local_position
        d["local_euler_angles"] = Transform.local_euler_angles
        d["local_scale"] = Transform.local_scale
        return d

    def getSerializedProperties(self):
        """Return all properties for serialization. In the case of transform
        this only returns local position, -rotation and -scale, which are
        required to restore the state of the node.
        """
        d = {}
        d["local_position"] = self.local_position
        d["local_euler_angles"] = self.local_euler_angles
        d["local_scale"] = self.local_scale
        return d

    # We use the panda node to save the name on it for better debugging and
    # efficient finding of nodes with NodePath().find()
    @SerializedPropertyDecorator
    def name(self):
        return self.node.getName()

    @name.setter
    def name(self, name):
        if name == "render":
            name = "_render"
        self.node.setName(name)

    @SerializedPropertyDecorator
    def position(self):
        return self.node.getPos(self.root.node)

    @position.setter
    def position(self, position):
        self.node.setPos(self.root.node, *position)

    @SerializedPropertyDecorator
    def local_position(self):
        return self.node.getPos()

    @local_position.setter
    def local_position(self, position):
        self.node.setPos(*position)

    @SerializedPropertyDecorator
    def euler_angles(self):
        return self.node.getHpr(self.root.node)

    @euler_angles.setter
    def euler_angles(self, angles):
        self.node.setHpr(self.root.node, *angles)

    @SerializedPropertyDecorator
    def local_euler_angles(self):
        return self.node.getHpr()

    @local_euler_angles.setter
    def local_euler_angles(self, angles):
        self.node.setHpr(*angles)

    @SerializedPropertyDecorator
    def rotation(self):
        return self.node.getQuat(self.root.node)

    @rotation.setter
    def rotation(self, quaternion):
        self.node.setQuat(self.root.node, *quaternion)

    @SerializedPropertyDecorator
    def local_rotation(self):
        return self.node.getQuat()

    @local_rotation.setter
    def local_rotation(self, quaternion):
        self.node.setQuat(*quaternion)

    @SerializedPropertyDecorator
    def local_scale(self):
        return self.node.getScale()

    @local_scale.setter
    def local_scale(self, scale):
        self.node.setScale(*scale)

    @SerializedPropertyDecorator
    def parent(self):
        p = self.node.getParent()
        if p.isEmpty() or p.getName() == "render":
            return self
        elif p.hasPythonTag("transform"):
            return p.getPythonTag("transform")

    @parent.setter
    def parent(self, parent):
        self.node.wrtReparentTo(parent.node)

    @SerializedPropertyDecorator
    def root(self):
        if self.parent is not self:
            return self.parent.root()
        else:
            return self

    def destroy(self):
        """Ultimately remove this transform. Warning: this might cause errors
        for other components on this game object. Use this only when removing
        the whole GameObject.
        """
        self.node.removeNode()

    def getChildren(self):
        """Return children as Transforms."""
        # this requires the __iter__() method
        return [c for c in self]

    def __iter__(self):
        """Iterate over children nodes and yield the transform instances."""
        for child in self.node.getChildren():
            if child.hasPythonTag("transform"):
                yield child.getPythonTag("transform")

    def __str__(self):
        r = "Transform for '{}'\n".format(self.name)
        r += "local position: {}\n".format(self.local_position)
        r += "local rotation: {}\n".format(self.local_euler_angles)
        r += "local scale:    {}\n".format(self.local_scale)
        return r
Exemple #5
0
class StaticGeometricModel(object):
    """
    load an object as a static geometric model -> changing pos, rot, color, etc. are not allowed
    there is no extra elements for this model, thus is much faster
    author: weiwei
    date: 20190312
    """
    def __init__(self,
                 initor=None,
                 name="defaultname",
                 btransparency=True,
                 btwosided=False):
        """
        :param initor: path type defined by os.path or trimesh or nodepath
        :param btransparency
        :param name
        """
        if isinstance(initor, StaticGeometricModel):
            self._objpath = copy.deepcopy(initor.objpath)
            self._objtrm = copy.deepcopy(initor.objtrm)
            self._objpdnp = copy.deepcopy(initor.objpdnp)
            self._name = copy.deepcopy(initor.name)
            self._localframe = copy.deepcopy(initor.localframe)
        else:
            # make a grandma nodepath to separate decorations (-autoshader) and raw nodepath (+autoshader)
            self._name = name
            self._objpdnp = NodePath(name)
            if isinstance(initor, str):
                self._objpath = initor
                self._objtrm = da.trm.load(self._objpath)
                objpdnp_raw = da.trimesh_to_nodepath(self._objtrm,
                                                     name='pdnp_raw')
                objpdnp_raw.reparentTo(self._objpdnp)
            elif isinstance(initor, da.trm.Trimesh):
                self._objpath = None
                self._objtrm = initor
                objpdnp_raw = da.trimesh_to_nodepath(self._objtrm)
                objpdnp_raw.reparentTo(self._objpdnp)
            elif isinstance(initor, o3d.geometry.PointCloud
                            ):  # TODO should pointcloud be pdnp or pdnp_raw
                self._objpath = None
                self._objtrm = da.trm.Trimesh(np.asarray(initor.points))
                objpdnp_raw = da.nodepath_from_points(self._objtrm.vertices,
                                                      name='pdnp_raw')
                objpdnp_raw.reparentTo(self._objpdnp)
            elif isinstance(
                    initor,
                    np.ndarray):  # TODO should pointcloud be pdnp or pdnp_raw
                self._objpath = None
                if initor.shape[1] == 3:
                    self._objtrm = da.trm.Trimesh(initor)
                    objpdnp_raw = da.nodepath_from_points(
                        self._objtrm.vertices)
                elif initor.shape[1] == 7:
                    self._objtrm = da.trm.Trimesh(initor[:, :3])
                    objpdnp_raw = da.nodepath_from_points(
                        self._objtrm.vertices, initor[:, 3:].tolist())
                    objpdnp_raw.setRenderMode(RenderModeAttrib.MPoint, 3)
                else:
                    # TODO depth UV?
                    raise NotImplementedError
                objpdnp_raw.reparentTo(self._objpdnp)
            elif isinstance(initor, o3d.geometry.TriangleMesh):
                self._objpath = None
                self._objtrm = da.trm.Trimesh(
                    vertices=initor.vertices,
                    faces=initor.triangles,
                    face_normals=initor.triangle_normals)
                objpdnp_raw = da.trimesh_to_nodepath(self._objtrm,
                                                     name='pdnp_raw')
                objpdnp_raw.reparentTo(self._objpdnp)
            elif isinstance(initor, NodePath):
                self._objpath = None
                self._objtrm = None  # TODO nodepath to trimesh?
                objpdnp_raw = initor
                objpdnp_raw.reparentTo(self._objpdnp)
            else:
                self._objpath = None
                self._objtrm = None
                objpdnp_raw = NodePath("pdnp_raw")
                objpdnp_raw.reparentTo(self._objpdnp)
            if btransparency:
                self._objpdnp.setTransparency(TransparencyAttrib.MDual)
            if btwosided:
                self._objpdnp.getChild(0).setTwoSided(True)
            self._localframe = None

    @property
    def name(self):
        # read-only property
        return self._name

    @property
    def objpath(self):
        # read-only property
        return self._objpath

    @property
    def objpdnp(self):
        # read-only property
        return self._objpdnp

    @property
    def objpdnp_raw(self):
        # read-only property
        return self._objpdnp.getChild(0)

    @property
    def objtrm(self):
        # read-only property
        # 20210328 comment out, allow None
        # if self._objtrm is None:
        #     raise ValueError("Only applicable to models with a trimesh!")
        return self._objtrm

    @property
    def localframe(self):
        # read-only property
        return self._localframe

    @property
    def volume(self):
        # read-only property
        if self._objtrm is None:
            raise ValueError("Only applicable to models with a trimesh!")
        return self._objtrm.volume

    def set_rgba(self, rgba):
        self._objpdnp.setColor(rgba[0], rgba[1], rgba[2], rgba[3])

    def get_rgba(self):
        return da.pdv4_to_npv4(
            self._objpdnp.getColor())  # panda3d.core.LColor -> LBase4F

    def clear_rgba(self):
        self._objpdnp.clearColor()

    def set_scale(self, scale=[1, 1, 1]):
        self._objpdnp.setScale(scale[0], scale[1], scale[2])
        self._objtrm.apply_scale(scale)

    def get_scale(self):
        return da.pdv3_to_npv3(self._objpdnp.getScale())

    def set_vert_size(self, size=.005):
        self.objpdnp_raw.setRenderModeThickness(size * 1000)

    def attach_to(self, obj):
        if isinstance(obj, ShowBase):
            # for rendering to base.render
            self._objpdnp.reparentTo(obj.render)
        elif isinstance(obj, StaticGeometricModel
                        ):  # prepared for decorations like local frames
            self._objpdnp.reparentTo(obj.objpdnp)
        elif isinstance(obj, mc.ModelCollection):
            obj.add_gm(self)
        else:
            print(
                "Must be ShowBase, modeling.StaticGeometricModel, GeometricModel, CollisionModel, or CollisionModelCollection!"
            )

    def detach(self):
        self._objpdnp.detachNode()

    def remove(self):
        self._objpdnp.removeNode()

    def show_localframe(self):
        self._localframe = gen_frame()
        self._localframe.attach_to(self)

    def unshow_localframe(self):
        if self._localframe is not None:
            self._localframe.remove()
            self._localframe = None

    def copy(self):
        return copy.deepcopy(self)
Exemple #6
0
class Transform(Component):
    """Each game object has exactly one of these. A transform holds data
    about position, rotation, scale and parent relationship.
    
    In Panity this is a wrapper for a NodePath.
    """

    def __init__(self, game_object, name):
        # Component class sets self.game_object = game_object
        Component.__init__(self, game_object)
        self.node = NodePath(name)
        self.node.setPythonTag("transform", self)

    @classmethod
    def getClassSerializedProperties(cls):
        """Return all special property attributes in a dict. Only attributes
        derived from SerializedProperty are respected.

        On transform component this method always returns local position,
        -rotation and scale only.
        """
        d = {}
        d["local_position"] = Transform.local_position
        d["local_euler_angles"] = Transform.local_euler_angles
        d["local_scale"] = Transform.local_scale
        return d

    def getSerializedProperties(self):
        """Return all properties for serialization. In the case of transform
        this only returns local position, -rotation and -scale, which are
        required to restore the state of the node.
        """
        d = {}
        d["local_position"] = self.local_position
        d["local_euler_angles"] = self.local_euler_angles
        d["local_scale"] = self.local_scale
        return d

    # We use the panda node to save the name on it for better debugging and
    # efficient finding of nodes with NodePath().find()
    @SerializedPropertyDecorator
    def name(self):
        return self.node.getName()
    @name.setter
    def name(self, name):
        if name == "render":
            name = "_render"
        self.node.setName(name)

    @SerializedPropertyDecorator
    def position(self):
        return self.node.getPos(self.root.node)
    @position.setter
    def position(self, position):
        self.node.setPos(self.root.node, *position)
    
    @SerializedPropertyDecorator
    def local_position(self):
        return self.node.getPos()
    @local_position.setter
    def local_position(self, position):
        self.node.setPos(*position)

    @SerializedPropertyDecorator
    def euler_angles(self):
        return self.node.getHpr(self.root.node)
    @euler_angles.setter
    def euler_angles(self, angles):
        self.node.setHpr(self.root.node, *angles)
    
    @SerializedPropertyDecorator
    def local_euler_angles(self):
        return self.node.getHpr()
    @local_euler_angles.setter
    def local_euler_angles(self, angles):
        self.node.setHpr(*angles)
    
    @SerializedPropertyDecorator
    def rotation(self):
        return self.node.getQuat(self.root.node)
    @rotation.setter
    def rotation(self, quaternion):
        self.node.setQuat(self.root.node, *quaternion)

    @SerializedPropertyDecorator
    def local_rotation(self):
        return self.node.getQuat()
    @local_rotation.setter
    def local_rotation(self, quaternion):
        self.node.setQuat(*quaternion)

    @SerializedPropertyDecorator
    def local_scale(self):
        return self.node.getScale()
    @local_scale.setter
    def local_scale(self, scale):
        self.node.setScale(*scale)

    @SerializedPropertyDecorator
    def parent(self):
        p = self.node.getParent()
        if p.isEmpty() or p.getName() == "render":
            return self
        elif p.hasPythonTag("transform"):
            return p.getPythonTag("transform")
    @parent.setter
    def parent(self, parent):
        self.node.wrtReparentTo(parent.node)

    @SerializedPropertyDecorator
    def root(self):
        if self.parent is not self:
            return self.parent.root()
        else:
            return self
    
    def destroy(self):
        """Ultimately remove this transform. Warning: this might cause errors
        for other components on this game object. Use this only when removing
        the whole GameObject.
        """
        self.node.removeNode()

    def getChildren(self):
        """Return children as Transforms."""
        # this requires the __iter__() method
        return [c for c in self]

    def __iter__(self):
        """Iterate over children nodes and yield the transform instances."""
        for child in self.node.getChildren():
            if child.hasPythonTag("transform"):
                yield child.getPythonTag("transform")
    
    def __str__(self):
        r = "Transform for '{}'\n".format(self.name)
        r += "local position: {}\n".format(self.local_position)
        r += "local rotation: {}\n".format(self.local_euler_angles)
        r += "local scale:    {}\n".format(self.local_scale)
        return r
Exemple #7
0
class MapObject(MapWritable):

    ObjectName = "object"

    def __init__(self, id):
        MapObjectInit.start()

        MapWritable.__init__(self, base.document)
        self.temporary = False
        self.id = id
        self.selected = False
        self.classname = ""
        self.parent = None
        self.children = {}
        self.boundingBox = BoundingBox(Vec3(-0.5, -0.5, -0.5), Vec3(0.5, 0.5, 0.5))
        self.boundsBox = Box()
        self.boundsBox.addView(GeomView.Lines, VIEWPORT_3D_MASK, state = BoundsBox3DState)
        self.boundsBox.addView(GeomView.Lines, VIEWPORT_2D_MASK, state = BoundsBox2DState)
        self.boundsBox.generateGeometry()
        self.collNp = None

        self.group = None

        self.properties = {}

        # All MapObjects have transform
        self.addProperty(OriginProperty(self))
        self.addProperty(AnglesProperty(self))
        self.addProperty(ScaleProperty(self))
        self.addProperty(ShearProperty(self))

        self.np = NodePath(ModelNode(self.ObjectName + ".%i" % self.id))
        self.np.setPythonTag("mapobject", self)
        self.applyCollideMask()
        # Test bounding volume at this node and but nothing below it.
        self.np.node().setFinal(True)

        MapObjectInit.stop()

    def getClassName(self):
        return self.classname

    def isWorld(self):
        return False

    def r_findAllParents(self, parents, type):
        if not self.parent or self.parent.isWorld():
            return

        if type is None or isinstance(self.parent, type):
            parents.append(self.parent)

        self.parent.r_findAllParents(parents, type)

    def findAllParents(self, type = None):
        parents = []
        self.r_findAllParents(parents, type)
        return parents

    def findTopmostParent(self, type = None):
        parents = self.findAllParents(type)
        if len(parents) == 0:
            return None

        return parents[len(parents) - 1]

    def r_findAllChildren(self, children, type):
        for child in self.children.values():
            if type is None or isinstance(child, type):
                children.append(child)
            child.r_findAllChildren(children, type)

    def findAllChildren(self, type = None):
        children = []

        self.r_findAllChildren(children, type)

        return children

    def applyCollideMask(self):
        self.np.setCollideMask(LEGlobals.ObjectMask)

    def setTemporary(self, flag):
        self.temporary = flag

    # Returns the bounding volume of the object itself, not including children objects.
    def getObjBounds(self, other = None):
        if not other:
            other = self.np.getParent()
        return self.np.getTightBounds(other)

    # Returns the min and max points of the bounds of the object, not including children.
    def getBounds(self, other = None):
        if not other:
            other = self.np.getParent()
        mins = Point3()
        maxs = Point3()
        self.np.calcTightBounds(mins, maxs, other)
        return [mins, maxs]

    def findChildByID(self, id):
        if id == self.id:
            return self

        if id in self.children:
            return self.children[id]

        for child in self.children.values():
            ret = child.findChildByID(id)
            if ret is not None:
                return ret

        return None

    def hasChildWithID(self, id):
        return id in self.children

    def copy(self, generator):
        raise NotImplementedError

    def paste(self, o, generator):
        raise NotImplementedError

    def clone(self):
        raise NotImplementedError

    def unclone(self, o):
        raise NotImplementedError

    #
    # Base copy and paste functions shared by all MapObjects.
    # Each specific MapObject must implement the functions above for their
    # specific functionality.
    #

    def copyProperties(self, props):
        newProps = {}
        for key, prop in props.items():
            newProp = prop.clone(self)
            newProp.setValue(prop.getValue())
            newProps[key] = newProp
        self.updateProperties(newProps)

    def copyBase(self, other, generator, clone = False):
        if clone and other.id != self.id:
            parent = other.parent
            setPar = other.parent is not None and other.parent.hasChildWithID(other.id) and other.parent.children[other.id] == other
            if setPar:
                other.reparentTo(NodePath())
            other.id = self.id
            if setPar:
                other.reparentTo(parent)

        other.parent = self.parent

        for child in self.children.values():
            if clone:
                newChild = child.clone()
            else:
                newChild = child.copy(generator)
            newChild.reparentTo(other)

        other.setClassname(self.classname)
        other.copyProperties(self.properties)
        other.selected = self.selected

    def pasteBase(self, o, generator, performUnclone = False):
        if performUnclone and o.id != self.id:
            parent = self.parent
            setPar = self.parent is not None and self.parent.hasChildWithID(self.id) and self.parent.children[self.id] == self
            if setPar:
                self.reparentTo(NodePath())
            self.id = o.id
            if setPar:
                self.reparentTo(parent)

        for child in o.children.values():
            if performUnclone:
                newChild = child.clone()
            else:
                newChild = child.copy(generator)
            newChild.reparentTo(self)

        self.setClassname(o.classname)
        self.copyProperties(o.properties)
        self.selected = o.selected

    def getName(self):
        return "Object"

    def getDescription(self):
        return "Object in a map."

    def addProperty(self, prop):
        self.properties[prop.name] = prop

    # Returns list of property names with the specified value types.
    def getPropsWithValueType(self, types):
        if isinstance(types, str):
            types = [types]
        props = []
        for propName, prop in self.properties.items():
            if prop.valueType in types:
                props.append(propName)
        return props

    def getPropNativeType(self, key):
        prop = self.properties.get(key, None)
        if not prop:
            return str

        return prop.getNativeType()

    def getPropValueType(self, key):
        prop = self.properties.get(key, None)
        if not prop:
            return "string"

        return prop.valueType

    def getPropDefaultValue(self, prop):
        if isinstance(prop, str):
            prop = self.properties.get(prop, None)

        if not prop:
            return ""

        return prop.defaultValue

    def getPropertyValue(self, key, asString = False, default = ""):
        prop = self.properties.get(key, None)
        if not prop:
            return default

        if asString:
            return prop.getSerializedValue()
        else:
            return prop.getValue()

    def getProperty(self, name):
        return self.properties.get(name, None)

    def updateProperties(self, data):
        for key, value in data.items():
            if not isinstance(value, ObjectProperty):
                # If only a value was specified and not a property object itself,
                # this is an update to an existing property.

                prop = self.properties.get(key, None)
                if not prop:
                    continue

                oldValue = prop.getValue()

                val = prop.getUnserializedValue(value)

                # If the property has a min/max range, ensure the value we want to
                # set is within that range.
                if (not prop.testMinValue(val)) or (not prop.testMaxValue(val)):
                    # Not within range. Use the default value
                    val = prop.defaultValue

                prop.setValue(val)
            else:
                # A property object was given, simply add it to the dict of properties.
                prop = value
                oldValue = None
                val = prop.getValue()
                self.properties[prop.name] = prop

            self.propertyChanged(prop, oldValue, val)

    def propertyChanged(self, prop, oldValue, newValue):
        if oldValue != newValue:
            self.send('objectPropertyChanged', [self, prop, newValue])

    def setAbsOrigin(self, origin):
        self.np.setPos(base.render, origin)
        self.transformChanged()

    def setOrigin(self, origin):
        self.np.setPos(origin)
        self.transformChanged()

    def getAbsOrigin(self):
        return self.np.getPos(base.render)

    def getOrigin(self):
        return self.np.getPos()

    def setAngles(self, angles):
        self.np.setHpr(angles)
        self.transformChanged()

    def setAbsAngles(self, angles):
        self.np.setHpr(base.render, angles)
        self.transformChanged()

    def getAbsAngles(self):
        return self.np.getHpr(base.render)

    def getAngles(self):
        return self.np.getHpr()

    def setScale(self, scale):
        self.np.setScale(scale)
        self.transformChanged()

    def setAbsScale(self, scale):
        self.np.setScale(base.render, scale)
        self.transformChanged()

    def getAbsScale(self):
        return self.np.getScale(base.render)

    def getScale(self):
        return self.np.getScale()

    def setShear(self, shear):
        self.np.setShear(shear)
        self.transformChanged()

    def setAbsShear(self, shear):
        self.np.setShear(base.render, shear)
        self.transformChanged()

    def getAbsShear(self):
        return self.np.getShear(base.render)

    def getShear(self):
        return self.np.getShear()

    def transformChanged(self):
        self.recalcBoundingBox()
        self.send('objectTransformChanged', [self])

    def showBoundingBox(self):
        self.boundsBox.np.reparentTo(self.np)

    def hideBoundingBox(self):
        self.boundsBox.np.reparentTo(NodePath())

    def select(self):
        self.selected = True
        self.showBoundingBox()
        #self.np.setColorScale(1, 0, 0, 1)

    def deselect(self):
        self.selected = False
        self.hideBoundingBox()
        #self.np.setColorScale(1, 1, 1, 1)

    def setClassname(self, classname):
        self.classname = classname

    def fixBounds(self, mins, maxs):
        # Ensures that the bounds are not flat on any axis
        sameX = mins.x == maxs.x
        sameY = mins.y == maxs.y
        sameZ = mins.z == maxs.z

        invalid = False

        if sameX:
            # Flat horizontal
            if sameY and sameZ:
                invalid = True
            elif not sameY:
                mins.x = mins.y
                maxs.x = maxs.y
            elif not sameZ:
                mins.x = mins.z
                maxs.x = maxs.z

        if sameY:
            # Flat forward/back
            if sameX and sameZ:
                invalid = True
            elif not sameX:
                mins.y = mins.x
                maxs.y = maxs.x
            elif not sameZ:
                mins.y = mins.z
                maxs.y = maxs.z

        if sameZ:
            if sameX and sameY:
                invalid = True
            elif not sameX:
                mins.z = mins.x
                maxs.z = maxs.x
            elif not sameY:
                mins.z = mins.y
                maxs.z = maxs.y

        return [invalid, mins, maxs]

    def recalcBoundingBox(self):
        if not self.np:
            return

        # Don't have the picker box or selection visualization contribute to the
        # calculation of the bounding box.
        if self.collNp:
            self.collNp.stash()
        self.hideBoundingBox()

        # Calculate a bounding box relative to ourself
        mins, maxs = self.getBounds(self.np)

        invalid, mins, maxs = self.fixBounds(mins, maxs)
        if invalid:
            mins = Point3(-8)
            maxs = Point3(8)

        self.boundingBox = BoundingBox(mins, maxs)
        self.boundsBox.setMinMax(mins, maxs)
        if self.selected:
            self.showBoundingBox()

        if self.collNp:
            self.collNp.unstash()
            self.collNp.node().clearSolids()
            self.collNp.node().addSolid(CollisionBox(mins, maxs))
            self.collNp.hide(~VIEWPORT_3D_MASK)

        self.send('mapObjectBoundsChanged', [self])

    def removePickBox(self):
        if self.collNp:
            self.collNp.removeNode()
            self.collNp = None

    def delete(self):
        if not self.temporary:
            # Take the children with us
            for child in list(self.children.values()):
                child.delete()
            self.children = None
            # if we are selected, deselect
            base.selectionMgr.deselect(self)

        if self.boundsBox:
            self.boundsBox.cleanup()
            self.boundsBox = None

        self.removePickBox()

        if not self.temporary:
            self.reparentTo(NodePath())
        self.np.removeNode()
        self.np = None
        self.properties = None
        self.metaData = None

        self.temporary = None

    def __clearParent(self):
        if self.parent:
            self.parent.__removeChild(self)
            self.np.reparentTo(NodePath())
            self.parent = None

    def __setParent(self, other):
        if isinstance(other, NodePath):
            # We are reparenting directly to a NodePath, outside of the MapObject tree.
            self.parent = None
            self.np.reparentTo(other)
        else:
            self.parent = other
            if self.parent:
                self.parent.__addChild(self)
                self.np.reparentTo(self.parent.np)
            else:
                # If None was passed, assume base.render
                self.np.reparentTo(base.render)

    def reparentTo(self, other):
        # If a NodePath is passed to this method, the object will be placed under the specified node
        # in the Panda3D scene graph, but will be taken out of the MapObject tree. If None is passed,
        # the object will be parented to base.render and taken out of the MapObject tree.
        #
        # Use reparentTo(NodePath()) to place the object outside of both the scene graph and the
        # MapObject tree.
        self.__clearParent()
        self.__setParent(other)

    def __addChild(self, child):
        self.children[child.id] = child
        #self.recalcBoundingBox()

    def __removeChild(self, child):
        if child.id in self.children:
            del self.children[child.id]
            #self.recalcBoundingBox()

    def doWriteKeyValues(self, parent):
        kv = CKeyValues(self.ObjectName, parent)
        self.writeKeyValues(kv)
        for child in self.children.values():
            child.doWriteKeyValues(kv)

    def writeKeyValues(self, keyvalues):
        keyvalues.setKeyValue("id", str(self.id))
        # Write out our object properties
        for name, prop in self.properties.items():
            prop.writeKeyValues(keyvalues)

    def readKeyValues(self, keyvalues):
        for i in range(keyvalues.getNumKeys()):
            key = keyvalues.getKey(i)
            value = keyvalues.getValue(i)
            if MetaData.isPropertyExcluded(key):
                continue
            # Find the property with this name.
            prop = self.properties.get(key, None)
            if not prop:
                # Prop wasn't explicit or part of FGD metadata (if it's an Entity)
                continue

            nativeValue = prop.getUnserializedValue(value)

            # Set the value!
            self.updateProperties({prop.name: nativeValue})
class FaceDetection(DirectObject):
    def __init__(self):
        #Muestra un texto en pantalla, utilizando la interfaz 2D de Panda3D.
        #Posteriormente se actualizara con otros datos, por eso se mantiene la referencia
        self.title = OnscreenText(text="prueba 1", style=1, fg=(0,0,0,1), pos=(0.8,-0.95), scale = .07)

        #Lee los datos de configuracion de los clasificadores HAAR. En este caso, para deteccion de rostros en posicion frontal
        self.cascade = cv.Load("haarcascades\\haarcascade_frontalface_alt.xml")
        
        #Utiliza por defecto la camara para obtener las imagenes, y no guarda un archivo
        self.cameraHelper = CameraHelper(True, False, "d:\\face-detection.avi")
	
        #Crea una textura para utilizar como fondo, donde se mostraran las imagenes de video
	#CardMaker en realidad es un poligono rectangular, que es util para asociarlo al renderer2D, ya que
	#podremos hacer que ocupe toda la pantalla y mostrar nuestras imagenes como fondo.
        self.cardMaker = CardMaker("My Fullscreen Card");
        self.cardMaker.setFrameFullscreenQuad()

	#Agrega el rectangulo al renderer render2dp. Asi como existe render2d, existe uno adicional que es utilizado
	#en general para casos donde necesita mostrarse un fondo, sea estatico o de video. El render2d estandar,
	#por el contrario, se usa para mostrar informacion al usuario que siempre debe ser visible
        self.card = NodePath(self.cardMaker.generate())
        self.card.reparentTo(render2dp)
	
        #Le da baja prioridad para que los objetos dibujados posteriormente siempre se vean sobre ella
        base.cam2dp.node().getDisplayRegion(0).setSort(-20)
        
        #Crea un rectangulo que se utilizara para mostrar la imagen superpuesta sobre la cara
        self.faceMaker = CardMaker("Face Texture");
        self.faceImage = NodePath(self.faceMaker.generate())
        self.faceImage.setTexture(loader.loadTexture("margarita-glass3.png"))
        self.faceImage.reparentTo(aspect2d)
        #self.faceImage.reparentTo(render2d)
        self.faceImage.setTransparency(TransparencyAttrib.MAlpha)
        
        self.setup_handlers()
        self.setup_lights()

        self.count = 0
        
        #Establece un fondo negro
        #base.setBackgroundColor(0, 0, 0)

        #Carga el objeto tetera (incluido con Panda3D), y lo ubica en el mundo
        #self.teapot = loader.loadModel('models/teapot')
        #self.teapot.reparentTo(base.render)
        #self.teapot.setPos(-10, -10, -10)

        #Coloca la camara en el origen de coordenadas, y hace que apunte hacia la tetera
        #camera.setPos(0, 0, 0)
        #camera.lookAt(-10, -10, -10)

        taskMgr.add(self.onFrameChanged, "frameChange")


    def exitApplication(self):
        self.cameraHelper = None
        sys.exit()
    
    def setup_handlers(self):
	#Deshabilita el manejo por defecto del mouse
        base.disableMouse()
	
	#Agrega un gestor que al recibir el mensaje de que se presiono ESC, sale del programa
        self.accept("escape", self.exitApplication)

    def setup_lights(self):
        #Crea una luz ambiente, para que los objetos tengan una iluminacion base por defecto
        self.ambientLight = AmbientLight("ambientLight")
        self.ambientLight.setColor(Vec4(.8, .8, .75, 1))
        render.setLight(render.attachNewNode(self.ambientLight))

        #Crea una luz direccional, que va a permitir destacar la profundidad de los objetos
        self.directionalLight = DirectionalLight("directionalLight")
        self.directionalLight.setDirection(Vec3(0, 0, -2.5))
        self.directionalLight.setColor(Vec4(0.9, 0.8, 0.9, 1))
        render.setLight(render.attachNewNode(self.directionalLight))
	
    def onFrameChanged(self, task):
	#Captura un cuadro del origen indicado
        image = self.cameraHelper.captureFrame()
        
	#Si no quedan imagenes para mostrar, sale del programa (por ejemplo, se termina el video grabado)
        if image == None:
            return Task.done

        nuevaImagen = self.detectFaces(image)
        
        #Crea una textura utilizando la imagen capturada por OpenCV
        texture = self.createTexture(image)

        #En caso de que haya ocurrido un error, utilizar la imagen previa
        if texture != None:
            self.oldTexture = texture
		
        #Muestra la captura como fondo de la imagen
        self.card.setTexture(self.oldTexture)
        
        return Task.cont

    def createTexture(self, image):
        (width, height) = cv.GetSize(image)
	
        #Panda3D interpreta las imagenes al reves que OpenCV (verticalmente invertidas), por lo que es necesario tener esto en cuenta
        cv.Flip(image, image, 0)
	
        #OpenCV permite convertir la representacion interna de las imagenes a un formato descomprimido que puede ser guardado en un archivo.
        #Esto puede utilizarse desde Panda3D para tomar la imagen y utilizarla como una textura.
        imageString = image.tostring()
        #PTAUchar es una clase que permite tomar un bloque de datos y utilizarlo desde componentes de Panda3D (en particular es util para texturas)
        imagePointer = PTAUchar.emptyArray(0)
        imagePointer.setData(imageString)
        
        try:
            self.count += 1
            #Crea un nuevo objeto textura
            texture = Texture('image' + str(self.count))
            #Establece propiedades de la textura, como tamanio, tipo de datos y modelo de color. Las imagenes de OpenCV las estamos manejando
            #como RGB, donde cada canal es de 8bits (un numero entero)
            texture.setup2dTexture(width, height, Texture.TUnsignedByte, Texture.FRgb)
            #Indicamos que utilice el bloque de datos obtenido anteriormente como origen de datos para la textura
            texture.setRamImage(CPTAUchar(imagePointer), MovieTexture.CMOff)
        except:
            texture = None
        
        return texture

    def detectFaces(self, image):
        #Tamanio minimo de los rostros a detectar
        minFaceSize = (20, 20)
        #Escala a aplicar a la imagen para reducir tiempo de procesamiento
        imageScale = 2
        haarScale = 1.2
        minNeighbors = 2
        haarFlags = 0

        (screenWidth, screenHeight) = cv.GetSize(image)
        aspectRatio = float(screenWidth) / screenHeight
        
        #Crea imagenes temporales para la conversion a escala de grises y el escalado de la imagen
        grayImage = cv.CreateImage((image.width,image.height), 8, 1)
        smallImage = cv.CreateImage((cv.Round(image.width / imageScale), cv.Round (image.height / imageScale)), 8, 1)

        #Convierte la imagen capturada a escala de grises
        cv.CvtColor(image, grayImage, cv.CV_BGR2GRAY)

        #Crea una version de tamanio reducido para reducir el tiempo de procesamiento
        cv.Resize(grayImage, smallImage, cv.CV_INTER_LINEAR)

        #Ecualiza la imagen, con el fin de mejorar el contraste
        cv.EqualizeHist(smallImage, smallImage)

        #Detecta los rostros existentes en la imagen, y calcula el tiempo utilizado para hacerlo
        t = cv.GetTickCount()
        faces = cv.HaarDetectObjects(smallImage, self.cascade, cv.CreateMemStorage(0), haarScale, minNeighbors, haarFlags, minFaceSize)
        t = cv.GetTickCount() - t

        #Crea una imagen nueva donde se va a dibujar el cuadrado sin mostrar el fondo
        nuevaImagen = cv.CreateImage((image.width,image.height), 8, 3)
        pt1 = (int(0 * imageScale), int(0 * imageScale))
        pt2 = (int((image.width) * imageScale), int((image.height) * imageScale))
        cv.Rectangle(nuevaImagen, pt1, pt2, cv.RGB(118, 152, 141), -1, 8, 0)

        print "detection time = %gms" % (t/(cv.GetTickFrequency()*1000.))
        
        if faces:
            for ((x, y, w, h), n) in faces:
                #Calcula los puntos de inicio y fin del rectangulo detectado
                pt1 = (int(x * imageScale), int(y * imageScale))
                pt2 = (int((x + w) * imageScale), int((y + h) * imageScale))

                #Dibuja un rectangulo en la imagen origen, para destacar el rostro detectado
                #cv.Rectangle(nuevaImagen, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0)
                cv.Rectangle(image, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0)

                #Calcula la posicion del objeto a mostrar en Panda3D
                #Se utiliza aspect2d, ya que permite tener independencia de dimensiones entre ancho y alto (con render2d la imagen se veria comprimida)
                #En aspect2d, la coordenada x corre desde -aspectRatio hasta aspectRatio. La coordenada y corre desde -1 a 1.
                #
                #En este caso en que utilizamos 2D, es mas notoria la necesidad de definir correctamente el "centro" de nuestros modelos.
                #Esto se realiza en los programas de modelado, pero afectara el posicionamiento dentro de nuestra aplicacion
                #position = (float(x) * imageScale, float(y + h) * imageScale)
                #position = (position[0] - screenWidth / 2, position[1] - screenHeight / 2)
                #position = (2 * aspectRatio * position[0] / screenWidth, 2 * (-position[1] / screenHeight), 0)
                
                position = (float(x) * imageScale, float(y) * imageScale)
                position = (position[0] - screenWidth / 2, position[1] - screenHeight / 2)
                position = (2 * aspectRatio * position[0] / screenWidth, 2 * (-position[1] / screenHeight), 0)

                #Ubica el centro del objeto en la posicion correcta
                self.faceImage.setPos(position[0], 0, position[1])
                
                texture = self.faceImage.getTexture()
                #self.faceImage.setScale(2 * imageScale * float(w) / texture.getXSize(), 1, 2 * imageScale * float(h) / texture.getYSize())
                self.faceImage.setScale(imageScale * float(w) / texture.getXSize(), 1, imageScale * float(h) / texture.getYSize())
                print self.faceImage.getScale()
        return nuevaImagen
Exemple #9
0
class DirectionHandle(PandaNode, MouseEventListener):
    geomNode = makeGeomNode()
    clickableGeomNode = makeClickableGeom()

    def __init__(self, parent, color, hpr, dim):
        PandaNode.__init__(self, dim+'handle')
        self.path = NodePath(self)
        self.parent = parent
        self.dim = dim

        arrow = GeomNode('gnode')
        arrow.addGeomsFrom(self.geomNode)
        arrownp = self.path.attachNewNode(arrow)
        arrownp.hide(BitMask32(1))
        
        clickNode = ClickableNode('clicknode')
        clickNode.setDepthLevel(0.5)
        clickNode.addMouseListener(self)
        clicknp = self.path.attachNewNode(clickNode)
        
        clickgeom = clicknp.attachNewNode(GeomNode('clicknode'))
        clickgeom.hide(BitMask32(7))
        clickgeom.node().addGeomsFrom(self.clickableGeomNode)

        linesegs = LineSegs()
        linesegs.setColor(color)
        linesegs.setThickness(2)
        linesegs.moveTo(Vec3(0, 0, -30))
        linesegs.drawTo(Vec3(0, 0, -0.5))
        linesegs.moveTo(Vec3(0, 0, 0.5))
        linesegs.drawTo(Vec3(0, 0, 30))
        lines = self.path.attachNewNode(linesegs.create())
        lines.show(BitMask32(1))
        lines.hide(BitMask32(2|4|8|16))
        lines.setBin('opaque', 30, 100)
        lines.setAntialias(AntialiasAttrib.MNone)

        self.path.setColor(color)
        self.path.setHpr(hpr)
        
        self.mDownPos = Vec2()

    def mouseEntered(self, event):
        self.path.setColorScale(2, 2, 2, 2)

    def mouseExited(self, event):
        self.path.setColorScale(1, 1, 1, 1)

    def mousePressed(self, event):
        self.parent.beginTransformation()
       
        lens = base.cam.node().getLens()

        scale = Mat4.scaleMat(self.path.getScale(base.camera))
        descale = Mat4()
        descale.invertAffineFrom(scale)
        mvMat = descale * self.path.getMat(base.camera)

        origin = Point3(mvMat.xformPoint(Point3()))
        dir = Vec3(mvMat.xformVec(Vec3(0, 0, 1)))
        xprod = dir.cross(origin)
        planeNorm = xprod.cross(dir)
        planeNorm.normalize()
        d = planeNorm.dot(origin)
        self.dir = dir
        self.origin = origin
        self.planeNorm = planeNorm
        self.d = d
        self.lens = lens

        self.fromCam = base.camera.getMat(self.parent.path) * scale
        
        transl = self.mouse2Vec(event.pos)
        self.origin = transl + self.origin

    def mouseMoved(self, event):
        transl = self.fromCam.xformVec(self.mouse2Vec(event.pos))
        self.parent.transform(Mat4.translateMat(transl))

    def mouse2Vec(self, mpos):
        ray = Vec3()
        ray.xz = self.lens.getFilmSize()
        ray.x = mpos.x * ray.x / 2
        ray.z = mpos.y * ray.z / 2
        ray.y = self.lens.getFocalLength()
        ray.normalize()
        k = self.d/(ray.dot(self.planeNorm))
        return self.dir * (ray*k - self.origin).dot(self.dir)
Exemple #10
0
class RotationHandle(PandaNode, MouseEventListener):
    geomNode = makeRotationGeomNode()
    
    def __init__(self, parent, color, hpr, dim):
        PandaNode.__init__(self, dim+'rotHandle')
        self.path = NodePath(self)
        self.parent = parent
        self.dim = dim

        circle = GeomNode('gnode')
        circle.addGeomsFrom(self.geomNode)

        self.clickable = ClickableNode('clickable')
        self.clickable.addChild(circle)
        self.clickable.addMouseListener(self)
        circlenp = self.path.attachNewNode(self.clickable)

        self.path.setColor(color)
        self.path.setHpr(hpr)
        self.mDownPos = Vec2()

    def mouseEntered(self, event):
        self.path.setColorScale(2, 2, 2, 2)

    def mouseExited(self, event):
        self.path.setColorScale(1, 1, 1, 1)

    def mousePressed(self, event):
        self.parent.beginTransformation()
       
        self.lens = base.cam.node().getLens()

        scale = Mat4.scaleMat(self.path.getScale(base.camera))
        descale = Mat4()
        descale.invertAffineFrom(scale)
        mvMat = descale * self.path.getMat(base.camera)

        self.origin = Point3(mvMat.xformPoint(Point3()))
        self.planeNorm = Vec3(mvMat.xformVec(Vec3(0, 0, 1)))
        self.planeNorm.normalize()
        self.d = self.planeNorm.dot(self.origin)

        self.fromCam = base.camera.getMat(self.parent.path) * scale
        
        self.dir = Vec3(self.mouse2Vec(event.pos))
        self.dir.normalize()

    def mouseMoved(self, event):
        transl = Vec3(self.mouse2Vec(event.pos))
        transl.normalize()
        angle = self.dir.signedAngleDeg(transl, self.planeNorm)
        axis = Vec3()
        setattr(axis, self.dim, 1)
        self.parent.transform(Mat4.rotateMatNormaxis(angle, axis))

    def mouse2Vec(self, mpos):
        ray = Vec3()
        ray.xz = self.lens.getFilmSize()
        ray.x = mpos.x * ray.x / 2
        ray.z = mpos.y * ray.z / 2
        ray.y = self.lens.getFocalLength()
        ray.normalize()
        k = self.d/ray.dot(self.planeNorm)
        return ray*k - self.origin
Exemple #11
0
class DirectionHandle(PandaNode, MouseEventListener):
    geomNode = makeGeomNode()
    clickableGeomNode = makeClickableGeom()

    def __init__(self, parent, color, hpr, dim):
        PandaNode.__init__(self, dim + 'handle')
        self.path = NodePath(self)
        self.parent = parent
        self.dim = dim

        arrow = GeomNode('gnode')
        arrow.addGeomsFrom(self.geomNode)
        arrownp = self.path.attachNewNode(arrow)
        arrownp.hide(BitMask32(1))

        clickNode = ClickableNode('clicknode')
        clickNode.setDepthLevel(0.5)
        clickNode.addMouseListener(self)
        clicknp = self.path.attachNewNode(clickNode)

        clickgeom = clicknp.attachNewNode(GeomNode('clicknode'))
        clickgeom.hide(BitMask32(7))
        clickgeom.node().addGeomsFrom(self.clickableGeomNode)

        linesegs = LineSegs()
        linesegs.setColor(color)
        linesegs.setThickness(2)
        linesegs.moveTo(Vec3(0, 0, -30))
        linesegs.drawTo(Vec3(0, 0, -0.5))
        linesegs.moveTo(Vec3(0, 0, 0.5))
        linesegs.drawTo(Vec3(0, 0, 30))
        lines = self.path.attachNewNode(linesegs.create())
        lines.show(BitMask32(1))
        lines.hide(BitMask32(2 | 4 | 8 | 16))
        lines.setBin('opaque', 30, 100)
        lines.setAntialias(AntialiasAttrib.MNone)

        self.path.setColor(color)
        self.path.setHpr(hpr)

        self.mDownPos = Vec2()

    def mouseEntered(self, event):
        self.path.setColorScale(2, 2, 2, 2)

    def mouseExited(self, event):
        self.path.setColorScale(1, 1, 1, 1)

    def mousePressed(self, event):
        self.parent.beginTransformation()

        lens = base.cam.node().getLens()

        scale = Mat4.scaleMat(self.path.getScale(base.camera))
        descale = Mat4()
        descale.invertAffineFrom(scale)
        mvMat = descale * self.path.getMat(base.camera)

        origin = Point3(mvMat.xformPoint(Point3()))
        dir = Vec3(mvMat.xformVec(Vec3(0, 0, 1)))
        xprod = dir.cross(origin)
        planeNorm = xprod.cross(dir)
        planeNorm.normalize()
        d = planeNorm.dot(origin)
        self.dir = dir
        self.origin = origin
        self.planeNorm = planeNorm
        self.d = d
        self.lens = lens

        self.fromCam = base.camera.getMat(self.parent.path) * scale

        transl = self.mouse2Vec(event.pos)
        self.origin = transl + self.origin

    def mouseMoved(self, event):
        transl = self.fromCam.xformVec(self.mouse2Vec(event.pos))
        self.parent.transform(Mat4.translateMat(transl))

    def mouse2Vec(self, mpos):
        ray = Vec3()
        ray.xz = self.lens.getFilmSize()
        ray.x = mpos.x * ray.x / 2
        ray.z = mpos.y * ray.z / 2
        ray.y = self.lens.getFocalLength()
        ray.normalize()
        k = self.d / (ray.dot(self.planeNorm))
        return self.dir * (ray * k - self.origin).dot(self.dir)
Exemple #12
0
class RotationHandle(PandaNode, MouseEventListener):
    geomNode = makeRotationGeomNode()

    def __init__(self, parent, color, hpr, dim):
        PandaNode.__init__(self, dim + 'rotHandle')
        self.path = NodePath(self)
        self.parent = parent
        self.dim = dim

        circle = GeomNode('gnode')
        circle.addGeomsFrom(self.geomNode)

        self.clickable = ClickableNode('clickable')
        self.clickable.addChild(circle)
        self.clickable.addMouseListener(self)
        circlenp = self.path.attachNewNode(self.clickable)

        self.path.setColor(color)
        self.path.setHpr(hpr)
        self.mDownPos = Vec2()

    def mouseEntered(self, event):
        self.path.setColorScale(2, 2, 2, 2)

    def mouseExited(self, event):
        self.path.setColorScale(1, 1, 1, 1)

    def mousePressed(self, event):
        self.parent.beginTransformation()

        self.lens = base.cam.node().getLens()

        scale = Mat4.scaleMat(self.path.getScale(base.camera))
        descale = Mat4()
        descale.invertAffineFrom(scale)
        mvMat = descale * self.path.getMat(base.camera)

        self.origin = Point3(mvMat.xformPoint(Point3()))
        self.planeNorm = Vec3(mvMat.xformVec(Vec3(0, 0, 1)))
        self.planeNorm.normalize()
        self.d = self.planeNorm.dot(self.origin)

        self.fromCam = base.camera.getMat(self.parent.path) * scale

        self.dir = Vec3(self.mouse2Vec(event.pos))
        self.dir.normalize()

    def mouseMoved(self, event):
        transl = Vec3(self.mouse2Vec(event.pos))
        transl.normalize()
        angle = self.dir.signedAngleDeg(transl, self.planeNorm)
        axis = Vec3()
        setattr(axis, self.dim, 1)
        self.parent.transform(Mat4.rotateMatNormaxis(angle, axis))

    def mouse2Vec(self, mpos):
        ray = Vec3()
        ray.xz = self.lens.getFilmSize()
        ray.x = mpos.x * ray.x / 2
        ray.z = mpos.y * ray.z / 2
        ray.y = self.lens.getFocalLength()
        ray.normalize()
        k = self.d / ray.dot(self.planeNorm)
        return ray * k - self.origin
Exemple #13
0
class TQGraphicsNodePath:
    """ Anything that fundamentally is a only a graphics object in this engine should have these properties.

    Kwargs:
        TQGraphicsNodePath_creation_parent_node : this is assigned in the constructor, and after makeObject and the return of
        the p3d Nodepath, each TQGraphicsNodePath object has to call set_p3d_node """

    def __init__(self, **kwargs):
        """ """
        self.TQGraphicsNodePath_creation_parent_node = None
        self.p3d_nodepath = NodePath("empty")
        self.p3d_nodepath_changed_post_init_p = False
        self.p3d_parent_nodepath = NodePath("empty")

        self.node_p3d = None

        self.p3d_nodepath.reparentTo(self.p3d_parent_nodepath)

        self.apply_kwargs(**kwargs)

    def apply_kwargs(self, **kwargs):
        """ """
        if 'color' in kwargs:
            TQGraphicsNodePath.setColor(self, kwargs.get('color'))
        else:
            TQGraphicsNodePath.setColor(self, Vec4(1., 1., 1., 1.))

    def attach_to_render(self):
        """ """
        # assert self.p3d_nodepath
        # self.p3d_nodepath.reparentTo(render)
        assert self.p3d_nodepath
        self.reparentTo(engine.tq_graphics_basics.tq_render)

    def attach_to_aspect2d(self):
        """ """
        # assert self.p3d_nodepath
        # self.p3d_nodepath.reparentTo(aspect2d)
        assert self.p3d_nodepath
        self.reparentTo(engine.tq_graphics_basics.tq_aspect2d)

    def _set_p3d_nodepath_plain_post_init(p3d_nodepath):
        """ """
        self.p3d_nodepath = p3d_nodepath
        self.p3d_nodepath_changed_post_init_p = True

    @staticmethod
    def from_p3d_nodepath(p3d_nodepath, **tq_graphics_nodepath_kwargs):
        """ factory """
        go = TQGraphicsNodePath(**tq_graphics_nodepath_kwargs)
        go.set_p3d_nodepath(p3d_nodepath)
        return go

    def set_parent_node_for_nodepath_creation(
            self, TQGraphicsNodePath_creation_parent_node):
        """ when calling attachNewNode_p3d, a new node pat is generated.
        E.g.: To attach a line to render (3d world) is different than attaching
        it to aspect2d (2d GUI plane), since the aspect2d children are not directly
        affected by camera movements
        Args:
        - TQGraphicsNodePath_creation_parent_node : the NodePath to attach the TQGraphicsNodePath to
                                          (e.g. render or aspect2d in p3d)
        """

        # self.set_parent_node_for_nodepath_creation(self.TQGraphicsNodePath_creation_parent_node)
        self.TQGraphicsNodePath_creation_parent_node = TQGraphicsNodePath_creation_parent_node

    def set_p3d_nodepath(self, p3d_nodepath, remove_old_nodepath=True):
        """ """
        self.p3d_nodepath = p3d_nodepath

    def get_p3d_nodepath(self):
        """ """
        return self.p3d_nodepath

    def set_render_above_all(self, p):
        """ set render order to be such that it renders normally (false), or above all (true)
        Args:
            p: True or False to enable or disable the 'above all' rendering mode """

        try:
            if p == True:
                self.p3d_nodepath.setBin("fixed", 0)
                self.p3d_nodepath.setDepthTest(False)
                self.p3d_nodepath.setDepthWrite(False)
            else:
                self.p3d_nodepath.setBin("default", 0)
                self.p3d_nodepath.setDepthTest(True)
                self.p3d_nodepath.setDepthWrite(True)
        except NameError:       # if p3d_nodepath is not yet defined
            print("NameError in set_render_above_all()")

    def remove(self):
        """ """
        self.p3d_nodepath.removeNode()

    def setPos(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setPos(*args, **kwargs)

    def getPos(self):
        """ """
        return self.p3d_nodepath.getPos()

    def setScale(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setScale(*args, **kwargs)

    def getScale(self):
        """ """
        return self.p3d_nodepath.getScale()

    def setMat(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setMat(*args, **kwargs)

    def setMat_normal(self, mat4x4_normal_np):
        """ normal convention (numpy array), i.e. convert to forrowvecs convention for p3d setMat call """
        return self.p3d_nodepath.setMat(math_utils.to_forrowvecs(mat4x4_normal_np))


    def getMat(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getMat(*args, **kwargs)

    def getMat_normal(self, *args, **kwargs):
        """ """
        return math_utils.from_forrowvecs(self.p3d_nodepath.getMat(*args, **kwargs))

    def setTexture(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTexture(*args, **kwargs)

    def setColor(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setColor(*args, **kwargs)

    def setTwoSided(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTwoSided(*args, **kwargs)

    def setRenderModeWireframe(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeWireframe(*args, **kwargs)

    def reparentTo(self, *args, **kwargs):
        """ """
        new_args = list(args)
        new_args[0] = new_args[0].p3d_nodepath
        return self.p3d_nodepath.reparentTo(*new_args, **kwargs)

    def reparentTo_p3d(self, *args, **kwargs):
        """ input a p3d nodepath directly """
        # new_args = list(args)
        # new_args[0] = new_args[0].p3d_nodepath
        return self.p3d_nodepath.reparentTo(*args, **kwargs)

    def get_node_p3d(self):
        """ """
        # return self.p3d_nodepath.node()
        return self.node_p3d

    def set_node_p3d(self, node_p3d):
        """ not available in p3d NodePath class """
        self.node_p3d = node_p3d

    def setRenderModeFilled(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeFilled(*args, **kwargs)

    def setLightOff(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setLightOff(*args, **kwargs)

    def show(self):
        """ """
        return self.p3d_nodepath.show()

    def hide(self):
        """ """
        return self.p3d_nodepath.hide()

    def setRenderModeThickness(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setRenderModeThickness(*args, **kwargs)

    def removeNode(self):
        """ """
        return self.p3d_nodepath.removeNode()

    def setHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setHpr(*args, **kwargs)

    def showBounds(self):
        """ """
        return self.p3d_nodepath.showBounds()

    def setCollideMask(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setCollideMask(*args, **kwargs)

    def getParent_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getParent(*args, **kwargs)

    def wrtReparentTo(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.wrtReparentTo(*args, **kwargs)

    def setBin(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setBin(*args, **kwargs)

    def setDepthTest(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setDepthTest(*args, **kwargs)

    def setDepthWrite(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setDepthWrite(*args, **kwargs)

    def get_children_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.get_children(*args, **kwargs)

    def attachNewNode_p3d(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.attachNewNode(*args, **kwargs)

    def lookAt(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.lookAt(*args, **kwargs)

    def setLight(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setLight(*args, **kwargs)

    def setAntialias(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setAntialias(*args, **kwargs)

    def getRelativeVector(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getRelativeVector(*args, **kwargs)

    def setTransparency(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setTransparency(*args, **kwargs)

    def getHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getHpr(*args, **kwargs)

    def setHpr(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.setHpr(*args, **kwargs)

    def getTightBounds(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.getTightBounds(*args, **kwargs)

    def showTightBounds(self, *args, **kwargs):
        """ """
        return self.p3d_nodepath.showTightBounds(*args, **kwargs)