Browse Source

bam files

David Rose 16 years ago
parent
commit
5bb225ab1f
1 changed files with 171 additions and 13 deletions
  1. 171 13
      direct/src/showutil/Packager.py

+ 171 - 13
direct/src/showutil/Packager.py

@@ -8,6 +8,7 @@ import os
 import glob
 import marshal
 import new
+from direct.showbase import Loader
 from direct.showutil import FreezeTool
 from direct.directnotify.DirectNotifyGlobal import *
 from pandac.PandaModules import *
@@ -31,10 +32,13 @@ class Packager:
             self.deleteTemp = deleteTemp
 
     class Package:
-        def __init__(self, packageName):
+        def __init__(self, packageName, packager):
             self.packageName = packageName
+            self.packager = packager
             self.version = 'dev'
             self.files = []
+            self.compressionLevel = 0
+            self.importedMapsDir = 'imported_maps'
 
             # This records the current list of modules we have added so
             # far.
@@ -52,31 +56,58 @@ class Packager:
             except OSError:
                 pass
             
-            multifile = Multifile()
-            multifile.openReadWrite(packageFilename)
+            self.multifile = Multifile()
+            self.multifile.openReadWrite(packageFilename)
 
-            sourceFilename = {}
-            targetFilename = {}
+            # Build up a cross-reference of files we've already
+            # discovered.
+            self.sourceFilenames = {}
+            self.targetFilenames = {}
             for file in self.files:
                 if not file.newName:
                     file.newName = file.filename
-                sourceFilename[file.filename] = file
-                targetFilename[file.newName] = file
+
+                # Convert the source filename to an unambiguous
+                # filename for searching.
+                filename = Filename(file.filename)
+                filename.makeCanonical()
+                
+                self.sourceFilenames[filename] = file
+                self.targetFilenames[file.newName] = file
 
             for file in self.files:
                 ext = file.filename.getExtension()
                 if ext == 'py':
                     self.addPyFile(file)
                 else:
-                    # An ordinary file.
-                    multifile.addSubfile(file.newName, file.filename, 0)
+                    if ext == 'pz':
+                        # Strip off an implicit .pz extension.
+                        filename = Filename(file.filename)
+                        filename.setExtension('')
+                        filename = Filename(filename.cStr())
+                        ext = filename.getExtension()
+
+                        filename = Filename(file.newName)
+                        if filename.getExtension() == 'pz':
+                            filename.setExtension('')
+                            file.newName = filename.cStr()
+
+                    if ext == 'egg':
+                        self.addEggFile(file)
+                    elif ext == 'bam':
+                        self.addBamFile(file)
+                    elif ext in self.packager.imageExtensions:
+                        self.addTexture(file)
+                    else:
+                        # An ordinary file.
+                        self.multifile.addSubfile(file.newName, file.filename, self.compressionLevel)
 
             # Pick up any unfrozen Python files.
             self.freezer.done()
-            self.freezer.addToMultifile(multifile)
+            self.freezer.addToMultifile(self.multifile)
 
-            multifile.repack()
-            multifile.close()
+            self.multifile.repack()
+            self.multifile.close()
 
             # Now that all the files have been packed, we can delete
             # the temporary files.
@@ -106,6 +137,122 @@ class Packager:
             self.freezer.addModule(moduleName, newName = moduleName,
                                    filename = file.filename)
 
