My camera rotation origin shifts after collisions

So, I’m not sure whether I can spam questions here, but hey since i cant figure it out myself, I decided it’ll be told if I overstay my welcome. So i have this issue at the moment that camera shifts its rotation point after the object its reparented to the child of gets stuck in collision.

Here’s the code for camera, (bit dirty as i tried a lot of things and messed it up few times):

from panda3d import core
from panda3d.core import CollisionRay, CollisionNode, CollideMask, CollisionHandlerQueue, BitMask32


class FollowCam(object):
    NAMEID = 'camera_follow'

    def __init__(self, cam, follow, ctrav, QQ, follow_model):
        self.follow_model = follow_model
        self.cam = cam
        self.follow = follow
        self.fake_node = self.follow_model.attachNewNode(self.NAMEID)
        self.fake_node.setPos(self.follow_model,0,0,0)
        self.cNode = CollisionNode(f'collision_{self.NAMEID}')
        self.cNodeP = self.fake_node.attachNewNode(self.cNode)
        self.cNodeP.setPos(self.fake_node, (70, 0, 0))
        self.ray = CollisionRay()
        self.ray.setOrigin(self.cNodeP.getPos())
        self.ray.setDirection(0, 0, -1)
        self.cNode.add_solid(self.ray)
        self.cNode.setFromCollideMask(CollideMask.bit(1))
        self.cam.reparentTo(self.cNodeP)
        self.qq = QQ
        ctrav.addCollider(self.cNodeP, self.qq)






    def DBG(self):
        print(f'pt.1 {self.cam.getX()},{self.cam.getY()}')
        print(f'pt.2 {self.follow.getX()},{self.follow.getY()}')











    def update(self):

        entries = list(self.qq.entries)
        for entry in entries:
            self.cam.setZ(entry.getSurfacePoint(render).getZ()+12)

        self.fake_node.setPos(self.follow_model.getPos())
        self.cNodeP.setPos(self.fake_node, (70, 0, 0))
        self.fake_node.setHpr(self.follow_model.getHpr())
        self.cam.lookAt(self.follow_model)

And that’s the camera init cript:

from panda3d.core import Vec3, CollisionHandlerPusher, CollisionHandlerQueue
from panda3d.bullet import BulletWorld
from direct.showbase.ShowBase import ShowBase, CollisionTraverser
from panda3d.core import MouseWatcher
from panda3d.core import WindowProperties
from Char_spherecollision import Char
from dafollowcam import FollowCam
from cone import cone
from map import Map
from test_ground import test_ground
from wall import wall
import cv2
winder = cv2.namedWindow('oooooooooo')

def noting(nut):
    pass


cv2.createTrackbar('x','oooooooooo',30,100,noting)
cv2.createTrackbar('y','oooooooooo',0,100,noting)
cv2.createTrackbar('z','oooooooooo',10,100,noting)






