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 glob
 import marshal
 import marshal
 import new
 import new
+from direct.showbase import Loader
 from direct.showutil import FreezeTool
 from direct.showutil import FreezeTool
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.directnotify.DirectNotifyGlobal import *
 from pandac.PandaModules import *
 from pandac.PandaModules import *
@@ -31,10 +32,13 @@ class Packager:
             self.deleteTemp = deleteTemp
             self.deleteTemp = deleteTemp
 
 
     class Package:
     class Package:
-        def __init__(self, packageName):
+        def __init__(self, packageName, packager):
             self.packageName = packageName
             self.packageName = packageName
+            self.packager = packager
             self.version = 'dev'
             self.version = 'dev'
             self.files = []
             self.files = []
+            self.compressionLevel = 0
+            self.importedMapsDir = 'imported_maps'
 
 
             # This records the current list of modules we have added so
             # This records the current list of modules we have added so
             # far.
             # far.
@@ -52,31 +56,58 @@ class Packager:
             except OSError:
             except OSError:
                 pass
                 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:
             for file in self.files:
                 if not file.newName:
                 if not file.newName:
                     file.newName = file.filename
                     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:
             for file in self.files:
                 ext = file.filename.getExtension()
                 ext = file.filename.getExtension()
                 if ext == 'py':
                 if ext == 'py':
                     self.addPyFile(file)
                     self.addPyFile(file)
                 else:
                 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.
             # Pick up any unfrozen Python files.
             self.freezer.done()
             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
             # Now that all the files have been packed, we can delete
             # the temporary files.
             # the temporary files.
@@ -106,6 +137,122 @@ class Packager:
             self.freezer.addModule(moduleName, newName = moduleName,
             self.freezer.addModule(moduleName, newName = moduleName,
                                    filename = file.filename)
                                    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):
     def __init__(self):
 
 
         # The following are config settings that the caller may adjust
         # 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.
         # are server-side only and should be ignored by the Scrubber.
         self.dcClientSuffixes = ['OV']
         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):
     def setup(self):
         """ Call this method to initialize the class after filling in
         """ Call this method to initialize the class after filling in
         some of the values in the constructor. """
         some of the values in the constructor. """
@@ -337,7 +495,7 @@ class Packager:
         basename of the package.  Follow this with a number of calls
         basename of the package.  Follow this with a number of calls
         to file() etc., and close the package with endPackage(). """
         to file() etc., and close the package with endPackage(). """
         
         
-        package = self.Package(packageName)
+        package = self.Package(packageName, self)
         if self.currentPackage:
         if self.currentPackage:
             package.freezer.excludeFrom(self.currentPackage.freezer)
             package.freezer.excludeFrom(self.currentPackage.freezer)