ODE Middleware

Ah, that’s what I thought, just didn’t want to get into explaining without making sure :wink:.

I forgot to add…

self.objectType = "trigger"

…in the odeTrigger class. Put it right after:

class odeTrigger(staticObject):
	def __init__(self, map, model, name):
		staticObject.__init__(self, map)

Additionally, change the player’s setFly method to this:

def setFly(self, value, object, trigger):
		print "SET FLY", value
		if object is not self:
			return
		if value:
			self.state = "fly"
			self.movementParent = base.cam
		else:
			self.state = "ground"
			self.movementParent = self.geom

And, AFAIR, that should be all.

On a side note, I’m not complaining about the need to post a few lines here, but why don’t you just download the fixed version? :wink:. Asking just out of curiosity :wink:.

Thank you =)

I downloaded the new version, but I removed the old one =/ (stupid me). So I had not had a file to compare, in my game I’ve already changed a lot so I can’t see so easy what was changed =)

Thank you

blenderkid

Hi,

I finally found some time to continue the game and I have a problem, which I don’t realy understand because I do almost exactly the same as you in your map script.

I am trying to load a gun:

firearmtest = Firearm()  #<--basically your gun class
firearmtest.loadConfig("config/guns/P90/main.txt") #<-- this does not matter
firearmtest.setupGeomAndPhysics(g.map, (0,0,10))  #<--Here the error occurs

The error message is:

Traceback (most recent call last):
  File "test.py", line 72, in <module>
    firearmtest.setupGeomAndPhysics(g.map, (0,0,10))
  File "/home/filip/develop/panda-shooter/dev/new/pickables.py", line 45, in setupGeomAndPhysics
    DynamicObject.__init__(self, map)
  File "/home/filip/develop/panda-shooter/dev/new/odeWorldManager.py", line 456, in __init__
    DynamicObjectNoCCD.__init__(self, map)
  File "/home/filip/develop/panda-shooter/dev/new/odeWorldManager.py", line 342, in __init__
    StaticObject.__init__(self, map)
TypeError: unbound method __init__() must be called with StaticObject instance as first argument (got Firearm instance instead)
Segmentation fault

I am a bit confused. I know why it has Firearm as “self”, but firearm is an Extension of StaticObject… so I dont understand the problem…

I hope you know what to do =)

Thank you very much
blenderkid

I guess “basically” means “not exactly”, right? :wink:. Can you show me the code of that class? Or the changes made to it, if it’s a changed gun class and not a new one derived from it.

My first thought is that Firearm does not correctly inherit from the classes it’s supposed to inherit from. The original Gun class inherits from pickableObject. Maybe you’ve made some changes to that class and removed correct inheritance by accident?

Anyhow, please show the code for Firearm.

But it is almost exactly the same ( at least the necessary parts )

from pandac.PandaModules import Point3, Vec3
from direct.interval.IntervalGlobal import *
from direct.showbase.DirectObject import DirectObject

from odeWorldManager import *
from pickables import PickableObject

import random
import configurations


