718 lines
21 KiB
Ruby
Executable File
718 lines
21 KiB
Ruby
Executable File
#
|
|
# File:: scenexml.rb
|
|
# Description:: SceneXml 3dsmax exporter XML file loader.
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# Date:: 30 October 2008
|
|
#
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Uses
|
|
#-----------------------------------------------------------------------------
|
|
require 'pipeline/fileformats/scenexml_attrs'
|
|
require 'pipeline/log/log'
|
|
require 'pipeline/math/bbox'
|
|
require 'pipeline/math/bsphere'
|
|
require 'pipeline/math/quat'
|
|
require 'pipeline/math/vector3'
|
|
require 'pipeline/math/vector4'
|
|
require 'pipeline/math/matrix34'
|
|
require 'xml'
|
|
require 'uuidtools'
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Implementation
|
|
#-----------------------------------------------------------------------------
|
|
module Pipeline
|
|
module FileFormats
|
|
|
|
#
|
|
# == Description
|
|
# The SceneXml module contains all of the SceneXml related classes,
|
|
# including the XML parsers and in-memory SceneXml tree classes.
|
|
#
|
|
# The module is expanded in the scenexml_ide.rb and scenexml_ipl.rb files
|
|
# to include IDE and IPL file serialisers for map export.
|
|
#
|
|
module SceneXml
|
|
# Loader version.
|
|
VERSION = 1.20
|
|
|
|
#
|
|
# == Description
|
|
# SceneXml scene object. This class is an abstraction of the 3dsmax
|
|
# scene information written by the SceneXml exporter, in XML format.
|
|
#
|
|
# The scene information is quite limited but should include enough to
|
|
# be useful for validation and IDE/IPL creation.
|
|
#
|
|
class Scene
|
|
WALK_MODE_DEPTH_FIRST = 0
|
|
WALK_MODE_BREADTH_FIRST = 1
|
|
|
|
attr_reader :filename
|
|
attr_reader :version
|
|
attr_reader :export_filename
|
|
attr_reader :export_timestamp
|
|
attr_reader :export_user
|
|
attr_reader :objects
|
|
attr_reader :materialdb
|
|
|
|
def initialize( filename )
|
|
Scene::log().info( "Loading SceneXml from #{filename}." )
|
|
@filename = filename
|
|
@lookup_table = {}
|
|
parse( )
|
|
build_object_lookup_table( )
|
|
end
|
|
|
|
#
|
|
# Add an object reference (UUID and ObjectDef).
|
|
#
|
|
def add_object( obj )
|
|
@lookup_table[obj.guid] = obj
|
|
end
|
|
|
|
#
|
|
# Find an object by UUID reference.
|
|
#
|
|
def find_object( uuid )
|
|
return nil unless ( @lookup_table.has_key?( uuid ) )
|
|
@lookup_table[uuid]
|
|
end
|
|
|
|
#
|
|
# Walk down a ScemeXml::Scene object definitions invoking the user-
|
|
# defined code block with the current object and its parent (nil if
|
|
# its the root).
|
|
#
|
|
# The user should not supply a value for the parent parameter.
|
|
#
|
|
# === Example Usage
|
|
# scene.walk( SceneXml::Scene::WALK_MODE_DEPTH_FIRST ) do |node, parent|
|
|
# puts "Node: #{node.name}, class: #{node.class}"
|
|
# end
|
|
#
|
|
def walk( mode = WALK_MODE_DEPTH_FIRST, &block )
|
|
|
|
@objects.each do |node|
|
|
# Invoke parent-less node.
|
|
yield( node ) if ( block_given? )
|
|
# Recurse
|
|
walk_internal( mode, node, &block )
|
|
end
|
|
end
|
|
|
|
def Scene::log()
|
|
@@log = Pipeline::Log.new( 'scenexml' ) if ( @@log.nil? )
|
|
@@log
|
|
end
|
|
|
|
#---------------------------------------------------------------------
|
|
# Protected
|
|
#---------------------------------------------------------------------
|
|
protected
|
|
@@log = nil
|
|
|
|
#---------------------------------------------------------------------
|
|
# Private
|
|
#---------------------------------------------------------------------
|
|
private
|
|
#
|
|
# Create an internal Hash to provide lookup services for ObjectDef
|
|
# objects. This should make resolving ObjectDef links quicker.
|
|
#
|
|
def build_object_lookup_table( )
|
|
Scene::log().info( "Building SceneXml object lookup table." )
|
|
start = Time.now()
|
|
walk() do |object|
|
|
# Complete LODHierarchy references
|
|
object.lods.resolve_guids( self ) unless ( object.lods.nil? )
|
|
end
|
|
Scene::log().debug( " LOD resolution time: #{Time.now()-start}s." )
|
|
end
|
|
|
|
def walk_internal( mode, node, &block )
|
|
|
|
case mode
|
|
when WALK_MODE_DEPTH_FIRST
|
|
# Process children, depth first by processing their children
|
|
# before moving onto neighbour.
|
|
node.children.each do |child_node|
|
|
# Invoke child node.
|
|
yield( child_node )
|
|
walk_internal( mode, child_node, &block )
|
|
end
|
|
when WALK_MODE_BREADTH_FIRST
|
|
# Process children, breadth first by processing all children
|
|
# first before then recursing through children.
|
|
node.children.each do |child_node|
|
|
# Invoke child node.
|
|
yield( child_node )
|
|
end
|
|
node.children.each do |child_node|
|
|
walk_internal( mode, child_node, &block )
|
|
end
|
|
else
|
|
throw RuntimeError.new( "Invalid walk mode #{mode}." )
|
|
end
|
|
end
|
|
|
|
def parse( )
|
|
|
|
begin
|
|
start = Time.now( )
|
|
xmldoc = LibXML::XML::Document::file( @filename )
|
|
Scene::log().info( "LibXML2 XML parse took: #{Time.now()-start}s." )
|
|
raise RuntimeError.new( "Libxml2 XML parse fail." ) \
|
|
if ( xmldoc.nil? )
|
|
|
|
@version = xmldoc.root['version'].to_f
|
|
if ( SceneXml::VERSION != @version ) then
|
|
Scene::log().warn( "Version numbers differ, some scene information may be lost (#{@version}, #{SceneXml::VERSION})." )
|
|
end
|
|
|
|
@export_filename = xmldoc.root['filename']
|
|
@export_timestamp = DateTime::parse( xmldoc.root['timestamp'] )
|
|
@export_user = xmldoc.root['user']
|
|
start = Time.now( )
|
|
@objects = parse_objects( xmldoc )
|
|
Scene::log().info( "Scene object parse took: #{Time.now()-start}s." )
|
|
start = Time.now( )
|
|
@materialdb = SceneXml::MaterialDB::from_xml( xmldoc )
|
|
Scene::log().info( "Scene material parse took: #{Time.now()-start}s." )
|
|
xmldoc = nil
|
|
|
|
rescue Exception => ex
|
|
Scene::log().exception( ex, "Exception parsing SceneXml #{@filename}" )
|
|
|
|
puts "Unhandled exception parsing SceneXml: #{@filename} #{ex.message}."
|
|
ex.backtrace.each do |m| Scene::log().error( m ); end
|
|
throw
|
|
end
|
|
end
|
|
|
|
#
|
|
# Parse all object information, returning an Array of ObjectDef objects.
|
|
#
|
|
def parse_objects( xmldoc )
|
|
|
|
@objects = []
|
|
xmldoc.find( '//scene/objects/object' ).each do |xml_node|
|
|
object = SceneXml::ObjectDef::from_xml( self, xml_node )
|
|
add_object( object )
|
|
@objects << object
|
|
end
|
|
@objects
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# SceneXml object definition.
|
|
#
|
|
class ObjectDef
|
|
|
|
#----------------------------------------------------------------
|
|
# Attribute Accessor Methods
|
|
#----------------------------------------------------------------
|
|
attr_reader :name
|
|
attr_reader :guid # GUID
|
|
attr_reader :parent
|
|
attr_reader :classname # 3dsmax classname (friendly)
|
|
attr_reader :superclass # 3dsmax superclass (friendly)
|
|
attr_reader :boundingbox
|
|
attr_reader :transform # TransformDef object
|
|
attr_reader :attribute_class
|
|
attr_reader :attributes # Hash of AttributeDef objects/
|
|
attr_reader :material # Associated material GUID
|
|
attr_reader :subobjects # Array of subobject ObjectDefs.
|
|
attr_reader :children # Array of children ObjectDefs.
|
|
attr_reader :lods # LODHierarchyDef object
|
|
attr_accessor :reffile
|
|
attr_accessor :refname
|
|
|
|
#----------------------------------------------------------------
|
|
# Virtual Attributes
|
|
#----------------------------------------------------------------
|
|
def parent=( obj )
|
|
throw RuntimeError.new( "Cannot set parent as its already been set." ) \
|
|
unless ( @parent.nil? )
|
|
throw ArgumentError.new( "Invalid parent object, must be ObjectDef (#{obj.class})." ) \
|
|
unless ( obj.is_a?( SceneXml::ObjectDef ) )
|
|
|
|
@parent = obj
|
|
end
|
|
|
|
#----------------------------------------------------------------
|
|
# Instance Methods
|
|
#----------------------------------------------------------------
|
|
def initialize( guid,
|
|
name,
|
|
_class,
|
|
_superclass,
|
|
bbox,
|
|
transform,
|
|
attrs_class,
|
|
attrs,
|
|
params,
|
|
props,
|
|
subobjects,
|
|
children,
|
|
lods )
|
|
#throw ArgumentError.new( "Invalid UUID (#{name}, #{guid.class})." ) \
|
|
# unless ( gui.nil? or guid.is_a?( UUID ) )
|
|
#throw ArgumentError.new( "Invalid transform (#{name}, #{transform.class})." ) \
|
|
# unless ( transform.nil? or transform.is_a?( TransformDef ) )
|
|
#throw ArgumentError.new( "Invalid bounding box object (#{name}, #{bbox.class})." ) \
|
|
# unless ( bbox.nil? or bbox.is_a?( Math::BoundingBox3 ) )
|
|
#throw ArgumentError.new( "Invalid attribute class (#{name}, #{attrs_class})." ) \
|
|
# if ( attrs_class.nil? and attrs.size > 0 )
|
|
#throw ArgumentError.new( "Invalid LOD hierarchy class (#{name}, #{lods.class})." ) \
|
|
# unless ( lods.nil? or lods.is_a?( LODHierarchyDef ) )
|
|
|
|
@name = name
|
|
@guid = guid
|
|
@parent = nil
|
|
@classname = _class
|
|
@superclass = _superclass
|
|
@boundingbox = bbox
|
|
@transform = transform
|
|
@attribute_class = attrs_class.nil? ? '' : attrs_class.downcase
|
|
@attributes = attrs
|
|
@parameters = params
|
|
@properties = props
|
|
@subobjects = subobjects
|
|
@children = children
|
|
@lods = lods
|
|
end
|
|
|
|
|
|
#
|
|
# Return the object name to be used in the IDE/IPL file. This is object
|
|
# class dependent.
|
|
#
|
|
def get_object_name( )
|
|
|
|
if ( is_xref? or is_internalref? ) then
|
|
return @refname
|
|
|
|
elsif ( is_milotri? )
|
|
@name.sub( '_milo_', '' )
|
|
|
|
elsif ( is_fragment? ) then
|
|
@name.sub( '_frag_', '' )
|
|
|
|
elsif ( is_2dfx? ) then
|
|
if ( is_2dfx_light_effect? and not @parent.nil? )
|
|
@parent.name
|
|
elsif ( is_2dfx_particle_effect? and not @parent.nil? )
|
|
@parent.name
|
|
elsif ( is_2dfx_explosion_effect? and not @parent.nil? )
|
|
@parent.name
|
|
elsif ( is_2dfx_swayable_effect? and not @parent.nil? )
|
|
@parent.name
|
|
end
|
|
return @name unless ( @parent.nil? )
|
|
return "Scene Root" if ( @parent.nil? )
|
|
|
|
else
|
|
@name
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return whether or not this object is a LOD child.
|
|
#
|
|
# An object is considered a LOD if it has a LOD parent as defined
|
|
# in its LODHierarchyDef structure.
|
|
#
|
|
def is_lod?( )
|
|
return ( false ) unless ( @lods )
|
|
return ( false ) if ( @lods.parent_guid.nil? )
|
|
true
|
|
end
|
|
|
|
#
|
|
# Return an ObjectDef reference (or nil) for the LOD parent of
|
|
# this ObjectDef.
|
|
#
|
|
def get_lod_parent( )
|
|
return nil unless ( is_lod? )
|
|
@lods.parent
|
|
end
|
|
|
|
#
|
|
# Return an Array of ObjectDef references for the LOD children of
|
|
# this ObjectDef.
|
|
#
|
|
def get_lod_children( )
|
|
return [] if ( @lods.nil? )
|
|
return [] unless ( @lods.children.is_a?( Array ) )
|
|
return [] unless ( @lods.children.size > 0 )
|
|
|
|
@lods.children
|
|
end
|
|
|
|
#----------------------------------------------------------------
|
|
# Class Methods
|
|
#----------------------------------------------------------------
|
|
|
|
#
|
|
# Parse a REXML::Element, creating a ObjectDef instance.
|
|
#
|
|
def ObjectDef::from_xml( scene, xml_node )
|
|
#raise RuntimeError.new( "Invalid object node, name: #{xml_node.name}." ) \
|
|
# unless ( 'object' == xml_node.name )
|
|
|
|
guid = nil
|
|
guid = UUID::parse( xml_node['guid'].gsub( /[{|}]/, '' ) ) unless ( xml_node['guid'].nil? )
|
|
name = xml_node['name']
|
|
_class = xml_node['class']
|
|
_superclass = xml_node['superclass']
|
|
refname = xml_node['refname']
|
|
reffile = xml_node['reffile']
|
|
|
|
bbox_node = xml_node.find_first( 'boundingbox3' )
|
|
bbox = Math::BoundingBox3::from_xml( bbox_node ) unless ( bbox_node.nil? )
|
|
|
|
transform_node = xml_node.find_first( 'transform' )
|
|
transform = nil
|
|
transform = TransformDef::from_xml( transform_node ) unless ( transform_node.nil? )
|
|
|
|
attributes_node = xml_node.find_first( 'attributes' )
|
|
attributes = {}
|
|
attributes_class = nil
|
|
begin
|
|
attributes_class = attributes_node['class']
|
|
xml_node.find( 'attributes/attribute' ).each do |attr_node|
|
|
attr = AttributeDef::from_xml( attr_node )
|
|
attributes[attr.name] = attr
|
|
end
|
|
end unless ( attributes_node.nil? )
|
|
|
|
params = {}
|
|
params_node = xml_node.find_first( 'paramblocks' )
|
|
begin
|
|
xml_node.find( 'paramblocks/paramblock/parameter' ).each do |param_node|
|
|
param = AttributeDef::from_xml( param_node )
|
|
params[param.name] = param
|
|
end
|
|
end unless ( params_node.nil? )
|
|
|
|
properties = {}
|
|
properties_node = xml_node.find_first( 'properties' )
|
|
begin
|
|
xml_node.find( 'properties/property' ).each do |property_node|
|
|
property = AttributeDef::from_xml( property_node )
|
|
properties[property.name] = property
|
|
end
|
|
end unless ( properties_node.nil? )
|
|
|
|
children = []
|
|
children_node = xml_node.find_first( 'children' )
|
|
begin
|
|
xml_node.find( 'children/object' ).each do |child_node|
|
|
child = ObjectDef::from_xml( scene, child_node )
|
|
children << child
|
|
scene.add_object( child )
|
|
end
|
|
end unless ( children_node.nil? )
|
|
|
|
subobjects = []
|
|
subobjects_node = xml_node.find_first( 'subobjects' )
|
|
begin
|
|
xml_node.find( 'subobjects/object' ).each do |subobj_node|
|
|
subobjects << ObjectDef::from_xml( scene, subobj_node )
|
|
end
|
|
end unless ( subobjects_node.nil? )
|
|
|
|
lods_node = xml_node.find_first( 'lod_hierarchy' )
|
|
lods = LODHierarchyDef::from_xml( lods_node )
|
|
|
|
obj = ObjectDef.new( guid, name, _class, _superclass, bbox, transform,
|
|
attributes_class, attributes, params, properties, subobjects, children, lods )
|
|
obj.refname = refname
|
|
obj.reffile = reffile
|
|
# Fixup child and subobject parent references.
|
|
obj.children.each do |child|
|
|
child.parent = obj
|
|
end
|
|
obj.subobjects.each do |subobj|
|
|
subobj.parent = obj
|
|
end
|
|
|
|
obj
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# SceneXml attribute definition class, representing an attribute name,
|
|
# value and type.
|
|
#
|
|
class AttributeDef
|
|
attr_reader :name
|
|
attr_reader :type
|
|
attr_reader :value
|
|
|
|
def initialize( name, type, value )
|
|
@name = name
|
|
@type = type
|
|
@value = value
|
|
end
|
|
|
|
#
|
|
# Create a SceneXMLObjectAttrDef object from an XML node.
|
|
#
|
|
def AttributeDef::from_xml( xml_node )
|
|
name = xml_node['name'].downcase
|
|
type = xml_node['type'].downcase
|
|
value = nil
|
|
case type
|
|
when 'array'
|
|
value = []
|
|
xml_node.find( 'parameter' ).each do |child_node|
|
|
value << AttributeDef::from_xml( child_node )
|
|
end
|
|
when 'int'
|
|
value = xml_node['value'].to_i
|
|
when 'float'
|
|
value = xml_node['value'].to_f
|
|
when 'bool'
|
|
value = ( 'true' == xml_node['value'] ? true : false )
|
|
when 'rgb'
|
|
value = Math::Vector3::from_xml( xml_node.find_first( 'colour' ) )
|
|
when 'rgba'
|
|
value = Math::Vector4::from_xml( xml_node.find_first( 'colour' ) )
|
|
else # default string
|
|
value = xml_node['value']
|
|
end
|
|
|
|
AttributeDef.new( name, type, value )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# Object transformation definition consisting of the object's
|
|
# transformation matrix.
|
|
#
|
|
class TransformDef
|
|
attr_reader :matrix # Matrix34 object
|
|
|
|
#----------------------------------------------------------------
|
|
# Virtual Attributes
|
|
#----------------------------------------------------------------
|
|
def position
|
|
@matrix.d
|
|
end
|
|
|
|
def rotation
|
|
Math::Quat::from_matrix34( @matrix )
|
|
end
|
|
|
|
def initialize( matrix )
|
|
throw ArgumentError.new( "Invalid Matrix34 object (#{matrix.class})." ) \
|
|
unless ( matrix.is_a?( Math::Matrix34 ) )
|
|
@matrix = matrix
|
|
end
|
|
|
|
#----------------------------------------------------------------
|
|
# Class Methods
|
|
#----------------------------------------------------------------
|
|
|
|
#
|
|
# Construct a TransformDef object from an XML node.
|
|
#
|
|
def TransformDef::from_xml( xml_node )
|
|
#throw RuntimeError.new( "Invalid transform node, name: #{xml_node.name}." ) \
|
|
# unless ( 'transform' == xml_node.name )
|
|
|
|
# Matrix Transform Data
|
|
matrix = nil
|
|
mat_node = xml_node.find_first( 'matrix' )
|
|
pos_node = xml_node.find_first( 'position' )
|
|
rot_node = xml_node.find_first( 'rotation' )
|
|
if ( mat_node ) then
|
|
# Have an XML matrix defined, so erm, use it.
|
|
matrix = Math::Matrix34::from_xml( mat_node )
|
|
|
|
elsif ( pos_node )
|
|
# Have position, and maybe rotation. Make matrix.
|
|
pos = Math::Vector3::from_xml( pos_node )
|
|
rot = Math::Quat::from_xml( rot_node ) unless ( rot_node.nil? )
|
|
if ( rot.nil? ) then
|
|
matrix = Math::Matrix34.new
|
|
matrix.translation = pos
|
|
else
|
|
matrix = Math::Matrix34::from_quat( rot )
|
|
matrix.translation = pos
|
|
end
|
|
end
|
|
|
|
TransformDef::new( matrix )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# LOD hierarchy definition for an ObjectDef. This class contains
|
|
# references to LOD parent and LOD children (if applicable).
|
|
#
|
|
class LODHierarchyDef
|
|
attr_reader :parent # ObjectDef or nil link to parent
|
|
attr_reader :children # Array of ObjectDef or [] to children
|
|
attr_reader :parent_guid # UUID object
|
|
attr_reader :child_guids # Array of UUID objects
|
|
|
|
def initialize( parent_guid, child_guids )
|
|
@parent_guid = parent_guid
|
|
@child_guids = child_guids
|
|
end
|
|
|
|
def resolve_guids( scene )
|
|
@parent = scene.find_object( parent_guid )
|
|
@children = []
|
|
@child_guids.each do |g|
|
|
@children << scene.find_object( g )
|
|
end
|
|
end
|
|
|
|
#----------------------------------------------------------------
|
|
# Class Methods
|
|
#----------------------------------------------------------------
|
|
|
|
#
|
|
# Construct a LODDef object from an XML node.
|
|
#
|
|
def LODHierarchyDef::from_xml( xml_node )
|
|
return nil if ( xml_node.nil? )
|
|
#throw RuntimeError.new( "Invalid LOD hierarchy node, name: #{xml_node.name}." ) \
|
|
# unless ( 'lod_hierarchy' == xml_node.name )
|
|
|
|
# Parent
|
|
parent = nil
|
|
parent_node = xml_node.find_first( 'parent' )
|
|
parent = UUID::parse( parent_node['guid'].gsub( /[{|}]/, '' ) ) unless ( parent_node.nil? )
|
|
|
|
# Children
|
|
children = []
|
|
xml_node.find( 'children/child' ).each do |child_node|
|
|
children << UUID::parse( child_node['guid'].gsub( /[{|}]/, '' ) )
|
|
end
|
|
LODHierarchyDef.new( parent, children )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# Individual Material definition. A material definition currently just
|
|
# lists child materials and a list of textures.
|
|
#
|
|
# The unique UUID (:guid) can be used to link MaterialDef and
|
|
# ObjectDef objects.
|
|
#
|
|
class MaterialDef
|
|
attr_reader :name
|
|
attr_reader :type
|
|
attr_reader :guid
|
|
attr_reader :textures
|
|
attr_reader :children
|
|
|
|
def initialize( name, type, guid, textures, children )
|
|
@name = name
|
|
@type = type
|
|
@guid = guid
|
|
@textures = textures
|
|
@children = children
|
|
end
|
|
|
|
#
|
|
# Construct a MaterialDef object from an XML node.
|
|
#
|
|
def MaterialDef::from_xml( xml_node )
|
|
|
|
name = xml_node['name']
|
|
type = xml_node['type']
|
|
guid = UUID::parse( xml_node['guid'].gsub( /[{|}]/, '' ) )
|
|
|
|
textures = []
|
|
xml_node.find( 'textures/texture' ).each do |tex_node|
|
|
|
|
filename = tex_node.attributes['filename']
|
|
textures << filename
|
|
end
|
|
|
|
children = {}
|
|
xml_node.find( 'submaterials/material' ).each do |mat_node|
|
|
|
|
submat = MaterialDef::from_xml( mat_node )
|
|
|
|
raise RuntimeError.new( "Sub-material GUID already in database, #{submat.guid}." ) \
|
|
if ( children.has_key?( submat.guid ) )
|
|
|
|
children[submat.guid] = submat
|
|
end
|
|
|
|
MaterialDef.new( name, type, guid, textures, children )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# SceneXml material database. This class is an abstraction of all of the
|
|
# materials in the 3dsmax scene, and the minimal properties that are
|
|
# written to the SceneXml XML file.
|
|
#
|
|
class MaterialDB
|
|
attr_reader :materials
|
|
|
|
def initialize( materials )
|
|
@materials = materials
|
|
end
|
|
|
|
#
|
|
# Construct a MaterialDB object from an XML node.
|
|
#
|
|
def MaterialDB::from_xml( xmldoc )
|
|
materials = {}
|
|
xmldoc.find( '//scene/materials/material' ).each do |xml_node|
|
|
mat = MaterialDef::from_xml( xml_node )
|
|
|
|
raise RuntimeError.new( "Material GUID already in database, #{mat.guid}." ) \
|
|
if ( materials.has_key?( mat.guid ) )
|
|
|
|
materials[mat.guid] = mat
|
|
end
|
|
|
|
MaterialDB::new( materials )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# Base serialiser class that will take the SceneXml representation and
|
|
# serialise it to disk file. This is the base class for both the IDE
|
|
# and IPL serialisation classes and ensures they have a similar
|
|
# representation.
|
|
#
|
|
class Serialiser
|
|
attr_reader :scene
|
|
|
|
def initialize( scene )
|
|
@scene = scene
|
|
end
|
|
|
|
def write( filename, options = {} )
|
|
throw RuntimeError.new( "Virtual method, implement in concrete serialiser class." )
|
|
end
|
|
end
|
|
|
|
end # SceneXml module
|
|
|
|
end # FileFormats module
|
|
end # Pipeline module
|
|
|
|
# scenexml.rb
|