class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        self.dx,self.dy = 0,0
        # Showbase augmentations
        self.disable_mouse()
        self.taskMgr.add(self.update, 'update')
        self.setBackgroundColor(0,0,220,1)


        #Usefull objects monitor etc
        self.cTrav = CollisionTraverser()
        self.pusher = CollisionHandlerPusher()
        self.queue = CollisionHandlerQueue()
        self.rat = MouseWatcher()
        self.winder = WindowProperties()
        self.winder.setCursorHidden(True)
        self.winder.setMouseMode(WindowProperties.M_relative)
        self.win.requestProperties(self.winder)
        self.pusher = CollisionHandlerPusher()


        # events listeners
        self.accept("a", self.UpdateKeyMap, ["left", True])
        self.accept("a-up", self.UpdateKeyMap, ["left", False])
        self.accept("d", self.UpdateKeyMap, ["right", True])
        self.accept("d-up", self.UpdateKeyMap, ["right", False])
        self.accept("w", self.UpdateKeyMap, ["up", True])
        self.accept("w-up", self.UpdateKeyMap, ["up", False])
        self.accept("s", self.UpdateKeyMap, ["down", True])
        self.accept("s-up", self.UpdateKeyMap, ["down", False])
        self.accept("q", self.UpdateKeyMap, ["shoot", True])
        self.accept("q-up", self.UpdateKeyMap, ["shoot", False])
        self.accept("e", self.UpdateKeyMap, ["not shoot", True])
        self.accept("e-up", self.UpdateKeyMap, ["not shoot", False])
        self.accept("z", self.UpdateKeyMap, ["top", True])
        self.accept("z-up", self.UpdateKeyMap, ["top", False])
        self.accept("x", self.UpdateKeyMap, ["bottom", True])
        self.accept("x-up", self.UpdateKeyMap, ["bottom", False])
        self.MsX,self.MsY = None,None

        #keymap
        self.keyMap = {
            "up": False,
            "down": False,
            "left": False,
            "right": False,
            "shoot": False,
            'not shoot': False,
            'top': False,
            'bottom': False
        }



        # object instanciation
        #self.map = Map((0,0,0),self.cTrav,self.queue)
        self.grnd = test_ground(self.cTrav,self.queue)
        self.cones = []

        a,b,c = 0,0,0
        for i in range(5):
            a = a + 4
            placeholder = cone((a,b,c),self.cTrav,self.queue)
            self.cones.append(placeholder)



        self.character = Char(self.cTrav,self.pusher,self.queue)

        self.FCAM = FollowCam(self.cam,self.character.floater,self.cTrav,self.queue, self.character.model)


        #print(render.ls())


    def UpdateKeyMap(self, key, state):
        self.keyMap[key] = state




    def update(self,task):


        dt = globalClock.getDt()


        if self.mouseWatcherNode.hasMouse():
            self.MsX = ((base.mouseWatcherNode.get_mouse_x() + 1) / 2) * base.win.get_x_size()
            self.MsY = self.mouseWatcherNode.getMouseY()
            self.size = self.get_size()
            self.dx,self.dy = int(self.size[0]/2) - self.MsX, int(self.size[1]/2) - self.MsY



        x,y,z = cv2.getTrackbarPos('x','oooooooooo'),cv2.getTrackbarPos('y','oooooooooo'),cv2.getTrackbarPos('z','oooooooooo')
        if x != 0 and y != 0 and z != 0:
           pass


        self.character.Update_state(self.keyMap, dt, (self.dx,self.dy))
        self.FCAM.update()

        self.queue.clear_entries()


        if self.keyMap['shoot']:
            self.FCAM.DBG()
        return task.cont



app = MyApp()
app.run()

Yes, ik I need to clear collision queue after character, I’m still working on how to get second instance of it as .copy() causes error for some reason.(but that’s not the issue currently)

The original rotation of char comes from here:

from panda3d.bullet import BulletTriangleMeshShape, BulletTriangleMesh
from panda3d.bullet import BulletRigidBodyNode
from panda3d.core import CollisionHandlerPusher, CollideMask, CollisionNode, CollisionSphere, CollisionRay
from reference import REFERENCE