class Firearm(PickableObject, DirectObject):
	def __init__(self, name="handgun", weight=0.630):
		PickableObject.__init__(self, name, weight)
		
		self.config = configurations.Configuration()
		
		
		
		self.pickableType = "firearm"
		
		
		self.modelPath = "models/p90.egg"
		self.worldModelPath = "models/p90.egg"
		self.hudModelPath   = "models/p90.egg"
		self.geomSize = (0.054, 0.381, 0.227)
		
		"""
		Is it automatic or semi-automatic?
		"""
		self.automatic = False
		
		"""
		How far do you want the bullets to go?
		"""
		self.effectiveRange = 300.0
		
		"""
		Rate of Fire for automatic weapons.
		"""
		self.autoROF = 250.0
		
		"""
		Used for ROF, more on that in the update method.
		"""
		self.autoAccumulator = 0.0
		
		"""
		Number of rays to search for hits with. This basically
		translates to this: If it's more than one, you have a shotgun.
		It it's one -- a pistol or a rifle.
		"""
		self.aimRaysNum = 1
		
		"""
		Holds aim rays.
		"""
		self.aimRays = []
		
		"""
		If you have more than one aim ray, then here you can set how
		dispersed they're going to be.
		"""
		self.shotgunDispersion = 0.2
		
		"""
		Are we shooting ATM?
		"""
		self.shooting = False
		
		"""
		Contains the hits.
		"""
		self.aimCollisions = []
		
		
	
	"""
	The callback for aim ray collisions.
	"""
	def aimCollision(self, entry, object1, object2):
		"""
		No point hitting triggers or ccd helpers.
		"""
		if object2.objectType is ["ccd"]:
			return
		
		"""
		So we don't shoot ourselves.
		"""
		if object2 is self.owner:
			return
		
		"""
		Append the hit to the list.
		"""
		self.aimCollisions.append([entry, object2])
	
	def destroy(self):
		for ray in self.aimRays:
			self.map.worldManager.removeObject(ray)
			ray.destroy()
		del self.aimCollisions
		del self.aimRays
		pickableObject.destroy(self)
		self.ignoreAll()
	
	"""
	Create the requested number of aim rays.
	"""
	def createAimRays(self):
		for i in range(self.aimRaysNum):
			self.createAimRay()
	
	"""
	Create a single aim ray.
	"""
	def createAimRay(self):
		ray = rayObject(self.map)
		ray.objectType = "ray"
		ray.setRayGeom(self.effectiveRange, [Vec3(0,0,0), Vec3(0,0,-1)])
		ray.collisionCallback = self.aimCollision
		
		self.map.worldManager.addObject(ray)
		self.aimRays.append(ray)
	
	def destroyAimRays(self):
		for ray in self.aimRays:
			self.map.worldManager.removeObject(ray)
			ray.destroy()
		self.aimRays = []
		self.aimCollisions = []
	
	def selectionCallback(self, character, direction):
		PickableObject.selectionCallback(self, character, direction)
		
		"""
		Create the rays when we pick up the gun. No need having them around all the time.
		
		NOTE: In my game, I actually create the rays when the Player withdraws the gun
		from the inventory, but the actual usage depends on application's design.
		"""
		self.createAimRays()
	
	def drop(self):
		"""
		Destroy the rays when we drop the gun.
		"""
		self.destroyAimRays()
		PickableObject.drop(self)
	
	def useHeld(self):
		"""
		Start shooting...
		"""
		self.shooting = True
		return True
	
	def useHeldStop(self):
		"""
		Stop shooting an automatic weapon.
		Semi automatic weapons stop shooting by themselves, right after
		one update pass.
		"""
		if self.automatic:
			self.stopShooting()
		return
	
	def shoot(self, dir):
		"""
		Process the collected hits.
		"""
		for entry, object in self.aimCollisions:
			p = entry.getContactPoint(0)
			if object.body:
				object.body.enable()
				object.body.addForceAtPos(dir*(10**2), p)
	
	def stopShooting(self):
		"""
		Stop shooting and clear the collisions accumulator.
		"""
		self.shooting = False
		self.autoAccumulator = 0.0
		
	def update(self, stepSize):
		PickableObject.update(self,stepSize)
		
		if not self.aimRays:
			self.aimCollisions = []
			return
		
		"""
		Only process hits if the gun is picked up.
		"""
		if self.owner:
			"""
			Get the desired position and direction of the aim ray/s.
			
			NOTE that currently there's only support for Player (camera)
			and not NPCs. Supports for NPCs will come in the next major version.
			"""
			pos = base.cam.getPos(render) + render.getRelativeVector(base.cam, Vec3(0, 0.8, 0))
			dir = render.getRelativeVector(base.cam, Vec3(0, 1.0, 0))
			
			"""
			Handle the Rays and add some random dispersion.
			"""

			for ray in self.aimRays:
				"""
				Scatter the rays within the limits set by the shotgunDispersion variable.
				"""
				x = random.uniform(-self.shotgunDispersion, self.shotgunDispersion)
				z = random.uniform(-self.shotgunDispersion, self.shotgunDispersion)
				
				"""
				Tweak the direction to take the random values into account.
				"""
				rayDir = Vec3(dir)
				rayDir[0] += x
				rayDir[2] += z
				
				"""
				Position the ray.
				"""
				ray.geom.set(pos, rayDir)
			
			"""
			Handle automatic weapons.
			"""
			if self.automatic and self.shooting:
				"""
				If this is the first frame we're self.shooting == True,
				shoot once and then continue processing.
				"""
				if self.autoAccumulator == 0.0:
					self.shoot(dir)
				
				"""
				Add the rate-of-fire to the accumulator.
				"""
				self.autoAccumulator += self.autoROF
				
				"""
				Get the ROF per second
				"""
				currentROF = 1.0/self.autoAccumulator
				
				"""
				Call shoot method as many times as shots fitting in the
				simulation time step.
				
				That's what the accumulator is for -- thanx to that,
				the speed of shooting depends on values set, not on
				the simulation's update rate.
				"""
				if currentROF < stepSize:
					for i in range(int(stepSize / currentROF)):
						self.shoot(dir)
					self.autoAccumulator = 0.0
			
				"""
				Handle semi automatic shooting.
				"""
			elif self.shooting:
				self.shoot(dir)
		
		"""
		If semi-automatic, make sure to shoot only once.
		"""
		if not self.automatic:
			self.stopShooting()
		
		"""
		Clear aim collisions before the next frame.
		"""
		self.aimCollisions = []
		
	def loadConfig(self, filename):
		self.config.addConfig(filename)
		self.configName = self.config.configs.keys()[0]
		
		self.activateConfigs()
		
	def activateConfigs(self):
		self.name   = self.config.getConfig(self.configName)["properties"]["name"]
		self.rps    = self.config.getConfig(self.configName)["properties"]["rps"]
		self.type   = self.config.getConfig(self.configName)["properties"]["type"]
		self.hudModelPath  = self.config.getConfig(self.configName)["properties"]["model"]
		self.worldModelPath  = self.config.getConfig(self.configName)["properties"]["worldmodel"]

		self.modelPath = self.worldModelPath

