PandaNode.resetPreviousTransform()

I’m trying to get Panda to report a collision vector that I can put to use. To that end, I’m trying to set up the conditions to call getPosDelta(), which sounds like exactly what I need.

According to the documentation at Rapidly-Moving Objects — Panda3D Manual, this means I need to do several things. I have cTrav.respectPreviousTransform(), no problem. I have object.setFluidPos(), all fine. But when I try to implement PandaNode.resetPreviousTransform, I get a maddening strange loop of errors.

The documentation makes all of this sound simple. Apparently it’s not? Can anyone help clarify how I need to call these functions?

From the ball-in-maze example, the vector is calculated from the collision data. What you are trying to do is not entirely clear.

    # This function handles the collision between the ball and a wall
    def wallCollideHandler(self, colEntry):
        # First we calculate some numbers we need to do a reflection
        norm = colEntry.getSurfaceNormal(render) * -1  # The normal of the wall
        curSpeed = self.ballV.length()                # The current speed
        inVec = self.ballV / curSpeed                 # The direction of travel
        velAngle = norm.dot(inVec)                    # Angle of incidance
        hitDir = colEntry.getSurfacePoint(render) - self.ballRoot.getPos()
        hitDir.normalize()
        # The angle between the ball and the normal
        hitAngle = norm.dot(hitDir)

        # Ignore the collision if the ball is either moving away from the wall
        # already (so that we don't accidentally send it back into the wall)
        # and ignore it if the collision isn't dead-on (to avoid getting caught on
        # corners)
        if velAngle > 0 and hitAngle > .995:
            # Standard reflection equation
            reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec

            # This makes the velocity half of what it was if the hit was dead-on
            # and nearly exactly what it was if this is a glancing blow
            self.ballV = reflectVec * (curSpeed * (((1 - velAngle) * .5) + .5))
            # Since we have a collision, the ball is already a little bit buried in
            # the wall. This calculates a vector needed to move it so that it is
            # exactly touching the wall
            disp = (colEntry.getSurfacePoint(render) -
                    colEntry.getInteriorPoint(render))
            newPos = self.ballRoot.getPos() + disp
            self.ballRoot.setPos(newPos)

I think you will the ray test technique for this purpose.

To start with, what errors are you getting? Knowing that may help us to diagnose the issue.

That said, let me note that the method is, I believe, not “resetPreviousTransform” but rather “resetPrevTransform”. (Presuming that the usage of the former isn’t just a mistake during posting, of course, rather than what you actually have in your code.)

I am curious, if I may: Are you using a collision system other than the built-in one, that you’re not using that system’s reported collision-normal vector? Or is it perhaps that you have some purpose in mind for which that vector isn’t useful?

HI, folks. I was coding on a different computer, so I could only be approximate about the commands and errors involved. As far as what I’m trying to do, I found another way of approaching it. I was trying to adapt some old Dark Basic Pro code of mine for use in Panda, and I needed the collision normal (the normalized vector of the player moving into and hitting the polygon, not the normal of the polygon with which the player collided) in order to do it here as I had there. There are other ways to try it.

I am actually curious about the exact topic of the thread, though. Does anyone understand how to implement PandaNode.resetPrevTransform()? Because it’s right there in the docs and treated there as though it were obvious, but I find it baffling. Me, I’m not so smart, but I figure here the documentation is less than clear.

I think it’s easier, you need the player’s position in the old frame and the point of collision of the character with the wall in the current frame. Next, you subtract the position of the collision point from the previous position of the character, in the result you get a direction vector.

Yes, exactly. Your suggestion from above is one of the things I’ve been trying for that. That part of the problem is solved, I think, and thank you. I would like to understand PandaNode.resetPrevTransform(), because I begin to feel like the documentation is unclear, no one understands how to do it, and everyone is evading that question. But I should apologize, because this medication makes everything feel like I’m thinking through a layer of peanut butter. I’m probably not interacting or explaining well, and I suspect I’m pretty cranky, too.

I’ve never used it, I’ve had problems searching in the API.

https://docs.panda3d.org/1.10/python/reference/panda3d.core.PandaNode#panda3d.core.PandaNode.resetPrevTransform

Perhaps you forgot to call the node() method?

object.node().resetPrevTransform()

Ahh, fair enough.

Well, if we don’t find the source of the problem before you return to that computer, I would be interested in seeing those errors nevertheless, I think.

Ah, I’m glad that you did find a way! :slight_smile:

Ah, thank you for clarifying! (I see that serega has already addressed another way of doing this, so I won’t belabour the matter.)

For my part, I’m not really evading the question–I think that I’ve just felt that I don’t have much to say on the matter, and so have simply answered where I felt that I had answers.

