AI Libraries for Panda3D

@hesselKRaymond

I think your mesh is too complicated for PandAI. Did you try getting a simple mesh to work first ?

My best advice would be to get a simple mesh working and a character to pathfind on it, before trying something like this. Pathfinding is complicated so its best to get the basics working before proceeding.

Please post back when it works and we can proceed to a more complicated mesh after this.

Could it be possible that PandaAI has problems with the third dimension? Meaning up (in Pandas case Y)? As soon as I have got time I will try it with a simpler mesh. Do you guys think that there is maybe a work around?

Bye Merlin

Yes. Definitely :slight_smile:

PandAI pathfinding is only for a 2d plane mesh. (x-z plane)

There are some hacky alternatives :

  1. Make two different navigation meshes for each height planes and use maybe nodes to traverse between the nav mesh heights. This might take more time to code.

  2. Look at the roaming ralph navigation tutorial and see if you can salvage something from that which might be of use in your game.

Sorry, the feature you request was out of our project’s scope and so would maybe only come in a possible future version.

First of all, thank you NNair and company for bringing PandAI to Panda3d! I originally posted a seperate thread, but I deleted that to move my post here, so sorry if this sounds familiar.

I’m trying to make sure I understand pandai correctly, because the steering behaviors aren’t quite what I expected. Consider the demo I developed here out of examples on the pandai website.

#for directx window and functions
import direct.directbase.DirectStart
#for most bus3d stuff
from pandac.PandaModules import *
#for directx object support
from direct.showbase.DirectObject import DirectObject
#for tasks
from direct.task import Task
#for Actors
from direct.actor.Actor import Actor
#for Pandai
from panda3d.ai import *
#for Onscreen GUI
from direct.gui.OnscreenText import OnscreenText

def addTextField(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
            pos=(-1.3, pos), align=TextNode.ALeft, scale=.05, mayChange=True)

class Target:
    def __init__(self, number, gameWorld, pos = (0,0,0)):
        self.gameWorld = gameWorld
        self.model = loader.loadModel("models/arrow")
        r = number % 2
        g = (number / 2) % 2
        b = (number / 4) % 2
        self.model.setColor(r,g,b)
        self.model.setPos(pos)
        self.model.setScale(1)
        self.model.reparentTo(render)
        self.AIchar = AICharacter("target"+str(number),self.model, 100, 0.05, 5)
        self.gameWorld.addAiChar(self.AIchar)
        self.AIbehaviors = self.AIchar.getAiBehaviors()

        self.collcp = CollisionSphere(0,0,3,7)
        self.collcn = CollisionNode("Seeker")
        self.collcn.addSolid(self.collcp)
        self.collcn.setFromCollideMask(BitMask32(0x8))
        self.collcn.setIntoCollideMask(BitMask32(0x8))
        self.collcnp = NodePath(self.collcn)
        self.collcnp.reparentTo(self.model)

    def wander(self, wander_r = 2, flag = 0, aoe = 12, priority = 1.0):
        self.AIbehaviors.wander(wander_r, flag, aoe, priority)


class World(DirectObject):

    def __init__(self):
        base.disableMouse()
        base.cam.setPosHpr(0,0,55,0,-90,0)

        self.loadModels()
        self.setAI()
        self.last = 0.0

        self.text = addTextField(0.9, "Evasion and seeking behavior combined.")

    def loadModels(self):
        # Seeker
        ralphStartPos = Vec3(-10, 0, 0)
        self.evader = Actor("models/ralph",
                                 {"run":"models/ralph-run"})
        self.evader.reparentTo(render)
        self.evader.setScale(0.5)
        self.evader.setPos(ralphStartPos)


    def setAI(self):
        #Creating AI World
        self.AIworld = AIWorld(render)

        self.AIchar = AICharacter("evader",self.evader, 100, 2, 10)
        self.AIworld.addAiChar(self.AIchar)
        self.AIbehaviors = self.AIchar.getAiBehaviors()
        self.evader.loop("run")

        self.target = []
        self.numTargets = 7
        for x in range(self.numTargets):
            self.target.append(Target(x, self.AIworld))
            self.target[x].wander()
            self.AIbehaviors.evade(self.target[x].model, 4.0, 4.0, 0.5)

        self.target.append(Target(self.numTargets, self.AIworld))
        self.seekTarget = self.target[self.numTargets]
        #self.target[numTargets].wander()
        self.AIbehaviors.seek(self.seekTarget.model, 0.25)

        #AI World update
        taskMgr.add(self.AIUpdate,"AIUpdate")

    #to update the AIWorld
    def AIUpdate(self,task):