That’s the firearm module

I’ve renamed pickableObject to PickableObject because I am used to have classes capitalized :wink: I’ve capitalized all classes =)

I hope you can find my mistake

Thank you very much
blenderkid

Hm, it really is almost exactly the same… Sorry, but I still don’t see what’s wrong… Have you made any important changes to the PickableObject class? And do you experience the same problems with adding any other kind of objects inheriting from the PickableObject class? Like boxes or whatnot?

It seems like the problem is either in the Firearm class or the PickableObject class. I guess we can exclude the first one (well, frankly it was the less likely option since the error occurs in the setupGeomAndPhysics method).

Hi,

here is a gzip with all modules involved in that error =)
Modules with at least one error =P

I hope you find that error. I am still confused =/

Thank you very much!
blenderkid

Ok, found it. Line 340 to 342 in odeWorldManager.

class DynamicObjectNoCCD(PhysicalObject):
	def __init__(self, map):
		StaticObject.__init__(self, map)

Seems like you’ve made a really big mess in inheritance when changing the names to capitalized :wink:. Ok, so this error should be obvious – you inherit from PhysicalObject, but you attempt to call init on StaticObject. This can’t work.

However, there’s more. You’ve generally changed the inheritance (probably because of attempting to change the names too quickly) and, unless you’ve made some real changes to the code itself (and it doesn’t seem so at the first glance) the code just won’t work.

The correct inheritance is as follows:

PhysicalObject(object)
StaticObject(PhysicalObject)
RayObject(PhysicalObject)
KinematicObject(StaticObject)
DynamicObjectNoCCD(StaticObject)
DynamicObjectCCD(DynamicObjectNoCCD)
Explosion(KinematicObject)

In your version kinematic and basic dynamic objects inherit from the PhysicalObject instead from the StaticObject, which is incorrect.

When changing that pay attention to the contents of init and all other methods which might contain calls to the parent classes with “self”.

Assuming one has access to source code tracking down errors of that kind is very simple. It all comes down to walking through inheritance top-to-bottom.

Oh facepalm stupid me…

