floorplanner-fml 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ module Geom
2
+ class Triangle
3
+
4
+ attr_accessor :vertices, :texcoord, :normal
5
+
6
+ def initialize(vertices,texcoord=nil,normal=nil)
7
+ unless vertices.length == 3
8
+ raise "Triangle must consist of exactly 3 vertices"
9
+ end
10
+ @normal = normal || Number3D.new
11
+ @vertices = vertices
12
+ @texcoord = texcoord
13
+ create_normal
14
+ end
15
+
16
+ def flip_normal
17
+ @normal.x = -@normal.x
18
+ @normal.y = -@normal.y
19
+ @normal.z = -@normal.z
20
+ end
21
+
22
+ def clone
23
+ Triangle.new(@vertices.collect{|v| v.clone},@texcoord.collect{|uv| uv.clone})
24
+ end
25
+
26
+ private
27
+ def create_normal
28
+ vn0 = @vertices[0].position.clone
29
+ vn1 = @vertices[1].position.clone
30
+ vn2 = @vertices[2].position.clone
31
+ vn1.minus_eq(vn0)
32
+ vn2.minus_eq(vn0)
33
+ @normal = Number3D.cross(vn1,vn2,@normal)
34
+ @normal.normalize
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,94 @@
1
+ module Geom
2
+ class TriangleMesh
3
+
4
+ attr_accessor(:vertices,:texcoord,:faces,:meshes,:data,:tess)
5
+
6
+ def initialize( vertices=nil, faces=nil, user_data=nil )
7
+ @data = user_data || Hash.new
8
+ @faces = faces || Array.new
9
+ @vertices = vertices || Array.new
10
+ @meshes = Array.new
11
+
12
+ @tess = EarTrim
13
+ end
14
+
15
+ def << (mesh)
16
+ @meshes << mesh
17
+ end
18
+
19
+ def vertices
20
+ result = @meshes.collect {|m| m.vertices}.flatten
21
+ result.empty? ? @vertices : result
22
+ end
23
+
24
+ def faces
25
+ result = @meshes.collect {|m| m.faces}.flatten
26
+ result.empty? ? @faces : result
27
+ end
28
+
29
+ def texcoord
30
+ result = @meshes.collect {|m| m.texcoord}.flatten
31
+ result.empty? ? @texcoord : result
32
+ end
33
+
34
+ def update
35
+ @meshes.each {|m| m.update}
36
+ end
37
+
38
+ def reverse
39
+ faces.each {|f| f.vertices.reverse! }
40
+ end
41
+
42
+ def transform_vertices(transformation)
43
+ ta = transformation.to_a
44
+ m11 = ta[0][0]
45
+ m12 = ta[0][1]
46
+ m13 = ta[0][2]
47
+ m21 = ta[1][0]
48
+ m22 = ta[1][1]
49
+ m23 = ta[1][2]
50
+ m31 = ta[2][0]
51
+ m32 = ta[2][1]
52
+ m33 = ta[2][2]
53
+
54
+ m14 = ta[0][3]
55
+ m24 = ta[1][3]
56
+ m34 = ta[2][3]
57
+
58
+ @vertices.each do |v|
59
+ vx = v.x
60
+ vy = v.y
61
+ vz = v.z
62
+
63
+ tx = vx * m11 + vy * m12 + vz * m13 + m14
64
+ ty = vx * m21 + vy * m22 + vz * m23 + m24
65
+ tz = vx * m31 + vy * m32 + vz * m33 + m34
66
+
67
+ v.x = tx
68
+ v.y = ty
69
+ v.z = tz
70
+ end
71
+ end
72
+
73
+ def bounding_box
74
+ min = Geom::Number3D.new( 1000, 1000, 1000)
75
+ max = Geom::Number3D.new(-1000,-1000,-1000)
76
+
77
+ vertices.each do |v|
78
+ min.x = v.x if v.x < min.x
79
+ min.y = v.y if v.y < min.y
80
+ min.z = v.z if v.z < min.z
81
+
82
+ max.x = v.x if v.x > max.x
83
+ max.y = v.y if v.y > max.y
84
+ max.z = v.z if v.z > max.z
85
+ end
86
+
87
+ { :max => max , :min => min }
88
+ end
89
+
90
+ def merge(other)
91
+ @vertices.concat other.vertices
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,33 @@
1
+ require 'forwardable'
2
+
3
+ module Geom
4
+ class Vertex
5
+ attr_accessor(:position,:normal)
6
+ extend Forwardable
7
+ def_delegators(:@position,
8
+ :x, :y, :z, :x=, :y=, :z=,
9
+ :distance_x, :distance_y, :distance_z, :distance, :to_floats)
10
+ def initialize(x=0.0,y=0.0,z=0.0,normal=nil)
11
+ @position = Number3D.new(x,y,z)
12
+ @normal = normal
13
+ end
14
+
15
+ def == (other)
16
+ @position == other.position
17
+ end
18
+
19
+ def equal?(other,snap)
20
+ @position.x-snap < other.x && @position.x+snap > other.x &&
21
+ @position.y-snap < other.y && @position.y+snap > other.y &&
22
+ @position.z-snap < other.z && @position.z+snap > other.z
23
+ end
24
+
25
+ def clone
26
+ Vertex.new(x,y,z)
27
+ end
28
+
29
+ def to_s
30
+ "#<Geom::Vertex:#{@position.to_s}>"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ module Keyhole
2
+ class Archive < Zip::ZipFile
3
+ DAE_CACHE_PATH = File.join(Floorplanner.config['dae_cache_path'],'dae')
4
+ IMG_CACHE_PATH = File.join(Floorplanner.config['dae_cache_path'],'textures')
5
+
6
+ def dae_path(asset_id)
7
+ FileUtils.mkdir_p DAE_CACHE_PATH
8
+ dae = entries.select{|e| e.name.match(/\.dae$/)}.first
9
+ @relative_dae_path = dae.name
10
+ @dae_path = File.join(
11
+ DAE_CACHE_PATH,
12
+ "#{asset_id}_#{File.basename(dae.name)}"
13
+ )
14
+ dae.extract(@dae_path) unless File.exists?(@dae_path)
15
+ @dae_path
16
+ end
17
+
18
+ def image_path(asset_id,relative_to_dae,relative_out=false)
19
+ FileUtils.mkdir_p IMG_CACHE_PATH
20
+ img_path = File.join(File.dirname(@relative_dae_path),relative_to_dae)
21
+ target_path = File.join(IMG_CACHE_PATH,asset_id)
22
+ tex_path = File.join(target_path,File.basename(img_path))
23
+
24
+ unless File.exists?(tex_path)
25
+ FileUtils.mkdir_p target_path
26
+ zip_image = entries.select{|e| e.name.match(File.basename(img_path)) }.first
27
+ extract( zip_image , tex_path )
28
+ end
29
+ relative_out ? '../textures'+tex_path.gsub(IMG_CACHE_PATH,'') : tex_path
30
+ end
31
+
32
+ def destroy
33
+ File.unlink(@dae_path)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,439 @@
1
+ <?xml version="1.0"?>
2
+ <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
3
+ <asset>
4
+ <contributor>
5
+ <author>john doe &lt;johnny@floorplanner.com&gt;</author>
6
+ </contributor>
7
+ <created>2006-06-21T21:23:22Z</created>
8
+ <modified>2006-06-21T21:23:22Z</modified>
9
+ <unit meter="1.00" name="meter"/>
10
+ <up_axis>Z_UP</up_axis>
11
+ </asset>
12
+
13
+ <library_cameras>
14
+ <camera id="main-cam">
15
+ <optics>
16
+ <technique_common>
17
+ <perspective>
18
+ <yfov>70</yfov>
19
+ <znear>0.1</znear>
20
+ <zfar>100.0</zfar>
21
+ </perspective>
22
+ </technique_common>
23
+ </optics>
24
+ </camera>
25
+ </library_cameras>
26
+
27
+ <library_materials id="surrounding-materials">
28
+ <material id="walls-material" name="WallsMaterial">
29
+ <instance_effect url="#walls-fx"/>
30
+ </material>
31
+ <material id="windows-material" name="WindowsMaterial">
32
+ <instance_effect url="#windows-fx"/>
33
+ </material>
34
+ <% @areas.each do |area_mesh| %>
35
+ <material id="area_<%= area_mesh.data[:id] %>-material" name="<%= area_mesh.data[:name] %>">
36
+ <instance_effect url="#area_<%= area_mesh.data[:id] %>-fx"/>
37
+ </material>
38
+ <% end %>
39
+ <% unless @xrefs; @assets.each_value do |asset| %>
40
+ <% asset.library_materials.each do |materials| %>
41
+ <%= materials.to_s %>
42
+ <% end %>
43
+ <% end; end %>
44
+ </library_materials>
45
+
46
+ <library_effects id="surrounding-effects">
47
+ <effect id="walls-fx">
48
+ <profile_COMMON>
49
+ <technique>
50
+ <phong>
51
+ <emission>
52
+ <color>0.05 0.05 0.05 1</color>
53
+ </emission>
54
+ <ambient>
55
+ <color>0.15 0.15 0.15 1</color>
56
+ </ambient>
57
+ <diffuse>
58
+ <color>0.9 0.9 0.9 1</color>
59
+ </diffuse>
60
+ <specular>
61
+ <color>0 0 0 1</color>
62
+ </specular>
63
+ <shininess>
64
+ <float>0.0</float>
65
+ </shininess>
66
+ <reflective>
67
+ <color>1 1 1 1</color>
68
+ </reflective>
69
+ <reflectivity>
70
+ <float>0.0</float>
71
+ </reflectivity>
72
+ <transparent>
73
+ <color>1 1 1 1</color>
74
+ </transparent>
75
+ <transparency>
76
+ <float>0.0</float>
77
+ </transparency>
78
+ </phong>
79
+ </technique>
80
+ </profile_COMMON>
81
+ </effect>
82
+ <effect id="windows-fx">
83
+ <profile_COMMON>
84
+ <technique>
85
+ <phong>
86
+ <emission>
87
+ <color>0.0 0.0 0.0 1</color>
88
+ </emission>
89
+ <ambient>
90
+ <color>0.15 0.15 0.15 1</color>
91
+ </ambient>
92
+ <diffuse>
93
+ <color>0.9 0.9 1.0 1</color>
94
+ </diffuse>
95
+ <specular>
96
+ <color>0 0 0 1</color>
97
+ </specular>
98
+ <shininess>
99
+ <float>0.0</float>
100
+ </shininess>
101
+ <reflective>
102
+ <color>1 1 1 1</color>
103
+ </reflective>
104
+ <reflectivity>
105
+ <float>0.2</float>
106
+ </reflectivity>
107
+ <transparent>
108
+ <color>1 1 1 1</color>
109
+ </transparent>
110
+ <transparency>
111
+ <float>0.9</float>
112
+ </transparency>
113
+ </phong>
114
+ </technique>
115
+ </profile_COMMON>
116
+ </effect>
117
+ <% @areas.each do |area_mesh|; id = area_mesh.data[:id].to_s %>
118
+ <effect id="area_<%= id %>-fx" name="<%= area_mesh.data[:name] %>">
119
+ <% if area_mesh.data[:texture] %>
120
+ <extra>
121
+ <technique sid="sunflow">
122
+ <diffuse><%= area_mesh.data[:color].join(' ') %></diffuse>
123
+ </technique>
124
+ </extra>
125
+ <% end %>
126
+ <profile_COMMON>
127
+ <% if area_mesh.data[:texture] %>
128
+ <newparam sid="area_<%= id %>-surface">
129
+ <surface type="2D">
130
+ <init_from>area_<%= id %>-image</init_from>
131
+ </surface>
132
+ </newparam>
133
+ <newparam sid="area_<%= id %>-sampler">
134
+ <sampler2D>
135
+ <source>area_<%= id %>-surface</source>
136
+ </sampler2D>
137
+ </newparam>
138
+ <% end %>
139
+ <technique>
140
+ <phong>
141
+ <emission>
142
+ <color>0.05 0.05 0.05 1</color>
143
+ </emission>
144
+ <ambient>
145
+ <color>0.1 0.1 0.1 1</color>
146
+ </ambient>
147
+ <diffuse>
148
+ <% if area_mesh.data[:texture] %>
149
+ <texture texture="area_<%= id %>-sampler"/>
150
+ <% else %>
151
+ <color><%= area_mesh.data[:color].join(' ') %> 1</color>
152
+ <% end %>
153
+ </diffuse>
154
+ <specular>
155
+ <color>0 0 0 1</color>
156
+ </specular>
157
+ <shininess>
158
+ <float>0.0</float>
159
+ </shininess>
160
+ <reflective>
161
+ <color>1 1 1 1</color>
162
+ </reflective>
163
+ <reflectivity>
164
+ <float>0.0</float>
165
+ </reflectivity>
166
+ <transparent>
167
+ <color>1 1 1 1</color>
168
+ </transparent>
169
+ <transparency>
170
+ <float>0.0</float>
171
+ </transparency>
172
+ </phong>
173
+ </technique>
174
+ </profile_COMMON>
175
+ </effect>
176
+ <% end %>
177
+ <% unless @xrefs; @assets.each_value do |asset| %>
178
+ <% asset.library_effects.each do |effects| %>
179
+ <%= effects.to_s %>
180
+ <% end %>
181
+ <% end; end %>
182
+ </library_effects>
183
+
184
+ <library_images>
185
+ <% @areas.each do |area_mesh|; if area_mesh.data[:texture] %>
186
+ <image id="area_<%= area_mesh.data[:id] %>-image" name="<%= area_mesh.data[:name] %>">
187
+ <init_from><%= Floorplanner.config['area_textures_path'] +
188
+ area_mesh.data[:texture].gsub('textures/2d/','').gsub(/\.swf$/,'.png') %></init_from>
189
+ </image>
190
+ <% end; end %>
191
+
192
+ <% unless @xrefs; @assets.each_value do |asset| %>
193
+ <% asset.library_images.each do |image| %>
194
+ <%= image.to_s %>
195
+ <% end %>
196
+ <% end; end %>
197
+ </library_images>
198
+
199
+ <library_geometries id="surrounding-geometry">
200
+
201
+ <geometry id="walls-geom" name="WallsGeometry">
202
+ <mesh>
203
+ <source id="walls-positions" name="position">
204
+ <float_array id="walls-positions-array" count="<%= @walls.vertices.length*3 %>">
205
+ <%= @walls.vertices.collect {|v| v.to_floats }.join(' ') %>
206
+ </float_array>
207
+
208
+ <technique_common>
209
+ <accessor count="<%= @walls.vertices.length %>" offset="0" source="#walls-positions-array" stride="3">
210
+ <param name="X" type="float"></param>
211
+ <param name="Y" type="float"></param>
212
+ <param name="Z" type="float"></param>
213
+ </accessor>
214
+ </technique_common>
215
+ </source>
216
+
217
+ <vertices id="walls-vertices">
218
+ <input semantic="POSITION" source="#walls-positions"/>
219
+ </vertices>
220
+
221
+ <triangles count="<%= @walls.faces.length %>" material="Walls">
222
+ <input semantic="VERTEX" source="#walls-vertices" offset="0"/>
223
+ <p>
224
+ <%= @walls.faces.collect{|f| f.vertices.collect{|v| @walls.vertices.index(v)}}.flatten.join(' ') %>
225
+ </p>
226
+ </triangles>
227
+ </mesh>
228
+ </geometry>
229
+
230
+ <geometry id="windows-geom" name="WindowsGeometry">
231
+ <%
232
+ windows_vertices = @walls.windows.collect {|w| w.vertices}.flatten
233
+ windows_faces = @walls.windows.collect {|w| w.faces}.flatten
234
+ %>
235
+ <mesh>
236
+ <source id="windows-positions" name="position">
237
+ <float_array id="windows-positions-array" count="<%= @walls.vertices.length*3 %>">
238
+ <%= windows_vertices.collect {|v| v.to_floats }.join(' ') %>
239
+ </float_array>
240
+
241
+ <technique_common>
242
+ <accessor count="<%= windows_vertices.length %>" offset="0" source="#windows-positions-array" stride="3">
243
+ <param name="X" type="float"></param>
244
+ <param name="Y" type="float"></param>
245
+ <param name="Z" type="float"></param>
246
+ </accessor>
247
+ </technique_common>
248
+ </source>
249
+
250
+ <vertices id="windows-vertices">
251
+ <input semantic="POSITION" source="#windows-positions"/>
252
+ </vertices>
253
+
254
+ <triangles count="<%= windows_faces.length %>" material="Window">
255
+ <input semantic="VERTEX" source="#windows-vertices" offset="0"/>
256
+ <p>
257
+ <% offset = 0
258
+ @walls.windows.each do |w|
259
+ %>
260
+ <%= w.faces.collect{|f| f.vertices.collect{|v| w.vertices.index(v) + offset}}.flatten.join(' ') %>
261
+ <% offset += w.vertices.length ; end %>
262
+ </p>
263
+ </triangles>
264
+ </mesh>
265
+ </geometry>
266
+ <% @areas.each do |area_mesh|; id = area_mesh.data[:id] %>
267
+ <geometry id="area_<%= id %>-geom" name="Area_<%= id %>_<%= area_mesh.data[:name] %>-Geometry">
268
+ <mesh>
269
+ <source id="area_<%= id %>-positions" name="position">
270
+
271
+ <float_array id="area_<%= id %>-positions-array" count="<%= area_mesh.vertices.length*3 %>">
272
+ <%= area_mesh.vertices.collect {|v| v.to_floats }.join(' ') %>
273
+ </float_array>
274
+
275
+ <technique_common>
276
+ <accessor count="<%= area_mesh.vertices.length %>" offset="0" source="#area_<%= id %>-positions-array" stride="3">
277
+ <param name="X" type="float"></param>
278
+ <param name="Y" type="float"></param>
279
+ <param name="Z" type="float"></param>
280
+ </accessor>
281
+ </technique_common>
282
+ </source>
283
+
284
+ <source id="area_<%= id %>-uvs" name="texcoord">
285
+ <float_array id="area_<%= id %>-uvs-array" count="<%= area_mesh.texcoord.length %>">
286
+ <%= area_mesh.texcoord.map{|n| [n.u,n.v]}.flatten.join(' ') %>
287
+ </float_array>
288
+ <technique_common>
289
+ <accessor count="44" source="#area_<%= id %>-uvs-array" stride="2">
290
+ <param name="S" type="float"/>
291
+ <param name="T" type="float"/>
292
+ </accessor>
293
+ </technique_common>
294
+ </source>
295
+
296
+ <vertices id="area_<%= id %>-vertices">
297
+ <input semantic="POSITION" source="#area_<%= id %>-positions"/>
298
+ </vertices>
299
+
300
+ <triangles count="<%= area_mesh.faces.length %>" material="<%= "Area_"+id %>">
301
+ <input semantic="VERTEX" source="#area_<%= id %>-vertices" offset="0"/>
302
+ <input semantic="TEXCOORD" source="#area_<%= id %>-uvs" offset="1"/>
303
+ <p>
304
+ <%=
305
+ area_mesh.faces.collect { |f|
306
+ v = f.vertices.collect { |v| area_mesh.vertices.index(v) }
307
+ t = f.texcoord.collect { |t| area_mesh.texcoord.index(t) }
308
+ [v[0],t[0],v[1],t[1],v[2],t[2]]
309
+ }.flatten.join(' ')
310
+ %>
311
+ </p>
312
+ </triangles>
313
+ </mesh>
314
+ </geometry>
315
+ <% end %>
316
+ <% unless @xrefs; @assets.each_value do |asset| %>
317
+ <% asset.library_geometries.each do |geometries| %>
318
+ <%= geometries.to_s %>
319
+ <% end %>
320
+ <% end; end %>
321
+ </library_geometries>
322
+ <% unless @xrefs %>
323
+ <library_nodes>
324
+ <% @assets.each_value do |asset| %>
325
+ <% asset.library_nodes.each do |nodes| %>
326
+ <%= nodes.to_s %>
327
+ <% end %>
328
+ <% end %>
329
+ </library_nodes>
330
+ <% end %>
331
+ <library_visual_scenes>
332
+ <visual_scene id="VisualSceneNode" name="<%= @name %>">
333
+ <extra>
334
+ <technique sid="sunflow">
335
+ <image>
336
+ <resolution>800 600</resolution>
337
+ <aa>0 2</aa>
338
+ <filter>gaussian</filter>
339
+ <sampler>ipr</sampler>
340
+ </image>
341
+ <trace_depths>
342
+ <diffuse>4</diffuse>
343
+ <reflection>2</reflection>
344
+ </trace_depths>
345
+ <gi>
346
+ <type>ambocc</type>
347
+ <bright>1 1 1</bright>
348
+ <dark>0 0 0</dark>
349
+ <samples>64</samples>
350
+ <maxdist>3.0 </maxdist>
351
+ </gi>
352
+ <sunsky>
353
+ <up>0 0 1</up>
354
+ <east>1 0 0</east>
355
+ <sundir>0.2 0.4 0.7</sundir>
356
+ <ground>1 1 1</ground>
357
+ </sunsky>
358
+ </technique>
359
+ </extra>
360
+ <node id="Camera" name="Camera">
361
+ <lookat>
362
+ 9.0 -1.0 1.4
363
+ 8.0 -2.0 1.4
364
+ 0.0 0.0 1.0
365
+ </lookat>
366
+ <instance_camera url="#main-cam"/>
367
+ </node>
368
+ <node id="Walls" name="Walls">
369
+ <instance_geometry url="#walls-geom">
370
+ <bind_material>
371
+ <technique_common>
372
+ <instance_material symbol="Walls" target="#walls-material"/>
373
+ </technique_common>
374
+ </bind_material>
375
+ </instance_geometry>
376
+ </node>
377
+ <node id="Windows" name="Windows">
378
+ <instance_geometry url="#windows-geom">
379
+ <bind_material>
380
+ <technique_common>
381
+ <instance_material symbol="Window" target="#windows-material"/>
382
+ </technique_common>
383
+ </bind_material>
384
+ </instance_geometry>
385
+ </node>
386
+ <node id="Areas" name="Areas">
387
+ <% @areas.each do |area_mesh|; id = area_mesh.data[:id] %>
388
+ <node id="Area_<%= id %>" name="Area_<%= id %>">
389
+ <instance_geometry url="#area_<%= id %>-geom">
390
+ <bind_material>
391
+ <technique_common>
392
+ <instance_material symbol="Area_<%= id %>" target="#area_<%= id %>-material"/>
393
+ </technique_common>
394
+ </bind_material>
395
+ </instance_geometry>
396
+ </node>
397
+ <% if area_mesh.data[:type] == 'generated_area' %>
398
+ <node id="Area_<%= id %>-cap" name="Area_<%= id %>-cap">
399
+ <translate>0.0 0.0 <%= @areas.ceiling_z %></translate>
400
+ <instance_geometry url="#area_<%= id %>-geom">
401
+ <bind_material>
402
+ <technique_common>
403
+ <instance_material symbol="Area_<%= id %>" target="#walls-material"/>
404
+ </technique_common>
405
+ </bind_material>
406
+ </instance_geometry>
407
+ </node>
408
+ <% end %>
409
+ <% end %>
410
+ </node>
411
+ <% @elements.each do |element| %>
412
+ <node name="<%= element[:asset].title %>">
413
+ <translate><%= element[:position].to_floats %></translate>
414
+ <% if element[:matrix] %>
415
+ <matrix><%= element[:matrix].to_a.join ' ' %></matrix>
416
+ <% end %>
417
+ <% if element[:rotation].x > 0 %>
418
+ <rotate sid="rotateX">1 0 0 <%= element[:rotation].x %></rotate>
419
+ <% end %>
420
+ <% if element[:rotation].y > 0 %>
421
+ <rotate sid="rotateY">0 1 0 <%= element[:rotation].y %></rotate>
422
+ <% end %>
423
+ <% if element[:rotation].z > 0 %>
424
+ <rotate sid="rotateZ">0 0 1 <%= element[:rotation].z %></rotate>
425
+ <% end %>
426
+ <scale><%= element[:scale].to_floats %></scale>
427
+ <% if @xrefs %>
428
+ <instance_node url="<%= element[:asset].dae_path %>"/>
429
+ <% else %>
430
+ <%= element[:asset].visual_scene_node.to_s %>
431
+ <% end %>
432
+ </node>
433
+ <% end %>
434
+ </visual_scene>
435
+ </library_visual_scenes>
436
+ <scene>
437
+ <instance_visual_scene url="#VisualSceneNode"/>
438
+ </scene>
439
+ </COLLADA>