class Char():
    speed = 20
    NAMEID = 'apple_char'
    ORIGIN = (0,0,0)
    def __init__(self,ctrav,pusher,QQ):
        x,y,z = self.ORIGIN
        self.model = loader.loadModel(REFERENCE.loadTexturePath('toad.bam'))
        self.node = render.attachNewNode(self.NAMEID)
        self.model.setPos(x,y,z)
        self.node.setPos(x,y,z)
        self.model.reparentTo(self.node)
        self.futurepos = self.node.getPos()
        self.qq = QQ
        self.cNode = CollisionNode(f'{self.NAMEID}')
        self.cNodeP = self.model.attachNewNode(self.cNode)
        self.cNodeP.setPos(x,y,z)
        self.cNode.addSolid(CollisionSphere(center=(x, y, z), radius=2))
        self.cNode.setFromCollideMask(CollideMask.bit(0))
        self.cNode.setIntoCollideMask(CollideMask.bit(0))

        #self.cNodeP.show()

        self.gcNode = CollisionNode(f'Gcollision_{self.NAMEID}')
        self.GroundColl = CollisionRay()
        self.GroundColl.setOrigin(z, y, z)
        self.GroundColl.setDirection(0, 0, -1)

        self.gcNode.add_solid(self.GroundColl)
        self.gcNode.setFromCollideMask(CollideMask.bit(1))
        self.gcNodeP = self.node.attachNewNode(self.gcNode)
        self.gcNodeP.setPos(self.node,0,0,0)
        #self.gcNodeP.show()

        self.floater = self.node.attachNewNode(f'camera_{self.NAMEID}')
        self.floater.setPos(self.node,0,0,2)
        self.floater.show()


        ctrav.addCollider(self.gcNodeP, QQ)




        #self.cNodeP.show()

        pusher.horizontal = True
        pusher.addCollider(self.cNodeP, self.model)
        ctrav.addCollider(self.cNodeP, pusher)

        self.lastDelta = [0,0]
        self.node.setScale(2,2,2)

        self.cmddict = {
            "up": self.Up,
            "down": self.Down,
            "left": self.Left,
            "right": self.Right,
            "shoot": self.beep,
            'not shoot': self.beep,
            'top' : self.top,
            'bottom' : self.bottom
        }






    def Update_state(self,keypass,dt, tup):
        self.futurepos = self.node.getPos()
        dx, dy = tup







        for k, v in keypass.items():
            if v:
                self.cmddict[k](dt)

        self.node.setPos(self.futurepos)

        entries = list(self.qq.entries)
        if len(entries) == 0:
            print('none')
            self.node.setZ(self.node.getZ()-0.1)


        for entry in entries:
            if entry.getIntoNode().getName() == "collision_tGround" or entry.getIntoNode().getName().startswith('cone'):
                self.node.setZ(entry.getSurfacePoint(render).getZ()+5)




        self.node.setHpr(dx, 0, 0)
        self.floater.setPos(self.node.getPos())


    def Up(self, dt):
        self.futurepos.x -= self.speed * dt


    def Down(self, dt):
        self.futurepos.x += self.speed * dt



    def Left(self, dt):
        self.futurepos.y -= self.speed * dt
        self.lastDelta = [-1,0]


    def Right(self, dt):
        self.futurepos.y += self.speed * dt
        self.lastDelta = [1, 0]

    def top(self, dt):
        self.futurepos.z += self.speed * dt

    def bottom(self, dt):
        self.futurepos.z -= self.speed * dt


    def beep(self,duduh):
       #print(f'DBG: floater_pos = {self.floater.getPos()}, node_pos = {self.node.getPos()}')
        pass


    #DEBUG METHODS
    def changecNode(self,tup):
        x,y,z = tup
        x = x/10
        y = y/10
        z = z/10









Ik its really bad code but, i just try to get grip of all this node mess, so if ireparented something to something wrong please dont be mad :slight_smile:

From what I see, you’ve posted two questions in as many days. Speaking for myself, that’s nowhere near spamming questions. :slight_smile:

As to this question:

There’s a fair bit of code to go through there, and I’ll confess that I’m rather tired today, so my apologies if I’ve missed anything in your code! ^^;

That said, I think that I may have spotted a few possible problems:

When you create your character, you have a root-node called “node”, a model-node called “model” that is a child of “node”, and a camera-related node called “floater” that is also a child of “node”.

However, when you set up your character’s collision, you pass “model” into the pusher. This means that when a collision occurs, it’s “model”, not “node”, that is moved. As a result, “floater”–being a child of “node”, and not of “model”–won’t be affected, and thus “model” and “floater” will move apart.

Furthermore, in your follow-cam code, you create a node called “fake_node”, which is a child of “follow_model”; “follow_model” being a reference to the above-mentioned node named “model”.

Then, in the follow-cam’s “update” method, you set the position of “fake_node” to be the same as that of “follow_model”.

But since the call to “setPos” doesn’t specify a node that the position should be seen as relative to, that call to “setPos” sets the position of “fake_node” relative to its parent. And since “fake_node” is a child of “follow_model”, that means that the call to “setPos” sets the position of “fake_node” relative to follow_model.

That is, if “follow_model” is at position (-1, 0, 0) relative to its parent, that call to “setPos” sets the position of “fake_node” to be (-1, 0, 0) relative to “follow_model”, and thus (-2, 0, 0) relative to the parent of “follow_model”. (Presuming no scaling or rotation between “follow_model” and “fake_node”.)

Thanks a lot, I’m really happy that someone is that skilled that can parse my code better then I can myself. :slight_smile: however I do know about passing model, my bad for not stating it. I had an assumption that the floater smh is shifted during collision. Turned out it was pusher all along!

1 Like