assimp-ruby 1.0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +170 -0
- data/Rakefile +66 -0
- data/generator/generate.rb +297 -0
- data/generator/layout_probe.c +288 -0
- data/generator/verify_layout.rb +33 -0
- data/lib/assimp/animation.rb +27 -0
- data/lib/assimp/animation_copier.rb +50 -0
- data/lib/assimp/embedded_texture.rb +23 -0
- data/lib/assimp/errors.rb +9 -0
- data/lib/assimp/import_source.rb +54 -0
- data/lib/assimp/importer.rb +54 -0
- data/lib/assimp/material.rb +73 -0
- data/lib/assimp/material_copier.rb +148 -0
- data/lib/assimp/math.rb +36 -0
- data/lib/assimp/mesh.rb +51 -0
- data/lib/assimp/mesh_copier.rb +150 -0
- data/lib/assimp/native/generated.rb +475 -0
- data/lib/assimp/native/platform.rb +67 -0
- data/lib/assimp/native/types.rb +60 -0
- data/lib/assimp/native.rb +148 -0
- data/lib/assimp/native_reader.rb +25 -0
- data/lib/assimp/node.rb +29 -0
- data/lib/assimp/post_process.rb +35 -0
- data/lib/assimp/raw_scene.rb +97 -0
- data/lib/assimp/scene.rb +42 -0
- data/lib/assimp/scene_copier.rb +46 -0
- data/lib/assimp/texture_copier.rb +17 -0
- data/lib/assimp/version.rb +5 -0
- data/lib/assimp.rb +46 -0
- data/rakelib/native_package.rake +187 -0
- data/script/smoke_gem.rb +25 -0
- data/script/verify_release.rb +33 -0
- metadata +90 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "thread"
|
|
6
|
+
require_relative "native/platform"
|
|
7
|
+
require_relative "native/types"
|
|
8
|
+
|
|
9
|
+
module Assimp
|
|
10
|
+
module Native
|
|
11
|
+
extend FFI::Library
|
|
12
|
+
|
|
13
|
+
ENVIRONMENT_KEY = "ASSIMP_LIBRARY_PATH"
|
|
14
|
+
REQUIRED_MAJOR = 5
|
|
15
|
+
LOAD_MUTEX = Mutex.new
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def load!
|
|
19
|
+
return self if @loaded
|
|
20
|
+
|
|
21
|
+
LOAD_MUTEX.synchronize do
|
|
22
|
+
return self if @loaded
|
|
23
|
+
|
|
24
|
+
ffi_lib(resolve_library)
|
|
25
|
+
attach_api
|
|
26
|
+
verify_version!
|
|
27
|
+
@loaded = true
|
|
28
|
+
end
|
|
29
|
+
self
|
|
30
|
+
rescue FFI::NotFoundError, LoadError => e
|
|
31
|
+
raise LibraryError, "could not load Assimp 5.x: #{e.message}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def loaded?
|
|
35
|
+
@loaded == true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def version
|
|
39
|
+
load!
|
|
40
|
+
[aiGetVersionMajor, aiGetVersionMinor, @version_patch ? aiGetVersionPatch : 0]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def revision
|
|
44
|
+
load!
|
|
45
|
+
aiGetVersionRevision
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def supports_extension?(extension)
|
|
49
|
+
load!
|
|
50
|
+
aiIsExtensionSupported(normalize_extension(extension)) != 0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def extension_list
|
|
54
|
+
load!
|
|
55
|
+
output = StringValue.new
|
|
56
|
+
aiGetExtensionList(output)
|
|
57
|
+
output.to_s.split(";").map { |extension| extension.delete_prefix("*.") }.freeze
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def library_candidates
|
|
61
|
+
explicit = ENV[ENVIRONMENT_KEY]
|
|
62
|
+
return [File.expand_path(explicit)] if explicit && !explicit.empty?
|
|
63
|
+
|
|
64
|
+
packaged = packaged_library_candidates.select { |path| File.file?(path) }
|
|
65
|
+
packaged + platform_library_names
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def resolve_library
|
|
71
|
+
explicit = ENV[ENVIRONMENT_KEY]
|
|
72
|
+
if explicit && !File.file?(File.expand_path(explicit))
|
|
73
|
+
raise LoadError, "#{ENVIRONMENT_KEY} does not point to a file: #{File.expand_path(explicit)}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
library_candidates.find { |candidate| loadable?(candidate) } ||
|
|
77
|
+
raise(LoadError, missing_library_message)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def loadable?(candidate)
|
|
81
|
+
flags = FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL
|
|
82
|
+
FFI::DynamicLibrary.open(candidate, flags)
|
|
83
|
+
true
|
|
84
|
+
rescue LoadError
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def packaged_library_candidates
|
|
89
|
+
platform = Platform.tag
|
|
90
|
+
roots = $LOAD_PATH.map { |path| File.join(path, "assimp", "native") }
|
|
91
|
+
roots.flat_map do |root|
|
|
92
|
+
Platform.library_names.flat_map do |name|
|
|
93
|
+
[File.join(root, platform, name), File.join(root, name)]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def platform_library_names
|
|
99
|
+
["assimp", *Platform.library_names]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def attach_api
|
|
103
|
+
attach_function :aiImportFile, %i[string uint], :pointer, blocking: true
|
|
104
|
+
attach_function :aiImportFileFromMemory, %i[pointer uint uint string], :pointer, blocking: true
|
|
105
|
+
attach_function :aiReleaseImport, [:pointer], :void
|
|
106
|
+
attach_function :aiGetErrorString, [], :string
|
|
107
|
+
attach_function :aiGetVersionMajor, [], :uint
|
|
108
|
+
attach_function :aiGetVersionMinor, [], :uint
|
|
109
|
+
attach_function :aiGetVersionRevision, [], :uint
|
|
110
|
+
@version_patch = attach_optional_function(:aiGetVersionPatch, [], :uint)
|
|
111
|
+
attach_function :aiIsExtensionSupported, [:string], :int
|
|
112
|
+
attach_function :aiGetExtensionList, [:pointer], :void
|
|
113
|
+
|
|
114
|
+
attach_function :aiGetMaterialProperty, %i[pointer string uint uint pointer], :int
|
|
115
|
+
attach_function :aiGetMaterialColor, %i[pointer string uint uint pointer], :int
|
|
116
|
+
attach_function :aiGetMaterialFloatArray, %i[pointer string uint uint pointer pointer], :int
|
|
117
|
+
attach_function :aiGetMaterialIntegerArray, %i[pointer string uint uint pointer pointer], :int
|
|
118
|
+
attach_function :aiGetMaterialString, %i[pointer string uint uint pointer], :int
|
|
119
|
+
attach_function :aiGetMaterialTextureCount, %i[pointer int], :uint
|
|
120
|
+
attach_function :aiGetMaterialTexture,
|
|
121
|
+
%i[pointer int uint pointer pointer pointer pointer pointer pointer pointer], :int
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def attach_optional_function(name, arguments, result)
|
|
125
|
+
attach_function(name, arguments, result)
|
|
126
|
+
true
|
|
127
|
+
rescue FFI::NotFoundError
|
|
128
|
+
false
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def normalize_extension(extension)
|
|
132
|
+
value = String(extension)
|
|
133
|
+
value.start_with?(".") ? value : ".#{value}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def verify_version!
|
|
137
|
+
major = aiGetVersionMajor
|
|
138
|
+
return if major == REQUIRED_MAJOR
|
|
139
|
+
|
|
140
|
+
raise IncompatibleLibraryError, "Assimp #{REQUIRED_MAJOR}.x is required, found #{major}.x"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def missing_library_message
|
|
144
|
+
"set #{ENVIRONMENT_KEY} or install a compatible libassimp shared library"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
module NativeReader
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def pointers(pointer, count)
|
|
8
|
+
return [] if count.zero?
|
|
9
|
+
raise ImportError, "native pointer array is null" if pointer.null?
|
|
10
|
+
|
|
11
|
+
pointer.read_array_of_pointer(count)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def struct_at(pointer, type, index)
|
|
15
|
+
type.new(pointer + (type.size * index))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bytes(pointer, size)
|
|
19
|
+
return "".b.freeze if size.zero?
|
|
20
|
+
raise ImportError, "native data pointer is null" if pointer.null?
|
|
21
|
+
|
|
22
|
+
pointer.read_bytes(size).force_encoding(Encoding::BINARY).freeze
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/assimp/node.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
class Node
|
|
5
|
+
attr_reader :name, :matrix, :children, :mesh_indices
|
|
6
|
+
|
|
7
|
+
def initialize(name:, matrix:, children:, mesh_indices:)
|
|
8
|
+
@name = String(name).dup.freeze
|
|
9
|
+
@matrix = matrix.map { |value| Float(value) }.freeze
|
|
10
|
+
raise ArgumentError, "node matrix must contain 16 values" unless @matrix.length == 16
|
|
11
|
+
|
|
12
|
+
@children = children.freeze
|
|
13
|
+
@mesh_indices = mesh_indices.map { |index| Integer(index) }.freeze
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def find(name)
|
|
18
|
+
target = String(name)
|
|
19
|
+
stack = [self]
|
|
20
|
+
until stack.empty?
|
|
21
|
+
node = stack.pop
|
|
22
|
+
return node if node.name == target
|
|
23
|
+
|
|
24
|
+
stack.concat(node.children.reverse)
|
|
25
|
+
end
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "native/generated"
|
|
4
|
+
|
|
5
|
+
module Assimp
|
|
6
|
+
POST_PROCESS = Native::POST_PROCESS
|
|
7
|
+
|
|
8
|
+
PROCESS_PRESETS = {
|
|
9
|
+
none: [].freeze,
|
|
10
|
+
fast: %i[triangulate join_identical_vertices gen_normals].freeze,
|
|
11
|
+
realtime: %i[
|
|
12
|
+
triangulate join_identical_vertices gen_smooth_normals calc_tangent_space
|
|
13
|
+
limit_bone_weights improve_cache_locality sort_by_p_type
|
|
14
|
+
].freeze
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
module PostProcess
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def flags(value)
|
|
21
|
+
names = value.is_a?(Symbol) ? preset(value) : Array(value)
|
|
22
|
+
names.reduce(0) do |result, name|
|
|
23
|
+
result | POST_PROCESS.fetch(name.to_sym) do
|
|
24
|
+
raise ArgumentError, "unknown post-process flag #{name.inspect}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def preset(name)
|
|
30
|
+
PROCESS_PRESETS.fetch(name) do
|
|
31
|
+
raise ArgumentError, "unknown post-process preset #{name.inspect}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
class RawScene
|
|
5
|
+
class View
|
|
6
|
+
attr_reader :native_type
|
|
7
|
+
|
|
8
|
+
def initialize(owner, native_type, pointer)
|
|
9
|
+
@owner = owner
|
|
10
|
+
@native_type = native_type
|
|
11
|
+
@pointer = pointer
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def [](field)
|
|
15
|
+
native[field]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_ptr
|
|
19
|
+
ensure_valid!
|
|
20
|
+
@pointer
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def inspect
|
|
24
|
+
state = @owner.valid? ? "valid" : "released"
|
|
25
|
+
"#<#{self.class.name} #{@native_type.name} #{state}>"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def native
|
|
31
|
+
ensure_valid!
|
|
32
|
+
@native_type.new(@pointer)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ensure_valid!
|
|
36
|
+
return if @owner.valid?
|
|
37
|
+
|
|
38
|
+
raise ReleasedError, "raw view is only valid inside the Assimp.open block"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(pointer)
|
|
43
|
+
@pointer = pointer
|
|
44
|
+
@valid = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def valid?
|
|
48
|
+
@valid
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def native
|
|
52
|
+
ensure_valid!
|
|
53
|
+
View.new(self, Native::Scene, @pointer)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def pointer
|
|
57
|
+
ensure_valid!
|
|
58
|
+
@pointer
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def meshes
|
|
62
|
+
source = native_struct
|
|
63
|
+
NativeReader.pointers(source[:meshes], source[:num_meshes]).map do |item|
|
|
64
|
+
View.new(self, Native::Mesh, item)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def materials
|
|
69
|
+
source = native_struct
|
|
70
|
+
NativeReader.pointers(source[:materials], source[:num_materials]).map do |item|
|
|
71
|
+
View.new(self, Native::Material, item)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def root
|
|
76
|
+
node_pointer = native_struct[:root_node]
|
|
77
|
+
node_pointer.null? ? nil : View.new(self, Native::Node, node_pointer)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def invalidate!
|
|
81
|
+
@valid = false
|
|
82
|
+
@pointer = nil
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def native_struct
|
|
89
|
+
ensure_valid!
|
|
90
|
+
Native::Scene.new(@pointer)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ensure_valid!
|
|
94
|
+
raise ReleasedError, "raw scene is only valid inside the Assimp.open block" unless valid?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/assimp/scene.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
class Scene
|
|
5
|
+
INCOMPLETE_FLAG = 0x1
|
|
6
|
+
|
|
7
|
+
attr_reader :name, :meshes, :materials, :root, :animations, :textures, :flags
|
|
8
|
+
|
|
9
|
+
def initialize(name:, meshes:, materials:, root:, animations:, textures:, flags:)
|
|
10
|
+
@name = String(name).dup.freeze
|
|
11
|
+
@meshes = meshes.freeze
|
|
12
|
+
@materials = materials.freeze
|
|
13
|
+
@root = root
|
|
14
|
+
@animations = animations.freeze
|
|
15
|
+
@textures = textures.freeze
|
|
16
|
+
@flags = Integer(flags)
|
|
17
|
+
freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def incomplete?
|
|
21
|
+
(flags & INCOMPLETE_FLAG) != 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def find_node(name)
|
|
25
|
+
root&.find(name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def each_mesh_instance
|
|
29
|
+
return enum_for(__method__) unless block_given?
|
|
30
|
+
return self unless root
|
|
31
|
+
|
|
32
|
+
stack = [[root, Matrix::IDENTITY]]
|
|
33
|
+
until stack.empty?
|
|
34
|
+
node, parent_matrix = stack.pop
|
|
35
|
+
world_matrix = Matrix.multiply(parent_matrix, node.matrix)
|
|
36
|
+
node.mesh_indices.each { |index| yield meshes.fetch(index), world_matrix }
|
|
37
|
+
node.children.reverse_each { |child| stack << [child, world_matrix] }
|
|
38
|
+
end
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
class SceneCopier
|
|
5
|
+
def initialize(native: Native)
|
|
6
|
+
@native = native
|
|
7
|
+
@minor_version = native.version[1]
|
|
8
|
+
@mesh_copier = MeshCopier.new(native:)
|
|
9
|
+
@animation_copier = AnimationCopier.new(native:)
|
|
10
|
+
@texture_copier = TextureCopier.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def copy(pointer)
|
|
14
|
+
source = Native::Scene.new(pointer)
|
|
15
|
+
textures = map_pointers(source[:textures], source[:num_textures], @texture_copier)
|
|
16
|
+
material_copier = MaterialCopier.new(native: @native, embedded_textures: textures)
|
|
17
|
+
Scene.new(
|
|
18
|
+
name: @minor_version.zero? ? "" : source[:name].to_s,
|
|
19
|
+
meshes: map_pointers(source[:meshes], source[:num_meshes], @mesh_copier),
|
|
20
|
+
materials: map_pointers(source[:materials], source[:num_materials], material_copier),
|
|
21
|
+
root: copy_node(source[:root_node]),
|
|
22
|
+
animations: map_pointers(source[:animations], source[:num_animations], @animation_copier),
|
|
23
|
+
textures:,
|
|
24
|
+
flags: source[:flags]
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def map_pointers(pointer, count, copier)
|
|
31
|
+
NativeReader.pointers(pointer, count).map { |item| copier.copy(item) }.freeze
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def copy_node(pointer)
|
|
35
|
+
return nil if pointer.null?
|
|
36
|
+
|
|
37
|
+
source = Native::Node.new(pointer)
|
|
38
|
+
Node.new(
|
|
39
|
+
name: source[:name].to_s,
|
|
40
|
+
matrix: source[:transformation].to_column_major,
|
|
41
|
+
children: NativeReader.pointers(source[:children], source[:num_children]).map { |child| copy_node(child) },
|
|
42
|
+
mesh_indices: source[:num_meshes].zero? ? [] : source[:meshes].read_array_of_uint(source[:num_meshes])
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assimp
|
|
4
|
+
class TextureCopier
|
|
5
|
+
def copy(pointer)
|
|
6
|
+
source = Native::Texture.new(pointer)
|
|
7
|
+
byte_size = source[:height].zero? ? source[:width] : source[:width] * source[:height] * 4
|
|
8
|
+
EmbeddedTexture.new(
|
|
9
|
+
width: source[:width],
|
|
10
|
+
height: source[:height],
|
|
11
|
+
format_hint: source[:format_hint].to_ptr.read_string(9).delete("\0"),
|
|
12
|
+
filename: source[:filename].to_s,
|
|
13
|
+
bytes: NativeReader.bytes(source[:data], byte_size)
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/assimp.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "assimp/version"
|
|
4
|
+
require_relative "assimp/errors"
|
|
5
|
+
require_relative "assimp/post_process"
|
|
6
|
+
require_relative "assimp/math"
|
|
7
|
+
require_relative "assimp/mesh"
|
|
8
|
+
require_relative "assimp/node"
|
|
9
|
+
require_relative "assimp/material"
|
|
10
|
+
require_relative "assimp/animation"
|
|
11
|
+
require_relative "assimp/embedded_texture"
|
|
12
|
+
require_relative "assimp/scene"
|
|
13
|
+
require_relative "assimp/native"
|
|
14
|
+
require_relative "assimp/native_reader"
|
|
15
|
+
require_relative "assimp/mesh_copier"
|
|
16
|
+
require_relative "assimp/material_copier"
|
|
17
|
+
require_relative "assimp/animation_copier"
|
|
18
|
+
require_relative "assimp/texture_copier"
|
|
19
|
+
require_relative "assimp/scene_copier"
|
|
20
|
+
require_relative "assimp/import_source"
|
|
21
|
+
require_relative "assimp/raw_scene"
|
|
22
|
+
require_relative "assimp/importer"
|
|
23
|
+
|
|
24
|
+
module Assimp
|
|
25
|
+
class << self
|
|
26
|
+
def import(source, process: :realtime, hint: nil)
|
|
27
|
+
Importer.new.import(source, process:, hint:)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def open(source, process: :realtime, hint: nil, &block)
|
|
31
|
+
Importer.new.open(source, process:, hint:, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def native_version
|
|
35
|
+
Native.version
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def supports?(extension)
|
|
39
|
+
Native.supports_extension?(extension)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def supported_extensions
|
|
43
|
+
Native.extension_list
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
require_relative "../lib/assimp/native/platform"
|
|
7
|
+
require_relative "../lib/assimp/version"
|
|
8
|
+
|
|
9
|
+
module AssimpNativePackage
|
|
10
|
+
extend Rake::FileUtilsExt
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
ROOT = File.expand_path("..", __dir__)
|
|
14
|
+
ASSIMP_VERSION = "5.4.3"
|
|
15
|
+
PLATFORM = Assimp::Native::Platform.tag
|
|
16
|
+
SOURCE_DIR = File.expand_path(
|
|
17
|
+
ENV.fetch("ASSIMP_SOURCE_DIR", File.join(ROOT, "tmp", "assimp-#{ASSIMP_VERSION}"))
|
|
18
|
+
)
|
|
19
|
+
BUILD_DIR = File.join(ROOT, "tmp", "native", PLATFORM)
|
|
20
|
+
|
|
21
|
+
def explicit_library
|
|
22
|
+
value = ENV["ASSIMP_LIBRARY_PATH"]
|
|
23
|
+
return if value.nil? || value.empty?
|
|
24
|
+
|
|
25
|
+
path = File.expand_path(value)
|
|
26
|
+
raise "ASSIMP_LIBRARY_PATH does not point to a file: #{path}" unless File.file?(path)
|
|
27
|
+
|
|
28
|
+
path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fetch_source
|
|
32
|
+
return if File.file?(File.join(SOURCE_DIR, "CMakeLists.txt"))
|
|
33
|
+
raise "#{SOURCE_DIR} exists but is not an Assimp source tree" if File.exist?(SOURCE_DIR)
|
|
34
|
+
|
|
35
|
+
FileUtils.mkdir_p(File.dirname(SOURCE_DIR))
|
|
36
|
+
sh(
|
|
37
|
+
"git", "clone", "--depth", "1", "--branch", "v#{ASSIMP_VERSION}",
|
|
38
|
+
"https://github.com/assimp/assimp.git", SOURCE_DIR
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def configure
|
|
43
|
+
fetch_source
|
|
44
|
+
host_os = RbConfig::CONFIG.fetch("host_os")
|
|
45
|
+
bundled_zlib = host_os.match?(/mswin|mingw/) ? "ON" : "OFF"
|
|
46
|
+
command = [
|
|
47
|
+
"cmake",
|
|
48
|
+
"-S", SOURCE_DIR,
|
|
49
|
+
"-B", BUILD_DIR,
|
|
50
|
+
"-DCMAKE_BUILD_TYPE=Release",
|
|
51
|
+
"-DBUILD_SHARED_LIBS=ON",
|
|
52
|
+
"-DASSIMP_BUILD_ASSIMP_TOOLS=OFF",
|
|
53
|
+
"-DASSIMP_BUILD_SAMPLES=OFF",
|
|
54
|
+
"-DASSIMP_BUILD_TESTS=OFF",
|
|
55
|
+
"-DASSIMP_BUILD_DOCS=OFF",
|
|
56
|
+
"-DASSIMP_BUILD_DRACO=OFF",
|
|
57
|
+
"-DASSIMP_BUILD_ZLIB=#{bundled_zlib}",
|
|
58
|
+
"-DASSIMP_WARNINGS_AS_ERRORS=OFF",
|
|
59
|
+
"-DASSIMP_INJECT_DEBUG_POSTFIX=OFF",
|
|
60
|
+
"-DASSIMP_IGNORE_GIT_HASH=ON",
|
|
61
|
+
"-DASSIMP_NO_EXPORT=ON"
|
|
62
|
+
]
|
|
63
|
+
command << "-DUSE_STATIC_CRT=ON" if host_os.match?(/mswin|mingw/)
|
|
64
|
+
if host_os.include?("darwin")
|
|
65
|
+
command << "-DCMAKE_OSX_ARCHITECTURES=#{RbConfig::CONFIG.fetch("host_cpu")}"
|
|
66
|
+
end
|
|
67
|
+
sh(*command)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def compile
|
|
71
|
+
configure
|
|
72
|
+
sh("cmake", "--build", BUILD_DIR, "--config", "Release", "--parallel")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def built_library
|
|
76
|
+
names = Assimp::Native::Platform.library_names
|
|
77
|
+
files = Dir.glob(File.join(BUILD_DIR, "**", "*")).select { |path| File.file?(path) }
|
|
78
|
+
match = names.lazy.filter_map do |name|
|
|
79
|
+
files.find { |path| File.basename(path) == name }
|
|
80
|
+
end.first
|
|
81
|
+
match ||= files.find do |path|
|
|
82
|
+
basename = File.basename(path)
|
|
83
|
+
basename.match?(/\Alibassimp(?:\.\d+(?:\.\d+)*)?\.(?:so|dylib)\z/) ||
|
|
84
|
+
basename.match?(/\Aassimp-vc\d+-mt\.dll\z/)
|
|
85
|
+
end
|
|
86
|
+
raise "Assimp shared library was not found in #{BUILD_DIR}" unless match
|
|
87
|
+
|
|
88
|
+
File.realpath(match)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def stage
|
|
92
|
+
source = explicit_library
|
|
93
|
+
unless source
|
|
94
|
+
compile
|
|
95
|
+
source = built_library
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
destination = File.join(ROOT, "lib", "assimp", "native", PLATFORM)
|
|
99
|
+
FileUtils.rm_rf(destination)
|
|
100
|
+
FileUtils.mkdir_p(destination)
|
|
101
|
+
FileUtils.cp(
|
|
102
|
+
source,
|
|
103
|
+
File.join(destination, Assimp::Native::Platform.packaged_library_name)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
license = File.join(SOURCE_DIR, "LICENSE")
|
|
107
|
+
FileUtils.cp(license, File.join(destination, "ASSIMP-LICENSE.txt")) if File.file?(license)
|
|
108
|
+
destination
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def platform_gem_path
|
|
112
|
+
platform = Gem::Platform.new(PLATFORM)
|
|
113
|
+
File.join(ROOT, "assimp-ruby-#{Assimp::VERSION}-#{platform}.gem")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
module AssimpPackageSmoke
|
|
118
|
+
extend Rake::FileUtilsExt
|
|
119
|
+
module_function
|
|
120
|
+
|
|
121
|
+
ROOT = File.expand_path("..", __dir__)
|
|
122
|
+
|
|
123
|
+
def run(gem_path, label)
|
|
124
|
+
raise "gem was not built: #{gem_path}" unless File.file?(gem_path)
|
|
125
|
+
|
|
126
|
+
install_dir = File.join(ROOT, "tmp", "gem-smoke", label)
|
|
127
|
+
FileUtils.rm_rf(install_dir)
|
|
128
|
+
FileUtils.mkdir_p(install_dir)
|
|
129
|
+
sh(
|
|
130
|
+
"gem", "install", "--local", "--ignore-dependencies", "--no-document",
|
|
131
|
+
"--install-dir", install_dir, gem_path
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
environment = {
|
|
135
|
+
"GEM_HOME" => install_dir,
|
|
136
|
+
"GEM_PATH" => [install_dir, *Gem.path].uniq.join(File::PATH_SEPARATOR),
|
|
137
|
+
"ASSIMP_RUBY_SMOKE_HOME" => install_dir
|
|
138
|
+
}
|
|
139
|
+
Bundler.with_unbundled_env do
|
|
140
|
+
sh(environment, RbConfig.ruby, File.join(ROOT, "script", "smoke_gem.rb"))
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
namespace :native do
|
|
146
|
+
desc "Configure Assimp #{AssimpNativePackage::ASSIMP_VERSION}"
|
|
147
|
+
task :configure do
|
|
148
|
+
AssimpNativePackage.configure
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
desc "Compile the Assimp shared library"
|
|
152
|
+
task :compile do
|
|
153
|
+
AssimpNativePackage.compile
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
desc "Stage a shared library for the current platform gem"
|
|
157
|
+
task :stage do
|
|
158
|
+
AssimpNativePackage.stage
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
desc "Build a platform gem containing Assimp"
|
|
162
|
+
task gem: :stage do
|
|
163
|
+
Bundler.with_unbundled_env do
|
|
164
|
+
sh({ "ASSIMP_RUBY_PLATFORM_GEM" => "1" }, "gem", "build", "assimp.gemspec")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
desc "Build, install, and execute the current platform gem"
|
|
169
|
+
task smoke_gem: :gem do
|
|
170
|
+
AssimpPackageSmoke.run(AssimpNativePackage.platform_gem_path, "platform")
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
namespace :package do
|
|
175
|
+
desc "Build the source gem"
|
|
176
|
+
task :source_gem do
|
|
177
|
+
Bundler.with_unbundled_env do
|
|
178
|
+
sh("gem", "build", "assimp.gemspec")
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
desc "Build, install, and execute the source gem against a system Assimp"
|
|
183
|
+
task smoke_source: :source_gem do
|
|
184
|
+
gem_path = File.join(__dir__, "..", "assimp-ruby-#{Assimp::VERSION}.gem")
|
|
185
|
+
AssimpPackageSmoke.run(gem_path, "source")
|
|
186
|
+
end
|
|
187
|
+
end
|