require 'sketchup.rb' # By Forrester Cole, Princeton Univ #----------------------------------------------------------------------------- def roundToPlace( x, place ) val = (x / place).round.to_f * place if val.abs < place val = 0 end return val end def pointToString( v ) rounded = v.to_a.collect { |a| roundToPlace( a.to_m, 0.0001 ) } return rounded[0].to_s + " " + rounded[1].to_s + " " + rounded[2].to_s end def normalToString( v ) rounded = v.to_a.collect { |a| roundToPlace( a, 0.0001 ) } return rounded[0].to_s + " " + rounded[1].to_s + " " + rounded[2].to_s end def matTranspose( t ) inar = t.to_a outar = Array.new(16) for i in 0...16 outar[(i/4) + (i%4)*4] = inar[i] end return Geom::Transformation.new(outar) end def lookupInHash( hash, key ) if hash.has_key? key return hash[key] else size = hash.length hash[key] = size return size end end # returns an array of triangles, updates the vertex_hash with any new vertices def getTriangles( transformation, entity_list, vertex_hash, normal_hash ) all_faces = entity_list.find_all { |e| e.kind_of? Sketchup::Face } tris = Array.new norm_trans = matTranspose(transformation).inverse all_faces.each { |f| color = [1,1,1] if f.material for i in 0...3 color[i] = roundToPlace(f.material.color.to_a[i] / 255.0, 0.0001) end end mesh = f.mesh 4 mesh.polygons.each { |p| if p.length == 3 tri = Array.new trans_verts = Array.new trans_norms = Array.new # a negative index in p means the edge is hidden for i in 0...3 trans_verts.push(mesh.point_at(p[i].abs).transform(transformation)) vertkey = pointToString( trans_verts[i] ) tri.push( lookupInHash( vertex_hash, vertkey ) ) end for i in 0...3 trans_norms.push(mesh.normal_at(p[i].abs).transform(norm_trans)) normkey = normalToString( trans_norms[i] ) tri.push( lookupInHash( normal_hash, normkey ) ) end # see if vertex order needs to be flipped edge_1 = trans_verts[1] - trans_verts[0] edge_2 = trans_verts[2] - trans_verts[1] computed_norm = edge_1.cross(edge_2) if computed_norm.dot(trans_norms[0]) < 0 # face is backwards swap = tri[0] # swap a vertex tri[0] = tri[1] tri[1] = swap swap = tri[3] # swap a normal tri[3] = tri[4] tri[4] = swap end tri = tri + color tris.push tri end } } return tris end def getPaths( transformation, entity_list, vertex_hash ) all_edges = entity_list.find_all { |e| e.kind_of? Sketchup::Edge } # can't get curves for the scene this way, for some reason #all_curves = entity_list.find_all { |e| e.kind_of? Sketchup::ArcCurve } paths = Array.new curve_hash = Hash.new all_edges.each{ |e| if not e.curve edge = Array.new edge.push [e.soft?, e.smooth?] verts = Array.new for i in 0...2 trans_vert = e.vertices[i].position.transform(transformation) vertkey = pointToString( trans_vert ) verts.push( lookupInHash( vertex_hash, vertkey ) ) end edge.push verts paths.push edge else curve_hash[e.curve] = curve_hash.length end } all_curves = curve_hash.keys all_curves.each{ |c| path = Array.new path.push [c.edges[0].soft?, c.edges[0].smooth?] verts = Array.new trans_vert = c.edges[0].vertices[0].position.transform(transformation) vertkey = pointToString( trans_vert ) verts.push( lookupInHash( vertex_hash, vertkey ) ) c.each_edge { |e| trans_vert = e.vertices[1].position.transform(transformation) vertkey = pointToString( trans_vert ) verts.push( lookupInHash( vertex_hash, vertkey ) ) } path.push verts paths.push path } return paths end def traverseSceneGraph( transformation, entity_list, vertex_hash, normal_hash, tri_list, path_list ) tri_list.concat getTriangles( transformation, entity_list, vertex_hash, normal_hash ) path_list.concat getPaths( transformation, entity_list, vertex_hash ) all_components = entity_list.find_all { |e| e.kind_of? Sketchup::ComponentInstance } all_components.each { |c| new_trans = transformation * c.transformation traverseSceneGraph( new_trans, c.definition.entities, vertex_hash, normal_hash, tri_list, path_list ) } all_groups = entity_list.find_all { |e| e.kind_of? Sketchup::Group } all_groups.each { |g| new_trans = transformation * g.transformation traverseSceneGraph( new_trans, g.entities, vertex_hash, normal_hash, tri_list, path_list ) } end def writeVersion( output_file ) output_file.print "OFF\n" end def writeCountHeader( output_file, num_verts, num_tris, num_edges ) output_file.print num_verts, " ", num_tris, " ", num_edges, "\n" end def writeVertices( output_file, vertex_hash ) vertex_array = vertex_hash.sort { |a, b| a[1] <=> b[1] } vertex_array.each { |v| output_file.print v[0], "\n" } output_file.print "\n" end def writeTriangles( output_file, tris ) tris.each { |t| # 0 to 6 contain position and normals # print a 3 for number of verts in this face (triangles) output_file.print "3 " for i in 0...3 output_file.print t[i], " " end # 6 to 8 contain color # for i in 6...8 # output_file.print t[i], " " # end # print a 1 for alpha # output_file.print t[8], " 1\n" output_file.print "\n" } output_file.print "\n" end def exportOFF( filename ) output_file = File.open(filename, "w") writeVersion(output_file) vertex_hash = Hash.new normal_hash = Hash.new tri_list = Array.new path_list = Array.new traverseSceneGraph( Geom::Transformation.new, Sketchup.active_model.entities, vertex_hash, normal_hash, tri_list, path_list ) writeCountHeader( output_file, vertex_hash.size, tri_list.size, tri_list.size ) writeVertices( output_file, vertex_hash ) writeTriangles( output_file, tri_list ) output_file.close end def chooseName filename = UI.savepanel "Export OFF", ".", "blah.off" if (filename) exportOFF( filename ) end end if( not file_loaded?("off_exporter.rb") ) plugins_menu = UI.menu("Plugins") #plugins_menu.add_separator plugins_menu.add_item("Export OFF...") { chooseName } end #----------------------------------------------------------------------------- file_loaded("off_exporter.rb")