Replacing model in Actor

hi,

is there a way to replace the model of an actor?

i can load and unload animations, but there is no way to unload a certain model that has been loaded with Actor.loadModel.

i want to be able to reload the actors model when changed without stopping the application.

is this doable using the Actor class?

thanks,
kaweh

seems i was able to do so, using this sequence:


actor.detachNode()
actor.removePart('modelRoot')

not sure though if that’s 100% correct (and i assume that the model is still in memory, but that’s currently fine).

maybe someone knows a smarter way.

If that works, then it seems OK to me. The model won’t be in memory once the last reference to it goes away; almost everything in Panda is reference-counted and will be automatically destructed when they are no longer referenced.

David

hm. it does work as needed from a functionality-pov.

doing


resource.detachNode()
resource.removePart('modelRoot')
loader.unloadModel(resource.path)

i get rid of the model and can reload it (yes, it does a disk-read as indicated in the console output).

though i wonder why my memory is rising when i do a reload? the disk-read indicates that all references are gone, also it seems that all references to textures are gone (the ':gobj: reloading texture ’ indicates that when reloading a model). so i’m not sure, why i loose MBs really.

i will need to reduce my code to a minimal test to see what is going.

any facilities in Panda3D to help me debug this?

(ps: this is on OS X so there might be something else involved here, as i’m not using dmalloc)

thanks,
kaweh

ok. i reduced my code to a minimal test (see below for the code).

you can use any model, but i used a fairly big model to show the effect best.

the code will load a model as an actor, by pressing ‘r’ the model (but not the actor) will be “thrown out” and by pressing ‘r’ again, reloaded. my intention is to have a “real” unload/reload of the model - meaning disk access when reloaded.

the program works as expected, BUT: :slight_smile:

if you look at the output, you’ll notice that the number of vertices/normals/etc. is constantly growing as you reload the model. the working disk access indicates that the real mesh model has been removed and is not reference counted, though the numbers from ‘render’ indicate otherwise. doing that for some time will result in decreased FPS.

running task manager and observing the memory, you’ll see that memory is constantly lost while doing the unload/reload routine.

because of the seen disk access - indicitated by

:loader: Loading model models/world/world.bam
:loader: Reading ./models/world/world.bam

my assumption was that i’m doing the correct things to get the model out of memory (which really seems to work, because if i change the model in between unload/reload, i see the correct and changed model).

but then something is holding on to some memory somewhere.

btw: i tested this with my OS X build AND Panda3D 1.2.2 under Windows XP (don’t have 1.2.3 on that machine). both platforms show the same behaviour, this is not platform specific.

  • i might not have done all things needed to really get rid of the model - any ideas what to try?

  • there is some kind of leak in Panda3D or my wanted behaviour is just not supported (i would hope for an easy-to-fix leak though, reloading on-the-fly would just be awesome for my project). David, do you think it’s my faulty assumptions or anything i can do to get this right?

thanks in advance and i hope that little code-snippet helps to find a solution.

cheers,
kaweh

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.actor.Actor import Actor 

ANY_MODEL = 'models/world/world.bam'

actor = Actor(ANY_MODEL)
actor.reparentTo(render)


class Reloader(DirectObject):
    def __init__(self):
        self.accept('r', self.reload)
        self.loaded = True
        
    def reload(self):
        if self.loaded:
            actor.detachNode()
            actor.removePart('modelRoot')
            loader.unloadModel(ANY_MODEL)        
            self.loaded = False
        else:
            actor.loadModel(ANY_MODEL)
            actor.reparentTo(render)
            self.loaded = True
        render.analyze()
        
reloader = Reloader()
run()

changing the code to use the ‘loader’ (instead of the Actor class) will result in the correct behaviour. so it seems to me that the Actor class is adding some additional overhead that i’m not sure how to free up:


import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.actor.Actor import Actor 

ANY_MODEL = 'models/world/world.bam'

actor = loader.loadModel(ANY_MODEL)
actor.reparentTo(render)

class Reloader(DirectObject):
    def __init__(self, actor):
        self.loaded = True
        self.actor = actor
        self.accept('r', self.reload)
        
    def reload(self):
        if self.loaded:
            self.actor.detachNode()
            self.actor = loader.unloadModel(ANY_MODEL)        
            self.loaded = False
        else:
            global actor
            actor  = loader.loadModel(ANY_MODEL)
            self.actor = actor
            self.actor.reparentTo(render)
            self.loaded = True
        render.analyze()
        
reloader = Reloader(actor)
run()

ok, last one from me regarding this topic (for now) :slight_smile:

the code below now works with the Actor class - at least as far as i can tell. i’m still not comfortable with the solution though, it would be nicer if the Actor class could provide such facility on it’s own. i’m willing to contribute that addition if someone would be able to confirm that the solution below seems sound.

essentially i would have believed that removing the various parts of the Actor would also free the corresponding meshes, but seems not to be the case.

i am now getting at the GeomNode of the Actor and detach all of it’s children. this should make sure all meshes are really gone (if not referenced somewhere else).

anyone knows more about the interals of the Actor class and has some more tips what I should take care of? (i haven’t yet touched animations, though i want to be able to reload them too)

hope this information will help others trying to achieve on-the-fly reloading of models using Panda3D. or maybe someone has solved this problem already with a smarter solution than mine… :wink:

cheers,
kaweh

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.actor.Actor import Actor 

ANY_MODEL = 'models/world/world.bam'

actor = Actor(ANY_MODEL)
actor.reparentTo(render)


class Reloader(DirectObject):
    def __init__(self):
        self.accept('r', self.reload)
        self.loaded = True
        
    def reload(self):
        if self.loaded:
            actor.detachNode()
            actor.removePart('modelRoot')
            children = actor.getGeomNode().getChildren()
            children.detach()
            loader.unloadModel(ANY_MODEL)        
            self.loaded = False
        else:
            actor.loadModel(ANY_MODEL)
            actor.reparentTo(render)
            self.loaded = True
        render.analyze()
        
reloader = Reloader()
run()

Hmm, I’m happy that you’ve found a solution, but it does seem that something in Actor is broken. Let me investigate this more closely.

David