LOD

In tinkering with LOD, I’ve found some funny things- I 'd like some clarification.

First, when you create a new LOD for an LODNode the argument is listed in the Panda API as “lodName” or string. However, looking at the LODNode section of the API (and experiencing some errors when doing stuff like getLODNames) leads me to believe that name should actually be an integer. Any insight into this?

Second, here’s what I am trying to do and failing. First, I have a create a bunch of objects for my scene which are basically classes with an actor, for example Box or Table. Now, AFTER I’ve already initialized Box.actor or Table.actor etc I want to go back and add LOD to these actors. I’ve found I can add a LODNode with an LOD like this:


	for o in self.objects:
		ln = LODNode('LODNode')
		o.myActor.setLODNode(ln)
		o.myActor.addLOD(1000, inDist=1000, outDist=40)
		o.myActor.loadModel(gOthersPath+"Flat/flat_model.egg",lodName="1000")

But now I need to get access to my originally loaded actor model and also assign it to an LOD. I would think I could simply access ‘lodRoot’ which is supposed to be the default LOD and indeed it appears when I print the actor like this:


print str(o.myActor)

which yields

Actor: partBundleDict = {'lodRoot': {'modelRoot': render/<Bush.Bush instance at 0x0EC317F0>_top/bush/actorGeom/__A
ctor_modelRoot}, '1000': {'modelRoot': render/<Bush.Bush instance at 0x0EC317F0>_top/bush/actorGeom/LODNode/1000/_
_Actor_modelRoot}}, animControlDict = {}

However, attepts to setLOD(“lodRoot”, in, out throw errors since (this is my suspicion) internally LODNode doesn’t like string names for LODs. Here’s the error:

  File "C:\Panda\direct\src\actor\Actor.py", line 424, in setLOD
    self.__LODNode.node().setSwitch(index, inDist, outDist)
  File "LODNode", line 137, in setSwitch
AssertionError: index >= 0 && index < (int)cdata->_lod._switch_vector.size() at line 90 of c:\dev\panda\panda3dsrc
\panda\src\pgraph\lodNode.I

SO- how do I a) get my already loaded actor geometry to move under some new LOD node; or b) change “lodRoot”'s in and out settings?

Insight is appreciated!

We’ve found away around this issue- not sure if it’s the best. Basically we manually move the actor geometry:

o.myActor.addLOD('0', inDist=radius*1.65, outDist=0)
o.myActor.getChild(0).getChild(0).wrtReparentTo(o.myActor.getLOD('0'))

In fact, there’s no requirement that your LODNames be integers. The crash you experienced when you tried to setLod(“lodRoot”) is because there is no level of detail named “lodRoot”–in fact, your only level of detail so far is named “1000”! By Actor convention, “lodRoot” is the name to use when there are no lod’s at all. Once you have added one or more lod’s, you should no longer pass “lodRoot” for the lodName parameter, but instead pass the name of one of your individual lod’s.

I agree it would be nice if the Actor issued a more useful error message for this sort of mistake. I’ll add some code for this. By the way, it looks like there is an additional assumption that Actor makes: you must add your lod’s (via addLOD) in alphabetical order. I’ll fix this too, but you should be aware of the requirement in the meantime.

David

Oh–back to your original problem. You have created an Actor that doesn’t use LOD’s, and the Actor’s geometry is therefore under some non-LOD node. Now you want to add LOD’s, which means you need to move the Actor’s existing geometry under some particular level of detail. Right.

The Actor class wasn’t designed for this in mind, but you have found a fine solution. Your reparent trick is fine, but you can be just a bit more robust if you like. I would do it like this:


actor.getGeomNode().getChild(0).reparentTo(actor.getLOD('0'))

I like getGeomNode() rather than the first getChild(0), and I like reparentTo() in general rather than wrtReparentTo(), unless I know there is some reason to play tricks with the transforms–it’s safer.

David

It looks like Actor.getLODNames() does assume that the names are integers:


lodNames = self.__partBundleDict.keys()
# Reverse sort the doing a string->int
lodNames.sort(lambda x,y : cmp(int(y), int(x)))

There are "str(lodName)"s sprinkled thoughout the code, so I assume at one point this assumption was stronger. I know from experience that handing integers to addLOD does in fact work. I would think these remnants should be taken out. I know they confused me when I was first looking at this stuff. I guess that would break a lot of existing code, though.

Also, Sabrina, you may want to check out FadeLODNode, which gives a nicer effect than the default LODNode. Do something like this:


fadeLodNode = FadeLODNode( "fade" )
fadeLodNode.setFadeTime( .5 )
actor.setLODNode( fadeLodNode )

before any addLOD() calls.

Everything works and fade looks great. Beautiful. There should be some LOD documentation put on main site from this little adventure.

Gratzi!

I wonder if Actor should use FadeLODNode by default. We use it for all of our LOD and I haven’t noticed a performance hit from it. It’s easy to overlook, although that could probably be fixed with documentation.

Perhaps it would be good enough to make it easier to switch the LODNode after the fact. If you create your actor with LOD using Actor( model{}, anims{} ), there’s no way to specify a custom LODNode, and calling setLODNode() after creation will ruin any LOD that you had setup.

Hey, I’ve been getting a prob with lods like with this code:

from pandac.PandaModules import *
toon = base.localAvatar
head = base.localAvatar.getPart('head')


def makeCard(book=False):
    cardMaker = CardMaker('laughing-man-cm')
    cardMaker.setHasUvs(1)
    cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5)

    nodePath = NodePath('laughing-man')
    nodePath.setBillboardPointEye()

    lmBase = nodePath.attachNewNode(cardMaker.generate())
    lmBase.setTexture(loader.loadTexture('phase_3/maps/lm_base.rgba'))
    lmBase.setY(-0.3)
    lmBase.setTransparency(True)

    lmText = nodePath.attachNewNode(cardMaker.generate())
    lmText.setTexture(loader.loadTexture('phase_3/maps/lm_text.rgba'))
    lmText.setY(-0.301)
    lmText.setTransparency(True)
    lmText.hprInterval(10, (0, 0, -360)).loop()

    lmFace = nodePath.attachNewNode(cardMaker.generate())
    lmFace.setTexture(loader.loadTexture('phase_3/maps/lm_face.rgba'))
    lmFace.setY(-0.302)
    lmFace.setTransparency(True)

    return nodePath


def addHeadEffect(head, book=False):
    card = makeCard(book=book)
    card.setScale(1.45 if book else 2.5)
    card.setZ(0.05 if book else 0.5)
    for nodePath in head.getChildren():
        nodePath.removeNode()
    card.instanceTo(base.localAvatar.getPart('head'))


def addToonEffect(toon):
    toon.getDialogueArray = lambda *args, **kwargs: []
    for lod in toon.getLODNames():
        addHeadEffect(base.localAvatar.getPart('head'), lod)

it says that there is no lod named lodRoot. Can someone here help?

lolman8776, did you by any chance look into Toontown Infinites source at their Magic Cat NPC coding?

O.O HOW DID YOU KNOW I WAS DOING IT FOR THAT O.O O.O also i fixed that, i just need to make it so everyone can see it. Can you help me with that?