#        elapsed = task.time - self.last
#        self.last = task.time
#        self.text.setText = (str(elapsed))
        self.AIworld.update()
        # this behavior has to be frequently reset for it to function
        self.AIbehaviors.seek(self.seekTarget.model, 0.25)
#        for x in range(self.numTargets):
#            d = self.distance(self.evader, self.target[x].model)
#            self.AIbehaviors.evade(self.target[x].model, 3.0, 5.0, 0)
        return Task.cont

    def distance(self, a, b):
        return((a.getPos(render) - b.getPos(render)).length())


w = World()
run()

I’ve observed several problems with the steering behaviors in my demo.

Firstly I expected that all recalculations would be done during AIworld.update(). However I’ve observed Ralph’s reactions to the evasion targets seem to be at an almost random distance suggesting it is not checking their proximity every frame. Am I just seeing things? If this is true I understand there may be a performance imperative behind it, but how would I go about improving the frequency of such checks?

Additionally, the behaviors of evasion and pursuit don’t appear in my experiments to actually behave as though the target is moving. A pursuit ai should be able to calculate an intercept point based on the speed of both the pursuer and target. At present it appears that pursuit and evasion ai simply move directly toward or away from the target with no option to improve upon this. I’ve actually solved this problem before in an AI aiming algorithm with finite speed projectiles. It was kind of frustrating and forced me to remember the law of cosines :blush: , but for someone who actually knows what their doing it should be just a few lines of c++. I’ll step up if no one else is offering.

Finally, and most importantly, I expected Ralph to act on multiple behavior priorities simultaneously, but this doesn’t appear to occur in my demo. For example if there were an evasion target above and slightly left of Ralph and one below and slightly left of Ralph, Ralph should be evading by moving to the right. In practice I see Ralph “bouncing” between the two as he alternates targets, or more commonly, just evading one and plowing straight through the other with no apparent regard.

I could attempt to emulate better behavior to some degree by each frame manipulating the priority of every target based on their distance.

I’m not trying to be demanding I’ve just worked with OpenSteer in the past and saw all of these behaviors working beautifully, but that is a much more specific and mature solution.

In any case see http://www.red3d.com/cwr/steer/ for some steering that seems to behave the way I would expect.

If some or all of my problems are in how I’ve used PandaAI, please let me know! If the problem is not with my implementation am I just expecting to much out of the still immature PandAI? Perhaps I could adapt PandaAI to borrow some code from OpenSteer. It does use the MIT license though…

Ok if I wanted to contribute something to PandAI, is there a repository hidden somewhere so I could work on the dev version?

I could only find the 1.0 source here:
etc.cmu.edu/projects/pandai/Download.html
and here:
sites.google.com/site/etcpandai/download

It’s in the contrib/src/ai directory on CVS. Any changes to PandAI have to be made there.

@AnimateDream

That would be a great idea. I am familiar with the Craig Reynolds works and in my personal projects have implemented his steering behaviors much more to the point. In fact, we did refer his paper for some of our work but modified them a little bit to work differently in some implementations.

PandAI was a project in a very short span of time created by us where we had to spend a considerable amount of time on videos, tutorials, demos and documentation too. This led to a not so perfect set of AI libraries and only a 2d path finding system which we could deliver. Our team would have loved to go fix all the bugs and add enhancements to both the steering behaviors and path finding but unfortunately due to present commitments we are unable to put that time required.

