obj_file_path = ARGV[0] s3d_file_path = ARGV[1] def next_largest_multiple_of_four(n) candidate = (n >> 2) << 2 if candidate < n then candidate + 4 else candidate end end def pad_to_16_bytes(data) size = data.size candidate = (size >> 4) << 4 new_size = if candidate < size then candidate + 16 else size end data + "\0" * (new_size - size) end DOUBLE = '(-?\d+(\.\d+)?(e(-|\+)?\d+)?)' positions = [] texture_coordinates = [] normals = [] current_material = 'UNKNOWN' meshes = {} File.open(obj_file_path) do |obj_file| obj_file.each_line do |line| if line =~ /^v\s+#{DOUBLE}\s+#{DOUBLE}\s+#{DOUBLE}/ then positions << [$1.to_f, $5.to_f, $9.to_f] elsif line =~ /^vt\s+#{DOUBLE}\s+#{DOUBLE}/ then texture_coordinates << [$1.to_f, $5.to_f] elsif line =~ /^vn\s+#{DOUBLE}\s+#{DOUBLE}\s+#{DOUBLE}/ then normals << [$1.to_f, $5.to_f, $9.to_f] elsif line =~ /^usemtl\s+(.*)/ then current_material = $1.chomp elsif line =~ /^f\s+(.*)/ then meshes[current_material] ||= [] face_vertices = $1.chomp.split face = face_vertices.collect do |vertex| if vertex =~ /(\d+)\/(\d+)\/(\d+)/ then [$1.to_i, $2.to_i, $3.to_i] else raise "vertex syntax error: #{vertex}" end end meshes[current_material] << face elsif line =~ /^#/ then else puts "ignoring line: #{line}" end end end s3d = 'Static3D' + [0, 1, meshes.size, 0, 0, 0].pack('V*') s3d += ([0] * next_largest_multiple_of_four(meshes.size)).pack('V*') mesh_info = [] meshes.keys.each do |shader_name| puts "processing mesh #{shader_name}..." faces = meshes[shader_name] next_index = 0 vertex_map = {} vertices = [] indices = [] vertex_count = 0 index_count = 0 faces.each do |face| if face.size < 3 then raise 'faces must have at least 3 vertices' end face.each do |vertex| index = vertex_map[vertex] if index == nil then vertex_map[vertex] = next_index vertices << [texture_coordinates[vertex[1] - 1], normals[vertex[2] - 1], positions[vertex[0] - 1]].flatten vertex_count += 1 next_index += 1 end end 2.upto(face.size - 1) do |i| indices << vertex_map[face[0]] indices << vertex_map[face[i - 1]] indices << vertex_map[face[i]] index_count += 3 end end vertex_array = pad_to_16_bytes(vertices.flatten.pack('e*')) index_array = pad_to_16_bytes(indices.pack('v*')) vertex_array_offset = s3d.size s3d += vertex_array index_array_offset = s3d.size s3d += index_array mesh_info << [vertex_count, vertex_array_offset, index_count, index_array_offset] end meshes.keys.each_with_index do |shader_name, i| info = mesh_info[i] data = pad_to_16_bytes(info.pack('V*') + shader_name + "\0") offset_offset = 32 + i * 4 s3d[offset_offset...offset_offset + 4] = [s3d.size].pack('V') s3d += data end File.open(s3d_file_path, 'wb') do |s3d_file| s3d_file.write(s3d) end