# # File:: content_map.rb # Description:: Content system map content tree node class implementation for a given map. # # Author:: Luke Openshaw # Author:: David Muir # Date:: 22 July 2008 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'pipeline/content/content_core' require 'pipeline/log/log' require 'rexml/document' include REXML module Pipeline module Content # # == Description # Define a map content module for common functionality. Really added # so we can get logging information about why this process takes so # damn long. # module ContentMap # Return our static ContentMaps log object. def ContentMap::log() @@log = Pipeline::Log.new( 'content_maps' ) @@log end private @@log = nil end # # # class MapComponent < Group attr_reader :p attr_reader :mapname attr_reader :stream_src attr_reader :stream_dst XML_CONTENT_TYPE = 'mapcomponent' def initialize( name, srcfile, project, mapname, stream_src, stream_dst ) super(name) @p = project @mapname = mapname @stream_src = stream_src @stream_dst = stream_dst if ::File.exist?(srcfile) ::File.open( srcfile ) { |sourcefile| sourcedoc = Document.new( sourcefile ) populate_lists( sourcedoc ) } end flush_stream() end def populate_lists( sourcedoc ) ContentMap::log().info( 'No method implemented for list population for this map component content') end # # # def build() ContentMap::log().info( 'No build method implemented for this map component.' ) end # # # def flush_stream() # Flush the stream. This happens in maxscript at the moment, consider moving end end # # # class MapAnim < MapComponent attr_reader :anim_list attr_reader :relative_path XML_CONTENT_TYPE = 'mapanim' # # # def initialize( srcfile, project, mapname, generic ) @anim_list = Array.new() @relative_path = "anim" stream_path = OS::Path.combine(project.netstream, "maps") super( "mapanim", srcfile, project, mapname, stream_path, stream_path ) end # # # def populate_lists( sourcedoc ) sourcedoc.root.each_element do | elem | @anim_list << elem.name end end # # # def build() ContentMap::log().info( "Building animation content: #{@anim_list.size}." ) build_anim_content() if @anim_list.size > 0 end # # # def build_anim_content() pack_content = Zip::new(@mapname, OS::Path.combine(@stream_dst, @mapname), "icd.zip", @p.ind_target, false ) project = Pipeline::Config.instance.project src_dir = OS::Path.combine(project.assets, "maps/anims", @mapname) @anim_list.each do | anim_file | # Only add the inputs where both the anim and the clip exist if ::File.exist?( OS::Path.combine( src_dir, anim_file + ".anim" ) ) and ::File.exist?( OS::Path.combine( src_dir, anim_file + ".clip" ) ) pack_content.add_input( File.new( anim_file, src_dir, "anim" ) ) pack_content.add_input( File.new( anim_file, src_dir, "clip" ) ) end end add_child( pack_content ) end end # # # class MapBounds < MapComponent attr_reader :bound_dict_list attr_reader :hidetail_bound_dict_list attr_reader :ext attr_reader :relative_path attr_reader :static XML_CONTENT_TYPE = 'mapbound' # # # def initialize( srcfile, project, mapname, relative_path, ext, generic, static ) @ext = ext @relative_path = relative_path @bound_dict_list = Hash.new() @hidetail_bound_dict_list = Hash.new() @static = static @mapzip_content_node = project.content.find_first( mapname, 'mapzip' ) stream_path = OS::Path.combine(project.netstream, "maps") super( "mapbounds", srcfile, project, mapname, stream_path, stream_path ) end # # # def populate_lists( sourcedoc ) if true == @mapzip_content_node.viaboundsprocessor then bounds = Array.new() sourcedoc.root.each_element do | elem |#for each item in the source xml bounds << elem.name end @bound_dict_list[mapname] = bounds#add the bounds files to the zip else#old school bounds resourcing if false == @static then sourcedoc.root.each_element do | elem | if elem.elements.size > 0 then bounds = Array.new() elem.each_element do | bound_elem | bounds << bound_elem.name end @bound_dict_list[elem.name] = bounds end end else sourcedoc.root.each_element do | elem | if elem.elements.size > 0 then case elem.name when "standard" elem.each_element do | bound_elem | bounds = Array.new() bound_elem.each_element do | subbound_elem | bounds << bound_elem.name end @bound_dict_list[bound_elem.name] = bounds end when "hidetail" elem.each_element do | bound_elem | bounds = Array.new() bound_elem.each_element do | subbound_elem | bounds << bound_elem.name end @hidetail_bound_dict_list[bound_elem.name] = bounds end end end end end end end # # # def build() ContentMap::log().info( "Building bound content: #{@bound_dict_list.size}." ) build_bound_content() if @bound_dict_list.size > 0 end # # # def build_bound_content() @bound_dict_list.each do | bound_dict | bound_dict_name = bound_dict[0] bound_list = bound_dict[1] if true == @mapzip_content_node.viaboundsprocessor then pack_content = Zip::new(bound_dict_name + "_collision", OS::Path.combine(@stream_dst, mapname), "zip", @p.ind_target, false ) else pack_content = Zip::new(bound_dict_name, OS::Path.combine(@stream_dst, mapname), "#{@ext}.zip", @p.ind_target, false ) end src_dir = "" if ( false == @static ) then if true == @mapzip_content_node.viaboundsprocessor then src_dir = OS::Path.combine(@stream_src, @mapname, "/bound_dynamic/") else src_dir = OS::Path.combine(@stream_src, @mapname, OS::Path.combine("/bound_dynamic/", bound_dict_name)) end else src_dir = OS::Path.combine(@stream_src, @mapname, @relative_path) end bound_list.each do | bound | pack_content.add_input( File.new( bound, src_dir, "bnd" ) ) end add_child( pack_content ) end @hidetail_bound_dict_list.each do | bound_dict | bound_dict_name = "hi@" + bound_dict[0] bound_list = bound_dict[1] pack_content = Zip::new(bound_dict_name, OS::Path.combine(@stream_dst, mapname), "#{@ext}.zip", @p.ind_target, false ) src_dir = OS::Path.combine(@stream_src, @mapname, @relative_path) bound_list.each do | bound | pack_content.add_input( File.new( bound, src_dir, "bnd" ) ) end add_child( pack_content ) end end end # # MapOcclusion content node is used to gather and process all occlusion # meshes and process them in the pipeline. # class MapOcclusion < MapComponent XML_CONTENT_TYPE = 'mapocclusion' attr_reader :filename attr_reader :occlusion_xml_list # Output XML list for IMAP serialising. attr_reader :occlusion_mesh_list # Input .mesh list from exporter. def initialize( srcfile, project, mapname, cacheFolder) @occlusion_xml_list = [] @occlusion_mesh_list = [] stream_src = OS::Path::combine( cacheFolder, 'raw', 'maps', mapname, 'occlusion' ) stream_dst = OS::Path::combine( cacheFolder, 'maps', 'dev', mapname ) puts "stream_src: #{stream_src}" puts "stream_dst: #{stream_dst}" super( 'mapocclusion', srcfile, project, mapname, stream_src, stream_dst ) @filename = OS::Path::combine( @stream_dst, "#{@mapname}.occl.zip" ) end # Parse XML occlusion to populate internal list. def populate_lists( sourcedoc ) puts "populate_lists hit" sourcedoc.elements.each( '/occlusion/occludeMeshes/*' ) do |elem| @occlusion_mesh_list << OS::Path::combine( @stream_src, "#{elem.name}.mesh" ) puts "stream_src: #{stream_src}" puts ".mesh: #{elem.name}" end # Even although the occludeBoxes are mentioned we don't do # any processing here; thats all handled in the IMAP serialiser. end # Build occlusion ZIP data for this map. def build( ) ContentMap::log().info( "Building occlusion content: #{@occlusion_mesh_list.size}." ) build_occlusion_content( ) if ( @occlusion_mesh_list.size() > 0 ) end #-------------------------------------------------------------------- # Private #-------------------------------------------------------------------- private def build_occlusion_content( ) @occlusion_xml_list = [] @occlusion_mesh_list.each do |occlusion_file| output_file = OS::Path::combine( @stream_src, "#{OS::Path::get_basename( occlusion_file )}.xml" ) if ( occludermesh_convert( occlusion_file, output_file ) ) then @occlusion_xml_list << output_file end end pack_content = Zip::new( @mapname, @stream_dst, "occl.zip", @p.ind_target, false ) if ( @occlusion_xml_list.size > 0 ) then @occlusion_xml_list.each do |occlusion_file| basename = OS::Path::get_basename( occlusion_file ) pack_content.add_input( File::new( basename, @stream_src, 'xml' ) ) end else ContentMap::log().error( "Occlusion list is empty!" ) end add_child( pack_content ) end # Convert .mesh to .xml def occludermesh_convert( infile, outfile ) puts "infile: #{infile}" puts "outfile: #{outfile}" c = Pipeline::Config::instance() branch = @p.branches[@p.default_branch] projtools = RageConvertTool::parse( @p, branch.name ) projtool = projtools['win32'] if ( projtools.has_key?( 'win32' ) ) projtool = projtools['win64'] if ( projtools.has_key?( 'win64' ) ) options = "#{OS::Path::combine( c.toolslib, 'util', 'ragebuilder', 'convert_occluders.rbs' )}" options += " -nopopups -build #{branch.build}" options += " -shader #{branch.shaders} -shaderdb #{OS::Path::combine(branch.shaders, 'db')}" options += " #{projtool.options}" options += " -input #{infile}" options += " -output #{outfile}" ContentMap::log().info( "Occluder mesh processing: '#{projtool.path} #{options}'." ) system( "#{projtool.path} #{options}" ) end end # # == Description # Map texture dictionary content node. # class MapTxd < MapComponent attr_reader :txd_list attr_reader :force_add_textures_list XML_CONTENT_TYPE = 'maptxd' # # # def initialize(srcfile, project, mapname, generic ) @txd_list = Hash.new() @force_add_textures_list = Hash.new() stream_path = OS::Path.combine(project.netstream, "maps") super( "maptxd", srcfile, project, mapname, OS::Path.combine(project.assets, "maps/textures"), stream_path ) end # # # def populate_lists( sourcedoc) sourcedoc.root.each_element do | elem | if nil!=elem.attributes["forceAddTexture"] @force_add_textures_list[elem.name] = true else @force_add_textures_list[elem.name] = false end if elem.elements.size > 0 textures = Array.new() elem.each_element do | tex_elem | path = tex_elem.attributes["path"] if path != nil tex_name = OS::Path.combine( path, tex_elem.name ) else tex_name = OS::Path.combine( @stream_src, tex_elem.name ) end textures << tex_name end @txd_list[elem.name] = textures end end end # # Build the map component. # def build() ContentMap::log().info( "Building texture dictionary content: #{@txd_list.size}." ) build_texture_dict_content() if @txd_list.size > 0 end #---------------------------------------------------------------- # Private #---------------------------------------------------------------- private # # Build the texture dictionary. # def build_texture_dict_content( ) @txd_list.each do |txd| txd_name = txd[0] tex_list = txd[1] path = OS::Path::combine( @stream_dst, @mapname ) zip_content = Zip::new( txd_name, path, 'itd.zip', @p.ind_target, false ) tex_list.each do |tex_file| next unless OS::Path.get_extension(tex_file) == "dds" #scoop up the related tcl file tex = OS::Path.get_basename( tex_file ) tcl_file = OS::Path.combine( @p.netstream, "maps/tcls", tex + ".tcl" ) zip_content.add_input( Content::File::from_filename( tcl_file ) ) if ::File.exist?( tcl_file ) if @force_add_textures_list[txd_name] # JWR - Eventually we will just pump out TCLs here as the TCS file will contain texture linkage information # GD - Well eventually this will hopefully go awa when we sorted ragebuilder... zip_content.add_input( File::from_filename( tex_file ) ) end end add_child( zip_content ) end end end # # == Description # Map geometry (drawables, fragments) content node. # class MapGeo < MapComponent attr_reader :geo_big_list # LODs and other large geom attr_reader :geo_list # LPXO: Think this will be deprecated by the time im finished attr_reader :frag_list # Fragments attr_reader :geo_tex_list # Geom with textures attr_reader :geo_tex_packed_list # Geom with packed textures attr_reader :frag_tex_packed_list # Frag with packed textures XML_CONTENT_TYPE = 'mapgeo' # # # def initialize( srcfile, project, mapname, generic, stream_subdir ) @geo_big_list = Hash.new() @geo_list = Array.new() @frag_list = Array.new() @geo_tex_list = Array.new() @geo_tex_packed_list = Hash.new() @frag_tex_packed_list = Hash.new() stream_path = OS::Path.combine(project.netstream, stream_subdir) super( "mapgeo", srcfile, project, mapname, stream_path, stream_path ) end # # # def build() ContentMap::log().info( "Building fragment content: #{@frag_list.size}." ) build_fragment_content() if @frag_list.size > 0 ContentMap::log().info( "Building geometry content: #{@geo_tex_list.size}." ) build_geo_with_tex_content() if @geo_tex_list.size > 0 ContentMap::log().info( "Building big geometry content: #{@geo_big_list.size}." ) build_geo_big_content() if @geo_big_list.size > 0 end # # # def populate_lists( sourcedoc ) sourcedoc.root.each_element do | elem | case elem.name when 'geo' @geo_list << elem.name when 'geotex' elem.each_element do | geo_elem | @geo_tex_list << geo_elem.name end when 'geotextex' elem.each_element do | geo_elem | if geo_elem.elements.size > 0 packed_texs = Array.new() geo_elem.each_element do | sub_elem | if ( sub_elem.attributes['path'].nil? ) then packed_texs << sub_elem.name else # Has path attribute; fix for tint palettes. # See Bug #262824. packed_texs << OS::Path::combine( sub_elem.attributes['path'], sub_elem.name ) end end @geo_tex_packed_list[geo_elem.name] = packed_texs end end when 'geobig' elem.each_element do | group_elem | if group_elem.elements.size > 0 big_geos = Array.new() group_elem.each_element do | big_geo | big_geos << big_geo.name end @geo_big_list[group_elem.name] = big_geos end end when 'frags' elem.each_element do | frag | @frag_list << frag.name end when 'fragtex' elem.each_element do | frag_elem | if frag_elem.elements.size > 0 packed_texs = Array.new() frag_elem.each_element do | sub_elem | if ( sub_elem.attributes['path'].nil? ) then packed_texs << sub_elem.name else packed_texs << OS::Path::combine( sub_elem.attributes['path'], sub_elem.name ) end end @frag_tex_packed_list[frag_elem.name] = packed_texs end end end end end #---------------------------------------------------------------- # Private #---------------------------------------------------------------- private def pack_textures(packed_texs, zip_content) packed_texs.each do | tex | tex_file = '' tcl_file = '' if ( tex.include?( ':' ) ) then # Have absolute path; probably from tint palette bug fix. tex_file = "#{tex}.dds" tcl_file = OS::Path::combine( @p.netstream, "maps/tcls", OS::Path::get_basename( tex ) + ".tcl" ) else tex_file = OS::Path.combine( @p.assets, "maps/textures", tex + ".dds" ) tcl_file = OS::Path.combine( @p.netstream, "maps/tcls", tex + ".tcl" ) end zip_content.add_input( Content::File::from_filename( tex_file ) ) if ::File.exist?( tex_file ) zip_content.add_input( Content::File::from_filename( tcl_file ) ) if ::File.exist?( tcl_file ) end end # Build fragment files. def build_fragment_content() fragments = Group.new( "fragments") @frag_list.each do | frag_name | zip_content = Zip::new( frag_name, OS::Path.combine(@stream_dst, @mapname), "ift.zip", @p.ind_target, false ) frag_dir = OS::Path.combine(@stream_src, @mapname, frag_name) filelist = OS::FindEx.find_files(frag_dir + "/*.*", false) filelist.each do | file | zip_content.add_input( File::from_filename( file ) ) \ if ::File.exist?( file ) end # Handle packed textures. packed_texs = @frag_tex_packed_list[frag_name] if ( packed_texs != nil ) then pack_textures(packed_texs, zip_content) end fragments.add_child( zip_content ) unless ( zip_content.nil? ) end add_child( fragments ) end # Build drawables with packed textures. def build_geo_with_tex_content() geo_with_texs = Group.new( "geo_with_texs") @geo_tex_list.each_with_index do |geo_name, index| ContentMap::log().info( " Building geometry: #{geo_name} [#{index}/#{@geo_tex_list.size}]" ) time_start = Time.now() zip_content = Zip::new( geo_name, OS::Path.combine(@stream_dst, @mapname), "idr.zip", @p.ind_target, false ) geo_dir = OS::Path.combine(@stream_src, @mapname, geo_name) filelist = OS::FindEx.find_files(geo_dir + "/*.*", false) # Handle the actual drawable data. filelist.each do |file| zip_content.add_input( Content::File::from_filename( file ) ) \ if ::File.exist?( file ) end # Handle packed textures. packed_texs = @geo_tex_packed_list[geo_name] if ( packed_texs != nil ) then pack_textures(packed_texs, zip_content) end geo_with_texs.add_child( zip_content ) unless ( zip_content.nil? ) ContentMap::log().info( " Built geometry: #{geo_name} [#{index}/#{@geo_tex_list.size}] [#{(Time.now() - time_start).to_s( 3 )}s]" ) end add_child( geo_with_texs ) end # def build_geo_big_content() geo_big = Group.new("geo_big") @geo_big_list.each do | geo_group_name | pack_content = Zip::new(geo_group_name[0], OS::Path.combine(@stream_dst, @mapname), 'idd.zip', @p.ind_target, false ) geo_list = geo_group_name[1] geo_list.each do | geo | geo_dir = OS::Path.combine( @stream_src, @mapname, geo ) filelist = OS::FindEx.find_files(geo_dir + "/*.*", false) geo_group = Group.new( geo, geo ) filelist.each do | file | geo_group.add_child( File.new( OS::Path.get_basename( file ), OS::Path.get_directory( file ), OS::Path.get_extension( file ) ) ) if ::File.exist?( file ) end pack_content.add_input( geo_group ) if geo_group.children.size > 0 end geo_big.add_child(pack_content) if pack_content != nil end add_child( geo_big ) end end end # module Content end # module Pipeline