In short, it’s very seldom indeed that I’ve used that feature–it’s not one that I’ve often found myself wanting, I think. Thus I don’t have experience from which to speak.

That said, looking over the relevant manual page, I note that it indicates that one usually doesn’t have to call “resetPrevTransform”–it’s generally done automatically by ShowBase.

And indeed, I’ve hacked together a quick text-program that shows the use of “setFluidPos”, in which it does seem to work as expected. Note that lines related to “fluid movement” are marked with a comment that reads “NB!”, and that I do not use “resetPrevTransform”!


from panda3d.core import loadPrcFile, loadPrcFileData
loadPrcFileData("", "show-frame-rate-meter #t")
# I'm limiting the clock's frame-rate so that
#  it's easier to have a moving object go so fast
#  that it "skips over" an obstacle.
loadPrcFileData("", "clock-mode limited")
loadPrcFileData("", "clock-frame-rate 10")

from direct.showbase.ShowBase import ShowBase

from panda3d.core import CollisionTraverser, CollisionSphere, CollisionCapsule, CollisionHandlerEvent, CollisionNode, NodePath


class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # The traverser
        self.cTrav = CollisionTraverser()
        self.cTrav.setRespectPrevTransform(True) # NB!

        # The collision-handler
        # For simplicity's sake, this is just
        #  an "event" handler, with a simple
        #  print-statement in the associated
        #  method.
        self.handler = CollisionHandlerEvent()
        self.handler.addInPattern("%fn")

        self.accept("collisionObject", self.collisionDetected)

        # The fast-moving object.
        # See the "update" method for the code
        #  that moves it.
        collisionNode = CollisionNode("collisionObject")
        solid = CollisionSphere(0, 0, 0, 0.2)
        collisionNode.addSolid(solid)
        
        self.collisionNP = render.attachNewNode(collisionNode)
        #### Place the object somewhere useful
        ####  for demonstrative purposes,
        ####  and make it visible
        self.collisionNP.setPos(20, 50, 0)
        self.collisionNP.show()
        
        self.cTrav.addCollider(self.collisionNP, self.handler)

        # A simple obstacle for the above
        #  object to collide with
        wallNode = CollisionNode("wall")
        solid = CollisionCapsule(0, 0, -10, 0, 0, 10, 0.2)
        wallNode.addSolid(solid)

        np = render.attachNewNode(wallNode)
        #### Again, place the object and
        ####  make it visible
        np.setPos(-10, 50, 0)
        np.show()

        # Set up a task to update the
        #  game's logic each frame
        self.taskMgr.add(self.update, "update")

    def update(self, task):
        # Get the time since the last frame
        dt = self.clock.getDt()

        # Move the object fluidly
        self.collisionNP.setFluidPos(self.collisionNP, -20 * dt, 0, 0) # NB!
        
        return task.cont

    def collisionDetected(self, entry):
        # A simple collision-response: print a message!
        print ("Collision detected!")


app = Game()
app.run()

I am sorry that this question has not been answered yet in this thread, and that the answers so far have been missing the main gist of your question.

To understand this method, you have to understand how PandaNode’s transform update works. It stores two transformations: the current transformation and the previous transformation.

If you call NodePath.setPos, it changes the current transform on the underlying PandaNode, and then calls resetPrevTransform. The latter method will change the previous transform to match the current transform. In other words, it sets both the current and previous transforms, so there will be no delta vector generated.

NodePath.setFluidPos is in fact slightly simpler than NodePath.setPos. It only changes the current transform, without calling resetPrevTransform. So, at the end of this operation, the two transforms will be different.

Note that this means that any normal setPos operation implicitly resets the transform and wipes out any delta vector. You need to make sure that all things that move an object use the “fluid” version, not the regular version!

The CollisionTraverser can make use of this information, because it can calculate the vector distance that the object moved since the last frame and incorporate this into the collision checks. This is done in order to prevent fast-moving objects from slipping through thin collision solids, such as polygons. For this, you need setRespectPrevTransform(True), and you also need to use a collision test for which this is supported (such as sphere-into-polygon) - not all tests use this information.

Finally, when preparing for the next frame, we need to reset the previous transform to the same as the current transform. For this, we have PandaNode.resetAllPrevTransform(), which will basically call resetPrevTransform() for all nodes in existence. (This sounds very slow, but actually is very fast, because Panda uses some trickery to accomplish this.) However, if you use ShowBase, this is already done for you automatically. So, you never actually need to call this yourself.

So, in conclusion, you very rarely actually need to use PandaNode.resetPrevTransform() - it is usually done for you.

Does this help at all?

1 Like