294 lines
11 KiB
Python
294 lines
11 KiB
Python
"""Loader module: contains the Loader class"""
|
|
|
|
from pandac.PandaModules import *
|
|
from direct.directnotify.DirectNotifyGlobal import *
|
|
|
|
# You can specify a phaseChecker callback to check
|
|
# a modelPath to see if it is being loaded in the correct
|
|
# phase
|
|
phaseChecker = None
|
|
|
|
class Loader:
|
|
"""
|
|
Load models, textures, sounds, and code.
|
|
"""
|
|
notify = directNotify.newCategory("Loader")
|
|
modelCount = 0
|
|
|
|
# special methods
|
|
def __init__(self, base):
|
|
self.base = base
|
|
self.loader = PandaLoader()
|
|
|
|
def destroy(self):
|
|
del self.base
|
|
del self.loader
|
|
|
|
# model loading funcs
|
|
def loadModel(self, modelPath, fMakeNodeNamesUnique = 0,
|
|
loaderOptions = None):
|
|
"""
|
|
modelPath is a string.
|
|
|
|
Attempt to load a model from given file path, return
|
|
a nodepath to the model if successful or None otherwise.
|
|
"""
|
|
assert Loader.notify.debug("Loading model: %s" % (modelPath))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
if loaderOptions == None:
|
|
loaderOptions = LoaderOptions()
|
|
node = self.loader.loadSync(Filename(modelPath), loaderOptions)
|
|
if (node != None):
|
|
nodePath = NodePath(node)
|
|
if fMakeNodeNamesUnique:
|
|
self.makeNodeNamesUnique(nodePath, 0)
|
|
else:
|
|
nodePath = None
|
|
return nodePath
|
|
|
|
def loadModelOnce(self, modelPath):
|
|
"""
|
|
modelPath is a string.
|
|
|
|
Attempt to load a model from modelPool, if not present
|
|
then attempt to load it from disk. Return a nodepath to
|
|
the model if successful or None otherwise
|
|
"""
|
|
assert Loader.notify.debug("Loading model once: %s" % (modelPath))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
node = ModelPool.loadModel(modelPath)
|
|
if (node != None):
|
|
nodePath = NodePath.anyPath(node)
|
|
else:
|
|
nodePath = None
|
|
return nodePath
|
|
|
|
def loadModelOnceUnder(self, modelPath, underNode):
|
|
"""loadModelOnceUnder(self, string, string | node | NodePath)
|
|
Behaves like loadModelOnce, but also implicitly creates a new
|
|
node to attach the model under, which helps to differentiate
|
|
different instances.
|
|
|
|
underNode may be either a node name, or a NodePath or a Node
|
|
to an already-existing node.
|
|
|
|
This is useful when you want to load a model once several
|
|
times before parenting each instance somewhere, or when you
|
|
want to load a model and immediately set a transform on it.
|
|
But also consider loadModelCopy().
|
|
"""
|
|
assert Loader.notify.debug("Loading model once: %s under %s" % (modelPath, underNode))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
node = ModelPool.loadModel(modelPath)
|
|
if (node != None):
|
|
nodePath = NodePath(underNode)
|
|
nodePath.attachNewNode(node)
|
|
else:
|
|
nodePath = None
|
|
return nodePath
|
|
|
|
def loadModelCopy(self, modelPath):
|
|
"""loadModelCopy(self, string)
|
|
Attempt to load a model from modelPool, if not present
|
|
then attempt to load it from disk. Return a nodepath to
|
|
a copy of the model if successful or None otherwise
|
|
"""
|
|
assert Loader.notify.debug("Loading model copy: %s" % (modelPath))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
node = ModelPool.loadModel(modelPath)
|
|
if (node != None):
|
|
return NodePath(node.copySubgraph())
|
|
else:
|
|
return None
|
|
|
|
def loadModelNode(self, modelPath):
|
|
"""
|
|
modelPath is a string.
|
|
|
|
This is like loadModelOnce in that it loads a model from the
|
|
modelPool, but it does not then instance it to hidden and it
|
|
returns a Node instead of a NodePath. This is particularly
|
|
useful for special models like fonts that you don't care about
|
|
where they're parented to, and you don't want a NodePath
|
|
anyway--it prevents accumulation of instances of the font
|
|
model under hidden.
|
|
|
|
However, if you're loading a font, see loadFont(), below.
|
|
"""
|
|
assert Loader.notify.debug("Loading model once node: %s" % (modelPath))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
return ModelPool.loadModel(modelPath)
|
|
|
|
def unloadModel(self, modelPath):
|
|
"""
|
|
modelPath is a string.
|
|
"""
|
|
assert Loader.notify.debug("Unloading model: %s" % (modelPath))
|
|
ModelPool.releaseModel(modelPath)
|
|
|
|
# font loading funcs
|
|
def loadFont(self, modelPath,
|
|
spaceAdvance = None, pointSize = None,
|
|
pixelsPerUnit = None, scaleFactor = None,
|
|
textureMargin = None, polyMargin = None,
|
|
minFilter = None, magFilter = None,
|
|
anisotropicDegree = None,
|
|
lineHeight = None):
|
|
"""
|
|
modelPath is a string.
|
|
|
|
This loads a special model as a TextFont object, for rendering
|
|
text with a TextNode. A font file must be either a special
|
|
egg file (or bam file) generated with egg-mkfont, or a
|
|
standard font file (like a TTF file) that is supported by
|
|
FreeType.
|
|
"""
|
|
assert Loader.notify.debug("Loading font: %s" % (modelPath))
|
|
if phaseChecker:
|
|
phaseChecker(modelPath)
|
|
|
|
font = FontPool.loadFont(modelPath)
|
|
if font == None:
|
|
# If we couldn't load the model, at least return an
|
|
# empty font.
|
|
font = StaticTextFont(PandaNode("empty"))
|
|
|
|
# The following properties may only be set for dynamic fonts.
|
|
if hasattr(font, "setPointSize"):
|
|
if pointSize != None:
|
|
font.setPointSize(pointSize)
|
|
if pixelsPerUnit != None:
|
|
font.setPixelsPerUnit(pixelsPerUnit)
|
|
if scaleFactor != None:
|
|
font.setScaleFactor(scaleFactor)
|
|
if textureMargin != None:
|
|
font.setTextureMargin(textureMargin)
|
|
if polyMargin != None:
|
|
font.setPolyMargin(polyMargin)
|
|
if minFilter != None:
|
|
font.setMinFilter(minFilter)
|
|
if magFilter != None:
|
|
font.setMagFilter(magFilter)
|
|
if anisotropicDegree != None:
|
|
font.setAnisotropicDegree(anisotropicDegree)
|
|
|
|
if lineHeight is not None:
|
|
# If the line height is specified, it overrides whatever
|
|
# the font itself thinks the line height should be. This
|
|
# and spaceAdvance should be set last, since some of the
|
|
# other parameters can cause these to be reset to their
|
|
# default.
|
|
font.setLineHeight(lineHeight)
|
|
|
|
if spaceAdvance is not None:
|
|
font.setSpaceAdvance(spaceAdvance)
|
|
|
|
return font
|
|
|
|
# texture loading funcs
|
|
def loadTexture(self, texturePath, alphaPath = None):
|
|
"""
|
|
texturePath is a string.
|
|
|
|
Attempt to load a texture from the given file path using
|
|
TexturePool class. Returns None if not found
|
|
"""
|
|
if alphaPath is None:
|
|
assert Loader.notify.debug("Loading texture: %s" % (texturePath))
|
|
if phaseChecker:
|
|
phaseChecker(texturePath)
|
|
texture = TexturePool.loadTexture(texturePath)
|
|
else:
|
|
assert Loader.notify.debug("Loading texture: %s %s" % (texturePath, alphaPath))
|
|
if phaseChecker:
|
|
phaseChecker(texturePath)
|
|
texture = TexturePool.loadTexture(texturePath, alphaPath)
|
|
return texture
|
|
|
|
def load3DTexture(self, texturePattern):
|
|
"""
|
|
texturePattern is a string that contains a sequence of one or
|
|
more '#' characters, which will be filled in with the sequence
|
|
number.
|
|
|
|
Returns a 3-D Texture object, suitable for rendering
|
|
volumetric textures, if successful, or None if not.
|
|
"""
|
|
assert Loader.notify.debug("Loading 3-D texture: %s" % (texturePattern))
|
|
if phaseChecker:
|
|
phaseChecker(texturePattern)
|
|
texture = TexturePool.load3dTexture(texturePattern)
|
|
return texture
|
|
|
|
def loadCubeMap(self, texturePattern):
|
|
"""
|
|
texturePattern is a string that contains a sequence of one or
|
|
more '#' characters, which will be filled in with the sequence
|
|
number.
|
|
|
|
Returns a six-face cube map Texture object if successful, or
|
|
None if not.
|
|
|
|
"""
|
|
assert Loader.notify.debug("Loading cube map: %s" % (texturePattern))
|
|
if phaseChecker:
|
|
phaseChecker(texturePattern)
|
|
texture = TexturePool.loadCubeMap(texturePattern)
|
|
return texture
|
|
|
|
def unloadTexture(self, texture):
|
|
|
|
"""
|
|
Removes the previously-loaded texture from the cache, so
|
|
that when the last reference to it is gone, it will be
|
|
released. This also means that the next time the same texture
|
|
is loaded, it will be re-read from disk (and duplicated in
|
|
texture memory if there are still outstanding references to
|
|
it).
|
|
|
|
The texture parameter may be the return value of any previous
|
|
call to loadTexture(), load3DTexture(), or loadCubeMap().
|
|
"""
|
|
assert Loader.notify.debug("Unloading texture: %s" % (texture))
|
|
TexturePool.releaseTexture(texture)
|
|
|
|
# sound loading funcs
|
|
def loadSfx(self, name):
|
|
assert Loader.notify.debug("Loading sound: %s" % (name))
|
|
if phaseChecker:
|
|
phaseChecker(name)
|
|
# should return a valid sound obj even if soundMgr is invalid
|
|
sound = None
|
|
if (name):
|
|
# showbase-created sfxManager should always be at front of list
|
|
sound=base.sfxManagerList[0].getSound(name)
|
|
if sound == None:
|
|
Loader.notify.warning("Could not load sound file %s." % name)
|
|
return sound
|
|
|
|
def loadMusic(self, name):
|
|
assert Loader.notify.debug("Loading sound: %s" % (name))
|
|
# should return a valid sound obj even if musicMgr is invalid
|
|
sound = None
|
|
if (name):
|
|
sound=base.musicManager.getSound(name)
|
|
if sound == None:
|
|
Loader.notify.warning("Could not load music file %s." % name)
|
|
return sound
|
|
|
|
|
|
def makeNodeNamesUnique(self, nodePath, nodeCount):
|
|
if nodeCount == 0:
|
|
Loader.modelCount += 1
|
|
nodePath.setName(nodePath.getName() +
|
|
('_%d_%d' % (Loader.modelCount, nodeCount)))
|
|
for i in range(nodePath.getNumChildren()):
|
|
nodeCount += 1
|
|
self.makeNodeNamesUnique(nodePath.getChild(i), nodeCount)
|
|
|