PandAI is open source and our vision was to make it as a learning tool or a starting AI tool for developers who are not so familiar with the concept or even as a base for more advanced developers to tinker with or add on to.

It would be great if you would like to give it a shot. I would advise that you are familiar with the C++ side of Panda too since our documentation doesn’t really cover that part yet.

Also, update me on any progress you make or maybe anything I could help out with.

Thanks rdb. I had the cvs on an older computer back when i was playing around with threading some things for my terrain engine. I’ll have to get cvs again and check it out.

@NNair
Great. I’m not too familiar with Panda3d’s c++ side, but I have a few ideas that should be very simple to implement.

Firstly, I was thinking about just adding vectors together for each the ai’s steering priorities. The lengths of these vectors would be multiplied by the priority of their behavior. Then the end result would be normalized to just get a direction. That would ignore speed, possibly complicating arrival or other future behaviors, but I think it would still be a great improvement.

Also I think priority should be adjusted by proximity somehow. I’ll probably just go the easy route at first and have additional input parameters for an inner radius and for an associated priority. I can avoid breaking the api by making them optional inputs.

Finally I can update evade and pursue to use vectors based on projected interceptions instead of current locations. Even using a rough approximation like taking distance to a pursuit target / the speed of the pursuer and projecting that far ahead should still be an improvement.

The formula for an exact answer is a bit weird because you form a triangle with the positions of the intercept, the pursuer, and the target. You only know the length of the pursuer->target side, the ratio between pursuer->intercept and target->intercept sides(same as the speeds of pursuer : target), and the angle at the target’s corner. So with one angle, one side, and a ratio you have to figure out the rest of that triangle. I really don’t do this kind of math on a regular basis. :confused:

The only problem I foresee is that my vector idea would work better if priorities weren’t clamped [0,1]. Anything I should know? Any thoughts on these ideas?

EDIT:
Ok after viewing the code I see it should already be compositing vectors from different behaviors. I’m unsure why it doesn’t appear to act on multiple priorities in practice yet.

I think the whole handling of force and physics needs to be completely replaced. The line “acceleration = _velocity;” in AICharacter::update() is all kinds of wrong. Force should not be retained between frames, velocity should. There should probably be a max velocity input instead of a max force. Moreover the whole thing appears to be frame rate dependent and has no regard for time. Once the physics are calculated more realistically it would also be simpler to pass the force to a physics body in an engine like ODE.

Other than the wonky physics everything looks pretty well coded.

UPDATE: 3/9/11

~ I’ve replaced the physics so things move more naturally.
~ Steering has been improved to apply force in the correct direction to alter velocity toward the destination rather than simply applying force toward the destination itself (don’t want ai’s to orbit their destination).
~ Before the steering ai and the physics were strangely tangled. Now clearly separated, AI’s should be more responsive to new and changing priorities.
~ Evasion behaviors have their priority adjusted by proximity. This makes for AI that can combine evasion and other behaviors much more effectively.

I’ve already worked out my intercept function. I’ll have ai that knows how far to aim ahead of its target as soon as I can figure out what should really go in this function.

LVecBase3f Pursue::get_target_velocity() {
  LVecBase3f target_velocity = LVecBase3f(0.0f,0.0f,0.0f);
  return target_velocity;
}

The pursuit target is stored as a NodePath, which has no properties or methods related to velocity.

@AnimateDream

Could you please send me your source with the recent changes you have made. I would like to take a look at it.

You could email it to me at : navinnair1985@gmail.com

Or any other way (svn etc…)

Thank you.

Just a quick question.

Is the “area of effect” parameter of “wander” in absolute render coordinates?

I try a very simple program where I set aoe to, say, 5 but the actor does not necessarily wander inside the disc with radius 5. It goes beyond the aoe radius.

Is this expected?

EDIT:

