fml 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/TODO +2 -0
- data/bin/fml2dae.rb +14 -9
- data/fml.gemspec +10 -7
- data/lib/collada/document.rb +21 -23
- data/lib/collada/geometry.rb +22 -23
- data/lib/config.yml +4 -4
- data/lib/floorplanner/area_builder.rb +3 -8
- data/lib/floorplanner/asset.rb +32 -27
- data/lib/floorplanner/collada_export.rb +76 -71
- data/lib/floorplanner/design.rb +42 -76
- data/lib/floorplanner/document.rb +16 -43
- data/lib/floorplanner/opening3d.rb +5 -4
- data/lib/floorplanner/wall_builder.rb +6 -5
- data/lib/floorplanner/xml.rb +151 -0
- data/lib/floorplanner.rb +11 -9
- data/lib/geom/edge.rb +1 -1
- data/lib/geom/matrix3d.rb +7 -2
- data/lib/geom/number.rb +8 -0
- data/lib/geom/polygon.rb +18 -22
- data/lib/geom/vertex.rb +8 -0
- data/tasks/github-gem.rake +47 -4
- data/views/design.dae.erb +37 -92
- metadata +53 -40
- data/bin/fml2obj.rb +0 -10
- data/lib/floorplanner/obj_export.rb +0 -24
- data/lib/floorplanner/rib_export.rb +0 -24
- data/views/design.obj.erb +0 -17
- data/views/design.rib.erb +0 -17
data/.gitignore
CHANGED
data/bin/fml2dae.rb
CHANGED
@@ -3,13 +3,18 @@ $: << File.join(File.dirname(__FILE__), "/../lib" )
|
|
3
3
|
require 'floorplanner'
|
4
4
|
|
5
5
|
if ARGV.length < 2
|
6
|
-
puts "\n Usage: fml2dae.rb [-xrefs]
|
7
|
-
|
8
|
-
xrefs = false
|
9
|
-
if ARGV[0] == "-xrefs"
|
10
|
-
ARGV.shift
|
11
|
-
xrefs = true
|
12
|
-
end
|
13
|
-
doc = Floorplanner::Document.new(ARGV[1])
|
14
|
-
doc.to_dae(ARGV[0],ARGV[2],xrefs)
|
6
|
+
puts "\n Usage: fml2dae.rb [-xrefs] path/to/fml out.dae"
|
7
|
+
exit
|
15
8
|
end
|
9
|
+
|
10
|
+
conf = {
|
11
|
+
:ceiling => false,
|
12
|
+
:window_glass => true
|
13
|
+
}
|
14
|
+
|
15
|
+
if ARGV[0] == "-xrefs"
|
16
|
+
ARGV.shift
|
17
|
+
conf[:xrefs] = true
|
18
|
+
end
|
19
|
+
doc = Floorplanner::XML::Document.from_xml(open(ARGV[0]))
|
20
|
+
doc.to_dae(ARGV[1], conf)
|
data/fml.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "fml"
|
5
|
-
s.version = "0.2.
|
6
|
-
s.date = "2010-
|
5
|
+
s.version = "0.2.4"
|
6
|
+
s.date = "2010-11-25"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.0") if s.respond_to? :required_rubygems_version=
|
9
9
|
|
10
10
|
s.authors = ["Dusan Maliarik"]
|
11
11
|
s.description = %q{Floor plan document toolkit}
|
12
12
|
s.email = %q{dusan.maliarik@gmail.com}
|
13
|
-
s.executables = ["fml2dae.rb"
|
14
|
-
s.files = %w(
|
13
|
+
s.executables = ["fml2dae.rb"]
|
14
|
+
s.files = %w(.gitignore README Rakefile TODO bin/fml2dae.rb fml.gemspec lib/collada/document.rb lib/collada/geometry.rb lib/config.yml lib/floorplanner.rb lib/floorplanner/area_builder.rb lib/floorplanner/asset.rb lib/floorplanner/collada_export.rb lib/floorplanner/design.rb lib/floorplanner/document.rb lib/floorplanner/opening3d.rb lib/floorplanner/svg_export.rb lib/floorplanner/wall3d.rb lib/floorplanner/wall_builder.rb lib/floorplanner/xml.rb lib/geom.rb lib/geom/connection.rb lib/geom/ear_trim.rb lib/geom/edge.rb lib/geom/glu_tess.rb lib/geom/intersection.rb lib/geom/matrix3d.rb lib/geom/number.rb lib/geom/plane.rb lib/geom/polygon.rb lib/geom/triangle.rb lib/geom/triangle_mesh.rb lib/geom/vertex.rb lib/keyhole/archive.rb tasks/github-gem.rake views/design.dae.erb views/design.svg.erb xml/collada_schema_1_4.xsd xml/fml.rng xml/fml2kml.xsl)
|
15
15
|
s.homepage = %q{http://floorplanner.com/}
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
s.rubygems_version = %q{1.3.1}
|
@@ -22,15 +22,18 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.specification_version = 2
|
23
23
|
|
24
24
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
25
|
-
s.add_runtime_dependency(%q<
|
25
|
+
s.add_runtime_dependency(%q<nokogiri>)
|
26
26
|
s.add_runtime_dependency(%q<rubyzip>)
|
27
|
+
s.add_runtime_dependency(%q<roxml>)
|
27
28
|
else
|
28
|
-
s.add_dependency(%q<
|
29
|
+
s.add_dependency(%q<nokogiri>)
|
29
30
|
s.add_dependency(%q<rubyzip>)
|
31
|
+
s.add_dependency(%q<roxml>)
|
30
32
|
end
|
31
33
|
else
|
32
|
-
s.add_dependency(%q<
|
34
|
+
s.add_dependency(%q<nokogiri>)
|
33
35
|
s.add_dependency(%q<rubyzip>)
|
36
|
+
s.add_dependency(%q<roxml>)
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
data/lib/collada/document.rb
CHANGED
@@ -1,34 +1,32 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
|
3
3
|
module Collada
|
4
|
-
class Document < XML::Document
|
5
|
-
|
6
|
-
|
4
|
+
class Document < Nokogiri::XML::Document
|
5
|
+
COLLADA_NS = "http://www.collada.org/2005/11/COLLADASchema"
|
6
|
+
COLLADA_VERSION = "1.4.1"
|
7
7
|
|
8
|
-
COLLADA = 'COLLADA'
|
9
8
|
ASSET = 'asset'
|
10
|
-
LIBRARY_MATERIALS
|
11
|
-
LIBRARY_EFFECTS
|
12
|
-
LIBRARY_GEOMETRIES
|
9
|
+
LIBRARY_MATERIALS = 'library_materials'
|
10
|
+
LIBRARY_EFFECTS = 'library_effects'
|
11
|
+
LIBRARY_GEOMETRIES = 'library_geometries'
|
13
12
|
LIBRARY_VISUAL_SCENES = 'library_visual_scenes'
|
14
|
-
SCENE
|
15
|
-
VISUAL_SCENE
|
13
|
+
SCENE = 'scene'
|
14
|
+
VISUAL_SCENE = 'visual_scene'
|
16
15
|
INSTANCE_VISUAL_SCENE = 'instance_visual_scene'
|
17
16
|
|
18
17
|
attr_accessor :assets_libraries
|
19
18
|
|
20
19
|
def initialize
|
21
20
|
super
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
root = create_element 'COLLADA'
|
22
|
+
root.namespace = COLLADA_NS
|
23
|
+
root.attr['version'] = COLLADA_VERSION
|
25
24
|
|
26
25
|
create_structure
|
27
|
-
|
28
|
-
self.assets_libraries = []
|
26
|
+
assets_libraries = []
|
29
27
|
end
|
30
28
|
|
31
|
-
def <<
|
29
|
+
def << node
|
32
30
|
root << node
|
33
31
|
end
|
34
32
|
|
@@ -51,18 +49,18 @@ module Collada
|
|
51
49
|
|
52
50
|
private
|
53
51
|
def create_structure
|
54
|
-
self <<
|
52
|
+
self << create_element(ASSET)
|
55
53
|
|
56
|
-
self <<
|
57
|
-
self <<
|
58
|
-
self <<
|
59
|
-
self << (visual_scenes =
|
60
|
-
visual_scenes << (visual_scene =
|
54
|
+
self << create_element(LIBRARY_MATERIALS)
|
55
|
+
self << create_element(LIBRARY_EFFECTS)
|
56
|
+
self << create_element(LIBRARY_GEOMETRIES)
|
57
|
+
self << (visual_scenes = create_element(LIBRARY_VISUAL_SCENES))
|
58
|
+
visual_scenes << (visual_scene = create_element(VISUAL_SCENE))
|
61
59
|
visual_scene['id'] = 'MainScene'
|
62
60
|
visual_scene['name'] = 'MainScene'
|
63
61
|
|
64
|
-
self << (scene =
|
65
|
-
scene << (scene_node =
|
62
|
+
self << (scene = create_element(SCENE))
|
63
|
+
scene << (scene_node = create_element(INSTANCE_VISUAL_SCENE))
|
66
64
|
scene_node['url'] = '#MainScene'
|
67
65
|
end
|
68
66
|
|
data/lib/collada/geometry.rb
CHANGED
@@ -6,14 +6,14 @@ module Collada
|
|
6
6
|
GEOMETRY_QUERY = '/COLLADA/library_geometries/geometry[@id="%s"]'
|
7
7
|
NODE_QUERY = '/COLLADA/library_nodes//node[@id="%s"]'
|
8
8
|
|
9
|
-
def self.doc
|
9
|
+
def self.doc doc
|
10
10
|
result = Geometry.new
|
11
11
|
result.instance_eval do
|
12
|
-
@
|
12
|
+
@doc = doc
|
13
13
|
@cache = {}
|
14
14
|
|
15
|
-
scene_id =
|
16
|
-
scene =
|
15
|
+
scene_id = @doc.xpath(SCENE_QUERY).first.attribute('url').value[1..-1]
|
16
|
+
scene = @doc.xpath(VISUAL_SCENE_QUERY % scene_id).first
|
17
17
|
scene.children.each do |child|
|
18
18
|
eval_node(child) if child.name == "node"
|
19
19
|
end
|
@@ -23,7 +23,7 @@ module Collada
|
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
def eval_node
|
26
|
+
def eval_node node, parent=nil
|
27
27
|
parent = node.parent unless parent
|
28
28
|
parent_matrix = Geom::Matrix3D.identity
|
29
29
|
if parent.name == "node"
|
@@ -43,23 +43,22 @@ module Collada
|
|
43
43
|
when "node"
|
44
44
|
eval_node(child,node)
|
45
45
|
when "instance_geometry"
|
46
|
-
load_geometry(child.
|
46
|
+
load_geometry(child.attribute('url').value[1..-1],node_matrix)
|
47
47
|
when "instance_node"
|
48
48
|
eval_node(expand_node(child),node)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
def load_geometry
|
54
|
-
geometry = @
|
53
|
+
def load_geometry gid, matrix=nil
|
54
|
+
geometry = @doc.xpath(GEOMETRY_QUERY % gid).first
|
55
55
|
mesh = Geom::TriangleMesh.new
|
56
56
|
|
57
57
|
floats = Array.new
|
58
|
-
geometry.
|
59
|
-
vertices_id = triangles.
|
60
|
-
source_id = geometry.
|
61
|
-
|
62
|
-
floats.concat geometry.find('mesh/source[@id="%s"]/float_array' % source_id).first.get_floats
|
58
|
+
geometry.xpath('mesh/triangles').each do |triangles|
|
59
|
+
vertices_id = triangles.xpath('input[@semantic="VERTEX"]').first.attribute('source').value[1..-1]
|
60
|
+
source_id = geometry.xpath('mesh/vertices[@id="%s"]/input[@semantic="POSITION"]' % vertices_id).first.attribute('source').value[1..-1]
|
61
|
+
floats.concat geometry.xpath('mesh/source[@id="%s"]/float_array' % source_id).first.content.split.map(&:to_f)
|
63
62
|
end
|
64
63
|
|
65
64
|
(floats.length/3).times do |i|
|
@@ -71,36 +70,36 @@ module Collada
|
|
71
70
|
@meshes << mesh
|
72
71
|
end
|
73
72
|
|
74
|
-
def expand_node
|
75
|
-
node_id = instance.
|
76
|
-
@
|
73
|
+
def expand_node instance
|
74
|
+
node_id = instance.attribute('url').value[1..-1]
|
75
|
+
@doc.xpath(NODE_QUERY % node_id).first
|
77
76
|
end
|
78
77
|
|
79
|
-
def get_node_matrix
|
78
|
+
def get_node_matrix node, source=nil
|
80
79
|
result = source ? source : Geom::Matrix3D.identity
|
81
80
|
node.children.each do |child|
|
82
81
|
case child.name
|
83
82
|
when "translate"
|
84
83
|
f = child.get_floats
|
85
84
|
t = Geom::Matrix3D.translation(f[0],f[1],f[2])
|
86
|
-
result
|
85
|
+
result *= t
|
87
86
|
when "scale"
|
88
87
|
f = child.get_floats
|
89
88
|
t = Geom::Matrix3D.scale(f[0],f[1],f[2])
|
90
|
-
result
|
89
|
+
result *= t
|
91
90
|
when "rotate"
|
92
91
|
f = child.get_floats
|
93
|
-
t = Geom::Matrix3D.
|
94
|
-
result
|
92
|
+
t = Geom::Matrix3D.rotation(f[0],f[1],f[2],f[3])
|
93
|
+
result *= t
|
95
94
|
when "matrix"
|
96
|
-
f = child.
|
95
|
+
f = child.content.split.map(&:to_f)
|
97
96
|
t = Geom::Matrix3D[
|
98
97
|
[ *f[0..3] ],
|
99
98
|
[ *f[4..7] ],
|
100
99
|
[ *f[8..11] ],
|
101
100
|
[ *f[12..15] ]
|
102
101
|
]
|
103
|
-
result
|
102
|
+
result *= t
|
104
103
|
end
|
105
104
|
end
|
106
105
|
result
|
data/lib/config.yml
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
content_base_url: http://cdn.floorplanner.com/assets/
|
2
|
-
dae_cache_path:
|
3
|
-
area_textures_path:
|
2
|
+
dae_cache_path: fml2dae_cache/
|
3
|
+
area_textures_path: fml2dae_cache/textures_2d/
|
4
4
|
|
5
5
|
geom_snap: 0.1
|
6
6
|
uniq_snap: 0.0000005
|
7
7
|
openings:
|
8
8
|
window_base: 0.65
|
9
|
-
window_height: 1.
|
10
|
-
door_height: 2.
|
9
|
+
window_height: 1.5
|
10
|
+
door_height: 2.1
|
11
11
|
|
12
12
|
svg:
|
13
13
|
width: 1024
|
@@ -21,14 +21,9 @@ module Floorplanner
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
if params[:color] =~ /\A#((?:#{HEX_RE}{2,2}){3,4})\z/
|
28
|
-
params[:color] = [*$1.scan(/.{2,2}/).collect {|value| value.hex / 255.0}]
|
29
|
-
else
|
30
|
-
params[:color] = [1,1,1]
|
31
|
-
end
|
24
|
+
def area(vertices, params=nil)
|
25
|
+
a_id = vertices.hash.abs.to_s + "_" + params[:type].to_s
|
26
|
+
params[:color] = Floorplanner.read_color params[:color]
|
32
27
|
@meshes[a_id] = Geom::Polygon.new(vertices, nil, params.merge({:id => a_id}))
|
33
28
|
end
|
34
29
|
|
data/lib/floorplanner/asset.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Floorplanner
|
2
|
-
class
|
2
|
+
class DAE
|
3
3
|
LIBRARY_GEOMETRIES = '/COLLADA/library_geometries/geometry'
|
4
4
|
LIBRARY_EFFECTS = '/COLLADA/library_effects/effect'
|
5
5
|
LIBRARY_MATERIALS = '/COLLADA/library_materials/material'
|
@@ -13,15 +13,15 @@ module Floorplanner
|
|
13
13
|
|
14
14
|
attr_reader :id, :name, :title, :dae_path
|
15
15
|
|
16
|
-
def self.get(
|
16
|
+
def self.get(asset)
|
17
17
|
FileUtils.mkdir_p(CACHE_PATH)
|
18
|
-
asset_url = Floorplanner.config['content_base_url'] + URI.escape(
|
18
|
+
asset_url = Floorplanner.config['content_base_url'] + URI.escape(asset.url3d)
|
19
19
|
|
20
|
-
cached_path = File.join(CACHE_PATH,
|
20
|
+
cached_path = File.join(CACHE_PATH, asset.id)
|
21
21
|
if File.exists?(cached_path)
|
22
|
-
$stderr.puts("Cached asset: %s" %
|
22
|
+
$stderr.puts("Cached asset: %s" % asset.id)
|
23
23
|
@kmz = Keyhole::Archive.new(cached_path)
|
24
|
-
|
24
|
+
DAE.new(asset.id, asset.name, @kmz)
|
25
25
|
else
|
26
26
|
$stderr.puts("Downloading asset: %s" % asset_url)
|
27
27
|
cached = File.new(cached_path,'w')
|
@@ -30,19 +30,24 @@ module Floorplanner
|
|
30
30
|
cached.close
|
31
31
|
|
32
32
|
@kmz = Keyhole::Archive.new(cached_path)
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
dae = DAE.new(asset.id, asset.name, @kmz)
|
34
|
+
dae.adjust_paths!
|
35
|
+
dae
|
36
36
|
end
|
37
|
+
rescue
|
38
|
+
$stderr.puts "Failed to get asset: %s, %s" % [asset.id, asset_url]
|
39
|
+
raise
|
37
40
|
end
|
38
41
|
|
39
|
-
def initialize(id,title,kmz)
|
42
|
+
def initialize(id, title, kmz)
|
40
43
|
@dae_path = kmz.dae_path(id)
|
41
44
|
@kmz = kmz
|
42
45
|
@id = id
|
43
46
|
@title = title
|
44
|
-
@xml = XML::Document.string(File.read(@dae_path).gsub(/xmlns=".+"/, ''))
|
45
47
|
@name = File.basename(@dae_path.gsub(/\.|dae/,''))
|
48
|
+
|
49
|
+
@doc = Nokogiri::XML.parse(open(@dae_path))
|
50
|
+
@doc.remove_namespaces!
|
46
51
|
@images_dict = {}
|
47
52
|
end
|
48
53
|
|
@@ -51,46 +56,46 @@ module Floorplanner
|
|
51
56
|
|
52
57
|
def library_materials
|
53
58
|
return @materials if @materials
|
54
|
-
materials = @
|
59
|
+
materials = @doc.xpath(LIBRARY_MATERIALS)
|
55
60
|
materials.each {|mat| namespace!(mat)}
|
56
61
|
@materials = materials
|
57
62
|
end
|
58
63
|
|
59
64
|
def library_effects
|
60
65
|
return @effects if @effects
|
61
|
-
effects = @
|
66
|
+
effects = @doc.xpath(LIBRARY_EFFECTS)
|
62
67
|
effects.each {|eff| namespace!(eff)}
|
63
68
|
@effects = effects
|
64
69
|
end
|
65
70
|
|
66
71
|
def library_geometries
|
67
72
|
return @geometries if @geometries
|
68
|
-
geometries = @
|
73
|
+
geometries = @doc.xpath(LIBRARY_GEOMETRIES)
|
69
74
|
geometries.each{|geo| namespace!(geo)}
|
70
75
|
@geometries = geometries
|
71
76
|
end
|
72
77
|
|
73
78
|
def library_nodes
|
74
79
|
return @nodes if @nodes
|
75
|
-
nodes = @
|
80
|
+
nodes = @doc.xpath(LIBRARY_NODES)
|
76
81
|
nodes.each{|nod| namespace!(nod)}
|
77
82
|
@nodes = nodes
|
78
83
|
end
|
79
84
|
|
80
85
|
def library_images
|
81
86
|
return @images if @images
|
82
|
-
images = @
|
87
|
+
images = @doc.xpath(LIBRARY_IMAGES)
|
83
88
|
images.each{|img| namespace!(img) && update_path!(img)}
|
84
89
|
@images = images
|
85
90
|
end
|
86
91
|
|
87
92
|
def visual_scene_node
|
88
93
|
return @scene_node if @scene_node
|
89
|
-
@scene_node = namespace!(@
|
94
|
+
@scene_node = namespace!(@doc.xpath(VISUAL_SCENE_QUERY).first)
|
90
95
|
end
|
91
96
|
|
92
97
|
def bounding_box
|
93
|
-
mesh = Collada::Geometry.doc @
|
98
|
+
mesh = Collada::Geometry.doc @doc
|
94
99
|
mesh.bounding_box
|
95
100
|
end
|
96
101
|
|
@@ -115,11 +120,11 @@ module Floorplanner
|
|
115
120
|
end
|
116
121
|
|
117
122
|
def save_textures(root_path)
|
118
|
-
images = @
|
119
|
-
FileUtils.mkdir_p(root_path)
|
123
|
+
images = @doc.xpath(LIBRARY_IMAGES)
|
124
|
+
FileUtils.mkdir_p(root_path)
|
120
125
|
|
121
126
|
images.each do |image|
|
122
|
-
relative_to_dae = image.
|
127
|
+
relative_to_dae = image.xpath('init_from').first.content
|
123
128
|
img_path = @kmz.image_path(@id,relative_to_dae)
|
124
129
|
target_path = File.join(root_path,@id)
|
125
130
|
FileUtils.mkdir_p target_path
|
@@ -132,15 +137,15 @@ module Floorplanner
|
|
132
137
|
end
|
133
138
|
|
134
139
|
def adjust_paths!
|
135
|
-
images = @
|
140
|
+
images = @doc.xpath(LIBRARY_IMAGES)
|
136
141
|
|
137
142
|
images.each do |image|
|
138
|
-
init_from = image.
|
143
|
+
init_from = image.xpath('init_from').first
|
139
144
|
img_path = @kmz.image_path(@id,init_from.content,true)
|
140
145
|
init_from.content = img_path
|
141
146
|
end
|
142
147
|
open(@dae_path, 'w') do |f|
|
143
|
-
f.write @
|
148
|
+
f.write @doc.to_s
|
144
149
|
end
|
145
150
|
end
|
146
151
|
|
@@ -160,12 +165,12 @@ module Floorplanner
|
|
160
165
|
node['texture'] = "#{@name}_#{node['texture'].gsub('#','')}" if node['texture']
|
161
166
|
|
162
167
|
if node.name == 'surface'
|
163
|
-
n = node.
|
168
|
+
n = node.xpath('init_from').first
|
164
169
|
n.content = "#{@name}_#{n.content}"
|
165
170
|
end
|
166
171
|
|
167
172
|
if node.name == 'sampler2D'
|
168
|
-
n = node.
|
173
|
+
n = node.xpath('source').first
|
169
174
|
n.content = "#{@name}_#{n.content}"
|
170
175
|
end
|
171
176
|
|
@@ -177,7 +182,7 @@ module Floorplanner
|
|
177
182
|
|
178
183
|
# updates image path to export output folder
|
179
184
|
def update_path!(node)
|
180
|
-
init_from_node = node.
|
185
|
+
init_from_node = node.xpath('init_from').first
|
181
186
|
relative = init_from_node.content
|
182
187
|
init_from_node.content = @images_dict[relative]
|
183
188
|
end
|
@@ -1,32 +1,37 @@
|
|
1
|
-
module Floorplanner
|
1
|
+
module Floorplanner::XML
|
2
2
|
class Document
|
3
3
|
|
4
|
-
def to_dae
|
5
|
-
@design = Design.new(
|
4
|
+
def to_dae thing, conf={}
|
5
|
+
@design = Floorplanner::Design.new(self)
|
6
6
|
@design.build_geometries
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
if thing.kind_of? String
|
8
|
+
unless conf[:xrefs]
|
9
|
+
@design.save_textures(File.dirname(thing))
|
10
|
+
end
|
11
|
+
dae = File.new(thing,'w')
|
12
|
+
dae.write @design.to_dae(conf)
|
13
|
+
dae.close
|
14
|
+
elsif thing.respond_to? :write
|
15
|
+
thing.write @design.to_dae(conf)
|
16
|
+
end
|
11
17
|
end
|
12
|
-
|
13
18
|
end
|
19
|
+
end
|
14
20
|
|
21
|
+
module Floorplanner
|
15
22
|
module ColladaExport
|
16
|
-
DESIGN_QUERY = "/project/floors/floor/designs/design[id='%s']"
|
17
|
-
ASSET_QUERY = DESIGN_QUERY+"/assets/asset[@id='%s']"
|
18
|
-
ASSETS_QUERY = DESIGN_QUERY+"/assets/asset"
|
19
|
-
OBJECTS_QUERY = DESIGN_QUERY+"/objects/object"
|
20
23
|
|
21
|
-
|
22
|
-
|
24
|
+
CACHE_PATH = File.join(Floorplanner.config['dae_cache_path'], 'textures_2d')
|
25
|
+
|
26
|
+
def to_dae conf
|
27
|
+
raise "No geometries to export" unless @areas && @walls
|
23
28
|
@assets = assets
|
24
29
|
@elements = objects
|
25
30
|
|
26
31
|
# somehow...
|
27
32
|
@walls.reverse
|
28
33
|
@areas.each {|a| a.reverse}
|
29
|
-
@
|
34
|
+
@conf = conf
|
30
35
|
|
31
36
|
template = ERB.new(
|
32
37
|
File.read(
|
@@ -37,82 +42,82 @@ module Floorplanner
|
|
37
42
|
def assets
|
38
43
|
return @assets if @assets
|
39
44
|
@assets = {}
|
40
|
-
@
|
41
|
-
|
42
|
-
name = asset_node.find('name').first.content
|
43
|
-
url3d = asset_node.find('url3d').first
|
44
|
-
next unless url3d
|
45
|
-
url3d = url3d.content
|
45
|
+
@doc.assets.each do |asset|
|
46
|
+
next unless asset.url3d && !asset.url3d.empty?
|
46
47
|
|
47
48
|
# TODO: store asset bounding box
|
48
|
-
|
49
|
-
next unless
|
50
|
-
@assets.store(
|
49
|
+
dae = Floorplanner::DAE.get(asset)
|
50
|
+
next unless dae
|
51
|
+
@assets.store(asset.id, dae)
|
51
52
|
end
|
52
53
|
@assets
|
53
54
|
end
|
54
55
|
|
55
56
|
def objects
|
56
57
|
result = []
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
rotation = unless object.find('rotation').empty?
|
69
|
-
object.find('rotation').first.content
|
70
|
-
else
|
71
|
-
'0 0 0'
|
72
|
-
end
|
73
|
-
rotation = Geom::Number3D.from_str(rotation)
|
74
|
-
rotation.z += 360 if rotation.z < 0
|
75
|
-
rotation.z += 180
|
76
|
-
|
77
|
-
# find proper scale for object
|
78
|
-
size = object.find('size').first.content
|
79
|
-
scale = asset.scale_ratio(Geom::Number3D.from_str(size))
|
80
|
-
|
81
|
-
mirrored = object.find('mirrored').first
|
82
|
-
reflection = Geom::Matrix3D.reflection(Geom::Plane.new(Geom::Number3D.new(0.0,1.0,0.0), Geom::Number3D.new))
|
83
|
-
if mirrored
|
84
|
-
mirror = Geom::Number3D.from_str(mirrored)
|
85
|
-
if mirror.x != 0 || mirror.y != 0 || mirror.z != 0
|
86
|
-
mirror.x = mirror.x > 0 ? 1 : 0
|
87
|
-
mirror.y = mirror.y > 0 ? 1 : 0
|
88
|
-
mirror.z = mirror.z > 0 ? 1 : 0
|
89
|
-
|
90
|
-
origin = Geom::Number3D.new
|
91
|
-
plane = Geom::Plane.new(mirror,origin)
|
92
|
-
reflection = Geom::Matrix3D.reflection(plane).multiply reflection
|
93
|
-
end
|
58
|
+
@doc.objects.each do |item|
|
59
|
+
next unless assets[item.asset]
|
60
|
+
asset = assets[item.asset]
|
61
|
+
|
62
|
+
pos = item.position
|
63
|
+
rot = item.rotation || Geom::Number3D.new
|
64
|
+
scale = asset.scale_ratio(item.size)
|
65
|
+
|
66
|
+
if item.mirrored
|
67
|
+
m_mirror = Geom::Matrix3D.reflection(
|
68
|
+
Geom::Plane.new(item.mirrored, Geom::Number3D.new))
|
94
69
|
end
|
95
70
|
|
71
|
+
m_scale = Geom::Matrix3D.scale(scale.x, scale.y, scale.z)
|
72
|
+
m_rotate = Geom::Matrix3D.rotation(0, 0, 1, (Math::PI/180)*rot.z)
|
73
|
+
m_translate = Geom::Matrix3D.translation(pos.x, pos.y, pos.z)
|
74
|
+
|
75
|
+
m_combined = m_rotate * m_scale
|
76
|
+
m_combined = m_mirror * m_combined if m_mirror
|
77
|
+
m_combined = m_translate * m_combined
|
78
|
+
|
96
79
|
result << {
|
97
|
-
:asset
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:scale => scale,
|
101
|
-
:matrix => reflection
|
80
|
+
:asset => asset,
|
81
|
+
:matrix => m_combined,
|
82
|
+
:color => Floorplanner.read_color(item.color)
|
102
83
|
}
|
103
|
-
rescue
|
104
|
-
# TODO: handle text
|
105
|
-
end
|
106
84
|
end
|
107
85
|
result
|
108
86
|
end
|
109
87
|
|
110
|
-
def save_textures
|
111
|
-
img_path = File.join(root_path,'textures')
|
88
|
+
def save_textures root_path
|
89
|
+
img_path = File.join(root_path, 'textures')
|
112
90
|
FileUtils.mkdir_p img_path
|
113
91
|
assets.each_value do |asset|
|
114
92
|
asset.save_textures img_path
|
115
93
|
end
|
94
|
+
|
95
|
+
t2d_path = File.join(img_path, 'textures_2d')
|
96
|
+
FileUtils.mkdir_p t2d_path
|
97
|
+
@areas.each do |area|
|
98
|
+
next unless area.data[:texture]
|
99
|
+
texture_url = area.data[:texture]
|
100
|
+
fn = texture_url.match(/.*\/(.*)/)[1]
|
101
|
+
tex_path = File.join(t2d_path, fn)
|
102
|
+
area.data[:texture] = File.join('textures', 'textures_2d', fn)
|
103
|
+
next if File.exists?(tex_path)
|
104
|
+
|
105
|
+
FileUtils.mkdir_p CACHE_PATH
|
106
|
+
cached_path = File.join(CACHE_PATH, fn)
|
107
|
+
unless File.exists?(cached_path)
|
108
|
+
puts "Downloading texture: %s" % texture_url
|
109
|
+
begin
|
110
|
+
cached = File.new(cached_path, 'w')
|
111
|
+
remote = open(Floorplanner.config['content_base_url'] +
|
112
|
+
URI.escape(texture_url))
|
113
|
+
cached.write(remote.read)
|
114
|
+
cached.close
|
115
|
+
rescue
|
116
|
+
$stderr.puts "Error downloading texture: %s" % fn
|
117
|
+
end
|
118
|
+
end
|
119
|
+
FileUtils.cp(cached_path, tex_path)
|
120
|
+
end
|
116
121
|
end
|
117
122
|
end
|
118
123
|
end
|