+        def addEggFile(self, file):
+            # Precompile egg files to bam's.
+            np = self.packager.loader.loadModel(file.filename, okMissing = True)
+            if not np:
+                raise StandardError, 'Could not read egg file %s' % (file.filename)
+
+            bamName = Filename(file.newName)
+            bamName.setExtension('bam')
+            self.addNode(np.node(), bamName.cStr())
+
+        def addBamFile(self, file):
+            # Load the bam file so we can massage its textures.
+            bamFile = BamFile()
+            if not bamFile.openRead(file.filename):
+                raise StandardError, 'Could not read bam file %s' % (file.filename)
+
+            if not bamFile.resolve():
+                raise StandardError, 'Could not resolve bam file %s' % (file.filename)
+
+            node = bamFile.readNode()
+            if not node:
+                raise StandardError, 'Not a model file: %s' % (file.filename)
+
+            self.addNode(node, file.newName)
+
+        def addNode(self, node, filename):
+            """ Converts the indicated node to a bam stream, and adds the
+            bam file to the multifile under the indicated filename. """
+
+            # If the Multifile already has a file by this name, don't
+            # bother adding it again.
+            if self.multifile.findSubfile(filename) >= 0:
+                return
+
+            # Be sure to import all of the referenced textures, and tell
+            # them their new location within the multifile.
+
+            for tex in NodePath(node).findAllTextures():
+                if not tex.hasFullpath() and tex.hasRamImage():
+                    # We need to store this texture as a raw-data image.
+                    # Clear the filename so this will happen
+                    # automatically.
+                    tex.clearFilename()
+                    tex.clearAlphaFilename()
+
+                else:
+                    # We can store this texture as a file reference to its
+                    # image.  Copy the file into our multifile, and rename
+                    # its reference in the texture.
+                    if tex.hasFilename():
+                        tex.setFilename(self.addFoundTexture(tex.getFullpath()))
+                    if tex.hasAlphaFilename():
+                        tex.setAlphaFilename(self.addFoundTexture(tex.getAlphaFullpath()))
+
+            # Now generate an in-memory bam file.  Tell the bam writer to
+            # keep the textures referenced by their in-multifile path.
+            bamFile = BamFile()
+            stream = StringStream()
+            bamFile.openWrite(stream)
+            bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged)
+            bamFile.writeObject(node)
+            bamFile.close()
+
+            # Clean the node out of memory.
+            node.removeAllChildren()
+
+            # Now we have an in-memory bam file.
+            stream.seekg(0)
+            self.multifile.addSubfile(filename, stream, self.compressionLevel)
+
+            # Flush it so the data gets written to disk immediately, so we
+            # don't have to keep it around in ram.
+            self.multifile.flush()
+
+        def addFoundTexture(self, filename):
+            """ Adds the newly-discovered texture to the output, if it has
+            not already been included.  Returns the new name within the
+            package tree. """
+
+            assert not filename.isLocal()
+
+            filename = Filename(filename)
+            filename.makeCanonical()
+
+            file = self.sourceFilenames.get(filename, None)
+            if file:
+                # Never mind, it's already on the list.
+                return file.newName
+
+            # We have to copy the image into the plugin tree somewhere.
+            newName = self.importedMapsDir + '/' + filename.getBasename()
+            uniqueId = 0
+            while newName in self.targetFilenames:
+                uniqueId += 1
+                newName = '%s/%s_%s.%s' % (
+                    self.importedMapsDir, filename.getBasenameWoExtension(),
+                    uniqueId, filename.getExtension())
+
+            file = Packager.PackFile(filename, newName = newName)
+            self.sourceFilenames[filename] = file
+            self.targetFilenames[newName] = file
+            self.addTexture(file)
+
+            return newName
+
+        def addTexture(self, file):
+            """ Adds a texture image to the output. """
+
+            if self.multifile.findSubfile(file.newName) >= 0:
+                # Already have this texture.
+                return
+
+            # Texture file formats are generally already compressed and
+            # not further compressible.
+            self.multifile.addSubfile(file.newName, file.filename, 0)
+
     def __init__(self):
 
         # The following are config settings that the caller may adjust
@@ -141,6 +288,17 @@ class Packager:
         # are server-side only and should be ignored by the Scrubber.
         self.dcClientSuffixes = ['OV']
 
+        # Get the list of filename extensions that are recognized as
+        # image files.
+        self.imageExtensions = []
+        for type in PNMFileTypeRegistry.getGlobalPtr().getTypes():
+            self.imageExtensions += type.getExtensions()
+
+        # A Loader for loading models.
+        self.loader = Loader.Loader(self)
+        self.sfxManagerList = None
+        self.musicManager = None
+
     def setup(self):
         """ Call this method to initialize the class after filling in
         some of the values in the constructor. """
@@ -337,7 +495,7 @@ class Packager:
         basename of the package.  Follow this with a number of calls
         to file() etc., and close the package with endPackage(). """
         
-        package = self.Package(packageName)
+        package = self.Package(packageName, self)
         if self.currentPackage:
             package.freezer.excludeFrom(self.currentPackage.freezer)