Thank you soo much!
blenderkid

A new minor version is up. It contains just one important bug fix, no new features were added this time. I’ve been away from any codding for some time, that’s why it took so long.

Just to keep you informed, after that long silence, for the next version I’m planning full moving platforms support, for elevators and the like (no API changes planned at this time).

I’m having trouble animating a model using this system. In the map.create method, I’ve added the following code after the self.processBranch call:

		mNodePath = Actor.Actor("legs.egg",
                                {"walk": "legs-Walk.egg"})
		mNodePath.reparentTo(render) 
		mNodePath.setPos(-10,-10,5.5)
		mNodePath.loop('walk')

The model shows up fine, but the animation doesn’t play. The model is animated if I put it in the walking panda tutorial though; so I know the files are fine. mNodePath.getNumFrames(‘walk’) is returning 21 frames, which is correct; so I’m not sure why it’s not actually doing anything. Does the loop call need to be done at some later point?

To be honest I have no idea why it doesn’t work.

Can you show the whole method, i.e. this and the surrounding code? I don’t think the problem is the framework, to be honest. If your model isn’t a part of some dynamic, kinematic or static object, but just an arbitrary model loaded with the code you’ve shown, then the framework shouldn’t affect it in any way (in fact, it shouldn’t even if it was used by physical objects).

Sure, here:

	def create(self):
		"""
		Load the map Egg file and get it's root node
		"""
		# MAP PARENT
		self.map = loader.loadModel(self.mapFile)
		self.map.reparentTo(render)
		self.mapRootNode = self.map.find("-PandaNode")

		"""
		See the processBranch method definition lower
		"""
		self.processBranch(self.mapRootNode)
		
		#Quick animation check
		mNodePath = Actor.Actor("legs",
                                {"walk": "legs-Walk"})
		mNodePath.reparentTo(render) 
		mNodePath.setPos(-10,-10,5.5)
		mNodePath.loop('walk')


		"""
		Start the simulation at the required time step size
		"""
		self.worldManager.startSimulation(self.simTimeStep)
		
		"""
		Enable player
		"""
		self.player.enableInput()
		self.player.enableMovement()

This is a common mistake with Actor. You are creating an Actor, and saving it in a local variable (mNodePath). But you are not then saving that variable anywhere, so when it goes out of scope, Python cleans it up, and deletes the Actor part of the object.

The NodePath part of it remains and is visible in the scene graph, but since it is no longer an Actor it doesn’t animate.

Solution: self.mNodePath = mNodePath

David

Yep, that fixed it. Ironically, if I hadn’t been testing each part first, I probably wouldn’t have run into this :stuck_out_tongue:

Thanks, both of you!

New major version (1.2.0) has been released bringing a lot of new stuff. See the first post for details.

Is there a limit on how large the kcc.setCapsuleData can be set? I can set the player to half-sized without any issues, but if I double the player’s size ( self.setCapsuleData(3.7,1.6,0.6,0.7) ), the player “bounces” in place.

I haven’t played with the code much yet - is there an easy way around this?

Platform looks good so far! Did finally see that memory leak in panda 1.7 with this version though; once I set another kcc object near it :stuck_out_tongue:

Fixed. Looks like I just left the footRay’s (and the environment checker ray’s too) length hard codded for some reason. This caused the controller to bounce on the tip of the ray, like on a pogo stick. I’ve updated the download – same version, don’t think it really deserves it’s own number :wink:.

Hey Coppertop,

This is totally awesome, I’m currently getting a pipeline established between Maya and this framework. I am having troubles with running the latest 1.2 version however, getting this error:

---------------------------
ODE INTERNAL ERROR 2
---------------------------
Bad argument(s) (..\..\ode\src\capsule.cpp:84)
---------------------------
OK   
---------------------------

The previous version seems to work fine however.

Ok, I’ve found the problem. Updated the download link with the fixed version.

Additionally, I also figured that I left the Player controller extremely big after fixing the previous bug… That’s what happens when you try to do things too quickly. Anyway, I’ve got no idea why this latest bug didn’t show on Linux, but it should be fine now.