blender-3d 0.1.0

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14b1b6b25b75bc3144579623d6ebaf1eb20ed295
4
+ data.tar.gz: 94d505a329ebe729c2970ad9cf563829b365bcf4
5
+ SHA512:
6
+ metadata.gz: b10f4977194b4c18d5790b7bcb2aaf2674404751bf0aa63843eaafbd6df47626ad5db875099e6a5f090f2e4e49a2b76258ed97ce84db43eb9fb5afa4ac1a491f
7
+ data.tar.gz: 4eea3fb816ffb01d0fcf08e1a77a7ba7cad124a813dcc953b837bb7b7c2e64feba455f62635053fc3c033b0c7aec11f97a7014bcd1cd728c3885481ec2610e00
@@ -0,0 +1,31 @@
1
+ require 'stringio'
2
+
3
+ require_relative 'blender-3d/version'
4
+ require_relative 'blender-3d/object_reader'
5
+
6
+ require_relative 'blender-3d/pointer'
7
+ require_relative 'blender-3d/types'
8
+ require_relative 'blender-3d/field'
9
+ require_relative 'blender-3d/char_field_parser'
10
+ require_relative 'blender-3d/generic_field_parser'
11
+ require_relative 'blender-3d/field_parser_factory'
12
+
13
+ require_relative 'blender-3d/structure'
14
+ require_relative 'blender-3d/structure_definition'
15
+
16
+ require_relative 'blender-3d/file_header'
17
+ require_relative 'blender-3d/file_block'
18
+ require_relative 'blender-3d/dna_block'
19
+ require_relative 'blender-3d/model'
20
+
21
+
22
+ =begin
23
+
24
+ Usage:
25
+
26
+ require 'blender-3d'
27
+ # require_relative 'blender-3d'
28
+
29
+ model = Blender3d::Model.from_file('cube.blend');
30
+
31
+ =end
@@ -0,0 +1,39 @@
1
+ module Blender3d
2
+ class CharFieldParser
3
+ def initialize(name)
4
+ @name = name
5
+ end
6
+
7
+ def parse
8
+ type = case @name
9
+ when GenericFieldParser::FUNCTION_POINTER
10
+ FunctionPointerType.new(SimpleType.new(FieldParserFactory::CHAR_TYPE))
11
+
12
+ when GenericFieldParser::POINTER_ARRAY
13
+ array_size = $2.to_i
14
+ ArrayType.new(NullTerminatedStringType.new, array_size)
15
+
16
+ when GenericFieldParser::MULTI_POINTER
17
+ PointerType.new(NullTerminatedStringType.new)
18
+
19
+ when GenericFieldParser::POINTER
20
+ NullTerminatedStringType.new
21
+
22
+ when GenericFieldParser::MULTI_ARRAY
23
+ array_size, string_size = $2.to_i, $3.to_i
24
+ ArrayType.new(FixedLengthStringType.new(string_size), array_size)
25
+
26
+ when GenericFieldParser::ARRAY
27
+ array_size = $2.to_i
28
+ FixedLengthStringType.new(array_size)
29
+
30
+ else
31
+ SimpleType.new(FieldParserFactory::CHAR_TYPE)
32
+
33
+ end
34
+
35
+ name = $1 || @name
36
+ Field.new(type, name)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ module Blender3d
2
+ class DnaBlock
3
+ attr_accessor :code, :types, :structures
4
+
5
+ def initialize(reader = nil)
6
+ deserialize(reader) if reader
7
+ end
8
+
9
+ def deserialize(reader)
10
+ Reader.new(reader, self).read
11
+ end
12
+
13
+ class Reader
14
+ def initialize(reader, dna_block = DnaBlock.new)
15
+ @reader, @dna_block = reader, dna_block
16
+ end
17
+
18
+ def read
19
+ @dna_block.code = @reader.read(4)
20
+ @names = read_names
21
+ read_types
22
+ read_structures
23
+ @dna_block
24
+ end
25
+
26
+ private
27
+
28
+ def read_names
29
+ @reader.read(4) # identifier 'NAME'
30
+ count = @reader.read_int32
31
+ count.times.map { @reader.read_string.chop.freeze }
32
+ end
33
+
34
+ def read_types
35
+ read_aligned # identifier 'TYPE' 4 byte aligned
36
+
37
+ count = @reader.read_int32
38
+ types = count.times.map { @reader.read_string.chop.freeze }
39
+
40
+ read_aligned # identifier 'TLEN' 4 byte aligned
41
+ lengths = count.times.map { @reader.read_int16 }
42
+
43
+ @dna_block.types = types.zip(lengths)
44
+ end
45
+
46
+ def read_aligned
47
+ offset = 4 + (( 4 - (@reader.tell % 4) ) % 4)
48
+ @reader.read(offset)
49
+ end
50
+
51
+ def read_structures
52
+ read_aligned # identifier 'STRC' 4 byte aligned
53
+ count = @reader.read_int32
54
+ @dna_block.structures = count.times.map { read_structure }
55
+ end
56
+
57
+ def read_structure
58
+ type_index = @reader.read_int16
59
+ num_fields = @reader.read_int16
60
+
61
+ fields = num_fields.times.map { read_field }
62
+
63
+ StructureDefinition.new(*@dna_block.types[type_index], fields)
64
+ end
65
+
66
+ def read_field
67
+ field_type = @dna_block.types[@reader.read_int16][0]
68
+ field_name = @names[@reader.read_int16]
69
+ factory = FieldParserFactory.create_for(field_type, field_name)
70
+ factory.parse
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,17 @@
1
+ module Blender3d
2
+ class Field
3
+ attr_reader :type, :name
4
+
5
+ def initialize(type, name)
6
+ @type, @name = type, name.to_sym
7
+ end
8
+
9
+ def to_s
10
+ "Field[#@name](#@type)"
11
+ end
12
+
13
+ def inspect
14
+ "#{@type.inspect} #@name"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Blender3d
2
+ module FieldParserFactory
3
+ CHAR_TYPE = 'char'.freeze
4
+
5
+ def self.create_for(type, name)
6
+ return CharFieldParser.new(name) if type == CHAR_TYPE
7
+ GenericFieldParser.new(type, name)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module Blender3d
2
+ class FileBlock
3
+ attr_accessor :code, :size, :pointer, :type_index, :count, :data, :type
4
+
5
+ def initialize(reader = nil)
6
+ deserialize(reader) if reader
7
+ end
8
+
9
+ def deserialize(reader)
10
+ Reader.new(reader, self).read
11
+ end
12
+
13
+ def parse_data(model)
14
+ file = StringIO.new(self.data)
15
+ reader = model.create_reader(file)
16
+ self.data = self.count.times.map { self.type.read(reader) }
17
+ end
18
+
19
+ class Reader
20
+ def initialize(reader, file_block = FileBlock.new)
21
+ @reader, @file_block = reader, file_block
22
+ end
23
+
24
+ def read
25
+ @file_block.code = @reader.read(4).gsub(/\0.*$/, '')
26
+ @file_block.size = @reader.read_uint32
27
+ @file_block.pointer = Pointer.new(@reader.read_pointer)
28
+ @file_block.type_index = @reader.read_uint32
29
+ @file_block.count = @reader.read_uint32
30
+ @file_block.data = @reader.read(@file_block.size)
31
+ read_dna if @file_block.code == 'DNA1'
32
+ @file_block
33
+ end
34
+
35
+ private def read_dna
36
+ file = StringIO.new(@file_block.data)
37
+ @reader = @reader.model.create_reader(file)
38
+ @file_block.data = DnaBlock.new(@reader)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+
@@ -0,0 +1,41 @@
1
+ module Blender3d
2
+ class FileHeader
3
+ attr_accessor :identifier,
4
+ :version
5
+
6
+ attr_reader :pointer_size, :size_code,
7
+ :endianness, :endian_code
8
+
9
+ def initialize(file = nil)
10
+ deserialize(file) if file
11
+ end
12
+
13
+ def deserialize(file)
14
+ self.identifier = file.read(7)
15
+ self.pointer_size = file.read(1) == '-'.freeze ? 8 : 4
16
+ self.endianness = file.read(1) == 'v'.freeze ? :little : :big
17
+ self.version = file.read(1) + '.'.freeze + file.read(2)
18
+ self
19
+ end
20
+
21
+ def pointer_size=(value)
22
+ @pointer_size = value
23
+ @size_code = @pointer_size == 8 ? 'Q'.freeze : 'L'.freeze
24
+ end
25
+
26
+ def endianness=(value)
27
+ @endianness = value
28
+ @endian_code = little_endian? ? '<'.freeze : '>'.freeze
29
+ end
30
+
31
+ def little_endian?
32
+ @endianness == :little
33
+ end
34
+
35
+ def big_endian?
36
+ !little_endian?
37
+ end
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,54 @@
1
+ module Blender3d
2
+ class GenericFieldParser
3
+ FUNCTION_POINTER = /^\(\*([^\)]+)\)\(\)$/
4
+ POINTER_ARRAY = /^\*([^\[]+)\[(\d+)\]$/
5
+ MULTI_POINTER = /^\*{2}(.+)$/
6
+ POINTER = /^\*(.+)$/
7
+ MULTI_ARRAY = /^([^\[]+)\[(\d+)\]\[(\d+)\]$/
8
+ ARRAY = /^([^\[]+)\[(\d+)\]$/
9
+
10
+ def initialize(type, name)
11
+ @type, @name = type, name
12
+ end
13
+
14
+ def parse
15
+ type = case @name
16
+ when FUNCTION_POINTER
17
+ FunctionPointerType.new(@type)
18
+
19
+ when POINTER_ARRAY
20
+ array_size = $2.to_i
21
+ ArrayType.new(build_pointer_type, array_size)
22
+
23
+ when MULTI_POINTER
24
+ PointerType.new(build_pointer_type)
25
+
26
+ when POINTER
27
+ build_pointer_type
28
+
29
+ when MULTI_ARRAY
30
+ array1_size, array2_size = $2.to_i, $3.to_i
31
+ ArrayType.new(ArrayType.new(build_simple_type, array2_size), array1_size)
32
+
33
+ when ARRAY
34
+ array_size = $2.to_i
35
+ ArrayType.new(build_simple_type, array_size)
36
+
37
+ else
38
+ build_simple_type
39
+
40
+ end
41
+
42
+ name = $1 || @name
43
+ Field.new(type, name)
44
+ end
45
+
46
+ private def build_pointer_type
47
+ PointerType.new(build_simple_type)
48
+ end
49
+
50
+ private def build_simple_type
51
+ SimpleType.new(@type)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,56 @@
1
+ module Blender3d
2
+ class Model
3
+ attr_reader :header, :blocks, :pointers
4
+
5
+ def self.from_file(path)
6
+ File.open(path, 'rb') { |file| new(file) }
7
+ end
8
+
9
+ def initialize(file = nil)
10
+ @blocks = []
11
+ deserialize(file) if file
12
+ end
13
+
14
+ def deserialize(file)
15
+ @header = FileHeader.new(file)
16
+ reader = create_reader(file)
17
+ @blocks.clear
18
+
19
+ loop do
20
+ @blocks << FileBlock.new(reader)
21
+ break if reader.tell == reader.file.size
22
+ end
23
+
24
+ dna_block = self.dna_block
25
+ return self unless dna_block
26
+
27
+ @blocks.select { |block| block.type_index != 0 }.each do |block|
28
+ block.type = dna_block.data.structures[block.type_index]
29
+ block.parse_data(self)
30
+ end
31
+
32
+ @pointers = {}
33
+ @blocks.each do |block|
34
+ if block.data.is_a?(Array)
35
+ pointer = block.pointer.location
36
+ block.data.each do |data|
37
+ @pointers[Pointer.new(pointer)] = data
38
+ pointer += data.class.definition.size
39
+ end
40
+ else
41
+ @pointers[block.pointer] = block.data
42
+ end
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ def dna_block
49
+ @blocks.find { |block| block.code == 'DNA1' }
50
+ end
51
+
52
+ def create_reader(file)
53
+ ObjectReader.new(file, self)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,47 @@
1
+ module Blender3d
2
+ class ObjectReader
3
+ attr_reader :file
4
+ attr_accessor :model
5
+
6
+ def initialize(file, model = nil)
7
+ @file, @model = file, model
8
+ endian = @model.header.endian_code
9
+
10
+ %w'c s l q'.each.with_index do |code, i|
11
+ bytes = 1 << i
12
+ bits = 8 * bytes
13
+ name = 'int' + bits.to_s
14
+ define_reader name, bytes, code + endian
15
+ define_reader 'u' + name, bytes, code.upcase + endian
16
+ end
17
+
18
+ code = @model.header.little_endian? ? 'e' : 'g'
19
+ define_reader 'float', 4, code
20
+ define_reader 'double', 8, code.upcase
21
+
22
+ define_reader 'pointer', @model.header.pointer_size, @model.header.size_code + endian
23
+ end
24
+
25
+ private def define_reader(name, num_bytes, unpack_type)
26
+ instance_eval "def read_#{name}; @file.read(#{num_bytes}).unpack('#{unpack_type}').first end"
27
+ end
28
+
29
+ def read(length)
30
+ @file.read(length)
31
+ end
32
+
33
+ def read_string
34
+ @file.gets("\0")
35
+ end
36
+
37
+ def seek(position)
38
+ @file.seek(position)
39
+ end
40
+
41
+ def tell
42
+ @file.tell
43
+ end
44
+ end
45
+ end
46
+
47
+
@@ -0,0 +1,27 @@
1
+ module Blender3d
2
+ class Pointer
3
+ attr_reader :location
4
+
5
+ def initialize(location)
6
+ @location = location
7
+ end
8
+
9
+ def to_s
10
+ "Pointer(#{inspect})"
11
+ end
12
+
13
+ def inspect
14
+ return '0x0' if @location == 0
15
+ return '0x%08x' % @location if @location < (1 << 32)
16
+ '0x%016x' % @location
17
+ end
18
+
19
+ def <=>(other)
20
+ other.is_a?(Pointer) ? location <=> other.location : 1
21
+ end
22
+
23
+ def ==(other)
24
+ (self <=> other) == 0
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module Blender3d
2
+ class Structure
3
+ class << self
4
+ attr_reader :definition
5
+ end
6
+
7
+ def initialize(reader = nil)
8
+ deserialize(reader) if reader
9
+ end
10
+
11
+ def deserialize(reader)
12
+ self.class.definition.fields.each do |field|
13
+ value = field.type.read(reader)
14
+ instance_variable_set "@#{field.name}", value
15
+ end
16
+ self
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Blender3d
2
+ class StructureDefinition
3
+ attr_reader :name, :size, :fields
4
+
5
+ def initialize(name, size, fields = [])
6
+ name = name[0].upcase + name[1..-1]
7
+ @name, @size, @fields = name, size, fields
8
+ end
9
+
10
+ def read(reader)
11
+ structure_class.new(reader)
12
+ end
13
+
14
+ def structure_class
15
+ Blender3d.const_defined?(name, false) ? Blender3d.const_get(name, false) : define_structure_class
16
+ end
17
+
18
+ private
19
+
20
+ def define_structure_class
21
+ fields = self.fields
22
+ struct_class = Class.new(Structure) { attr_accessor(*fields.map(&:name)) }
23
+ struct_class.instance_variable_set :@definition, self
24
+ Blender3d.const_set(name, struct_class)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,140 @@
1
+ module Blender3d
2
+ class SimpleType
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def to_s
10
+ @name
11
+ end
12
+
13
+ def inspect
14
+ @name
15
+ end
16
+
17
+ def read(reader)
18
+ case @name
19
+ when 'char' then reader.read(1)
20
+ when 'double' then reader.read_double
21
+ when 'float' then reader.read_float
22
+ when 'int' then reader.read_int32
23
+ when 'int64_t' then reader.read_int64
24
+ when 'long' then reader.read_int32
25
+ when 'short' then reader.read_int16
26
+ when 'uchar' then reader.read_uint8
27
+ when 'uint64_t' then reader.read_uint64
28
+ when 'ulong' then reader.read_uint32
29
+ when 'ushort' then reader.read_uint16
30
+ else
31
+ dna = reader.model.dna_block.data
32
+ struct = dna.structures.find { |struct| struct.name == @name }
33
+ return struct.read(reader) if struct
34
+ length = dna.types.find { |name, size| name == @name }&.last
35
+ return reader.read(length) if length
36
+ raise 'type not found'
37
+ end
38
+ end
39
+ end
40
+
41
+ class PointerType
42
+ attr_reader :type
43
+
44
+ def initialize(type)
45
+ @type = type
46
+ end
47
+
48
+ def to_s
49
+ "Pointer(#@type)"
50
+ end
51
+
52
+ def inspect
53
+ "#{@type.inspect}*"
54
+ end
55
+
56
+ def read(reader)
57
+ Pointer.new(reader.read_pointer)
58
+ end
59
+ end
60
+
61
+ class FunctionPointerType
62
+ attr_reader :return_type
63
+
64
+ def initialize(return_type)
65
+ @return_type = return_type
66
+ end
67
+
68
+ def to_s
69
+ "Function(#@return_type)"
70
+ end
71
+
72
+ def inspect
73
+ "#{@return_type.inspect} (*)()"
74
+ end
75
+
76
+ def read(reader)
77
+ Pointer.new(reader.read_pointer)
78
+ end
79
+ end
80
+
81
+ # equivalent to PointerType<SimpleType<char>> (char*)
82
+ class NullTerminatedStringType
83
+ def to_s
84
+ "NullTerminatedString"
85
+ end
86
+
87
+ def inspect
88
+ 'char*'
89
+ end
90
+
91
+ def read(reader)
92
+ reader.read_string.chop
93
+ end
94
+ end
95
+
96
+ # equivalent to ArrayType<SimpleType<char>, length> (char[length])
97
+ class FixedLengthStringType
98
+ attr_reader :length
99
+
100
+ def initialize(length)
101
+ @length = length
102
+ end
103
+
104
+ def to_s
105
+ "FixedLengthString(#@length)"
106
+ end
107
+
108
+ def inspect
109
+ "char[#@length]"
110
+ end
111
+
112
+ def read(reader)
113
+ reader.read(@length).gsub(/\0.*$/, '')
114
+ end
115
+ end
116
+
117
+ class ArrayType
118
+ attr_reader :type, :size
119
+
120
+ def initialize(type, size)
121
+ @type, @size = type, size
122
+ end
123
+
124
+ def dimensions
125
+ @sizes.size
126
+ end
127
+
128
+ def to_s
129
+ "Array[#@size](#@type)"
130
+ end
131
+
132
+ def inspect
133
+ "#{@type.inspect}[#@size]"
134
+ end
135
+
136
+ def read(reader)
137
+ @size.times.map { @type.read(reader) }
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module Blender3d
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blender-3d
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SilverPhoenix99
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Blender 3d file parser.
14
+ email:
15
+ - silver.phoenix99@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/blender-3d.rb
21
+ - lib/blender-3d/char_field_parser.rb
22
+ - lib/blender-3d/dna_block.rb
23
+ - lib/blender-3d/field.rb
24
+ - lib/blender-3d/field_parser_factory.rb
25
+ - lib/blender-3d/file_block.rb
26
+ - lib/blender-3d/file_header.rb
27
+ - lib/blender-3d/generic_field_parser.rb
28
+ - lib/blender-3d/model.rb
29
+ - lib/blender-3d/object_reader.rb
30
+ - lib/blender-3d/pointer.rb
31
+ - lib/blender-3d/structure.rb
32
+ - lib/blender-3d/structure_definition.rb
33
+ - lib/blender-3d/types.rb
34
+ - lib/blender-3d/version.rb
35
+ homepage:
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.6.6
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Blender 3d file parser.
59
+ test_files: []