Also, does the pandaAI use some random number seeding mechanism that I can use from inside Python framework?

Thank you,

UB

Yes, it is absolute render coordinates. The Area of Effect, was sort of a tacked on feature that we made, so its less than perfect.

If the character gets enough force he will venture out of the radius, but then the force towards the center will start ticking in and he should get back. The velocity of the character to the size of the radius largely affects this offset.

– Sorcus

Thank you.

About the randomness. Is there a way I can set the PandaAI’s random number generator seed from Python? As it is, wander always follows the exact same trajectory for each run.

On another note, how can I contribute to the PandaAI source code?

UB

Ah, the random number generator wasn’t seeded randomly, hence the issue. It isn’t exposed to Python as such, so I am afraid you can’t change it. I can take a look at it and update it.

– Sorcus

Thank you Sorcus. I appreciate it.

The ability to seed the random number generator I believe is important, especially for Panda3D engine used as a 3D simulation environment.

Thank you again.

UB

i keep getting this error when i try to create a .csv file

Assertion failed: index >= 0 && index < (int)size() at line 467 of c:\panda3d-1.
7.0\panda\src\egg\eggPrimitive.I
Traceback (most recent call last):
  File "BlenderMeshGen.py", line 297, in <module>
    app = MyApp()
  File "BlenderMeshGen.py", line 47, in __init__
    self.iterateEggPoly(self.egg, "Full")
  File "BlenderMeshGen.py", line 94, in iterateEggPoly
    self.iterateEggPoly(child, type)
  File "BlenderMeshGen.py", line 94, in iterateEggPoly
    self.iterateEggPoly(child, type)
  File "BlenderMeshGen.py", line 71, in iterateEggPoly
    egg.getVertex(2), egg.getVertex(3))
AssertionError: index >= 0 && index < (int)size() at line 467 of c:\panda3d-1.7.
0\panda\src\egg\eggPrimitive.I

i am using the blender mesh gen, and i am NOT doing the egg-trans.exe step at all unsure of why i keep getting this, or how to fix it, it is a model i imported into blender from .obj file that i got off the internet, but i was able to get the test mesh to work, is there a way to undo the egg trans? or what ever causes the error?

thanks

I would like to change the PandaAI source code.

What would be the correct procedure to do it on a Windows platform? How should I build it such that it is exposed from inside Panda3D python framework?

Thank you,

UB

@daosm

I am guessing this is the problem. The mesh generation is very finicky and so you need to follow the exact steps in the manual or it will bug out.

@upperBound

You can get the source from the contrib/src/ai directory on CVS. To actually rebuild it in 1.70, I am unsure of the steps as I haven’t decided to tread that path yet. Someone with more experience in that might be able to help you better. When we built it it was a much more elaborate process for an older version of Panda3D but I think that has changed since. Maybe if you started another thread you would be able to get a quicker reply.

Thank you for the reply.

I guess I will build 1.7.2 from source and make my changes to the PandaAI from inside the complete source package. I believe it should work.

UB

ok so i just made a plane mesh to make the navmesh that is the same size as the obj i used before, that way it will still have the same paths, its on the z,x plane, same start point, still not using the eggtrans and now i get this error when trying to make the navmesh

Traceback (most recent call last):
  File "BlenderMeshGen.py", line 297, in <module>
    app = MyApp()
  File "BlenderMeshGen.py", line 50, in __init__
    self.createNewFullList()
  File "BlenderMeshGen.py", line 110, in createNewFullList
    if currentColNode.vertex[self.rightVertex] == self.oldList[i].vertex[self.lo
westVertex] and currentColNode.vertex[self.toprightVertex] == self.oldList[i].ve
rtex[self.topVertex]:
AttributeError: MyApp instance has no attribute 'rightVertex'

thanks.

@daocsm

Sorry for the delay, been swamped with stuff at work lately.

Which version of Blender are you using ?

Also, could you send me your files (Blender files), so I can test it at my end.