Compare commits
24 Commits
feature/im
...
develop
Author | SHA1 | Date |
---|---|---|
![]() |
3a043b41c6 | |
![]() |
8512600e7d | |
![]() |
a8916f6e60 | |
![]() |
0bef3b8633 | |
![]() |
46fd54f726 | |
![]() |
31dea75e7c | |
![]() |
8c02951c65 | |
![]() |
9a4685ba98 | |
![]() |
8e65bf0f64 | |
![]() |
8c0d86d542 | |
![]() |
956583145d | |
![]() |
bcb7294eb1 | |
![]() |
19c3089f7e | |
![]() |
f6c9d76e03 | |
![]() |
af4c4926c5 | |
![]() |
463a27bc55 | |
![]() |
6b5faed6f6 | |
![]() |
e69d2f12ff | |
![]() |
9e0a0a4dcb | |
![]() |
0aed670c0c | |
![]() |
aacc3a5ad0 | |
![]() |
f9575a721c | |
![]() |
e49ed1f3b9 | |
![]() |
ed9d49fc09 |
|
@ -0,0 +1,15 @@
|
|||
name: Swift
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build
|
||||
run: swift build -v
|
||||
- name: Run tests
|
||||
run: swift test -v
|
|
@ -1,6 +1,4 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
gltf_scenekit.xcodeproj/project.xcworkspace/xcuserdata
|
||||
gltf_scenekit.xcodeproj/xcuserdata
|
||||
glTFSceneKit.xcodeproj/xcuserdata
|
||||
**/xcuserdata
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "Draco"]
|
||||
path = Draco
|
||||
url = https://github.com/3d4medical/DracoSwiftPackage.git
|
1
Draco
1
Draco
|
@ -1 +0,0 @@
|
|||
Subproject commit 10caef92253b10439b3bc560ecef4a79804f612a
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Draco",
|
||||
"repositoryURL": "https://github.com/3d4medical/DracoSwiftPackage.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "0dac933ae63d023d2ba87decb2a5485beac7f548",
|
||||
"version": "0.0.9"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
}
|
|
@ -1,19 +1,18 @@
|
|||
// swift-tools-version:4.0
|
||||
// swift-tools-version:5.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "glTFSceneKit",
|
||||
platforms: [
|
||||
.macOS(.v10_12), .iOS(.v10),
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||
.library(
|
||||
name: "glTFSceneKit",
|
||||
targets: ["glTFSceneKit"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
.package(url: "https://github.com/3d4medical/DracoSwiftPackage.git", from: "0.0.9"),
|
||||
targets: ["glTFSceneKit"])
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
|
@ -23,10 +22,8 @@ let package = Package(
|
|||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "glTFSceneKitTests",
|
||||
dependencies: ["glTFSceneKit"]),
|
||||
dependencies: ["glTFSceneKit"])
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
// swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.12"
|
||||
|
||||
// swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.13"
|
||||
|
|
15
README.md
15
README.md
|
@ -32,13 +32,9 @@
|
|||
|
||||
|
||||
#### Extensions
|
||||
- [X] KHR_draco_mesh_compression - Draco (supported draft version, need to fix when indices is short)
|
||||
- [ ] KHR_draco_mesh_compression - Draco (supported draft version, need rework. temporary disabled)
|
||||
- [X] 3D4M_compressed_texture - [Draft of unofficial extension.](https://github.com/sakrist/glTF/tree/extensions/compressed_texture/extensions/2.0/Vendor/3D4M_compressed_texture)
|
||||
|
||||
## Dependecies
|
||||
|
||||
- [DracoSwiftPackage](https://github.com/3D4Medical/DracoSwiftPackage) - custom Draco package for decode
|
||||
|
||||
|
||||
Example:
|
||||
```swift
|
||||
|
@ -47,5 +43,12 @@ import glTFSceneKit
|
|||
let directory = "..." // path to folder where is gltf file located
|
||||
let decoder = JSONDecoder()
|
||||
let glTF = try? decoder.decode(GLTF.self, from: jsonData)
|
||||
let scene = glTF?.convert(view:sceneView, directoryPath:directory)
|
||||
if let converter = GLTFConverter(glTF: glTF) {
|
||||
let scene = converter.convert(to: view.scene!, geometryCompletionHandler: {
|
||||
// Geometries are loaded and textures are may still in loading process.
|
||||
}) { (error) in
|
||||
// Fully converted to SceneKit
|
||||
// TODO: handle error.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import Foundation
|
||||
import SceneKit
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTFConverter {
|
||||
|
||||
func parseAnimations() throws {
|
||||
|
@ -15,11 +16,11 @@ extension GLTFConverter {
|
|||
for animation in animations {
|
||||
for channel in animation.channels {
|
||||
let sampler = animation.samplers[channel.sampler]
|
||||
try constructAnimation(sampler: sampler, target:channel.target)
|
||||
try constructAnimation(sampler: sampler, target: channel.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for node in self.cache_nodes! {
|
||||
let group = node?.value(forUndefinedKey: "group") as? CAAnimationGroup
|
||||
if group != nil && self.animationDuration != 0 {
|
||||
|
@ -27,141 +28,141 @@ extension GLTFConverter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func constructAnimation(sampler:GLTFAnimationSampler, target:GLTFAnimationChannelTarget ) throws {
|
||||
|
||||
let targetIndex = target.node!
|
||||
guard let node:SCNNode = self.cache_nodes?[targetIndex] else {
|
||||
throw GLTFError("constructAnimation: Can't find target node with \(targetIndex), sampler:\(sampler) target:\(target)")
|
||||
}
|
||||
|
||||
guard let accessorInput = glTF.accessors?[sampler.input] else {
|
||||
throw GLTFError("Input accessor could not be found for sampler.input \(sampler.input)")
|
||||
}
|
||||
guard let accessorOutput = glTF.accessors?[sampler.output] else {
|
||||
throw GLTFError("Output accessor could not be found for sampler.output \(sampler.output)")
|
||||
}
|
||||
|
||||
var keyTimesFloat = [Float]()
|
||||
if let (bufferView, interleaved) = try determineAcessor(accessorInput),
|
||||
let data = try loadAcessor(accessorInput, bufferView, interleaved) {
|
||||
keyTimesFloat = dataAsArray(data, accessorInput.componentType, accessorInput.type) as! [Float]
|
||||
}
|
||||
let duration = Double(keyTimesFloat.last!)
|
||||
let f_duration = Float(duration)
|
||||
let keyTimes: [NSNumber] = keyTimesFloat.map { NSNumber(value: $0 / f_duration ) }
|
||||
|
||||
var values_ = [Any]()
|
||||
if let (bufferView, interleaved) = try determineAcessor(accessorOutput),
|
||||
let data = try loadAcessor(accessorInput, bufferView, interleaved) {
|
||||
values_ = dataAsArray(data, accessorOutput.componentType, accessorOutput.type)
|
||||
}
|
||||
|
||||
var groupDuration:Double = 0
|
||||
|
||||
var caanimations:[CAAnimation] = [CAAnimation]()
|
||||
if target.path == .weights {
|
||||
let weightPaths = node.value(forUndefinedKey: "weightPaths") as? [String]
|
||||
|
||||
groupDuration = duration
|
||||
|
||||
var keyAnimations = [CAKeyframeAnimation]()
|
||||
for path in weightPaths! {
|
||||
let animation = CAKeyframeAnimation()
|
||||
animation.keyPath = path
|
||||
animation.keyTimes = keyTimes
|
||||
animation.duration = duration
|
||||
keyAnimations.append(animation)
|
||||
}
|
||||
|
||||
let step = keyAnimations.count
|
||||
let dataLength = values_.count / step
|
||||
guard dataLength == keyTimes.count else {
|
||||
throw GLTFError("data count mismatch: \(dataLength) != \(keyTimes.count)")
|
||||
}
|
||||
|
||||
for i in 0..<keyAnimations.count {
|
||||
var valueIndex = i
|
||||
var v = [NSNumber]()
|
||||
v.reserveCapacity(dataLength)
|
||||
for _ in 0..<dataLength {
|
||||
v.append(NSNumber(value: (values_[valueIndex] as! Float) ))
|
||||
valueIndex += step
|
||||
}
|
||||
keyAnimations[i].values = v
|
||||
}
|
||||
|
||||
caanimations = keyAnimations
|
||||
|
||||
} else {
|
||||
let keyFrameAnimation = CAKeyframeAnimation()
|
||||
|
||||
self.animationDuration = max(self.animationDuration, duration)
|
||||
|
||||
keyFrameAnimation.keyPath = target.path.scn()
|
||||
keyFrameAnimation.keyTimes = keyTimes
|
||||
keyFrameAnimation.values = values_
|
||||
keyFrameAnimation.repeatCount = .infinity
|
||||
keyFrameAnimation.duration = duration
|
||||
|
||||
caanimations.append(keyFrameAnimation)
|
||||
|
||||
groupDuration = self.animationDuration
|
||||
}
|
||||
|
||||
let group = (node.value(forUndefinedKey: "group") as? CAAnimationGroup) ?? CAAnimationGroup()
|
||||
node.setValue(group, forUndefinedKey: "group")
|
||||
var animations = group.animations ?? []
|
||||
animations.append(contentsOf: caanimations)
|
||||
group.animations = animations
|
||||
group.duration = groupDuration
|
||||
group.repeatCount = .infinity
|
||||
node.addAnimation(group, forKey: target.path.rawValue)
|
||||
|
||||
func constructAnimation(sampler: GLTFAnimationSampler, target: GLTFAnimationChannelTarget ) throws {
|
||||
|
||||
// let targetIndex = target.node!
|
||||
// guard let node:SCNNode = self.cache_nodes?[targetIndex] else {
|
||||
// throw GLTFError("constructAnimation: Can't find target node with \(targetIndex), sampler:\(sampler) target:\(target)")
|
||||
// }
|
||||
//
|
||||
// guard let accessorInput = glTF.accessors?[sampler.input] else {
|
||||
// throw GLTFError("Input accessor could not be found for sampler.input \(sampler.input)")
|
||||
// }
|
||||
// guard let accessorOutput = glTF.accessors?[sampler.output] else {
|
||||
// throw GLTFError("Output accessor could not be found for sampler.output \(sampler.output)")
|
||||
// }
|
||||
//
|
||||
// var keyTimesFloat = [Float]()
|
||||
// if let (bufferView, interleaved) = try determineAcessor(accessorInput),
|
||||
// let data = try loadAcessor(accessorInput, bufferView, interleaved) {
|
||||
// keyTimesFloat = dataAsArray(data, accessorInput.componentType, accessorInput.type) as! [Float]
|
||||
// }
|
||||
// let duration = Double(keyTimesFloat.last!)
|
||||
// let f_duration = Float(duration)
|
||||
// let keyTimes: [NSNumber] = keyTimesFloat.map { NSNumber(value: $0 / f_duration ) }
|
||||
//
|
||||
// var values_ = [Any]()
|
||||
// if let (bufferView, interleaved) = try determineAcessor(accessorOutput),
|
||||
// let data = try loadAcessor(accessorInput, bufferView, interleaved) {
|
||||
// values_ = dataAsArray(data, accessorOutput.componentType, accessorOutput.type)
|
||||
// }
|
||||
//
|
||||
// var groupDuration:Double = 0
|
||||
//
|
||||
// var caanimations:[CAAnimation] = [CAAnimation]()
|
||||
// if target.path == .weights {
|
||||
// let weightPaths = node.value(forUndefinedKey: "weightPaths") as? [String]
|
||||
//
|
||||
// groupDuration = duration
|
||||
//
|
||||
// var keyAnimations = [CAKeyframeAnimation]()
|
||||
// for path in weightPaths! {
|
||||
// let animation = CAKeyframeAnimation()
|
||||
// animation.keyPath = path
|
||||
// animation.keyTimes = keyTimes
|
||||
// animation.duration = duration
|
||||
// keyAnimations.append(animation)
|
||||
// }
|
||||
//
|
||||
// let step = keyAnimations.count
|
||||
// let dataLength = values_.count / step
|
||||
// guard dataLength == keyTimes.count else {
|
||||
// throw GLTFError("data count mismatch: \(dataLength) != \(keyTimes.count)")
|
||||
// }
|
||||
//
|
||||
// for i in 0..<keyAnimations.count {
|
||||
// var valueIndex = i
|
||||
// var v = [NSNumber]()
|
||||
// v.reserveCapacity(dataLength)
|
||||
// for _ in 0..<dataLength {
|
||||
// v.append(NSNumber(value: (values_[valueIndex] as! Float) ))
|
||||
// valueIndex += step
|
||||
// }
|
||||
// keyAnimations[i].values = v
|
||||
// }
|
||||
//
|
||||
// caanimations = keyAnimations
|
||||
//
|
||||
// } else {
|
||||
// let keyFrameAnimation = CAKeyframeAnimation()
|
||||
//
|
||||
// self.animationDuration = max(self.animationDuration, duration)
|
||||
//
|
||||
// keyFrameAnimation.keyPath = target.path.scn()
|
||||
// keyFrameAnimation.keyTimes = keyTimes
|
||||
// keyFrameAnimation.values = values_
|
||||
// keyFrameAnimation.repeatCount = .infinity
|
||||
// keyFrameAnimation.duration = duration
|
||||
//
|
||||
// caanimations.append(keyFrameAnimation)
|
||||
//
|
||||
// groupDuration = self.animationDuration
|
||||
// }
|
||||
//
|
||||
// let group = (node.value(forUndefinedKey: "group") as? CAAnimationGroup) ?? CAAnimationGroup()
|
||||
// node.setValue(group, forUndefinedKey: "group")
|
||||
// var animations = group.animations ?? []
|
||||
// animations.append(contentsOf: caanimations)
|
||||
// group.animations = animations
|
||||
// group.duration = groupDuration
|
||||
// group.repeatCount = .infinity
|
||||
// node.addAnimation(group, forKey: target.path.rawValue)
|
||||
}
|
||||
|
||||
func loadSkin(_ skin:Int, _ scnNode:SCNNode) {
|
||||
|
||||
func loadSkin(_ skin: Int, _ scnNode: SCNNode) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
func dataAsArray(_ data:Data, _ componentType:GLTFAccessorComponentType, _ type:GLTFAccessorType) -> [Any] {
|
||||
|
||||
func dataAsArray(_ data: Data, _ componentType: GLTFAccessorComponentType, _ type: GLTFAccessorType) -> [Any] {
|
||||
var values = [Any]()
|
||||
switch componentType {
|
||||
case .BYTE:
|
||||
values = data.array() as [Int8]
|
||||
break
|
||||
|
||||
case .UNSIGNED_BYTE:
|
||||
values = data.array() as [UInt8]
|
||||
break
|
||||
|
||||
case .SHORT:
|
||||
values = data.array() as [Int16]
|
||||
break
|
||||
|
||||
case .UNSIGNED_SHORT:
|
||||
values = data.array() as [UInt16]
|
||||
break
|
||||
|
||||
case .UNSIGNED_INT:
|
||||
values = data.array() as [UInt32]
|
||||
break
|
||||
case .FLOAT:
|
||||
|
||||
case .FLOAT:
|
||||
do {
|
||||
switch type {
|
||||
case .SCALAR:
|
||||
values = data.array() as [Float]
|
||||
break
|
||||
|
||||
case .VEC2:
|
||||
values = data.array() as [SCNVector2]
|
||||
break
|
||||
|
||||
case .VEC3:
|
||||
values = data.array() as [GLKVector3]
|
||||
for i in 0..<values.count {
|
||||
values[i] = SCNVector3FromGLKVector3(values[i] as! GLKVector3)
|
||||
}
|
||||
break
|
||||
|
||||
case .VEC4:
|
||||
values = data.array() as [GLKVector4]
|
||||
for i in 0..<values.count {
|
||||
values[i] = SCNVector4FromGLKVector4(values[i] as! GLKVector4)
|
||||
}
|
||||
break
|
||||
|
||||
case .MAT2:
|
||||
break
|
||||
case .MAT3:
|
||||
|
@ -171,16 +172,15 @@ extension GLTFConverter {
|
|||
for i in 0..<values.count {
|
||||
values[i] = SCNMatrix4FromGLKMatrix4(values[i] as! GLKMatrix4)
|
||||
}
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension GLTFAnimationChannelTargetPath {
|
||||
fileprivate func scn() -> String {
|
||||
|
@ -196,5 +196,3 @@ extension GLTFAnimationChannelTargetPath {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,43 +8,45 @@
|
|||
import Foundation
|
||||
import SceneKit
|
||||
|
||||
enum CTLevel:Int {
|
||||
enum CTLevel: Int {
|
||||
case first = 0
|
||||
case last
|
||||
case all
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTF {
|
||||
|
||||
func loadCompressedTexture(descriptor:GLTF_3D4MCompressedTextureExtension, loadLevel:CTLevel, completionHandler: @escaping (Any?, Error?) -> Void ) {
|
||||
|
||||
|
||||
func loadCompressedTexture(descriptor: GLTF_3D4MCompressedTextureExtension, loadLevel: CTLevel, completionHandler: @escaping (Any?, Error?) -> Void ) {
|
||||
|
||||
let width = descriptor.width
|
||||
let height = descriptor.height
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
|
||||
if width == 0 || height == 0 {
|
||||
completionHandler(nil, GLTFError("GLTF_3D4MCompressedTextureExtension: Failed to load texture, inappropriate texture size."))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let (bytesPerRow, pixelFormat) = _get_bpp_pixelFormat(descriptor.compression)
|
||||
|
||||
if (pixelFormat == .invalid ) {
|
||||
|
||||
if pixelFormat == .invalid {
|
||||
completionHandler(nil, GLTFError("GLTF_3D4MCompressedTextureExtension: Failed to load texture, unsupported compression format \(descriptor.compression)."))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if loadLevel == .all {
|
||||
var buffers = [GLTFBuffer]()
|
||||
for bViewIndex in descriptor.sources {
|
||||
let buffer = self.buffers![self.bufferViews![bViewIndex].buffer]
|
||||
buffers.append(buffer)
|
||||
}
|
||||
|
||||
self.loader.load(gltf:self, resources: Set(buffers), options: ResourceType.texture) { (error) in
|
||||
|
||||
self.loader.load(gltf: self, resources: Set(buffers), options: ResourceType.texture) { (error) in
|
||||
var error_ = error
|
||||
var textureResult:Any?
|
||||
|
||||
var textureResult: Any?
|
||||
|
||||
if error == nil {
|
||||
var datas = [Data]()
|
||||
var datas = [Data]()
|
||||
for buffer in buffers {
|
||||
if buffer.data != nil {
|
||||
datas.append(buffer.data!)
|
||||
|
@ -58,65 +60,70 @@ extension GLTF {
|
|||
error_ = error
|
||||
}
|
||||
} else {
|
||||
print(error)
|
||||
error_ = error
|
||||
}
|
||||
completionHandler(textureResult, error_)
|
||||
}
|
||||
} else {
|
||||
let sizeWidth = (loadLevel == .first) ? 32 : descriptor.width
|
||||
let sizeHeight = (loadLevel == .first) ? 32 : descriptor.height
|
||||
let index = (loadLevel == .first) ? descriptor.sources.last! : descriptor.sources.first!
|
||||
|
||||
let index = (loadLevel == .first) ? descriptor.sources.last! : descriptor.sources.first!
|
||||
|
||||
if let bView = self.bufferViews?[index] {
|
||||
let buffer_ = self.buffers![bView.buffer]
|
||||
self.loader.load(gltf:self, resource: buffer_, options: ResourceType.texture) { (buffer, error) in
|
||||
self.loader.load(gltf: self, resource: buffer_, options: ResourceType.texture) { (buffer, error) in
|
||||
var error_ = error
|
||||
var textureResult:Any?
|
||||
var textureResult: Any?
|
||||
var datas = [Data]()
|
||||
if buffer.data != nil {
|
||||
datas.append(buffer.data!)
|
||||
do {
|
||||
textureResult = try self._createMetalTexture(sizeWidth, sizeHeight, pixelFormat, datas, bytesPerRow)
|
||||
if !buffer_.uri!.contains("lacrimal")
|
||||
&& !buffer_.uri!.contains("Bursa") {
|
||||
textureResult = try self._createMetalTexture(sizeWidth, sizeHeight, pixelFormat, datas, bytesPerRow)
|
||||
} else {
|
||||
|
||||
}
|
||||
} catch {
|
||||
error_ = error
|
||||
}
|
||||
} else {
|
||||
error_ = GLTFError("Can't load data for \(buffer.uri ?? "")")
|
||||
}
|
||||
|
||||
|
||||
completionHandler(textureResult, error_)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func _createMetalTexture( _ width:Int, _ height:Int, _ pixelFormat:MTLPixelFormat, _ mipmaps:[Data], _ bppBlock:(Int, Int)->Int) throws -> MTLTexture {
|
||||
|
||||
fileprivate func _createMetalTexture( _ width: Int, _ height: Int, _ pixelFormat: MTLPixelFormat, _ mipmaps: [Data], _ bppBlock: (Int, Int) -> Int) throws -> MTLTexture {
|
||||
var width = width
|
||||
var height = height
|
||||
let mipmapsCount = mipmaps.count
|
||||
|
||||
|
||||
if mipmapsCount == 0 {
|
||||
throw GLTFError("mipmaps array can't be empty.")
|
||||
}
|
||||
|
||||
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: pixelFormat,
|
||||
width: width,
|
||||
height: height,
|
||||
|
||||
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: pixelFormat,
|
||||
width: width,
|
||||
height: height,
|
||||
mipmapped: (mipmapsCount > 1))
|
||||
textureDescriptor.mipmapLevelCount = mipmapsCount
|
||||
|
||||
|
||||
guard let device = MetalDevice.device else {
|
||||
throw GLTFError("View has Metal's render APi but can't get instance of MTLDevice.")
|
||||
}
|
||||
|
||||
|
||||
guard let texture = device.makeTexture(descriptor: textureDescriptor) else {
|
||||
throw GLTFError("Failed to create metal texture with descriptor \(textureDescriptor)")
|
||||
}
|
||||
|
||||
|
||||
for i in 0 ..< mipmapsCount {
|
||||
let data = mipmaps[i]
|
||||
let bPr = bppBlock(width, height)
|
||||
data.withUnsafeBytes { (unsafeBufferPointer:UnsafeRawBufferPointer) in
|
||||
data.withUnsafeBytes { (unsafeBufferPointer: UnsafeRawBufferPointer) in
|
||||
if let unsafePointer = unsafeBufferPointer.bindMemory(to: UInt8.self).baseAddress {
|
||||
texture.replace(region: MTLRegionMake2D(0, 0, width, height),
|
||||
mipmapLevel: i,
|
||||
|
@ -124,237 +131,233 @@ extension GLTF {
|
|||
bytesPerRow: bPr)
|
||||
}
|
||||
}
|
||||
|
||||
width = max(width >> 1, 1);
|
||||
height = max(height >> 1, 1);
|
||||
|
||||
width = max(width >> 1, 1)
|
||||
height = max(height >> 1, 1)
|
||||
}
|
||||
|
||||
|
||||
return texture
|
||||
}
|
||||
|
||||
internal func _compress(image:OSImage) -> Any? {
|
||||
|
||||
internal func _compress(image: OSImage) -> Any? {
|
||||
#if (os(iOS) || os(tvOS)) && !targetEnvironment(simulator)
|
||||
if #available(iOS 11.0, tvOS 11.0, *) {
|
||||
// if let cg = image.cgImage(forProposedRect: nil, context: nil, hints: nil) {
|
||||
if let cg = image.cgImage {
|
||||
let data = CFDataCreateMutable(nil, 0)!
|
||||
let uti: CFString = "org.khronos.astc" as CFString
|
||||
let uti: CFString = "org.khronos.astc" as CFString
|
||||
let imageDestination = CGImageDestinationCreateWithData(data, uti, 1, nil)
|
||||
CGImageDestinationAddImage(imageDestination!, cg, nil)
|
||||
CGImageDestinationFinalize(imageDestination!)
|
||||
let (bytesPerRow, pixelFormat) = _get_bpp_pixelFormat(.COMPRESSED_RGBA_ASTC_4x4)
|
||||
|
||||
|
||||
var _data = (data as Data)
|
||||
// remove astc header of 16 bytes
|
||||
_data = _data.subdata(in: 16..<_data.count)
|
||||
|
||||
|
||||
return try? _createMetalTexture(cg.width, cg.height, pixelFormat, [_data as Data], bytesPerRow) as Any
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return image
|
||||
}
|
||||
|
||||
|
||||
fileprivate func _get_bpp_pixelFormat(_ compression:GLTF_3D4MCompressedTextureExtensionCompression) ->((Int, Int)->Int, MTLPixelFormat) {
|
||||
var bytesPerRow:(Int, Int)->Int = {_,_ in return 0 }
|
||||
var pixelFormat:MTLPixelFormat = .invalid;
|
||||
|
||||
|
||||
|
||||
fileprivate func _get_bpp_pixelFormat(_ compression: GLTF_3D4MCompressedTextureExtensionCompression) -> ((Int, Int) -> Int, MTLPixelFormat) {
|
||||
var bytesPerRow: (Int, Int) -> Int = {_, _ in return 0 }
|
||||
var pixelFormat: MTLPixelFormat = .invalid
|
||||
|
||||
#if os(macOS)
|
||||
if (compression == .COMPRESSED_RGBA_S3TC_DXT1) {
|
||||
if compression == .COMPRESSED_RGBA_S3TC_DXT1 {
|
||||
pixelFormat = .bc1_rgba
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 8 };
|
||||
} else if (compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT1) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 8 }
|
||||
} else if compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT1 {
|
||||
pixelFormat = .bc1_rgba_srgb
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 8 };
|
||||
} else if (compression == .COMPRESSED_RGBA_S3TC_DXT3) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 8 }
|
||||
} else if compression == .COMPRESSED_RGBA_S3TC_DXT3 {
|
||||
pixelFormat = .bc2_rgba
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
} else if (compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT3) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
} else if compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT3 {
|
||||
pixelFormat = .bc2_rgba_srgb
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
} else if (compression == .COMPRESSED_RGBA_S3TC_DXT5) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
} else if compression == .COMPRESSED_RGBA_S3TC_DXT5 {
|
||||
pixelFormat = .bc3_rgba
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
} else if (compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT5) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
} else if compression == .COMPRESSED_SRGB_ALPHA_S3TC_DXT5 {
|
||||
pixelFormat = .bc3_rgba_srgb
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
} else if (compression == .COMPRESSED_RGBA_BPTC_UNORM) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
} else if compression == .COMPRESSED_RGBA_BPTC_UNORM {
|
||||
pixelFormat = .bc7_rgbaUnorm
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
} else if (compression == .COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
} else if compression == .COMPRESSED_SRGB_ALPHA_BPTC_UNORM {
|
||||
pixelFormat = .bc7_rgbaUnorm_srgb
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 };
|
||||
bytesPerRow = {width, height in return ((width + 3) / 4) * 16 }
|
||||
}
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
|
||||
|
||||
switch compression {
|
||||
case .ETC1_RGB8_OES:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGB8_ETC2:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB8_ETC2:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGBA8_ETC2_EAC:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
|
||||
assert(false, " \(compression) not supported yet")
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB_PVRTC_2BPPV1:
|
||||
pixelFormat = .pvrtc_rgb_2bpp_srgb
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB_PVRTC_4BPPV1:
|
||||
pixelFormat = .pvrtc_rgb_4bpp_srgb
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1:
|
||||
pixelFormat = .pvrtc_rgba_2bpp_srgb
|
||||
break
|
||||
|
||||
case .COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1:
|
||||
pixelFormat = .pvrtc_rgba_4bpp_srgb
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGB_PVRTC_4BPPV1:
|
||||
pixelFormat = .pvrtc_rgb_4bpp
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGB_PVRTC_2BPPV1:
|
||||
pixelFormat = .pvrtc_rgb_2bpp
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGBA_PVRTC_4BPPV1:
|
||||
pixelFormat = .pvrtc_rgba_4bpp
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGBA_PVRTC_2BPPV1:
|
||||
pixelFormat = .pvrtc_rgba_2bpp
|
||||
break
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_4x4:
|
||||
pixelFormat = .astc_4x4_ldr
|
||||
bytesPerRow = {width, height in return (width + 4 - 1) / 4 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 4 - 1) / 4 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_5x4:
|
||||
pixelFormat = .astc_5x4_ldr
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_5x5:
|
||||
pixelFormat = .astc_5x5_ldr
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_6x5:
|
||||
pixelFormat = .astc_6x5_ldr
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_6x6:
|
||||
pixelFormat = .astc_6x6_ldr
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_8x5:
|
||||
pixelFormat = .astc_8x5_ldr
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_8x6:
|
||||
pixelFormat = .astc_8x6_ldr
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_8x8:
|
||||
pixelFormat = .astc_8x8_ldr
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_10x5:
|
||||
pixelFormat = .astc_10x5_ldr
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_10x6:
|
||||
pixelFormat = .astc_10x6_ldr
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_10x8:
|
||||
pixelFormat = .astc_10x8_ldr
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_10x10:
|
||||
pixelFormat = .astc_10x10_ldr
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_12x10:
|
||||
pixelFormat = .astc_12x10_ldr
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 }
|
||||
|
||||
case .COMPRESSED_RGBA_ASTC_12x12:
|
||||
pixelFormat = .astc_12x12_ldr
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_4x4:
|
||||
pixelFormat = .astc_4x4_srgb
|
||||
bytesPerRow = {width, height in return (width + 4 - 1) / 4 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 4 - 1) / 4 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_5x4:
|
||||
pixelFormat = .astc_5x4_srgb
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_5x5:
|
||||
pixelFormat = .astc_5x5_srgb
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 5 - 1) / 5 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_6x5:
|
||||
pixelFormat = .astc_6x5_srgb
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_6x6:
|
||||
pixelFormat = .astc_6x6_srgb
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 6 - 1) / 6 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_8x5:
|
||||
pixelFormat = .astc_8x5_srgb
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_8x6:
|
||||
pixelFormat = .astc_8x6_srgb
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_8x8:
|
||||
pixelFormat = .astc_8x8_srgb
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 8 - 1) / 8 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_10x5:
|
||||
pixelFormat = .astc_10x5_srgb
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_10x6:
|
||||
pixelFormat = .astc_10x6_srgb
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_10x8:
|
||||
pixelFormat = .astc_10x8_srgb
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_10x10:
|
||||
pixelFormat = .astc_10x10_srgb
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 10 - 1) / 10 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_12x10:
|
||||
pixelFormat = .astc_12x10_srgb
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 };
|
||||
break
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 }
|
||||
|
||||
case .COMPRESSED_SRGB8_ALPHA8_ASTC_12x12:
|
||||
pixelFormat = .astc_12x12_srgb
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 };
|
||||
break
|
||||
|
||||
bytesPerRow = {width, height in return (width + 12 - 1) / 12 * 16 }
|
||||
|
||||
default:
|
||||
assert(false, " \(compression) can't bbe supported on iOS")
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
return (bytesPerRow, pixelFormat)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,61 +9,60 @@ import Foundation
|
|||
import SceneKit
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md
|
||||
|
||||
#if DRACO
|
||||
extension GLTF {
|
||||
|
||||
func convertDracoMesh(_ dracoMesh:GLTFKHRDracoMeshCompressionExtension, triangleStrip:Bool = true) throws -> (SCNGeometryElement?, [SCNGeometrySource]?) {
|
||||
|
||||
func convertDracoMesh(_ dracoMesh: GLTFKHRDracoMeshCompressionExtension, triangleStrip: Bool = true) throws -> (SCNGeometryElement?, [SCNGeometrySource]?) {
|
||||
if let (bufferView, data) = try GLTF.requestData(glTF: self, bufferView: dracoMesh.bufferView) {
|
||||
var data = data
|
||||
let start = bufferView.byteOffset
|
||||
let end = start + bufferView.byteLength
|
||||
|
||||
|
||||
if start != 0 || end != data.count {
|
||||
data = data.subdata(in: start..<end)
|
||||
}
|
||||
|
||||
|
||||
let (indicesData, verticies, stride) = uncompressDracoData(data, triangleStrip: triangleStrip)
|
||||
|
||||
let indexSize = MemoryLayout<UInt32>.size;
|
||||
|
||||
let primitiveCount = (triangleStrip) ? ((indicesData.count / indexSize) - 2) : (indicesData.count / (indexSize * 3))
|
||||
|
||||
|
||||
let indexSize = MemoryLayout<UInt32>.size
|
||||
|
||||
let primitiveCount = (triangleStrip) ? ((indicesData.count / indexSize) - 2) : (indicesData.count / (indexSize * 3))
|
||||
|
||||
let element = SCNGeometryElement.init(data: indicesData,
|
||||
primitiveType: ((triangleStrip) ? .triangleStrip : .triangles),
|
||||
primitiveCount: primitiveCount,
|
||||
bytesPerIndex: indexSize)
|
||||
|
||||
|
||||
|
||||
let byteStride = (bufferView.byteStride != nil) ? bufferView.byteStride! : (stride * 4)
|
||||
let count = verticies.count / byteStride
|
||||
var byteOffset = 0
|
||||
|
||||
|
||||
var geometrySources = [SCNGeometrySource]()
|
||||
|
||||
|
||||
// sort attributes
|
||||
var sortedAttributes:[String] = [String](repeating: "", count: dracoMesh.attributes.count)
|
||||
var sortedAttributes: [String] = [String](repeating: "", count: dracoMesh.attributes.count)
|
||||
for pair in dracoMesh.attributes {
|
||||
sortedAttributes[pair.value] = pair.key
|
||||
}
|
||||
|
||||
var mtlBuffer:MTLBuffer?
|
||||
|
||||
var mtlBuffer: MTLBuffer?
|
||||
let device = MetalDevice.device
|
||||
verticies.withUnsafeBytes { (unsafeBufferPointer:UnsafeRawBufferPointer) in
|
||||
verticies.withUnsafeBytes { (unsafeBufferPointer: UnsafeRawBufferPointer) in
|
||||
let uint8Ptr = unsafeBufferPointer.bindMemory(to: Int8.self).baseAddress!
|
||||
mtlBuffer = device?.makeBuffer(bytes: uint8Ptr, length: verticies.count, options: .storageModeShared)
|
||||
}
|
||||
|
||||
let createGeometrySource:(SCNGeometrySource.Semantic) -> (SCNGeometrySource)
|
||||
|
||||
let createGeometrySource: (SCNGeometrySource.Semantic) -> (SCNGeometrySource)
|
||||
if let mtlB = mtlBuffer {
|
||||
createGeometrySource = { semantic in
|
||||
let vertexFormat:MTLVertexFormat
|
||||
let vertexFormat: MTLVertexFormat
|
||||
switch semantic {
|
||||
case .texcoord:
|
||||
vertexFormat = .float2
|
||||
default:
|
||||
vertexFormat = .float3
|
||||
}
|
||||
|
||||
|
||||
let geometrySource = SCNGeometrySource.init(buffer: mtlB,
|
||||
vertexFormat: vertexFormat,
|
||||
semantic: semantic,
|
||||
|
@ -72,7 +71,7 @@ extension GLTF {
|
|||
dataStride: byteStride)
|
||||
return geometrySource
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
createGeometrySource = { semantic in
|
||||
let geometrySource = SCNGeometrySource.init(data: verticies,
|
||||
|
@ -86,21 +85,22 @@ extension GLTF {
|
|||
return geometrySource
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for key in sortedAttributes {
|
||||
// convert string semantic to SceneKit enum type
|
||||
let semantic = GLTF.sourceSemantic(name:key)
|
||||
|
||||
let semantic = GLTF.sourceSemantic(name: key)
|
||||
|
||||
let geometrySource = createGeometrySource(semantic)
|
||||
|
||||
|
||||
geometrySources.append(geometrySource)
|
||||
|
||||
|
||||
byteOffset = byteOffset + ((semantic == .texcoord) ? 8 : 12)
|
||||
}
|
||||
|
||||
|
||||
return (element, geometrySources)
|
||||
}
|
||||
|
||||
|
||||
return (nil, nil)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,50 +8,50 @@
|
|||
import Foundation
|
||||
import SceneKit
|
||||
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTF {
|
||||
|
||||
|
||||
// MARK: - Material
|
||||
|
||||
|
||||
// load material by index
|
||||
internal func loadMaterial(index:Int, delegate: TextureLoaderDelegate, textureChangedCallback: ((Any?)-> Void)? = nil, completionHandler: @escaping (SCNMaterial) -> Void) {
|
||||
|
||||
internal func loadMaterial(index: Int, delegate: TextureLoaderDelegate, textureChangedCallback: ((Any?) -> Void)? = nil, completionHandler: @escaping (SCNMaterial) -> Void) {
|
||||
|
||||
if let material = self.materials?[index] {
|
||||
let scnMaterial = SCNMaterial()
|
||||
scnMaterial.name = material.name
|
||||
scnMaterial.isDoubleSided = material.doubleSided
|
||||
|
||||
|
||||
if let pbr = material.pbrMetallicRoughness {
|
||||
|
||||
|
||||
// set PBR type
|
||||
scnMaterial.lightingModel = .physicallyBased
|
||||
|
||||
|
||||
if let baseTextureInfo = pbr.baseColorTexture {
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index:baseTextureInfo.index, property: scnMaterial.diffuse, callback: textureChangedCallback)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: baseTextureInfo.index, property: scnMaterial.diffuse)
|
||||
} else {
|
||||
let color = (pbr.baseColorFactor.count < 4) ? [1, 1, 1, 1] : (pbr.baseColorFactor)
|
||||
scnMaterial.diffuse.contents = OSColor(red: CGFloat(color[0]), green: CGFloat(color[1]), blue: CGFloat(color[2]), alpha: CGFloat(color[3]))
|
||||
}
|
||||
|
||||
|
||||
// transparency/opacity
|
||||
scnMaterial.transparency = CGFloat(pbr.baseColorFactor[3])
|
||||
|
||||
|
||||
if let metallicRoughnessTextureInfo = pbr.metallicRoughnessTexture {
|
||||
if #available(OSX 10.13, iOS 11.0, tvOS 11.0, *) {
|
||||
scnMaterial.metalness.textureComponents = .blue
|
||||
scnMaterial.roughness.textureComponents = .green
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index:metallicRoughnessTextureInfo.index, property: scnMaterial.metalness)
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index:metallicRoughnessTextureInfo.index, property: scnMaterial.roughness)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: metallicRoughnessTextureInfo.index, property: scnMaterial.metalness)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: metallicRoughnessTextureInfo.index, property: scnMaterial.roughness)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
if let texture = self.textures?[metallicRoughnessTextureInfo.index] {
|
||||
|
||||
|
||||
if texture.source != nil {
|
||||
|
||||
loadSampler(sampler:texture.sampler, property: scnMaterial.roughness)
|
||||
loadSampler(sampler:texture.sampler, property: scnMaterial.metalness)
|
||||
|
||||
let image = self.image(byIndex:texture.source!)
|
||||
|
||||
loadSampler(sampler: texture.sampler, property: scnMaterial.roughness)
|
||||
loadSampler(sampler: texture.sampler, property: scnMaterial.metalness)
|
||||
|
||||
let image = self.image(byIndex: texture.source!)
|
||||
if let images = ((try? image?.channels()) as [OSImage]??) {
|
||||
scnMaterial.roughness.contents = images?[1]
|
||||
scnMaterial.metalness.contents = images?[2]
|
||||
|
@ -59,47 +59,47 @@ extension GLTF {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
scnMaterial.metalness.contents = pbr.metallicFactor
|
||||
scnMaterial.roughness.contents = pbr.roughnessFactor
|
||||
scnMaterial.fresnelExponent = 0.04
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let normalTextureInfo = material.normalTexture {
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index: normalTextureInfo.index!, property: scnMaterial.normal)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: normalTextureInfo.index!, property: scnMaterial.normal)
|
||||
}
|
||||
|
||||
|
||||
if let occlusionTextureInfo = material.occlusionTexture {
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index: occlusionTextureInfo.index!, property: scnMaterial.ambientOcclusion)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: occlusionTextureInfo.index!, property: scnMaterial.ambientOcclusion)
|
||||
scnMaterial.ambientOcclusion.intensity = CGFloat(occlusionTextureInfo.strength)
|
||||
}
|
||||
|
||||
|
||||
if let emissiveTextureInfo = material.emissiveTexture {
|
||||
TextureStorageManager.loadTexture(gltf:self, delegate: delegate, index: emissiveTextureInfo.index, property: scnMaterial.emission)
|
||||
TextureStorageManager.loadTexture(gltf: self, delegate: delegate, index: emissiveTextureInfo.index, property: scnMaterial.emission)
|
||||
} else {
|
||||
let color = (material.emissiveFactor.count < 3) ? [1, 1, 1] : (material.emissiveFactor)
|
||||
scnMaterial.emission.contents = SCNVector4Make(SCNFloat(color[0]), SCNFloat(color[1]), SCNFloat(color[2]), 1.0)
|
||||
}
|
||||
|
||||
|
||||
completionHandler(scnMaterial)
|
||||
} else {
|
||||
completionHandler(SCNMaterial())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get image by index
|
||||
fileprivate func image(byIndex index:Int) -> OSImage? {
|
||||
fileprivate func image(byIndex index: Int) -> OSImage? {
|
||||
if let gltf_image = self.images?[index] {
|
||||
if let image = ((try? self.loader.load(gltf:self, resource: gltf_image)) as OSImage??) {
|
||||
if let image = ((try? self.loader.load(gltf: self, resource: gltf_image)) as OSImage??) {
|
||||
return image
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSampler(sampler samplerIndex:Int?, property:SCNMaterialProperty) {
|
||||
|
||||
func loadSampler(sampler samplerIndex: Int?, property: SCNMaterialProperty) {
|
||||
if let sampler = self.samplers?[samplerIndex!] {
|
||||
property.wrapS = sampler.wrapS.scn()
|
||||
property.wrapT = sampler.wrapT.scn()
|
||||
|
@ -109,7 +109,6 @@ extension GLTF {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
extension GLTFSampler {
|
||||
fileprivate func magFilterScene() -> SCNFilterMode {
|
||||
if self.magFilter != nil {
|
||||
|
@ -117,7 +116,7 @@ extension GLTFSampler {
|
|||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
|
||||
fileprivate func minFilterScene() -> (SCNFilterMode, SCNFilterMode) {
|
||||
if self.minFilter != nil {
|
||||
return (self.minFilter?.scn())!
|
||||
|
@ -181,4 +180,3 @@ extension GLTFSamplerWrapT {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,41 +10,46 @@ import Foundation
|
|||
import SceneKit
|
||||
import os
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
let log_scenekit = OSLog(subsystem: "org.glTFSceneKit", category: "scene")
|
||||
|
||||
let dracoExtensionKey = "KHR_draco_mesh_compression"
|
||||
let compressedTextureExtensionKey = "3D4M_compressed_texture"
|
||||
let supportedExtensions = [dracoExtensionKey, compressedTextureExtensionKey]
|
||||
let meshExtensionKey = "3D4M_mesh"
|
||||
#if DRACO
|
||||
let dracoExtensionKey = "KHR_draco_mesh_compression"
|
||||
let supportedExtensions = [dracoExtensionKey, compressedTextureExtensionKey, meshExtensionKey]
|
||||
#else
|
||||
let supportedExtensions = [compressedTextureExtensionKey, meshExtensionKey]
|
||||
#endif
|
||||
|
||||
|
||||
struct ConvertionProgressMask : OptionSet {
|
||||
struct ConvertionProgressMask: OptionSet {
|
||||
let rawValue: Int
|
||||
|
||||
|
||||
static let nodes = ConvertionProgressMask(rawValue: 1 << 1)
|
||||
static let textures = ConvertionProgressMask(rawValue: 1 << 2)
|
||||
static let animations = ConvertionProgressMask(rawValue: 1 << 3)
|
||||
|
||||
|
||||
static func all() -> ConvertionProgressMask {
|
||||
return [.nodes, .textures, .animations]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc public protocol SceneLoadingDelegate {
|
||||
@objc optional func scene(_ didLoadScene: SCNScene )
|
||||
@objc optional func scene(_ scene: SCNScene, didCreate camera: SCNCamera)
|
||||
@objc optional func scene(_ scene: SCNScene, didCreate node: SCNNode)
|
||||
@objc optional func scene(_ scene: SCNScene, didCreate material: SCNMaterial, for node: SCNNode)
|
||||
@objc optional func scene(_ didLoadScene: SCNScene? )
|
||||
@objc optional func scene(_ scene: SCNScene?, didCreate camera: SCNCamera)
|
||||
@objc optional func scene(_ scene: SCNScene?, didCreate node: SCNNode)
|
||||
@objc optional func scene(_ scene: SCNScene?, didCreate material: SCNMaterial, for node: SCNNode)
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTF {
|
||||
|
||||
|
||||
struct Keys {
|
||||
static var resource_loader = "resource_loader"
|
||||
static var load_canceled = "load_canceled"
|
||||
}
|
||||
|
||||
public var loader:GLTFResourceLoader {
|
||||
|
||||
public var loader: GLTFResourceLoader {
|
||||
get {
|
||||
var loader_ = objc_getAssociatedObject(self, &Keys.resource_loader) as? GLTFResourceLoader
|
||||
if loader_ != nil {
|
||||
|
@ -56,34 +61,34 @@ extension GLTF {
|
|||
}
|
||||
set { objc_setAssociatedObject(self, &Keys.resource_loader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
||||
}
|
||||
|
||||
|
||||
/// Status set to true if `cancel` been call.
|
||||
@objc open private(set) var isCancelled:Bool {
|
||||
@objc open private(set) var isCancelled: Bool {
|
||||
get { return (objc_getAssociatedObject(self, &Keys.load_canceled) as? Bool) ?? false }
|
||||
set { objc_setAssociatedObject(self, &Keys.load_canceled, newValue, .OBJC_ASSOCIATION_ASSIGN) }
|
||||
}
|
||||
|
||||
|
||||
internal func cancel() {
|
||||
self.isCancelled = true
|
||||
self.loader.cancelAll()
|
||||
}
|
||||
|
||||
|
||||
internal func clearCache() {
|
||||
if self.buffers != nil {
|
||||
for buffer in self.buffers! {
|
||||
buffer.data = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if self.images != nil {
|
||||
for image in self.images! {
|
||||
image.image = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert attributes name to SceneKit semantic
|
||||
internal static func sourceSemantic(name:String) -> SCNGeometrySource.Semantic {
|
||||
internal static func sourceSemantic(name: String) -> SCNGeometrySource.Semantic {
|
||||
switch name {
|
||||
case "POSITION":
|
||||
return .vertex
|
||||
|
@ -103,11 +108,11 @@ extension GLTF {
|
|||
return .vertex
|
||||
}
|
||||
}
|
||||
|
||||
internal static func requestData(glTF: GLTF, bufferView:GLTFBufferView) throws -> Data? {
|
||||
|
||||
internal static func requestData(glTF: GLTF, bufferView: GLTFBufferView) throws -> Data? {
|
||||
if let buffer = glTF.buffers?[bufferView.buffer] {
|
||||
|
||||
if let data = try glTF.loader.load(gltf:glTF, resource: buffer) {
|
||||
|
||||
if let data = try glTF.loader.load(gltf: glTF, resource: buffer) {
|
||||
return data
|
||||
}
|
||||
} else {
|
||||
|
@ -115,8 +120,8 @@ extension GLTF {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal static func requestData(glTF: GLTF, bufferView:Int) throws -> (GLTFBufferView, Data)? {
|
||||
|
||||
internal static func requestData(glTF: GLTF, bufferView: Int) throws -> (GLTFBufferView, Data)? {
|
||||
if let bufferView = glTF.bufferViews?[bufferView] {
|
||||
if let data = try requestData(glTF: glTF, bufferView: bufferView) {
|
||||
return (bufferView, data)
|
||||
|
@ -126,24 +131,24 @@ extension GLTF {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension GLTFBuffer {
|
||||
|
||||
|
||||
static var data_associate_key = "data_associate_key"
|
||||
|
||||
public var data:Data? {
|
||||
|
||||
public var data: Data? {
|
||||
get { return objc_getAssociatedObject(self, &GLTFBuffer.data_associate_key) as? Data }
|
||||
set { objc_setAssociatedObject(self, &GLTFBuffer.data_associate_key, newValue, .OBJC_ASSOCIATION_RETAIN) }
|
||||
}
|
||||
}
|
||||
|
||||
extension GLTFImage {
|
||||
|
||||
|
||||
static var image_associate_key = "image_associate_key"
|
||||
|
||||
public var image:OSImage? {
|
||||
|
||||
public var image: OSImage? {
|
||||
get { return objc_getAssociatedObject(self, &GLTFImage.image_associate_key) as? OSImage }
|
||||
set { objc_setAssociatedObject(self, &GLTFImage.image_associate_key, newValue, .OBJC_ASSOCIATION_RETAIN) }
|
||||
}
|
||||
|
|
|
@ -9,66 +9,65 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// The root object for a glTF asset.
|
||||
@objcMembers
|
||||
open class GLTF : NSObject, Codable {
|
||||
open class GLTF: NSObject, Codable {
|
||||
/// An array of accessors.
|
||||
public var accessors:[GLTFAccessor]?
|
||||
public var accessors: [GLTFAccessor]?
|
||||
|
||||
/// An array of keyframe animations.
|
||||
public var animations:[GLTFAnimation]?
|
||||
public var animations: [GLTFAnimation]?
|
||||
|
||||
/// Metadata about the glTF asset.
|
||||
public var asset:GLTFAsset
|
||||
public var asset: GLTFAsset
|
||||
|
||||
/// An array of bufferViews.
|
||||
public var bufferViews:[GLTFBufferView]?
|
||||
public var bufferViews: [GLTFBufferView]?
|
||||
|
||||
/// An array of buffers.
|
||||
public var buffers:[GLTFBuffer]?
|
||||
public var buffers: [GLTFBuffer]?
|
||||
|
||||
/// An array of cameras.
|
||||
public var cameras:[GLTFCamera]?
|
||||
public var cameras: [GLTFCamera]?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Names of glTF extensions required to properly load this asset.
|
||||
public var extensionsRequired:[String]?
|
||||
public var extensionsRequired: [String]?
|
||||
|
||||
/// Names of glTF extensions used somewhere in this asset.
|
||||
public var extensionsUsed:[String]?
|
||||
public var extensionsUsed: [String]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// An array of images.
|
||||
public var images:[GLTFImage]?
|
||||
public var images: [GLTFImage]?
|
||||
|
||||
/// An array of materials.
|
||||
public var materials:[GLTFMaterial]?
|
||||
public var materials: [GLTFMaterial]?
|
||||
|
||||
/// An array of meshes.
|
||||
public var meshes:[GLTFMesh]?
|
||||
public var meshes: [GLTFMesh]?
|
||||
|
||||
/// An array of nodes.
|
||||
public var nodes:[GLTFNode]?
|
||||
public var nodes: [GLTFNode]?
|
||||
|
||||
/// An array of samplers.
|
||||
public var samplers:[GLTFSampler]?
|
||||
public var samplers: [GLTFSampler]?
|
||||
|
||||
/// The index of the default scene.
|
||||
public var scene:Int?
|
||||
public var scene: Int?
|
||||
|
||||
/// An array of scenes.
|
||||
public var scenes:[GLTFScene]?
|
||||
public var scenes: [GLTFScene]?
|
||||
|
||||
/// An array of skins.
|
||||
public var skins:[GLTFSkin]?
|
||||
public var skins: [GLTFSkin]?
|
||||
|
||||
/// An array of textures.
|
||||
public var textures:[GLTFTexture]?
|
||||
public var textures: [GLTFTexture]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case accessors
|
||||
|
@ -91,8 +90,8 @@ open class GLTF : NSObject, Codable {
|
|||
case skins
|
||||
case textures
|
||||
}
|
||||
|
||||
public init(asset a:GLTFAsset) {
|
||||
|
||||
public init(asset a: GLTFAsset) {
|
||||
asset = a
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,25 @@ import Foundation
|
|||
case MAT3
|
||||
case MAT4
|
||||
|
||||
public var rawIntValue: Int {
|
||||
switch self {
|
||||
case .SCALAR:
|
||||
return 0
|
||||
case .VEC2:
|
||||
return 1
|
||||
case .VEC3:
|
||||
return 2
|
||||
case .VEC4:
|
||||
return 3
|
||||
case .MAT2:
|
||||
return 4
|
||||
case .MAT3:
|
||||
return 5
|
||||
case .MAT4:
|
||||
return 6
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .SCALAR:
|
||||
|
@ -106,45 +125,44 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// A typed view into a bufferView. A bufferView contains raw binary data. An accessor provides a typed view into a bufferView or a subset of a bufferView similar to how WebGL's `vertexAttribPointer()` defines an attribute in a buffer.
|
||||
@objcMembers
|
||||
open class GLTFAccessor : NSObject, Codable {
|
||||
open class GLTFAccessor: NSObject, Codable {
|
||||
/// The index of the bufferView.
|
||||
public var bufferView:Int?
|
||||
public var bufferView: Int?
|
||||
|
||||
/// The offset relative to the start of the bufferView in bytes.
|
||||
public var byteOffset:Int
|
||||
public var byteOffset: Int
|
||||
|
||||
/// The datatype of components in the attribute.
|
||||
public var componentType:GLTFAccessorComponentType
|
||||
public var componentType: GLTFAccessorComponentType
|
||||
|
||||
/// The number of attributes referenced by this accessor.
|
||||
public var count:Int
|
||||
public var count: Int
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// Maximum value of each component in this attribute.
|
||||
public var max:[Double]?
|
||||
public var max: [Double]?
|
||||
|
||||
/// Minimum value of each component in this attribute.
|
||||
public var min:[Double]?
|
||||
public var min: [Double]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// Specifies whether integer data values should be normalized.
|
||||
public var normalized:Bool
|
||||
public var normalized: Bool = false
|
||||
|
||||
/// Sparse storage of attributes that deviate from their initialization value.
|
||||
public var sparse:GLTFAccessorSparse?
|
||||
public var sparse: GLTFAccessorSparse?
|
||||
|
||||
/// Specifies if the attribute is a scalar, vector, or matrix.
|
||||
public var type:GLTFAccessorType
|
||||
public var type: GLTFAccessorType
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case bufferView
|
||||
|
@ -161,6 +179,18 @@ open class GLTFAccessor : NSObject, Codable {
|
|||
case type
|
||||
}
|
||||
|
||||
public init (bufferView: Int?,
|
||||
byteOffset: Int,
|
||||
componentType: GLTFAccessorComponentType,
|
||||
count: Int,
|
||||
type: GLTFAccessorType) {
|
||||
self.bufferView = bufferView
|
||||
self.byteOffset = byteOffset
|
||||
self.componentType = componentType
|
||||
self.count = count
|
||||
self.type = type
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
bufferView = try? container.decode(Int.self, forKey: .bufferView)
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Sparse storage of attributes that deviate from their initialization value.
|
||||
@objcMembers
|
||||
open class GLTFAccessorSparse : NSObject, Codable {
|
||||
open class GLTFAccessorSparse: NSObject, Codable {
|
||||
/// Number of entries stored in the sparse array.
|
||||
public var count:Int
|
||||
public var count: Int
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// Indices of those attributes that deviate from their initialization value.
|
||||
public var indices:GLTFAccessorSparseIndices
|
||||
public var indices: GLTFAccessorSparseIndices
|
||||
|
||||
/// Array of size `accessor.sparse.count` times number of components storing the displaced accessor attributes pointed by `accessor.sparse.indices`.
|
||||
public var values:GLTFAccessorSparseValues
|
||||
public var values: GLTFAccessorSparseValues
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case count
|
||||
|
|
|
@ -40,24 +40,23 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// Indices of those attributes that deviate from their initialization value.
|
||||
@objcMembers
|
||||
open class GLTFAccessorSparseIndices : NSObject, Codable {
|
||||
open class GLTFAccessorSparseIndices: NSObject, Codable {
|
||||
/// The index of the bufferView with sparse indices. Referenced bufferView can't have ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target.
|
||||
public var bufferView:Int
|
||||
public var bufferView: Int
|
||||
|
||||
/// The offset relative to the start of the bufferView in bytes. Must be aligned.
|
||||
public var byteOffset:Int
|
||||
public var byteOffset: Int
|
||||
|
||||
/// The indices data type.
|
||||
public var componentType:GLTFAccessorSparseIndicesComponentType
|
||||
public var componentType: GLTFAccessorSparseIndicesComponentType
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case bufferView
|
||||
|
|
|
@ -9,21 +9,20 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Array of size `accessor.sparse.count` times number of components storing the displaced accessor attributes pointed by `accessor.sparse.indices`.
|
||||
@objcMembers
|
||||
open class GLTFAccessorSparseValues : NSObject, Codable {
|
||||
open class GLTFAccessorSparseValues: NSObject, Codable {
|
||||
/// The index of the bufferView with sparse values. Referenced bufferView can't have ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target.
|
||||
public var bufferView:Int
|
||||
public var bufferView: Int
|
||||
|
||||
/// The offset relative to the start of the bufferView in bytes. Must be aligned.
|
||||
public var byteOffset:Int
|
||||
public var byteOffset: Int
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case bufferView
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A keyframe animation.
|
||||
@objcMembers
|
||||
open class GLTFAnimation : NSObject, Codable {
|
||||
open class GLTFAnimation: NSObject, Codable {
|
||||
/// An array of channels, each of which targets an animation's sampler at a node's property. Different channels of the same animation can't have equal targets.
|
||||
public var channels:[GLTFAnimationChannel]
|
||||
public var channels: [GLTFAnimationChannel]
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// An array of samplers that combines input and output accessors with an interpolation algorithm to define a keyframe graph (but not its target).
|
||||
public var samplers:[GLTFAnimationSampler]
|
||||
public var samplers: [GLTFAnimationSampler]
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case channels
|
||||
|
|
|
@ -9,21 +9,20 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Targets an animation's sampler at a node's property.
|
||||
@objcMembers
|
||||
open class GLTFAnimationChannel : NSObject, Codable {
|
||||
open class GLTFAnimationChannel: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of a sampler in this animation used to compute the value for the target.
|
||||
public var sampler:Int
|
||||
public var sampler: Int
|
||||
|
||||
/// The index of the node and TRS property that an animation channel targets.
|
||||
public var target:GLTFAnimationChannelTarget
|
||||
public var target: GLTFAnimationChannelTarget
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -45,21 +45,20 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// The index of the node and TRS property that an animation channel targets.
|
||||
@objcMembers
|
||||
open class GLTFAnimationChannelTarget : NSObject, Codable {
|
||||
open class GLTFAnimationChannelTarget: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the node to target.
|
||||
public var node:Int?
|
||||
public var node: Int?
|
||||
|
||||
/// The name of the node's TRS property to modify, or the "weights" of the Morph Targets it instantiates. For the "translation" property, the values that are provided by the sampler are the translation along the x, y, and z axes. For the "rotation" property, the values are a quaternion in the order (x, y, z, w), where w is the scalar. For the "scale" property, the values are the scaling factors along the x, y, and z axes.
|
||||
public var path:GLTFAnimationChannelTargetPath
|
||||
public var path: GLTFAnimationChannelTargetPath
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -43,24 +43,23 @@ import Foundation
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Combines input and output accessors with an interpolation algorithm to define a keyframe graph (but not its target).
|
||||
@objcMembers
|
||||
open class GLTFAnimationSampler : NSObject, Codable {
|
||||
open class GLTFAnimationSampler: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of an accessor containing keyframe input values, e.g., time.
|
||||
public var input:Int
|
||||
public var input: Int
|
||||
|
||||
/// Interpolation algorithm.
|
||||
public var interpolation:GLTFAnimationSamplerInterpolation
|
||||
public var interpolation: GLTFAnimationSamplerInterpolation
|
||||
|
||||
/// The index of an accessor, containing keyframe output values.
|
||||
public var output:Int
|
||||
public var output: Int
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -9,27 +9,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Metadata about the glTF asset.
|
||||
@objcMembers
|
||||
open class GLTFAsset : NSObject, Codable {
|
||||
open class GLTFAsset: NSObject, Codable {
|
||||
/// A copyright message suitable for display to credit the content creator.
|
||||
public var copyright:String?
|
||||
public var copyright: String?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// Tool that generated this glTF model. Useful for debugging.
|
||||
public var generator:String?
|
||||
public var generator: String?
|
||||
|
||||
/// The minimum glTF version that this asset targets.
|
||||
public var minVersion:String?
|
||||
public var minVersion: String?
|
||||
|
||||
/// The glTF version that this asset targets.
|
||||
public var version:String
|
||||
public var version: String
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case copyright
|
||||
|
@ -39,8 +38,8 @@ open class GLTFAsset : NSObject, Codable {
|
|||
case minVersion
|
||||
case version
|
||||
}
|
||||
|
||||
public init(version v:String) {
|
||||
|
||||
public init(version v: String) {
|
||||
version = v
|
||||
}
|
||||
|
||||
|
|
|
@ -9,25 +9,24 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A buffer points to binary geometry, animation, or skins.
|
||||
@objcMembers
|
||||
open class GLTFBuffer : NSObject, Codable {
|
||||
open class GLTFBuffer: NSObject, Codable {
|
||||
/// The length of the buffer in bytes.
|
||||
public var byteLength:Int
|
||||
public var byteLength: Int
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The uri of the buffer.
|
||||
public var uri:String?
|
||||
|
||||
public var uri: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case byteLength
|
||||
case extensions
|
||||
|
@ -35,7 +34,7 @@ open class GLTFBuffer : NSObject, Codable {
|
|||
case name
|
||||
case uri
|
||||
}
|
||||
|
||||
|
||||
public override init() {
|
||||
byteLength = 0
|
||||
}
|
||||
|
|
|
@ -35,33 +35,32 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// A view into a buffer generally representing a subset of the buffer.
|
||||
@objcMembers
|
||||
open class GLTFBufferView : NSObject, Codable {
|
||||
open class GLTFBufferView: NSObject, Codable {
|
||||
/// The index of the buffer.
|
||||
public var buffer:Int
|
||||
public var buffer: Int
|
||||
|
||||
/// The length of the bufferView in bytes.
|
||||
public var byteLength:Int
|
||||
public var byteLength: Int
|
||||
|
||||
/// The offset into the buffer in bytes.
|
||||
public var byteOffset:Int
|
||||
public var byteOffset: Int
|
||||
|
||||
/// The stride, in bytes.
|
||||
public var byteStride:Int?
|
||||
public var byteStride: Int?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The target that the GPU buffer should be bound to.
|
||||
public var target:GLTFBufferViewTarget?
|
||||
public var target: GLTFBufferViewTarget?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case buffer
|
||||
|
@ -74,12 +73,12 @@ open class GLTFBufferView : NSObject, Codable {
|
|||
case target
|
||||
}
|
||||
|
||||
public init(buffer b:Int, byteLength bl:Int, byteOffset bo:Int) {
|
||||
public init(buffer b: Int, byteLength bl: Int, byteOffset bo: Int) {
|
||||
buffer = b
|
||||
byteLength = bl
|
||||
byteOffset = bo
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
buffer = try container.decode(Int.self, forKey: .buffer)
|
||||
|
|
|
@ -35,27 +35,26 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// A camera's projection. A node can reference a camera to apply a transform to place the camera in the scene.
|
||||
@objcMembers
|
||||
open class GLTFCamera : NSObject, Codable {
|
||||
open class GLTFCamera: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// An orthographic camera containing properties to create an orthographic projection matrix.
|
||||
public var orthographic:GLTFCameraOrthographic?
|
||||
public var orthographic: GLTFCameraOrthographic?
|
||||
|
||||
/// A perspective camera containing properties to create a perspective projection matrix.
|
||||
public var perspective:GLTFCameraPerspective?
|
||||
public var perspective: GLTFCameraPerspective?
|
||||
|
||||
/// Specifies if the camera uses a perspective or orthographic projection.
|
||||
public var type:GLTFCameraType
|
||||
public var type: GLTFCameraType
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -9,27 +9,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// An orthographic camera containing properties to create an orthographic projection matrix.
|
||||
@objcMembers
|
||||
open class GLTFCameraOrthographic : NSObject, Codable {
|
||||
open class GLTFCameraOrthographic: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The floating-point horizontal magnification of the view. Must not be zero.
|
||||
public var xmag:Double
|
||||
public var xmag: Double
|
||||
|
||||
/// The floating-point vertical magnification of the view. Must not be zero.
|
||||
public var ymag:Double
|
||||
public var ymag: Double
|
||||
|
||||
/// The floating-point distance to the far clipping plane. `zfar` must be greater than `znear`.
|
||||
public var zfar:Double
|
||||
public var zfar: Double
|
||||
|
||||
/// The floating-point distance to the near clipping plane.
|
||||
public var znear:Double
|
||||
public var znear: Double
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -9,27 +9,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A perspective camera containing properties to create a perspective projection matrix.
|
||||
@objcMembers
|
||||
open class GLTFCameraPerspective : NSObject, Codable {
|
||||
open class GLTFCameraPerspective: NSObject, Codable {
|
||||
/// The floating-point aspect ratio of the field of view.
|
||||
public var aspectRatio:Double?
|
||||
public var aspectRatio: Double?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The floating-point vertical field of view in radians.
|
||||
public var yfov:Double
|
||||
public var yfov: Double
|
||||
|
||||
/// The floating-point distance to the far clipping plane.
|
||||
public var zfar:Double?
|
||||
public var zfar: Double?
|
||||
|
||||
/// The floating-point distance to the near clipping plane.
|
||||
public var znear:Double
|
||||
public var znear: Double
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case aspectRatio
|
||||
|
|
|
@ -35,28 +35,27 @@ import Foundation
|
|||
|
||||
}
|
||||
|
||||
|
||||
/// Image data used to create a texture. Image can be referenced by URI or `bufferView` index. `mimeType` is required in the latter case.
|
||||
@objcMembers
|
||||
open class GLTFImage : NSObject, Codable {
|
||||
open class GLTFImage: NSObject, Codable {
|
||||
/// The index of the bufferView that contains the image. Use this instead of the image's uri property.
|
||||
public var bufferView:Int?
|
||||
public var bufferView: Int?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The image's MIME type.
|
||||
public var mimeType:GLTFImageMimeType?
|
||||
public var mimeType: GLTFImageMimeType?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The uri of the image.
|
||||
public var uri:String?
|
||||
|
||||
public var uri: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case bufferView
|
||||
case extensions
|
||||
|
@ -67,7 +66,7 @@ open class GLTFImage : NSObject, Codable {
|
|||
}
|
||||
|
||||
public override init() { }
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
bufferView = try? container.decode(Int.self, forKey: .bufferView)
|
||||
|
|
|
@ -9,26 +9,25 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Class template
|
||||
@objcMembers
|
||||
open class GLTFKHRDracoMeshCompressionExtension : NSObject, Codable {
|
||||
open class GLTFKHRDracoMeshCompressionExtension: NSObject, Codable {
|
||||
/// A dictionary object, where each key corresponds to an attribute and its unique attribute id stored in the compressed geometry.
|
||||
public var attributes:[String: Int]
|
||||
public var attributes: [String: Int]
|
||||
|
||||
/// The index of the bufferView.
|
||||
public var bufferView:Int
|
||||
public var bufferView: Int
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case attributes
|
||||
case bufferView
|
||||
}
|
||||
|
||||
public init(attributes a:[String: Int], bufferView bv:Int) {
|
||||
public init(attributes a: [String: Int], bufferView bv: Int) {
|
||||
attributes = a
|
||||
bufferView = bv
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
attributes = try container.decode([String: Int].self, forKey: .attributes)
|
||||
|
|
|
@ -43,43 +43,42 @@ import Foundation
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// The material appearance of a primitive.
|
||||
@objcMembers
|
||||
open class GLTFMaterial : NSObject, Codable {
|
||||
open class GLTFMaterial: NSObject, Codable {
|
||||
/// The alpha cutoff value of the material.
|
||||
public var alphaCutoff:Double
|
||||
public var alphaCutoff: Double
|
||||
|
||||
/// The alpha rendering mode of the material.
|
||||
public var alphaMode:GLTFMaterialAlphaMode
|
||||
public var alphaMode: GLTFMaterialAlphaMode
|
||||
|
||||
/// Specifies whether the material is double sided.
|
||||
public var doubleSided:Bool
|
||||
public var doubleSided: Bool
|
||||
|
||||
/// The emissive color of the material.
|
||||
public var emissiveFactor:[Double]
|
||||
public var emissiveFactor: [Double]
|
||||
|
||||
/// Reference to a texture.
|
||||
public var emissiveTexture:GLTFTextureInfo?
|
||||
public var emissiveTexture: GLTFTextureInfo?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The normal map texture.
|
||||
public var normalTexture:GLTFMaterialNormalTextureInfo?
|
||||
public var normalTexture: GLTFMaterialNormalTextureInfo?
|
||||
|
||||
/// The occlusion map texture.
|
||||
public var occlusionTexture:GLTFMaterialOcclusionTextureInfo?
|
||||
public var occlusionTexture: GLTFMaterialOcclusionTextureInfo?
|
||||
|
||||
/// A set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology.
|
||||
public var pbrMetallicRoughness:GLTFMaterialPBRMetallicRoughness?
|
||||
|
||||
public var pbrMetallicRoughness: GLTFMaterialPBRMetallicRoughness?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case alphaCutoff
|
||||
case alphaMode
|
||||
|
@ -100,7 +99,7 @@ open class GLTFMaterial : NSObject, Codable {
|
|||
doubleSided = false
|
||||
emissiveFactor = [0, 0, 0]
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
do {
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// The normal map texture.
|
||||
@objcMembers
|
||||
open class GLTFMaterialNormalTextureInfo : NSObject, Codable {
|
||||
open class GLTFMaterialNormalTextureInfo: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the texture.
|
||||
public var index:Int?
|
||||
public var index: Int?
|
||||
|
||||
/// The scalar multiplier applied to each normal vector of the normal texture.
|
||||
public var scale:Double
|
||||
public var scale: Double
|
||||
|
||||
/// The set index of texture's TEXCOORD attribute used for texture coordinate mapping.
|
||||
public var texCoord:Int
|
||||
public var texCoord: Int
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
@ -40,7 +39,7 @@ open class GLTFMaterialNormalTextureInfo : NSObject, Codable {
|
|||
scale = 1
|
||||
texCoord = 0
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
extensions = try? container.decode([String: Any].self, forKey: .extensions)
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// The occlusion map texture.
|
||||
@objcMembers
|
||||
open class GLTFMaterialOcclusionTextureInfo : NSObject, Codable {
|
||||
open class GLTFMaterialOcclusionTextureInfo: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the texture.
|
||||
public var index:Int?
|
||||
public var index: Int?
|
||||
|
||||
/// A scalar multiplier controlling the amount of occlusion applied.
|
||||
public var strength:Double
|
||||
public var strength: Double
|
||||
|
||||
/// The set index of texture's TEXCOORD attribute used for texture coordinate mapping.
|
||||
public var texCoord:Int
|
||||
public var texCoord: Int
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -9,30 +9,29 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology.
|
||||
@objcMembers
|
||||
open class GLTFMaterialPBRMetallicRoughness : NSObject, Codable {
|
||||
open class GLTFMaterialPBRMetallicRoughness: NSObject, Codable {
|
||||
/// The material's base color factor.
|
||||
public var baseColorFactor:[Double]
|
||||
public var baseColorFactor: [Double]
|
||||
|
||||
/// Reference to a texture.
|
||||
public var baseColorTexture:GLTFTextureInfo?
|
||||
public var baseColorTexture: GLTFTextureInfo?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The metalness of the material.
|
||||
public var metallicFactor:Double
|
||||
public var metallicFactor: Double
|
||||
|
||||
/// Reference to a texture.
|
||||
public var metallicRoughnessTexture:GLTFTextureInfo?
|
||||
public var metallicRoughnessTexture: GLTFTextureInfo?
|
||||
|
||||
/// The roughness of the material.
|
||||
public var roughnessFactor:Double
|
||||
public var roughnessFactor: Double
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case baseColorFactor
|
||||
|
@ -49,7 +48,7 @@ open class GLTFMaterialPBRMetallicRoughness : NSObject, Codable {
|
|||
metallicFactor = 1
|
||||
roughnessFactor = 1
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
do {
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A set of primitives to be rendered. A node can contain one mesh. A node's transform places the mesh in the scene.
|
||||
@objcMembers
|
||||
open class GLTFMesh : NSObject, Codable {
|
||||
open class GLTFMesh: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// An array of primitives, each defining geometry to be rendered with a material.
|
||||
public var primitives:[GLTFMeshPrimitive]
|
||||
public var primitives: [GLTFMeshPrimitive]
|
||||
|
||||
/// Array of weights to be applied to the Morph Targets.
|
||||
public var weights:[Double]?
|
||||
public var weights: [Double]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
@ -39,7 +38,7 @@ open class GLTFMesh : NSObject, Codable {
|
|||
public override init() {
|
||||
primitives = [GLTFMeshPrimitive]()
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
extensions = try? container.decode([String: Any].self, forKey: .extensions)
|
||||
|
|
|
@ -63,30 +63,29 @@ import Foundation
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Geometry to be rendered with the given material.
|
||||
@objcMembers
|
||||
open class GLTFMeshPrimitive : NSObject, Codable {
|
||||
open class GLTFMeshPrimitive: NSObject, Codable {
|
||||
/// A dictionary object, where each key corresponds to mesh attribute semantic and each value is the index of the accessor containing attribute's data.
|
||||
public var attributes:[String: Int]
|
||||
public var attributes: [String: Int]
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the accessor that contains the indices.
|
||||
public var indices:Int?
|
||||
public var indices: Int?
|
||||
|
||||
/// The index of the material to apply to this primitive when rendering.
|
||||
public var material:Int?
|
||||
public var material: Int?
|
||||
|
||||
/// The type of primitives to render.
|
||||
public var mode:GLTFMeshPrimitiveMode
|
||||
public var mode: GLTFMeshPrimitiveMode
|
||||
|
||||
/// An array of Morph Targets, each Morph Target is a dictionary mapping attributes (only `POSITION`, `NORMAL`, and `TANGENT` supported) to their deviations in the Morph Target.
|
||||
public var targets:[[String: Int]]?
|
||||
public var targets: [[String: Int]]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case attributes
|
||||
|
@ -97,16 +96,18 @@ open class GLTFMeshPrimitive : NSObject, Codable {
|
|||
case mode
|
||||
case targets
|
||||
}
|
||||
|
||||
public init(attributes a:[String: Int], mode m:GLTFMeshPrimitiveMode) {
|
||||
|
||||
public init(attributes a: [String: Int], mode m: GLTFMeshPrimitiveMode) {
|
||||
attributes = a
|
||||
mode = m
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
attributes = (try? container.decode([String: Int].self, forKey: .attributes)) ?? [String: Int]()
|
||||
attributes = (try? container.decode([String: Int].self, forKey: .attributes)) ?? [String: Int]()
|
||||
#if DRACO
|
||||
extensions = try? container.decode([String: GLTFKHRDracoMeshCompressionExtension].self, forKey: .extensions)
|
||||
#endif
|
||||
extras = try? container.decode([String: Any].self, forKey: .extras)
|
||||
indices = try? container.decode(Int.self, forKey: .indices)
|
||||
material = try? container.decode(Int.self, forKey: .material)
|
||||
|
@ -121,16 +122,19 @@ open class GLTFMeshPrimitive : NSObject, Codable {
|
|||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(attributes, forKey: .attributes)
|
||||
#if DRACO
|
||||
try? container.encode(extensions as? [String: GLTFKHRDracoMeshCompressionExtension], forKey: .extensions)
|
||||
#endif
|
||||
try container.encode(extras, forKey: .extras)
|
||||
try container.encode(indices, forKey: .indices)
|
||||
try container.encode(material, forKey: .material)
|
||||
try container.encode(mode, forKey: .mode)
|
||||
try container.encode(targets, forKey: .targets)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if DRACO
|
||||
extension KeyedEncodingContainerProtocol {
|
||||
mutating func encode(_ value: [String: GLTFKHRDracoMeshCompressionExtension]?, forKey key: Key) throws {
|
||||
if value != nil {
|
||||
|
@ -139,4 +143,4 @@ extension KeyedEncodingContainerProtocol {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,45 +9,44 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A node in the node hierarchy. When the node contains `skin`, all `mesh.primitives` must contain `JOINTS_0` and `WEIGHTS_0` attributes. A node can have either a `matrix` or any combination of `translation`/`rotation`/`scale` (TRS) properties. TRS properties are converted to matrices and postmultiplied in the `T * R * S` order to compose the transformation matrix; first the scale is applied to the vertices, then the rotation, and then the translation. If none are provided, the transform is the identity. When a node is targeted for animation (referenced by an animation.channel.target), only TRS properties may be present; `matrix` will not be present.
|
||||
@objcMembers
|
||||
open class GLTFNode : NSObject, Codable {
|
||||
open class GLTFNode: NSObject, Codable {
|
||||
/// The index of the camera referenced by this node.
|
||||
public var camera:Int?
|
||||
public var camera: Int?
|
||||
|
||||
/// The indices of this node's children.
|
||||
public var children:[Int]?
|
||||
public var children: [Int]?
|
||||
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// A floating-point 4x4 transformation matrix stored in column-major order.
|
||||
public var matrix:[Double]
|
||||
public var matrix: [Double]
|
||||
|
||||
/// The index of the mesh in this node.
|
||||
public var mesh:Int?
|
||||
public var mesh: Int?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The node's unit quaternion rotation in the order (x, y, z, w), where w is the scalar.
|
||||
public var rotation:[Double]
|
||||
public var rotation: [Double]
|
||||
|
||||
/// The node's non-uniform scale, given as the scaling factors along the x, y, and z axes.
|
||||
public var scale:[Double]
|
||||
public var scale: [Double]
|
||||
|
||||
/// The index of the skin referenced by this node.
|
||||
public var skin:Int?
|
||||
public var skin: Int?
|
||||
|
||||
/// The node's translation along the x, y, and z axes.
|
||||
public var translation:[Double]
|
||||
public var translation: [Double]
|
||||
|
||||
/// The weights of the instantiated Morph Target. Number of elements must match number of Morph Targets of used mesh.
|
||||
public var weights:[Double]?
|
||||
public var weights: [Double]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case camera
|
||||
|
@ -63,7 +62,7 @@ open class GLTFNode : NSObject, Codable {
|
|||
case translation
|
||||
case weights
|
||||
}
|
||||
|
||||
|
||||
public override init() {
|
||||
matrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
||||
rotation = [0, 0, 0, 1]
|
||||
|
|
|
@ -149,30 +149,29 @@ import Foundation
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Texture sampler properties for filtering and wrapping modes.
|
||||
@objcMembers
|
||||
open class GLTFSampler : NSObject, Codable {
|
||||
open class GLTFSampler: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// Magnification filter.
|
||||
public var magFilter:GLTFSamplerMagFilter?
|
||||
public var magFilter: GLTFSamplerMagFilter?
|
||||
|
||||
/// Minification filter.
|
||||
public var minFilter:GLTFSamplerMinFilter?
|
||||
public var minFilter: GLTFSamplerMinFilter?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// s wrapping mode.
|
||||
public var wrapS:GLTFSamplerWrapS
|
||||
public var wrapS: GLTFSamplerWrapS
|
||||
|
||||
/// t wrapping mode.
|
||||
public var wrapT:GLTFSamplerWrapT
|
||||
public var wrapT: GLTFSamplerWrapT
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
@ -184,11 +183,11 @@ open class GLTFSampler : NSObject, Codable {
|
|||
case wrapT
|
||||
}
|
||||
|
||||
public init(wrapS s:GLTFSamplerWrapS, wrapT t:GLTFSamplerWrapT) {
|
||||
public init(wrapS s: GLTFSamplerWrapS, wrapT t: GLTFSamplerWrapT) {
|
||||
wrapS = s
|
||||
wrapT = t
|
||||
}
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
extensions = try? container.decode([String: Any].self, forKey: .extensions)
|
||||
|
|
|
@ -9,21 +9,20 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// The root nodes of a scene.
|
||||
@objcMembers
|
||||
open class GLTFScene : NSObject, Codable {
|
||||
open class GLTFScene: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The indices of each root node.
|
||||
public var nodes:[Int]?
|
||||
public var nodes: [Int]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
@ -31,7 +30,7 @@ open class GLTFScene : NSObject, Codable {
|
|||
case name
|
||||
case nodes
|
||||
}
|
||||
|
||||
|
||||
public override init() {}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
|
|
|
@ -9,27 +9,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Joints and matrices defining a skin.
|
||||
@objcMembers
|
||||
open class GLTFSkin : NSObject, Codable {
|
||||
open class GLTFSkin: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the accessor containing the floating-point 4x4 inverse-bind matrices. The default is that each matrix is a 4x4 identity matrix, which implies that inverse-bind matrices were pre-applied.
|
||||
public var inverseBindMatrices:Int?
|
||||
public var inverseBindMatrices: Int?
|
||||
|
||||
/// Indices of skeleton nodes, used as joints in this skin.
|
||||
public var joints:[Int]
|
||||
public var joints: [Int]
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The index of the node used as a skeleton root. When undefined, joints transforms resolve to scene root.
|
||||
public var skeleton:Int?
|
||||
public var skeleton: Int?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
|
|
|
@ -9,25 +9,24 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// A texture and its sampler.
|
||||
@objcMembers
|
||||
open class GLTFTexture : NSObject, Codable {
|
||||
open class GLTFTexture: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The user-defined name of this object.
|
||||
public var name:String?
|
||||
public var name: String?
|
||||
|
||||
/// The index of the sampler used by this texture. When undefined, a sampler with repeat wrapping and auto filtering should be used.
|
||||
public var sampler:Int?
|
||||
public var sampler: Int?
|
||||
|
||||
/// The index of the image used by this texture.
|
||||
public var source:Int?
|
||||
|
||||
public var source: Int?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
case extras
|
||||
|
@ -37,10 +36,10 @@ open class GLTFTexture : NSObject, Codable {
|
|||
}
|
||||
|
||||
public override init() { }
|
||||
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
extensions = try? container.decode([String: GLTF_3D4MCompressedTextureExtension].self, forKey: .extensions)
|
||||
extensions = try? container.decode([String: GLTF_3D4MCompressedTextureExtension].self, forKey: .extensions)
|
||||
extras = try? container.decode([String: Any].self, forKey: .extras)
|
||||
name = try? container.decode(String.self, forKey: .name)
|
||||
sampler = try? container.decode(Int.self, forKey: .sampler)
|
||||
|
@ -57,7 +56,6 @@ open class GLTFTexture : NSObject, Codable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
extension KeyedEncodingContainerProtocol {
|
||||
mutating func encode(_ value: [String: GLTF_3D4MCompressedTextureExtension]?, forKey key: Key) throws {
|
||||
if value != nil {
|
||||
|
@ -66,4 +64,3 @@ extension KeyedEncodingContainerProtocol {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,27 +9,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Reference to a texture.
|
||||
@objcMembers
|
||||
open class GLTFTextureInfo : NSObject, Codable {
|
||||
open class GLTFTextureInfo: NSObject, Codable {
|
||||
/// Dictionary object with extension-specific objects.
|
||||
public var extensions:[String: Any]?
|
||||
public var extensions: [String: Any]?
|
||||
|
||||
/// Application-specific data.
|
||||
public var extras:[String: Any]?
|
||||
public var extras: [String: Any]?
|
||||
|
||||
/// The index of the texture.
|
||||
public var index:Int
|
||||
public var index: Int
|
||||
|
||||
/// The set index of texture's TEXCOORD attribute used for texture coordinate mapping.
|
||||
public var texCoord:Int
|
||||
public var texCoord: Int
|
||||
|
||||
public init(index i:Int) {
|
||||
public init(index i: Int) {
|
||||
index = i
|
||||
texCoord = 0
|
||||
}
|
||||
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case extensions
|
||||
case extras
|
||||
|
|
|
@ -7,36 +7,34 @@ import CoreGraphics
|
|||
|
||||
public struct JSONCodingKeys: CodingKey {
|
||||
public var stringValue: String
|
||||
|
||||
|
||||
public init(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
|
||||
public var intValue: Int?
|
||||
|
||||
|
||||
public init?(intValue: Int) {
|
||||
self.init(stringValue: "\(intValue)")
|
||||
self.intValue = intValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension KeyedDecodingContainer {
|
||||
|
||||
func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
|
||||
|
||||
func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] {
|
||||
let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
|
||||
return try container.decode(type)
|
||||
}
|
||||
|
||||
func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
|
||||
|
||||
func decode(_ type: [Any].Type, forKey key: K) throws -> [Any] {
|
||||
var container = try self.nestedUnkeyedContainer(forKey: key)
|
||||
return try container.decode(type)
|
||||
}
|
||||
|
||||
|
||||
func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
|
||||
var dictionary = Dictionary<String, Any>()
|
||||
|
||||
|
||||
func decode(_ type: [String: Any].Type) throws -> [String: Any] {
|
||||
var dictionary = [String: Any]()
|
||||
|
||||
for key in allKeys {
|
||||
if let boolValue = try? decode(Bool.self, forKey: key) {
|
||||
dictionary[key.stringValue] = boolValue
|
||||
|
@ -57,8 +55,8 @@ public extension KeyedDecodingContainer {
|
|||
}
|
||||
|
||||
public extension UnkeyedDecodingContainer {
|
||||
|
||||
mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
|
||||
|
||||
mutating func decode(_ type: [Any].Type) throws -> [Any] {
|
||||
var array: [Any] = []
|
||||
while isAtEnd == false {
|
||||
if let value = try? decode(Bool.self) {
|
||||
|
@ -69,22 +67,22 @@ public extension UnkeyedDecodingContainer {
|
|||
array.append(value)
|
||||
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
|
||||
array.append(nestedDictionary)
|
||||
} else if let nestedArray = try? decode(Array<Any>.self) {
|
||||
} else if var nestedContainer = try? nestedUnkeyedContainer(),
|
||||
let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
|
||||
array.append(nestedArray)
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
|
||||
|
||||
mutating func decode(_ type: [String: Any].Type) throws -> [String: Any] {
|
||||
let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
|
||||
return try nestedContainer.decode(type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension KeyedEncodingContainerProtocol where Key == JSONCodingKeys {
|
||||
mutating func encode(_ value: Dictionary<String, Any>) throws {
|
||||
mutating func encode(_ value: [String: Any]) throws {
|
||||
try value.forEach({ (key, value) in
|
||||
let key = JSONCodingKeys(stringValue: key)
|
||||
switch value {
|
||||
|
@ -98,9 +96,9 @@ public extension KeyedEncodingContainerProtocol where Key == JSONCodingKeys {
|
|||
try encode(value, forKey: key)
|
||||
case let value as CGFloat:
|
||||
try encode(value, forKey: key)
|
||||
case let value as Dictionary<String, Any>:
|
||||
case let value as [String: Any]:
|
||||
try encode(value, forKey: key)
|
||||
case let value as Array<Any>:
|
||||
case let value as [Any]:
|
||||
try encode(value, forKey: key)
|
||||
case Optional<Any>.none:
|
||||
try encodeNil(forKey: key)
|
||||
|
@ -112,14 +110,14 @@ public extension KeyedEncodingContainerProtocol where Key == JSONCodingKeys {
|
|||
}
|
||||
|
||||
public extension KeyedEncodingContainerProtocol {
|
||||
mutating func encode(_ value: Dictionary<String, Any>?, forKey key: Key) throws {
|
||||
mutating func encode(_ value: [String: Any]?, forKey key: Key) throws {
|
||||
if value != nil {
|
||||
var container = self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
|
||||
try container.encode(value!)
|
||||
}
|
||||
}
|
||||
|
||||
mutating func encode(_ value: Array<Any>?, forKey key: Key) throws {
|
||||
|
||||
mutating func encode(_ value: [Any]?, forKey key: Key) throws {
|
||||
if value != nil {
|
||||
var container = self.nestedUnkeyedContainer(forKey: key)
|
||||
try container.encode(value!)
|
||||
|
@ -128,7 +126,7 @@ public extension KeyedEncodingContainerProtocol {
|
|||
}
|
||||
|
||||
public extension UnkeyedEncodingContainer {
|
||||
mutating func encode(_ value: Array<Any>) throws {
|
||||
mutating func encode(_ value: [Any]) throws {
|
||||
try value.enumerated().forEach({ (index, value) in
|
||||
switch value {
|
||||
case let value as Bool:
|
||||
|
@ -141,9 +139,9 @@ public extension UnkeyedEncodingContainer {
|
|||
try encode(value)
|
||||
case let value as CGFloat:
|
||||
try encode(value)
|
||||
case let value as Dictionary<String, Any>:
|
||||
case let value as [String: Any]:
|
||||
try encode(value)
|
||||
case let value as Array<Any>:
|
||||
case let value as [Any]:
|
||||
try encode(value)
|
||||
case Optional<Any>.none:
|
||||
try encodeNil()
|
||||
|
@ -153,10 +151,9 @@ public extension UnkeyedEncodingContainer {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
mutating func encode(_ value: Dictionary<String, Any>) throws {
|
||||
|
||||
mutating func encode(_ value: [String: Any]) throws {
|
||||
var nestedContainer = self.nestedContainer(keyedBy: JSONCodingKeys.self)
|
||||
try nestedContainer.encode(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
import SceneKit
|
||||
|
||||
|
||||
extension GLTFMeshPrimitiveMode {
|
||||
fileprivate func scn() -> SCNGeometryPrimitiveType {
|
||||
public func scn() -> SCNGeometryPrimitiveType {
|
||||
switch self {
|
||||
case .POINTS:
|
||||
return .point
|
||||
|
@ -25,8 +24,9 @@ extension GLTFMeshPrimitiveMode {
|
|||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTFAccessor {
|
||||
fileprivate func components() -> Int {
|
||||
public func components() -> Int {
|
||||
switch type {
|
||||
case .SCALAR:
|
||||
return 1
|
||||
|
@ -42,8 +42,8 @@ extension GLTFAccessor {
|
|||
return 16
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func bytesPerElement() -> Int {
|
||||
|
||||
public func bytesPerElement() -> Int {
|
||||
switch componentType {
|
||||
case .UNSIGNED_BYTE, .BYTE:
|
||||
return 1
|
||||
|
@ -53,20 +53,20 @@ extension GLTFAccessor {
|
|||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func vertexFormat() -> MTLVertexFormat {
|
||||
|
||||
public func vertexFormat() -> MTLVertexFormat {
|
||||
switch type {
|
||||
case .SCALAR:
|
||||
switch componentType {
|
||||
case .UNSIGNED_SHORT, .SHORT, .UNSIGNED_BYTE, .BYTE:
|
||||
fatalError("Unsupported")
|
||||
|
||||
|
||||
case .UNSIGNED_INT:
|
||||
return .uint
|
||||
case .FLOAT:
|
||||
return .float
|
||||
}
|
||||
|
||||
|
||||
case .VEC2:
|
||||
switch componentType {
|
||||
case .UNSIGNED_SHORT:
|
||||
|
@ -82,7 +82,7 @@ extension GLTFAccessor {
|
|||
case .FLOAT:
|
||||
return .float2
|
||||
}
|
||||
|
||||
|
||||
case .VEC3:
|
||||
switch componentType {
|
||||
case .UNSIGNED_SHORT:
|
||||
|
@ -98,7 +98,7 @@ extension GLTFAccessor {
|
|||
case .FLOAT:
|
||||
return .float3
|
||||
}
|
||||
|
||||
|
||||
case .VEC4:
|
||||
switch componentType {
|
||||
case .UNSIGNED_SHORT:
|
||||
|
@ -114,7 +114,7 @@ extension GLTFAccessor {
|
|||
case .FLOAT:
|
||||
return .float4
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
fatalError("Unsupported")
|
||||
}
|
||||
|
@ -127,94 +127,93 @@ fileprivate extension GLTF {
|
|||
if let bufferIndex = self.bufferViews?[bufferView].buffer {
|
||||
return self.buffers?[bufferIndex]
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension GLTFConverter {
|
||||
|
||||
|
||||
|
||||
/// convert glTF mesh into SCNGeometry
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - node: gltf node
|
||||
/// - scnNode: SceneKit node, which is going to be parent node
|
||||
internal func geometryNode(_ node:GLTFNode, _ scnNode:SCNNode) throws {
|
||||
|
||||
internal func geometryNode(_ node: GLTFNode, _ scnNode: SCNNode) throws {
|
||||
|
||||
if glTF.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if let meshIndex = node.mesh {
|
||||
|
||||
var weightPaths = [String]()
|
||||
|
||||
|
||||
if let mesh = glTF.meshes?[meshIndex] {
|
||||
|
||||
|
||||
var primitiveIndex = 0
|
||||
|
||||
|
||||
for primitive in mesh.primitives {
|
||||
|
||||
var sources:[SCNGeometrySource] = [SCNGeometrySource]()
|
||||
var elements:[SCNGeometryElement] = [SCNGeometryElement]()
|
||||
|
||||
|
||||
var sources: [SCNGeometrySource] = [SCNGeometrySource]()
|
||||
var elements: [SCNGeometryElement] = [SCNGeometryElement]()
|
||||
|
||||
// get indices
|
||||
if let element = try self.geometryElement(primitive) {
|
||||
elements.append(element)
|
||||
}
|
||||
|
||||
|
||||
// get sources from attributes information
|
||||
if let geometrySources = try self.geometrySources(primitive.attributes) {
|
||||
sources.append(contentsOf: geometrySources)
|
||||
}
|
||||
|
||||
|
||||
#if DRACO
|
||||
// check on draco extension
|
||||
if let dracoMesh = primitive.extensions?[dracoExtensionKey] {
|
||||
let (dElement, dSources) = try glTF.convertDracoMesh(dracoMesh as! GLTFKHRDracoMeshCompressionExtension)
|
||||
|
||||
if (dElement != nil) {
|
||||
|
||||
if dElement != nil {
|
||||
elements.append(dElement!)
|
||||
}
|
||||
|
||||
if (dSources != nil) {
|
||||
|
||||
if dSources != nil {
|
||||
sources.append(contentsOf: dSources!)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if glTF.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let primitiveNode:SCNNode
|
||||
|
||||
let primitiveNode: SCNNode
|
||||
// create geometry
|
||||
let geometry = SCNGeometry.init(sources: sources, elements: elements)
|
||||
|
||||
if primitiveIndex < scnNode.childNodes.count {
|
||||
|
||||
if primitiveIndex < scnNode.childNodes.count {
|
||||
primitiveNode = scnNode.childNodes[primitiveIndex]
|
||||
primitiveNode.geometry = geometry
|
||||
} else {
|
||||
primitiveNode = SCNNode.init(geometry: geometry)
|
||||
scnNode.addChildNode(primitiveNode)
|
||||
}
|
||||
|
||||
|
||||
primitiveNode.name = mesh.name
|
||||
|
||||
|
||||
delegate?.scene?(loadingScene!, didCreate: primitiveNode)
|
||||
|
||||
|
||||
if primitiveNode.geometry?.firstMaterial != nil {
|
||||
// create empty SCNMaterial. Callbacks call later then materail will be download, so we must provide materail for selection
|
||||
let emptyMaterial = SCNMaterial()
|
||||
emptyMaterial.name = "empty"
|
||||
emptyMaterial.isDoubleSided = true
|
||||
|
||||
|
||||
primitiveNode.geometry!.firstMaterial = emptyMaterial
|
||||
}
|
||||
|
||||
|
||||
if let materialIndex = primitive.material {
|
||||
self.glTF.loadMaterial(index:materialIndex, delegate: self, textureChangedCallback: { _ in
|
||||
self.glTF.loadMaterial(index: materialIndex, delegate: self, textureChangedCallback: { _ in
|
||||
if let material = primitiveNode.geometry?.firstMaterial {
|
||||
if let texture = material.diffuse.contents as? MTLTexture {
|
||||
if texture.pixelFormat.hasAlpha() {
|
||||
|
@ -222,24 +221,25 @@ extension GLTFConverter {
|
|||
}
|
||||
}
|
||||
}
|
||||
}) { [unowned self] scnMaterial in
|
||||
}, completionHandler: { [unowned self] scnMaterial in
|
||||
self.delegate?.scene?(self.loadingScene!, didCreate: scnMaterial, for: primitiveNode)
|
||||
|
||||
let emissionContent = primitiveNode.geometry?.firstMaterial?.emission.contents
|
||||
scnMaterial.emission.contents = emissionContent
|
||||
|
||||
geometry.materials = [scnMaterial]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if let transparency = primitiveNode.geometry?.firstMaterial?.transparency,
|
||||
transparency < 1.0 {
|
||||
primitiveNode.renderingOrder = 10
|
||||
}
|
||||
|
||||
|
||||
if glTF.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if let targets = primitive.targets {
|
||||
let morpher = SCNMorpher()
|
||||
let targetsCount = targets.count
|
||||
|
@ -248,7 +248,7 @@ extension GLTFConverter {
|
|||
if let sourcesMorph = try geometrySources(target) {
|
||||
let geometryMorph = SCNGeometry(sources: sourcesMorph, elements: nil)
|
||||
morpher.targets.append(geometryMorph)
|
||||
|
||||
|
||||
let path = "childNodes[\(primitiveIndex)].morpher.weights[\(targetIndex)]"
|
||||
weightPaths.append(path)
|
||||
}
|
||||
|
@ -256,39 +256,37 @@ extension GLTFConverter {
|
|||
morpher.calculationMode = .additive
|
||||
primitiveNode.morpher = morpher
|
||||
}
|
||||
|
||||
|
||||
primitiveIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
scnNode.setValue(weightPaths, forUndefinedKey: "weightPaths")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate func geometryElement(_ primitive: GLTFMeshPrimitive) throws -> SCNGeometryElement? {
|
||||
if let indicesIndex = primitive.indices {
|
||||
if let accessor = glTF.accessors?[indicesIndex],
|
||||
let bufferViewIndex = accessor.bufferView,
|
||||
let bufferView = glTF.bufferViews?[bufferViewIndex] {
|
||||
|
||||
|
||||
if let indicesData = try loadAcessor(accessor, bufferView, false) {
|
||||
|
||||
|
||||
var count = accessor.count
|
||||
|
||||
|
||||
let primitiveType = primitive.mode.scn()
|
||||
switch primitiveType {
|
||||
case .triangles:
|
||||
count = count/3
|
||||
break
|
||||
count /= 3
|
||||
case .triangleStrip:
|
||||
count = count-2
|
||||
break
|
||||
count -= 2
|
||||
case .line:
|
||||
count = count/2
|
||||
count /= 2
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
return SCNGeometryElement.init(data: indicesData,
|
||||
primitiveType: primitiveType,
|
||||
primitiveCount: count,
|
||||
|
@ -300,48 +298,47 @@ extension GLTFConverter {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
/// Convert mesh/animation attributes into SCNGeometrySource
|
||||
///
|
||||
/// - Parameter attributes: dictionary of accessors
|
||||
/// - Returns: array of SCNGeometrySource objects
|
||||
fileprivate func geometrySources(_ attributes:[String:Int]) throws -> [SCNGeometrySource]? {
|
||||
fileprivate func geometrySources(_ attributes: [String: Int]) throws -> [SCNGeometrySource]? {
|
||||
var geometrySources = [SCNGeometrySource]()
|
||||
|
||||
|
||||
// accessors can point to different buffers. We cache last one.
|
||||
var previousBufferView = -1
|
||||
var mtlBuffer:MTLBuffer?
|
||||
|
||||
var mtlBuffer: MTLBuffer?
|
||||
|
||||
var byteOffset = 0
|
||||
var byteStride = 0
|
||||
|
||||
|
||||
|
||||
for (key, accessorIndex) in attributes {
|
||||
if let accessor = glTF.accessors?[accessorIndex],
|
||||
let (bufferView, interleaved) = try determineAcessor(accessor) {
|
||||
|
||||
|
||||
byteOffset = (interleaved) ? accessor.byteOffset : 0
|
||||
byteStride = bufferView.byteStride ?? 0
|
||||
|
||||
if (mtlBuffer == nil || previousBufferView != accessor.bufferView!) {
|
||||
|
||||
if mtlBuffer == nil || previousBufferView != accessor.bufferView! {
|
||||
if let data = try loadAcessor(accessor, bufferView, interleaved) {
|
||||
|
||||
|
||||
let device = self.device()
|
||||
data.withUnsafeBytes { (unsafeBufferPointer:UnsafeRawBufferPointer) in
|
||||
data.withUnsafeBytes { (unsafeBufferPointer: UnsafeRawBufferPointer) in
|
||||
let uint8Ptr = unsafeBufferPointer.bindMemory(to: Int8.self).baseAddress!
|
||||
mtlBuffer = device?.makeBuffer(bytes: uint8Ptr, length: data.count, options: .storageModeShared)
|
||||
}
|
||||
}
|
||||
previousBufferView = accessor.bufferView!
|
||||
}
|
||||
|
||||
|
||||
let count = accessor.count
|
||||
|
||||
let vertexFormat:MTLVertexFormat = accessor.vertexFormat()
|
||||
|
||||
|
||||
let vertexFormat: MTLVertexFormat = accessor.vertexFormat()
|
||||
|
||||
// convert string semantic to SceneKit semantic type
|
||||
let semantic = GLTF.sourceSemantic(name:key)
|
||||
|
||||
let semantic = GLTF.sourceSemantic(name: key)
|
||||
|
||||
if let mtlB = mtlBuffer {
|
||||
let geometrySource = SCNGeometrySource.init(buffer: mtlB,
|
||||
vertexFormat: vertexFormat,
|
||||
|
@ -352,7 +349,7 @@ extension GLTFConverter {
|
|||
geometrySources.append(geometrySource)
|
||||
} else {
|
||||
// TODO: implement fallback on init with data, which was deleted
|
||||
|
||||
|
||||
throw GLTFError("Metal device failed to allocate MTLBuffer with accessor.bufferView = \(accessor.bufferView!)")
|
||||
}
|
||||
} else {
|
||||
|
@ -361,18 +358,17 @@ extension GLTFConverter {
|
|||
}
|
||||
return geometrySources
|
||||
}
|
||||
|
||||
|
||||
// TODO: Collect associated buffers for node into a Set on Decode time.
|
||||
internal func _preloadBuffersData(nodeIndex:Int, completionHandler: @escaping (Error?) -> Void ) {
|
||||
|
||||
var buffers:Set = Set<GLTFBuffer>()
|
||||
|
||||
let insertBuffer: ( (Int?)->Void ) = { [weak self] index in
|
||||
internal func _preloadBuffersData(nodeIndex: Int, completionHandler: @escaping (Error?) -> Void ) {
|
||||
|
||||
var buffers: Set = Set<GLTFBuffer>()
|
||||
|
||||
let insertBuffer: ( (Int?) -> Void ) = { [weak self] index in
|
||||
guard let index = index else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if let accessor = self?.glTF.accessors?[index] {
|
||||
if let bufferView = accessor.bufferView,
|
||||
let buffer = self?.glTF.buffer(for: bufferView) {
|
||||
|
@ -382,13 +378,14 @@ extension GLTFConverter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let node = glTF.nodes?[nodeIndex],
|
||||
let meshIndex = node.mesh,
|
||||
let mesh = glTF.meshes?[meshIndex] {
|
||||
|
||||
|
||||
for primitive in mesh.primitives {
|
||||
// check on draco extension
|
||||
#if DRACO
|
||||
if let dracoMesh = primitive.extensions?[dracoExtensionKey] {
|
||||
if let dracoMesh = dracoMesh as? GLTFKHRDracoMeshCompressionExtension {
|
||||
if let buffer = glTF.buffer(for: dracoMesh.bufferView) {
|
||||
|
@ -396,73 +393,76 @@ extension GLTFConverter {
|
|||
} else {
|
||||
errorMessage = GLTFError("Can't locate draco buffer with bufferView at \(dracoMesh.bufferView) index")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
errorMessage = GLTFError("Draco extension not compatible for mesh at \(meshIndex) index")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
primitive.attributes.forEach { (_, index) in
|
||||
insertBuffer(index)
|
||||
}
|
||||
insertBuffer(primitive.indices)
|
||||
}
|
||||
#else
|
||||
primitive.attributes.forEach { (_, index) in
|
||||
insertBuffer(index)
|
||||
}
|
||||
insertBuffer(primitive.indices)
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
glTF.loader.load(gltf:glTF, resources: buffers, options: ResourceType.buffer, completionHandler:completionHandler)
|
||||
|
||||
glTF.loader.load(gltf: glTF, resources: buffers, options: ResourceType.buffer, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// determine where an accessor and a bufferView link are interleaved or not
|
||||
internal func determineAcessor(_ accessor:GLTFAccessor) throws -> (GLTFBufferView, Bool)? {
|
||||
|
||||
internal func determineAcessor(_ accessor: GLTFAccessor) throws -> (GLTFBufferView, Bool)? {
|
||||
|
||||
guard let index = accessor.bufferView else {
|
||||
throw GLTFError("Missing 'bufferView' for \(accessor.name ?? "") acessor")
|
||||
}
|
||||
|
||||
|
||||
if let bufferView = glTF.bufferViews?[index] {
|
||||
|
||||
|
||||
// Interleaved data usualy has bytesStride as correct value.
|
||||
// Some times non-interleaved data also has bytesStride, and in some cases don't. It's up to exporter
|
||||
// We do calculate bytesStride for accessor manualy and compare it later to determine if our data is interleaved or not.
|
||||
let byteStride:Int = bufferView.byteStride ?? 0
|
||||
let byteStride: Int = bufferView.byteStride ?? 0
|
||||
let accessorByteStride = accessor.components()*accessor.bytesPerElement()
|
||||
|
||||
|
||||
let interleaved = (byteStride != accessorByteStride)
|
||||
return (bufferView, interleaved)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get data by accessor
|
||||
internal func loadAcessor(_ accessor:GLTFAccessor, _ bufferView:GLTFBufferView, _ interleaved:Bool) throws -> Data? {
|
||||
|
||||
internal func loadAcessor(_ accessor: GLTFAccessor, _ bufferView: GLTFBufferView, _ interleaved: Bool) throws -> Data? {
|
||||
|
||||
if let data = try GLTF.requestData(glTF: glTF, bufferView: bufferView) {
|
||||
|
||||
var byteStride:Int = bufferView.byteStride ?? 0
|
||||
if (byteStride == 0) {
|
||||
|
||||
var byteStride: Int = bufferView.byteStride ?? 0
|
||||
if byteStride == 0 {
|
||||
byteStride = accessor.components()*accessor.bytesPerElement()
|
||||
}
|
||||
// calculate length
|
||||
let bytesLength = byteStride * accessor.count
|
||||
|
||||
|
||||
// calculate range
|
||||
let start = bufferView.byteOffset + ((!interleaved) ? accessor.byteOffset : 0)
|
||||
let end = start + bytesLength
|
||||
|
||||
|
||||
var subdata = data
|
||||
if start != 0 || end != data.count {
|
||||
subdata = data.subdata(in: start..<end)
|
||||
}
|
||||
|
||||
|
||||
return subdata
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,30 +8,30 @@
|
|||
import SceneKit
|
||||
import os
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
public class GLTFConverter: TextureLoaderDelegate {
|
||||
|
||||
|
||||
/// Status will be true if `cancel` was call.
|
||||
@objc internal var errorMessage: Error?
|
||||
|
||||
|
||||
internal private(set) var cache_nodes: [SCNNode?]?
|
||||
|
||||
|
||||
internal var _completionHandler: ((Error?) -> Void) = { _ in }
|
||||
internal var nodesDispatchGroup: DispatchGroup = DispatchGroup()
|
||||
internal var convertionProgressMask: ConvertionProgressMask = ConvertionProgressMask(rawValue: 0)
|
||||
|
||||
|
||||
public weak var renderer: SCNSceneRenderer?
|
||||
internal var loadingScene: SCNScene?
|
||||
public weak var delegate: SceneLoadingDelegate?
|
||||
|
||||
|
||||
var animationDuration: Double = 0.0
|
||||
|
||||
|
||||
internal var glTF: GLTF
|
||||
|
||||
|
||||
public init(glTF: GLTF) {
|
||||
self.glTF = glTF
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Convert glTF object to SceneKit scene.
|
||||
///
|
||||
/// - Parameter scene: Optional parameter. If property is set then loaded model will be add to existing objects in scene.
|
||||
|
@ -40,23 +40,24 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
/// - Parameter multiThread: By default model will be load in multiple threads.
|
||||
/// - Parameter completionHandler: Execute completion block once model fully loaded. If multiThread parameter set to true, then scene will be returned soon as possible and completion block will be executed later, after all textures load.
|
||||
/// - Returns: instance of Scene
|
||||
@objc open func convert(to scene:SCNScene = SCNScene.init(),
|
||||
directoryPath:String? = nil,
|
||||
multiThread:Bool = true,
|
||||
hidden:Bool = false,
|
||||
geometryCompletionHandler: @escaping ()->Void = { },
|
||||
completionHandler: @escaping ((Error?) -> Void) = {_ in } ) -> SCNScene? {
|
||||
|
||||
if (glTF.extensionsUsed != nil) {
|
||||
// for key in self.extensionsUsed! {
|
||||
// if !supportedExtensions.contains(key) {
|
||||
// completionHandler("Used `\(key)` extension is not supported!")
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
@objc open func convert(to scene: SCNScene = SCNScene.init(),
|
||||
rootNode: SCNNode? = nil,
|
||||
directoryPath: String? = nil,
|
||||
multiThread: Bool = true,
|
||||
hidden: Bool = false,
|
||||
geometryCompletionHandler: @escaping () -> Void = { },
|
||||
completionHandler: @escaping ((Error?) -> Void) = {_ in }) -> SCNScene? {
|
||||
|
||||
if glTF.extensionsUsed != nil {
|
||||
// for key in self.extensionsUsed! {
|
||||
// if !supportedExtensions.contains(key) {
|
||||
// completionHandler("Used `\(key)` extension is not supported!")
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (glTF.extensionsRequired != nil) {
|
||||
|
||||
if glTF.extensionsRequired != nil {
|
||||
for key in glTF.extensionsRequired! {
|
||||
if !supportedExtensions.contains(key) {
|
||||
completionHandler(GLTFError("Required `\(key)` extension is not supported!"))
|
||||
|
@ -64,57 +65,61 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assert(delegate != nil)
|
||||
|
||||
self.loadingScene = scene
|
||||
self._completionHandler = completionHandler
|
||||
|
||||
|
||||
if directoryPath != nil {
|
||||
glTF.loader.directoryPath = directoryPath!
|
||||
}
|
||||
|
||||
|
||||
// Get dispatch group for current GLTF
|
||||
let convertGroup = self.nodesDispatchGroup
|
||||
convertGroup.enter()
|
||||
|
||||
|
||||
if glTF.scenes != nil && glTF.scene != nil {
|
||||
let sceneGlTF = glTF.scenes![(glTF.scene)!]
|
||||
if let sceneName = sceneGlTF.name {
|
||||
scene.setAttribute(sceneName, forKey: "name")
|
||||
}
|
||||
|
||||
|
||||
self.cache_nodes = [SCNNode?](repeating: nil, count: glTF.nodes!.count)
|
||||
|
||||
|
||||
// run in multi-thread or single
|
||||
if (multiThread) {
|
||||
|
||||
if multiThread {
|
||||
|
||||
let start = Date()
|
||||
|
||||
|
||||
// this enter is requered here in case materials has few textures
|
||||
// which loaded very quickly even before all geometries submitted for load
|
||||
let texturesGroup = TextureStorageManager.manager.group(gltf: glTF, delegate: self, true)
|
||||
|
||||
|
||||
// construct nodes tree
|
||||
_constructNodesTree(rootNode: scene.rootNode, nodes: sceneGlTF.nodes!, group: convertGroup, hidden: hidden)
|
||||
|
||||
|
||||
_constructNodesTree(rootNode: rootNode ?? scene.rootNode, nodes: sceneGlTF.nodes!, group: convertGroup, hidden: hidden)
|
||||
|
||||
os_log("submit data to download %d ms", log: log_scenekit, type: .debug, Int(start.timeIntervalSinceNow * -1000))
|
||||
|
||||
|
||||
// completion
|
||||
convertGroup.notify(queue: DispatchQueue.main) {
|
||||
texturesGroup.leave()
|
||||
|
||||
|
||||
geometryCompletionHandler()
|
||||
os_log("geometry loaded %d ms", log: log_scenekit, type: .debug, Int(start.timeIntervalSinceNow * -1000))
|
||||
|
||||
|
||||
self._nodesConverted(self.errorMessage)
|
||||
}
|
||||
|
||||
|
||||
convertGroup.leave()
|
||||
} else {
|
||||
var err:Error?
|
||||
var err: Error?
|
||||
do {
|
||||
for nodeIndex in sceneGlTF.nodes! {
|
||||
if let scnNode = try self.buildNode(nodeIndex:nodeIndex) {
|
||||
if let scnNode = try self.buildNode(nodeIndex: nodeIndex) {
|
||||
scnNode.isHidden = hidden
|
||||
|
||||
scene.rootNode.addChildNode(scnNode)
|
||||
}
|
||||
}
|
||||
|
@ -126,41 +131,47 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
self._nodesConverted(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if glTF.isCancelled {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
return scene
|
||||
}
|
||||
|
||||
|
||||
@objc
|
||||
open func cancel() {
|
||||
glTF.cancel()
|
||||
TextureStorageManager.manager.clear(gltf: glTF);
|
||||
TextureStorageManager.manager.clear(gltf: glTF)
|
||||
}
|
||||
|
||||
internal func _constructNodesTree(rootNode:SCNNode, nodes:[Int], group:DispatchGroup, hidden:Bool) {
|
||||
|
||||
internal func _constructNodesTree(rootNode: SCNNode, nodes: [Int], group: DispatchGroup, hidden: Bool) {
|
||||
var cache_nodes = self.cache_nodes
|
||||
for nodeIndex in nodes {
|
||||
|
||||
if (glTF.isCancelled) {
|
||||
|
||||
if glTF.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
group.enter() // <=== enter group
|
||||
|
||||
let scnNode = SCNNode()
|
||||
scnNode.isHidden = hidden
|
||||
|
||||
if let node = glTF.nodes?[nodeIndex] {
|
||||
|
||||
group.enter() // <=== enter group
|
||||
|
||||
let scnNode: SCNNode
|
||||
if let name = node.name,
|
||||
let existedNode = rootNode.childNode(withName: name, recursively: false) {
|
||||
scnNode = existedNode
|
||||
} else {
|
||||
scnNode = SCNNode()
|
||||
}
|
||||
scnNode.isHidden = hidden
|
||||
scnNode.name = node.name
|
||||
|
||||
|
||||
let haveChilds = node.children != nil && node.children?.count != 0
|
||||
if haveChilds {
|
||||
_constructNodesTree(rootNode: scnNode, nodes: node.children!, group: group, hidden: hidden)
|
||||
}
|
||||
|
||||
|
||||
// create nodes up front to avoid deadlocks in multithreading
|
||||
if let meshIndex = node.mesh {
|
||||
let primitivesCount = glTF.meshes?[meshIndex].primitives.count ?? 0
|
||||
|
@ -169,37 +180,38 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
scnNode.addChildNode(scnNodePrimitiveNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
rootNode.addChildNode(scnNode)
|
||||
cache_nodes?[nodeIndex] = scnNode
|
||||
|
||||
self._preloadBuffersData(nodeIndex: nodeIndex) { error in
|
||||
if error != nil {
|
||||
print("Failed to load geometry node with error: \(error!)")
|
||||
self.errorMessage = error
|
||||
} else {
|
||||
do {
|
||||
_ = try self.buildNode(nodeIndex: nodeIndex, scnNode: scnNode)
|
||||
} catch {
|
||||
print(error)
|
||||
rootNode.addChildNode(scnNode)
|
||||
cache_nodes?[nodeIndex] = scnNode
|
||||
|
||||
self._preloadBuffersData(nodeIndex: nodeIndex) { error in
|
||||
if error != nil {
|
||||
print("Failed to load geometry node with error: \(error!)")
|
||||
self.errorMessage = error
|
||||
} else {
|
||||
do {
|
||||
_ = try self.buildNode(nodeIndex: nodeIndex, scnNode: scnNode)
|
||||
} catch {
|
||||
print(error)
|
||||
self.errorMessage = error
|
||||
}
|
||||
}
|
||||
group.leave() // <=== leave group
|
||||
}
|
||||
group.leave() // <=== leave group
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Nodes converted, start parse and create animation.
|
||||
/// And in case no textures required to load, complete convertion from glTF to SceneKit.
|
||||
fileprivate func _nodesConverted(_ error:Error?) {
|
||||
fileprivate func _nodesConverted(_ error: Error?) {
|
||||
self.convertionProgressMask.insert(.nodes)
|
||||
|
||||
|
||||
if let e = error {
|
||||
self.errorMessage = e
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try self.parseAnimations()
|
||||
} catch {
|
||||
|
@ -207,72 +219,72 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
}
|
||||
// probably should be inserted some where else and call on completion of animation parse
|
||||
self.convertionProgressMask.insert(.animations)
|
||||
|
||||
|
||||
self.nodesDispatchGroup.wait()
|
||||
|
||||
|
||||
if glTF.textures?.count == 0 {
|
||||
self.convertionProgressMask.insert(.textures)
|
||||
}
|
||||
|
||||
|
||||
if self.convertionProgressMask.rawValue == ConvertionProgressMask.all().rawValue {
|
||||
self._converted(self.errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal func texturesLoaded() {
|
||||
self.convertionProgressMask.insert(.textures)
|
||||
TextureStorageManager.manager.clear(gltf: glTF)
|
||||
|
||||
|
||||
if self.convertionProgressMask.rawValue == ConvertionProgressMask.all().rawValue {
|
||||
self._converted(self.errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Completion function and cache cleaning.
|
||||
internal func _converted(_ error:Error?) {
|
||||
internal func _converted(_ error: Error?) {
|
||||
os_log("convert completed", log: log_scenekit, type: .debug)
|
||||
|
||||
|
||||
delegate?.scene?(loadingScene!)
|
||||
delegate = nil
|
||||
loadingScene = nil
|
||||
|
||||
|
||||
// clear cache
|
||||
_completionHandler(error)
|
||||
_completionHandler = {_ in }
|
||||
|
||||
|
||||
self.cache_nodes?.removeAll()
|
||||
|
||||
|
||||
self.clearCache()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Nodes
|
||||
|
||||
fileprivate func buildNode(nodeIndex:Int, scnNode:SCNNode = SCNNode()) throws -> SCNNode? {
|
||||
|
||||
|
||||
fileprivate func buildNode(nodeIndex: Int, scnNode: SCNNode = SCNNode()) throws -> SCNNode? {
|
||||
|
||||
if let node = glTF.nodes?[nodeIndex] {
|
||||
|
||||
|
||||
// Get camera, if it has reference on any.
|
||||
constructCamera(node, scnNode)
|
||||
|
||||
|
||||
// convert meshes if any exists in gltf node
|
||||
try self.geometryNode(node, scnNode)
|
||||
|
||||
|
||||
// load skin if any reference exists
|
||||
if let skin = node.skin {
|
||||
self.loadSkin(skin, scnNode)
|
||||
}
|
||||
|
||||
|
||||
// bake all transformations into one mtarix
|
||||
scnNode.transform = bakeTransformationMatrix(node)
|
||||
|
||||
|
||||
if glTF.isCancelled {
|
||||
return scnNode
|
||||
}
|
||||
|
||||
|
||||
if let children = node.children {
|
||||
for i in children {
|
||||
if let subSCNNode = try self.buildNode(nodeIndex:i) {
|
||||
if let subSCNNode = try self.buildNode(nodeIndex: i) {
|
||||
scnNode.addChildNode(subSCNNode)
|
||||
}
|
||||
}
|
||||
|
@ -280,17 +292,17 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
}
|
||||
return scnNode
|
||||
}
|
||||
|
||||
fileprivate func bakeTransformationMatrix(_ node:GLTFNode) -> SCNMatrix4 {
|
||||
|
||||
fileprivate func bakeTransformationMatrix(_ node: GLTFNode) -> SCNMatrix4 {
|
||||
let rotation = GLKMatrix4MakeWithQuaternion(GLKQuaternion.init(q: (Float(node.rotation[0]), Float(node.rotation[1]), Float(node.rotation[2]), Float(node.rotation[3]))))
|
||||
var matrix = SCNMatrix4.init(array:node.matrix)
|
||||
var matrix = SCNMatrix4.init(array: node.matrix)
|
||||
matrix = SCNMatrix4Translate(matrix, SCNFloat(node.translation[0]), SCNFloat(node.translation[1]), SCNFloat(node.translation[2]))
|
||||
matrix = SCNMatrix4Mult(matrix, SCNMatrix4FromGLKMatrix4(rotation))
|
||||
matrix = SCNMatrix4Scale(matrix, SCNFloat(node.scale[0]), SCNFloat(node.scale[1]), SCNFloat(node.scale[2]))
|
||||
return matrix
|
||||
}
|
||||
|
||||
fileprivate func constructCamera(_ node:GLTFNode, _ scnNode:SCNNode) {
|
||||
|
||||
fileprivate func constructCamera(_ node: GLTFNode, _ scnNode: SCNNode) {
|
||||
if let cameraIndex = node.camera {
|
||||
scnNode.camera = SCNCamera()
|
||||
if glTF.cameras != nil {
|
||||
|
@ -305,12 +317,10 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
scnNode.camera?.wantsDepthOfField = true
|
||||
scnNode.camera?.motionBlurIntensity = 0.3
|
||||
}
|
||||
break
|
||||
case .orthographic:
|
||||
scnNode.camera?.usesOrthographicProjection = true
|
||||
scnNode.camera?.zNear = (camera.orthographic?.znear)!
|
||||
scnNode.camera?.zFar = (camera.orthographic?.zfar)!
|
||||
break
|
||||
}
|
||||
if let camera = scnNode.camera {
|
||||
delegate?.scene?(loadingScene!, didCreate: camera)
|
||||
|
@ -318,11 +328,11 @@ public class GLTFConverter: TextureLoaderDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal func clearCache() {
|
||||
glTF.clearCache()
|
||||
}
|
||||
|
||||
|
||||
internal func device() -> MTLDevice? {
|
||||
return MetalDevice.device
|
||||
}
|
||||
|
|
|
@ -13,47 +13,46 @@ public enum ResourceType {
|
|||
case image
|
||||
}
|
||||
|
||||
|
||||
/// Description for resources delivery by request from general GLTF converter.
|
||||
/// - Simple implementation is GLTFResourceLoaderDefault and utilised as default resource delivery instrument.
|
||||
/// - Optionaly can be implemented own resource loader, for example if requered to deliver content from remote server.
|
||||
/// - All functionas with completion handler considered as possible delayed delivery, i.e. multi-thread or work with network.
|
||||
public protocol GLTFResourceLoader {
|
||||
|
||||
|
||||
/// Set location where is gltf file is located.
|
||||
var directoryPath: String { get set }
|
||||
|
||||
|
||||
func load(gltf: GLTF, resource: GLTFBuffer) throws -> Data?
|
||||
func load(gltf: GLTF, resource: GLTFBuffer, options: Any?, completionHandler: @escaping (GLTFBuffer, Error?) -> Void )
|
||||
func load(gltf: GLTF, resources: Set<GLTFBuffer>, options: Any?, completionHandler: @escaping (Error?) -> Void )
|
||||
|
||||
|
||||
func load(gltf: GLTF, resource: GLTFImage) throws -> OSImage?
|
||||
func load(gltf: GLTF, resource: GLTFImage, completionHandler: @escaping (GLTFImage, Error?) -> Void )
|
||||
|
||||
|
||||
/// This function going to be call if `cancel` was occured on GLTF convert.
|
||||
func cancelAll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Default resource delivery instrument.
|
||||
open class GLTFResourceLoaderDefault : GLTFResourceLoader {
|
||||
|
||||
open class GLTFResourceLoaderDefault: GLTFResourceLoader {
|
||||
|
||||
public var directoryPath: String = ""
|
||||
|
||||
|
||||
public init() {}
|
||||
|
||||
|
||||
open func load(gltf: GLTF, resource: GLTFBuffer) throws -> Data? {
|
||||
if resource.data == nil && resource.uri != nil {
|
||||
resource.data = try loadUri(uri: resource.uri!)
|
||||
}
|
||||
return resource.data
|
||||
}
|
||||
|
||||
|
||||
open func load(gltf: GLTF, resource: GLTFBuffer, options: Any?, completionHandler: @escaping (GLTFBuffer, Error?) -> Void) {
|
||||
var error_:Error?
|
||||
var error_: Error?
|
||||
do {
|
||||
if resource.data == nil && resource.uri != nil {
|
||||
if let data = try loadUri(uri: resource.uri!) {
|
||||
resource.data = data
|
||||
resource.data = data
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -61,26 +60,26 @@ open class GLTFResourceLoaderDefault : GLTFResourceLoader {
|
|||
}
|
||||
completionHandler(resource, error_)
|
||||
}
|
||||
|
||||
|
||||
open func load(gltf: GLTF, resources: Set<GLTFBuffer>, options: Any?, completionHandler: @escaping (Error?) -> Void) {
|
||||
var error_:Error?
|
||||
var error_: Error?
|
||||
do {
|
||||
for resource in resources {
|
||||
if resource.data == nil && resource.uri != nil {
|
||||
if let data = try loadUri(uri: resource.uri!) {
|
||||
resource.data = data
|
||||
resource.data = data
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
error_ = error
|
||||
}
|
||||
DispatchQueue.global().async {
|
||||
completionHandler(error_)
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
completionHandler(error_)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
open func load(gltf: GLTF, resource: GLTFImage) throws -> OSImage? {
|
||||
if resource.image == nil && resource.uri != nil {
|
||||
if let imageData = try loadUri(uri: resource.uri!) {
|
||||
|
@ -89,13 +88,13 @@ open class GLTFResourceLoaderDefault : GLTFResourceLoader {
|
|||
}
|
||||
return resource.image
|
||||
}
|
||||
|
||||
|
||||
open func load(gltf: GLTF, resource: GLTFImage, completionHandler: @escaping (GLTFImage, Error?) -> Void) {
|
||||
var error_:Error?
|
||||
var error_: Error?
|
||||
do {
|
||||
if resource.image == nil && resource.uri != nil {
|
||||
if let imageData = try loadUri(uri: resource.uri!) {
|
||||
resource.image = OSImage.init(data: imageData)
|
||||
resource.image = OSImage.init(data: imageData)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -103,22 +102,21 @@ open class GLTFResourceLoaderDefault : GLTFResourceLoader {
|
|||
}
|
||||
completionHandler(resource, error_)
|
||||
}
|
||||
|
||||
|
||||
open func cancelAll() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
fileprivate func loadUri(uri: String) throws -> Data? {
|
||||
var data = uri.base64Decoded()
|
||||
if data == nil {
|
||||
|
||||
if (uri.hasPrefix("http")) {
|
||||
if uri.hasPrefix("http") {
|
||||
if let url = URL.init(string: uri) {
|
||||
data = try Data.init(contentsOf: url)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let filepath = [self.directoryPath, uri].joined(separator: "/")
|
||||
let url = URL(fileURLWithPath: filepath)
|
||||
data = try Data.init(contentsOf: url)
|
||||
|
@ -126,6 +124,5 @@ open class GLTFResourceLoaderDefault : GLTFResourceLoader {
|
|||
return (data == nil) ? nil : Data([UInt8](data!))
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -344,23 +344,22 @@ public enum GLTF_3D4MCompressedTextureExtensionTarget: Int, RawRepresentable, Co
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Class template
|
||||
open class GLTF_3D4MCompressedTextureExtension : Codable {
|
||||
open class GLTF_3D4MCompressedTextureExtension: Codable {
|
||||
/// Compression type.
|
||||
public var compression:GLTF_3D4MCompressedTextureExtensionCompression
|
||||
public var compression: GLTF_3D4MCompressedTextureExtensionCompression
|
||||
|
||||
/// Texture width size in pixels.
|
||||
public var width:Int
|
||||
public var width: Int
|
||||
|
||||
/// Texture height size in pixels.
|
||||
public var height:Int
|
||||
public var height: Int
|
||||
|
||||
/// Texture index of bufferView used for each level of texture. First source representing level 0. Each next is divide by 2 of previous texture size. For example 0 level is 1024, next is 512 and next 256 ans so on.
|
||||
public var sources:[Int]
|
||||
public var sources: [Int]
|
||||
|
||||
/// Texture 2D target.
|
||||
public var target:GLTF_3D4MCompressedTextureExtensionTarget
|
||||
public var target: GLTF_3D4MCompressedTextureExtensionTarget
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case compression
|
||||
|
@ -370,18 +369,18 @@ open class GLTF_3D4MCompressedTextureExtension : Codable {
|
|||
case width
|
||||
}
|
||||
|
||||
public init(compression c:GLTF_3D4MCompressedTextureExtensionCompression,
|
||||
width w:Int,
|
||||
height h:Int,
|
||||
sources s:[Int],
|
||||
target t:GLTF_3D4MCompressedTextureExtensionTarget) {
|
||||
compression = c
|
||||
public init(compression c: GLTF_3D4MCompressedTextureExtensionCompression,
|
||||
width w: Int,
|
||||
height h: Int,
|
||||
sources s: [Int],
|
||||
target t: GLTF_3D4MCompressedTextureExtensionTarget) {
|
||||
compression = c
|
||||
width = w
|
||||
height = h
|
||||
sources = s
|
||||
target = t
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
compression = try container.decode(GLTF_3D4MCompressedTextureExtensionCompression.self, forKey: .compression)
|
||||
|
|
|
@ -19,7 +19,6 @@ import SceneKit
|
|||
typealias SCNFloat = CGFloat
|
||||
#endif
|
||||
|
||||
|
||||
struct SCNVector2 {
|
||||
public var x: SCNFloat
|
||||
public var y: SCNFloat
|
||||
|
@ -29,16 +28,16 @@ struct SCNVector2 {
|
|||
}
|
||||
public init(x: SCNFloat, y: SCNFloat) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
self.y = y
|
||||
}
|
||||
}
|
||||
|
||||
class GLTFError : LocalizedError, CustomDebugStringConvertible {
|
||||
class GLTFError: LocalizedError, CustomDebugStringConvertible {
|
||||
var title: String?
|
||||
var code: Int
|
||||
var errorDescription: String? { return _description }
|
||||
var failureReason: String? { return _description }
|
||||
|
||||
|
||||
private var _description: String
|
||||
|
||||
init(_ description: String) {
|
||||
|
@ -46,14 +45,14 @@ class GLTFError : LocalizedError, CustomDebugStringConvertible {
|
|||
self._description = description
|
||||
self.code = -1000000
|
||||
}
|
||||
|
||||
|
||||
var debugDescription: String {
|
||||
return self.title! + ": " + self._description
|
||||
}
|
||||
}
|
||||
|
||||
extension SCNMatrix4 {
|
||||
init(array:[Double]) {
|
||||
init(array: [Double]) {
|
||||
self.init()
|
||||
self.m11 = SCNFloat(array[0])
|
||||
self.m12 = SCNFloat(array[1])
|
||||
|
@ -82,7 +81,7 @@ extension String {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
//: ### Base64 decoding a string
|
||||
func base64Decoded() -> Data? {
|
||||
if self.contains("base64") {
|
||||
|
@ -97,7 +96,7 @@ extension String {
|
|||
|
||||
extension Data {
|
||||
func array<Type>() -> [Type] {
|
||||
return withUnsafeBytes { (unsafeBufferPointer:UnsafeRawBufferPointer) -> [Type] in
|
||||
return withUnsafeBytes { (unsafeBufferPointer: UnsafeRawBufferPointer) -> [Type] in
|
||||
Array(UnsafeBufferPointer<Type>(start: unsafeBufferPointer.bindMemory(to: Type.self).baseAddress, count: self.count/MemoryLayout<Type>.size))
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +106,7 @@ extension Data {
|
|||
// https://github.com/magicien/GLTFSceneKit/blob/master/Source/Common/GLTFFunctions.swift
|
||||
|
||||
extension OSImage {
|
||||
|
||||
|
||||
func channels() throws -> [OSImage] {
|
||||
#if os(macOS)
|
||||
var rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
|
||||
|
@ -121,8 +120,7 @@ extension OSImage {
|
|||
#endif
|
||||
return try channels(from: cgImage)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func channels(from image: CGImage) throws -> [OSImage] {
|
||||
let w = image.width
|
||||
let h = image.height
|
||||
|
@ -134,29 +132,28 @@ extension OSImage {
|
|||
let srcDataSize = w * h * srcBytesPerPixel
|
||||
let rawPtr: UnsafeMutableRawPointer = malloc(srcDataSize)
|
||||
defer { free(rawPtr) }
|
||||
|
||||
|
||||
|
||||
guard let context = CGContext(data: rawPtr, width: w, height: h, bitsPerComponent: bitsPerComponent, bytesPerRow: srcBytesPerPixel * w, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) else {
|
||||
throw GLTFError("Failed to make textures")
|
||||
}
|
||||
context.draw(image, in: rect)
|
||||
|
||||
|
||||
let ptr = rawPtr.bindMemory(to: UInt8.self, capacity: srcDataSize)
|
||||
|
||||
|
||||
/// create data for each component
|
||||
let dstBytesPerPixel = bitsPerComponent / 8
|
||||
let dstDataSize = w * h * dstBytesPerPixel
|
||||
|
||||
var componentsRaw:[UnsafeMutableRawPointer] = [UnsafeMutableRawPointer]()
|
||||
|
||||
var componentsRaw: [UnsafeMutableRawPointer] = [UnsafeMutableRawPointer]()
|
||||
var componentsPtr = [Any]()
|
||||
|
||||
|
||||
for _ in 0..<componentsPerPixel {
|
||||
let componentRawPtr: UnsafeMutableRawPointer = malloc(dstDataSize)
|
||||
let componentPtr = componentRawPtr.bindMemory(to: UInt8.self, capacity: dstDataSize)
|
||||
componentsRaw.append(componentRawPtr)
|
||||
componentsPtr.append(componentPtr)
|
||||
}
|
||||
|
||||
|
||||
var srcPos = 0
|
||||
var dstPos = 0
|
||||
for _ in 0..<(w * h) {
|
||||
|
@ -170,12 +167,12 @@ extension OSImage {
|
|||
dstPos += dstBytesPerPixel
|
||||
}
|
||||
let dstColorSpace = CGColorSpaceCreateDeviceGray()
|
||||
|
||||
|
||||
var images = [OSImage]()
|
||||
|
||||
|
||||
for i in 0..<componentsPerPixel {
|
||||
let componentPtr = componentsPtr[i] as! UnsafeMutablePointer<UInt8>
|
||||
|
||||
|
||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
|
||||
guard let imageData = CFDataCreate(nil, componentPtr, dstDataSize) else {
|
||||
throw GLTFError("Failed to create Data")
|
||||
|
@ -208,13 +205,13 @@ extension OSImage {
|
|||
}
|
||||
componentsRaw.removeAll()
|
||||
componentsPtr.removeAll()
|
||||
|
||||
|
||||
return images
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension MTLPixelFormat {
|
||||
public func hasAlpha() -> Bool {
|
||||
switch self {
|
||||
|
@ -230,15 +227,16 @@ extension MTLPixelFormat {
|
|||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
extension SCNMaterial {
|
||||
public func hasAlpha() -> Bool {
|
||||
return (diffuse.contents as? MTLTexture)?.pixelFormat.hasAlpha() ?? false
|
||||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
class MetalDevice {
|
||||
static var device = {
|
||||
return MTLCreateSystemDefaultDevice()
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,27 +10,27 @@ import SceneKit
|
|||
import os
|
||||
|
||||
// Texture load status
|
||||
enum TextureStatus:Int {
|
||||
enum TextureStatus: Int {
|
||||
case no = 0
|
||||
case loading
|
||||
case loaded
|
||||
}
|
||||
|
||||
protocol TextureLoaderDelegate {
|
||||
protocol TextureLoaderDelegate: class {
|
||||
var renderer: SCNSceneRenderer? { get }
|
||||
func texturesLoaded()
|
||||
}
|
||||
|
||||
class TextureAssociator {
|
||||
var status:TextureStatus = .no
|
||||
|
||||
private var content_:Any?
|
||||
var content:Any? {
|
||||
set {
|
||||
var status: TextureStatus = .no
|
||||
|
||||
private var content_: Any? = OSColor.white
|
||||
var content: Any? {
|
||||
set {
|
||||
content_ = newValue
|
||||
if (newValue != nil) {
|
||||
if newValue != nil {
|
||||
self.status = .loaded
|
||||
|
||||
|
||||
for property in associatedProperties {
|
||||
property.contents = self.content_
|
||||
}
|
||||
|
@ -40,167 +40,166 @@ class TextureAssociator {
|
|||
return content_
|
||||
}
|
||||
}
|
||||
|
||||
lazy var associatedProperties = Set<SCNMaterialProperty>()
|
||||
|
||||
func associate(property:SCNMaterialProperty) {
|
||||
|
||||
lazy var associatedProperties = Set<SCNMaterialProperty>()
|
||||
|
||||
func associate(property: SCNMaterialProperty) {
|
||||
associatedProperties.insert(property)
|
||||
property.contents = self.content
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
associatedProperties.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.12, iOS 10.0, *)
|
||||
class TextureStorageManager {
|
||||
|
||||
|
||||
static let manager = TextureStorageManager()
|
||||
|
||||
private var worker = DispatchQueue(label: "textures_loader", qos: .userInteractive)
|
||||
private var groups:[Int : DispatchGroup] = [Int : DispatchGroup]()
|
||||
|
||||
lazy private var _associators:[Int : [Int : TextureAssociator]] = [Int : [Int : TextureAssociator]]()
|
||||
|
||||
|
||||
private var worker = DispatchQueue(label: "textures_loader")
|
||||
private var groups: [Int: DispatchGroup] = [Int: DispatchGroup]()
|
||||
|
||||
lazy private var _associators: [Int: [Int: TextureAssociator]] = [Int: [Int: TextureAssociator]]()
|
||||
|
||||
func clear(gltf: GLTF) {
|
||||
let hash = gltf.hashValue
|
||||
self.groups[hash] = nil
|
||||
self._associators[hash] = nil
|
||||
}
|
||||
|
||||
|
||||
func textureAssociator(gltf: GLTF, at index: Int) -> TextureAssociator {
|
||||
let hash = gltf.hashValue
|
||||
|
||||
|
||||
if self._associators[hash] == nil {
|
||||
self._associators[hash] = [Int : TextureAssociator]()
|
||||
self._associators[hash] = [Int: TextureAssociator]()
|
||||
}
|
||||
var tStatus = (self._associators[hash])![index]
|
||||
var tStatus = (self._associators[hash])![index]
|
||||
if tStatus == nil {
|
||||
tStatus = TextureAssociator()
|
||||
self._associators[hash]![index] = tStatus
|
||||
}
|
||||
return tStatus!
|
||||
}
|
||||
|
||||
func group(gltf: GLTF, delegate: TextureLoaderDelegate, _ enter:Bool = false) -> DispatchGroup {
|
||||
|
||||
func group(gltf: GLTF, delegate: TextureLoaderDelegate, _ enter: Bool = false) -> DispatchGroup {
|
||||
let index = gltf.hashValue
|
||||
var group: DispatchGroup? = groups[index]
|
||||
|
||||
|
||||
if group == nil {
|
||||
groups[index] = DispatchGroup()
|
||||
group = groups[index]
|
||||
group?.enter()
|
||||
|
||||
|
||||
let startLoadTextures = Date()
|
||||
|
||||
|
||||
// notify when all textures are loaded
|
||||
// this is last operation.
|
||||
group?.notify(queue: DispatchQueue.global(qos: .userInteractive)) {
|
||||
|
||||
|
||||
os_log("textures loaded %d ms", log: log_scenekit, type: .debug, Int(startLoadTextures.timeIntervalSinceNow * -1000))
|
||||
|
||||
|
||||
delegate.texturesLoaded()
|
||||
}
|
||||
|
||||
|
||||
} else if enter {
|
||||
group?.enter()
|
||||
}
|
||||
return group!
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Load texture by index.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - index: index of GLTFTexture in textures
|
||||
/// - property: material's property
|
||||
static func loadTexture(gltf: GLTF, delegate: TextureLoaderDelegate, index: Int, property: SCNMaterialProperty, callback: ((Any?)-> Void)? = nil) {
|
||||
self.manager._loadTexture(gltf: gltf, delegate: delegate, index: index, property: property, callback: callback)
|
||||
static func loadTexture(gltf: GLTF, delegate: TextureLoaderDelegate, index: Int, property: SCNMaterialProperty) {
|
||||
self.manager._loadTexture(gltf: gltf, delegate: delegate, index: index, property: property)
|
||||
}
|
||||
|
||||
fileprivate func _loadTexture(gltf: GLTF, delegate: TextureLoaderDelegate, index: Int, property: SCNMaterialProperty, callback: ((Any?)-> Void)? = nil) {
|
||||
|
||||
fileprivate func _loadTexture(gltf: GLTF, delegate: TextureLoaderDelegate, index: Int, property: SCNMaterialProperty) {
|
||||
guard let texture = gltf.textures?[index] else {
|
||||
print("Failed to find texture")
|
||||
return
|
||||
}
|
||||
|
||||
let group = self.group(gltf:gltf, delegate: delegate, true)
|
||||
|
||||
}
|
||||
|
||||
let group = self.group(gltf: gltf, delegate: delegate, true)
|
||||
|
||||
worker.async {
|
||||
let tStatus = self.textureAssociator(gltf:gltf, at:index)
|
||||
|
||||
let tStatus = self.textureAssociator(gltf: gltf, at: index)
|
||||
|
||||
if tStatus.status == .no {
|
||||
tStatus.status = .loading
|
||||
tStatus.associate(property: property)
|
||||
|
||||
gltf.loadSampler(sampler:texture.sampler, property: property)
|
||||
|
||||
|
||||
gltf.loadSampler(sampler: texture.sampler, property: property)
|
||||
|
||||
let device = MetalDevice.device
|
||||
let metalOn = (delegate.renderer?.renderingAPI == .metal || device != nil)
|
||||
|
||||
if let descriptor = texture.extensions?[compressedTextureExtensionKey] as? GLTF_3D4MCompressedTextureExtension, metalOn {
|
||||
|
||||
|
||||
// load first level mipmap as texture
|
||||
gltf.loadCompressedTexture(descriptor:descriptor, loadLevel: .first) { cTexture, error in
|
||||
|
||||
gltf.loadCompressedTexture(descriptor: descriptor, loadLevel: .first) { cTexture, error in
|
||||
tStatus.content = cTexture
|
||||
|
||||
if gltf.isCancelled {
|
||||
group.leave()
|
||||
return
|
||||
}
|
||||
|
||||
if (error != nil) {
|
||||
|
||||
if error != nil {
|
||||
print("Failed to load comressed texture \(error.debugDescription). Fallback on image source.")
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus, callback)
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus)
|
||||
group.leave()
|
||||
} else {
|
||||
tStatus.content = cTexture as Any?
|
||||
callback?(cTexture)
|
||||
|
||||
tStatus.content = cTexture
|
||||
|
||||
// load all levels
|
||||
gltf.loadCompressedTexture(descriptor:descriptor, loadLevel: .last) { (cTexture2, error) in
|
||||
|
||||
gltf.loadCompressedTexture(descriptor: descriptor, loadLevel: .last) { (cTexture2, error) in
|
||||
|
||||
if gltf.isCancelled {
|
||||
group.leave()
|
||||
return
|
||||
}
|
||||
|
||||
if (error != nil) {
|
||||
|
||||
if error != nil {
|
||||
print("Failed to load comressed texture \(error.debugDescription). Fallback on image source.")
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus, callback)
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus)
|
||||
} else {
|
||||
tStatus.content = cTexture2 as Any?
|
||||
callback?(cTexture2)
|
||||
tStatus.content = cTexture2
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus)
|
||||
group.leave()
|
||||
self._loadImageTexture(gltf, delegate, texture, tStatus, callback)
|
||||
}
|
||||
} else {
|
||||
group.leave()
|
||||
|
||||
tStatus.associate(property: property)
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// load original image source png or jpg
|
||||
fileprivate func _loadImageTexture(_ gltf: GLTF, _ delegate: TextureLoaderDelegate, _ texture: GLTFTexture, _ tStatus: TextureAssociator, _ callback: ((Any?)-> Void)? = nil) {
|
||||
fileprivate func _loadImageTexture(_ gltf: GLTF, _ delegate: TextureLoaderDelegate, _ texture: GLTFTexture, _ tStatus: TextureAssociator) {
|
||||
self.worker.async {
|
||||
if gltf.isCancelled {
|
||||
return
|
||||
}
|
||||
let group = self.group(gltf:gltf, delegate: delegate, true)
|
||||
|
||||
let group = self.group(gltf: gltf, delegate: delegate, true)
|
||||
|
||||
if let imageSourceIndex = texture.source {
|
||||
if let gltf_image = gltf.images?[imageSourceIndex] {
|
||||
|
||||
gltf.loader.load(gltf:gltf, resource: gltf_image) { resource, error in
|
||||
|
||||
gltf.loader.load(gltf: gltf, resource: gltf_image) { resource, _ in
|
||||
if resource.image != nil {
|
||||
tStatus.content = gltf._compress(image:resource.image!)
|
||||
callback?(tStatus.content)
|
||||
tStatus.content = gltf._compress(image: resource.image!)
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
|
@ -209,4 +208,3 @@ class TextureStorageManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@ import XCTest
|
|||
@testable import gltf_scenekitTests
|
||||
|
||||
XCTMain([
|
||||
testCase(gltf_scenekitTests.allTests),
|
||||
testCase(gltf_scenekitTests.allTests)
|
||||
])
|
||||
|
|
|
@ -2,43 +2,109 @@ import XCTest
|
|||
import SceneKit
|
||||
@testable import glTFSceneKit
|
||||
|
||||
let jsonString = """
|
||||
{ "accessors": [ ], "asset": { "copyright": "3D4Medical LLC", "generator": "Comanche", "version": "2.0" }, "bufferViews": [ { "buffer": 0, "byteLength": 118766, "byteStride": 44, "target": 34962 }], "buffers": [ { "byteLength": 118766, "uri": "draco/file.bin" }], "extensionsRequired": [ "KHR_draco_mesh_compression"], "extensionsUsed": [ "KHR_draco_mesh_compression"], "images": [ { "mimeType": "image/png", "uri": "png/texture.png" }, { "mimeType": "image/png", "uri": "png/texture.png" }], "materials": [ { "alphaMode": "OPAQUE", "name": "", "normalTexture": { "index": 1 }, "pbrMetallicRoughness": { "baseColorFactor": [ 0.725279, 0.700000, 0.734000, 1.000000], "baseColorTexture": { "index": 0 }, "metallicFactor": 0.000000, "roughnessFactor": 0.800000 } }], "meshes": [ { "primitives": [ { "attributes": { }, "extensions": { "KHR_draco_mesh_compression" : { "attributes": { "TEXCOORD_0" : 2, "NORMAL" : 1, "TANGENT" : 3, "POSITION" : 0}, "bufferView": 0 }}, "material": 0, "mode": 5 }] }], "nodes": [ { "mesh": 0 }], "samplers": [ { "magFilter": 9729, "minFilter": 9729, "wrapS": 10497, "wrapT": 10497 }], "scene": 0, "scenes": [ { "nodes": [ 0] }], "textures": [ { "sampler": 0, "source": 0 }, { "sampler": 0, "source": 1 }] }
|
||||
"""
|
||||
|
||||
class LoadingDelegate : SceneLoadingDelegate {
|
||||
|
||||
}
|
||||
|
||||
class gltf_scenekitTests: XCTestCase {
|
||||
func testGLTFinit() {
|
||||
|
||||
let view = SCNView()
|
||||
|
||||
|
||||
let jsonData = jsonString.data(using: .utf8)
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
|
||||
|
||||
self.measure {
|
||||
let glTF = try? decoder.decode(GLTF.self, from: jsonData!)
|
||||
let converter = GLTFConverter()
|
||||
|
||||
_ = converter.convert(renderer:view, directoryPath:nil, multiThread:false, geometryCompletionHandler: {
|
||||
|
||||
})
|
||||
// _ = glTF?.convert(renderer:view, directoryPath:nil, multiThread:false, geometryCompletionHandler: {
|
||||
//
|
||||
// })
|
||||
|
||||
}
|
||||
|
||||
let jsonDataArray = jsonData!.array() as [UInt8]
|
||||
|
||||
// XCTAssert(glTF != nil)
|
||||
|
||||
|
||||
let view = SCNView()
|
||||
let scene = SCNScene()
|
||||
let loadingDelegate = LoadingDelegate()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
override func setUp() {
|
||||
view.scene = scene
|
||||
}
|
||||
|
||||
let jsonStringSimple = """
|
||||
{ "accessors": [ ], "asset": { "copyright": "3D4Medical LLC", "generator": "Comanche", "version": "2.0" }, "bufferViews": [ { "buffer": 0, "byteLength": 118766, "byteStride": 44, "target": 34962 }], "buffers": [ { "byteLength": 118766, "uri": "draco/file.bin" }], "extensionsRequired": [ ], "extensionsUsed": [], "images": [ { "mimeType": "image/png", "uri": "png/texture.png" }, { "mimeType": "image/png", "uri": "png/texture.png" }], "materials": [ { "alphaMode": "OPAQUE", "name": "", "normalTexture": { "index": 1 }, "pbrMetallicRoughness": { "baseColorFactor": [ 0.725279, 0.700000, 0.734000, 1.000000], "baseColorTexture": { "index": 0 }, "metallicFactor": 0.000000, "roughnessFactor": 0.800000 } }], "meshes": [ { "primitives": [ { "attributes": { }, "extensions": { "KHR_draco_mesh_compression" : { "attributes": { "TEXCOORD_0" : 2, "NORMAL" : 1, "TANGENT" : 3, "POSITION" : 0}, "bufferView": 0 }}, "material": 0, "mode": 5 }] }], "nodes": [ { "mesh": 0 }], "samplers": [ { "magFilter": 9729, "minFilter": 9729, "wrapS": 10497, "wrapT": 10497 }], "scene": 0, "scenes": [ { "nodes": [ 0] }], "textures": [ { "sampler": 0, "source": 0 }, { "sampler": 0, "source": 1 }] }
|
||||
"""
|
||||
func testSimpleGLTFinit() {
|
||||
|
||||
let jsonData = jsonStringSimple.data(using: .utf8)
|
||||
|
||||
static var allTests = [
|
||||
("testGLTFinit", testGLTFinit),
|
||||
]
|
||||
let expectationGeometry = self.expectation(description: "Geometry")
|
||||
let expectationCompleted = self.expectation(description: "Completed")
|
||||
|
||||
if let glTF = try? decoder.decode(GLTF.self, from: jsonData!) {
|
||||
let converter = GLTFConverter(glTF: glTF)
|
||||
converter.delegate = loadingDelegate
|
||||
let scene = converter.convert(to: view.scene!, geometryCompletionHandler: {
|
||||
print("Geometry loaded")
|
||||
expectationGeometry.fulfill()
|
||||
}) { (error) in
|
||||
print("Completed with \((error != nil) ? "\(error.debugDescription)" : "no errors")")
|
||||
expectationCompleted.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 5, handler: nil)
|
||||
|
||||
XCTAssert(scene != nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Test where it's failed on extension
|
||||
let jsonStringExt = """
|
||||
{ "accessors": [ ], "asset": { "copyright": "3D4Medical LLC", "generator": "Comanche", "version": "2.0" }, "bufferViews": [ { "buffer": 0, "byteLength": 118766, "byteStride": 44, "target": 34962 }], "buffers": [ { "byteLength": 118766, "uri": "draco/file.bin" }], "extensionsRequired": [ "KHR_draco_mesh_compression"], "extensionsUsed": [ "KHR_draco_mesh_compression"], "images": [ { "mimeType": "image/png", "uri": "png/texture.png" }, { "mimeType": "image/png", "uri": "png/texture.png" }], "materials": [ { "alphaMode": "OPAQUE", "name": "", "normalTexture": { "index": 1 }, "pbrMetallicRoughness": { "baseColorFactor": [ 0.725279, 0.700000, 0.734000, 1.000000], "baseColorTexture": { "index": 0 }, "metallicFactor": 0.000000, "roughnessFactor": 0.800000 } }], "meshes": [ { "primitives": [ { "attributes": { }, "extensions": { "KHR_draco_mesh_compression" : { "attributes": { "TEXCOORD_0" : 2, "NORMAL" : 1, "TANGENT" : 3, "POSITION" : 0}, "bufferView": 0 }}, "material": 0, "mode": 5 }] }], "nodes": [ { "mesh": 0 }], "samplers": [ { "magFilter": 9729, "minFilter": 9729, "wrapS": 10497, "wrapT": 10497 }], "scene": 0, "scenes": [ { "nodes": [ 0] }], "textures": [ { "sampler": 0, "source": 0 }, { "sampler": 0, "source": 1 }] }
|
||||
"""
|
||||
func testGLTFfailure() {
|
||||
|
||||
let expectationGeometry = self.expectation(description: "Geometry")
|
||||
let expectationCompleted = self.expectation(description: "Completed")
|
||||
|
||||
let jsonData = jsonStringExt.data(using: .utf8)
|
||||
|
||||
if let glTF = try? decoder.decode(GLTF.self, from: jsonData!) {
|
||||
let converter = GLTFConverter(glTF: glTF)
|
||||
converter.delegate = loadingDelegate
|
||||
let scene = converter.convert(to: view.scene!, geometryCompletionHandler: {
|
||||
print("Geometry loaded")
|
||||
expectationGeometry.fulfill()
|
||||
}) { (error) in
|
||||
print("Completed with \((error != nil) ? "\(error.debugDescription)" : "no errors")")
|
||||
XCTAssert(error != nil) // expecting error here
|
||||
expectationGeometry.fulfill()
|
||||
expectationCompleted.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 5, handler: nil)
|
||||
// expecting no scene here
|
||||
XCTAssert(scene == nil)
|
||||
}
|
||||
}
|
||||
|
||||
let box_gltf = """
|
||||
{"asset":{"generator":"COLLADA2GLTF","version":"2.0"},"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"children":[1],"matrix":[1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1]},{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"NORMAL":1,"POSITION":2},"indices":0,"mode":4,"material":0}],"name":"Mesh"}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"byteOffset":288,"componentType":5126,"count":24,"max":[0.5,0.5,0.5],"min":[-0.5,-0.5,-0.5],"type":"VEC3"}],"materials":[{"pbrMetallicRoughness":{"baseColorFactor":[0.800000011920929,0,0,1],"metallicFactor":0},"name":"Red"}],"bufferViews":[{"buffer":0,"byteOffset":576,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":0,"byteLength":576,"byteStride":12,"target":34962}],"buffers":[{"byteLength":648,"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA"}]}
|
||||
"""
|
||||
func testGLTFBox() {
|
||||
|
||||
let expectationGeometry = self.expectation(description: "Geometry")
|
||||
let expectationCompleted = self.expectation(description: "Completed")
|
||||
|
||||
let jsonData = box_gltf.data(using: .utf8)
|
||||
|
||||
if let glTF = try? decoder.decode(GLTF.self, from: jsonData!) {
|
||||
let converter = GLTFConverter(glTF: glTF)
|
||||
converter.delegate = loadingDelegate
|
||||
let scene = converter.convert(to: view.scene!, geometryCompletionHandler: {
|
||||
print("Geometry loaded")
|
||||
expectationGeometry.fulfill()
|
||||
}) { (error) in
|
||||
print("Completed with \((error != nil) ? "\(error.debugDescription)" : "no errors")")
|
||||
XCTAssert(error == nil)
|
||||
expectationCompleted.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 5, handler: nil)
|
||||
|
||||
XCTAssert(scene != nil)
|
||||
let node = scene?.rootNode.childNodes.first?.childNodes.first?.childNodes.first!
|
||||
let geometry = node?.geometry
|
||||
|
||||
XCTAssert(geometry != nil)
|
||||
|
||||
// expecting 24 elements
|
||||
XCTAssert(geometry!.sources.first!.vectorCount == 24)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,7 @@
|
|||
49A51D192345E8BB002B5D24 /* GLTFConverter+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D182345E8BB002B5D24 /* GLTFConverter+Geometry.swift */; };
|
||||
49A51D1A2345E8BB002B5D24 /* GLTFConverter+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D182345E8BB002B5D24 /* GLTFConverter+Geometry.swift */; };
|
||||
49A51D1B2345E8BB002B5D24 /* GLTFConverter+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D182345E8BB002B5D24 /* GLTFConverter+Geometry.swift */; };
|
||||
49A51D1C2345E8BB002B5D24 /* GLTFConverter+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D182345E8BB002B5D24 /* GLTFConverter+Geometry.swift */; };
|
||||
49A51D23234601B7002B5D24 /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
49A51D24234601B7002B5D24 /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
49A51D25234601B7002B5D24 /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
49A51D26234601B7002B5D24 /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
D2157142204035E3009E9D16 /* JSONCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D215713A20402611009E9D16 /* JSONCodingKeys.swift */; };
|
||||
D2180CDF208745A6005353CF /* GLTF+Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2180CDD2087457D005353CF /* GLTF+Draco.swift */; };
|
||||
D2180CE0208745A7005353CF /* GLTF+Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2180CDD2087457D005353CF /* GLTF+Draco.swift */; };
|
||||
D256CD0620B2B9F1002E841E /* glTFSceneKit-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A67C0B20ADB9D0007523AF /* glTFSceneKit-Bridging-Header.h */; };
|
||||
D256CD0720B2B9F2002E841E /* glTFSceneKit-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A67C0B20ADB9D0007523AF /* glTFSceneKit-Bridging-Header.h */; };
|
||||
D2750CED2077B7AC00F6198D /* GLTF+SceneKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* GLTF+SceneKit.swift */; };
|
||||
|
@ -43,7 +36,6 @@
|
|||
D2750D012077B7AC00F6198D /* GLTFMaterialOcclusionTextureInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* GLTFMaterialOcclusionTextureInfo.swift */; };
|
||||
D2750D022077B7AC00F6198D /* GLTFMaterialPBRMetallicRoughness.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* GLTFMaterialPBRMetallicRoughness.swift */; };
|
||||
D2750D032077B7AC00F6198D /* GLTFMesh.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* GLTFMesh.swift */; };
|
||||
D2750D042077B7AC00F6198D /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27BDF82203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift */; };
|
||||
D2750D052077B7AC00F6198D /* GLTFMeshPrimitive.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* GLTFMeshPrimitive.swift */; };
|
||||
D2750D062077B7AC00F6198D /* GLTFNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* GLTFNode.swift */; };
|
||||
D2750D072077B7AC00F6198D /* JSONCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D215713A20402611009E9D16 /* JSONCodingKeys.swift */; };
|
||||
|
@ -53,15 +45,10 @@
|
|||
D2750D0B2077B7AC00F6198D /* GLTFTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* GLTFTexture.swift */; };
|
||||
D2750D0C2077B7AC00F6198D /* GLTFTextureInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* GLTFTextureInfo.swift */; };
|
||||
D2750D0D2077B7AC00F6198D /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* Helper.swift */; };
|
||||
D27BDF83203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27BDF82203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift */; };
|
||||
D287556D2077CB16009B7FA9 /* GLTF+CompressedTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287556B2077CABB009B7FA9 /* GLTF+CompressedTexture.swift */; };
|
||||
D287556E2077CB1B009B7FA9 /* GLTF+CompressedTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287556B2077CABB009B7FA9 /* GLTF+CompressedTexture.swift */; };
|
||||
D287E3F5209602EB00EBCFD1 /* TextureStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287E3F4209602EB00EBCFD1 /* TextureStorageManager.swift */; };
|
||||
D287E3F6209602EB00EBCFD1 /* TextureStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287E3F4209602EB00EBCFD1 /* TextureStorageManager.swift */; };
|
||||
D2A67C0020ADB91B007523AF /* Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A67BFF20ADB91B007523AF /* Draco.swift */; };
|
||||
D2A67C0120ADB91B007523AF /* Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A67BFF20ADB91B007523AF /* Draco.swift */; };
|
||||
D2A67C0320ADB92F007523AF /* libdraco.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2A67C0220ADB92F007523AF /* libdraco.a */; };
|
||||
D2A67C0520ADB93E007523AF /* libdraco_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2A67C0420ADB93E007523AF /* libdraco_ios.a */; };
|
||||
D2AC651E205D578B0091D5E0 /* GLTF_3D4MCompressedTextureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AC651D205D578A0091D5E0 /* GLTF_3D4MCompressedTextureExtension.swift */; };
|
||||
D2AFCFC0208F1DE00048A9DA /* GLTF+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AFCFBF208F1DE00048A9DA /* GLTF+Animation.swift */; };
|
||||
D2AFCFC1208F1DE00048A9DA /* GLTF+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AFCFBF208F1DE00048A9DA /* GLTF+Animation.swift */; };
|
||||
|
@ -84,7 +71,6 @@
|
|||
D2B064E9214FE0CC00309D6F /* GLTF+Material.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AFCFC2208F2A010048A9DA /* GLTF+Material.swift */; };
|
||||
D2B064EA214FE0CC00309D6F /* GLTFAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* GLTFAsset.swift */; };
|
||||
D2B064EB214FE0CC00309D6F /* GLTFBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* GLTFBuffer.swift */; };
|
||||
D2B064EC214FE0CC00309D6F /* Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A67BFF20ADB91B007523AF /* Draco.swift */; };
|
||||
D2B064ED214FE0CC00309D6F /* GLTFBufferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* GLTFBufferView.swift */; };
|
||||
D2B064EE214FE0CC00309D6F /* GLTFCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* GLTFCamera.swift */; };
|
||||
D2B064EF214FE0CC00309D6F /* GLTFResourceLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AFCFD1208F6F010048A9DA /* GLTFResourceLoader.swift */; };
|
||||
|
@ -96,21 +82,21 @@
|
|||
D2B064F5214FE0CC00309D6F /* GLTFMaterialOcclusionTextureInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* GLTFMaterialOcclusionTextureInfo.swift */; };
|
||||
D2B064F6214FE0CC00309D6F /* GLTFMaterialPBRMetallicRoughness.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* GLTFMaterialPBRMetallicRoughness.swift */; };
|
||||
D2B064F7214FE0CC00309D6F /* GLTFMesh.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* GLTFMesh.swift */; };
|
||||
D2B064F8214FE0CC00309D6F /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27BDF82203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift */; };
|
||||
D2B064F9214FE0CC00309D6F /* GLTFMeshPrimitive.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* GLTFMeshPrimitive.swift */; };
|
||||
D2B064FA214FE0CC00309D6F /* GLTFNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* GLTFNode.swift */; };
|
||||
D2B064FB214FE0CC00309D6F /* GLTF+CompressedTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287556B2077CABB009B7FA9 /* GLTF+CompressedTexture.swift */; };
|
||||
D2B064FC214FE0CC00309D6F /* JSONCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D215713A20402611009E9D16 /* JSONCodingKeys.swift */; };
|
||||
D2B064FD214FE0CC00309D6F /* GLTFSampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* GLTFSampler.swift */; };
|
||||
D2B064FE214FE0CC00309D6F /* GLTFScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* GLTFScene.swift */; };
|
||||
D2B064FF214FE0CC00309D6F /* GLTF+Draco.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2180CDD2087457D005353CF /* GLTF+Draco.swift */; };
|
||||
D2B06500214FE0CC00309D6F /* GLTFSkin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* GLTFSkin.swift */; };
|
||||
D2B06501214FE0CC00309D6F /* TextureStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D287E3F4209602EB00EBCFD1 /* TextureStorageManager.swift */; };
|
||||
D2B06502214FE0CC00309D6F /* GLTFTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* GLTFTexture.swift */; };
|
||||
D2B06503214FE0CC00309D6F /* GLTFTextureInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* GLTFTextureInfo.swift */; };
|
||||
D2B06504214FE0CC00309D6F /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* Helper.swift */; };
|
||||
D2B06509214FE0CC00309D6F /* glTFSceneKit-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A67C0B20ADB9D0007523AF /* glTFSceneKit-Bridging-Header.h */; };
|
||||
D2B0651F214FEC7200309D6F /* libdraco_tvos.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2B0651E214FEC7200309D6F /* libdraco_tvos.a */; };
|
||||
D2C6215623B4F77E00EA739E /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
D2C6215723B4F77F00EA739E /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
D2C6215823B4F78000EA739E /* GLTFConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A51D22234601B7002B5D24 /* GLTFConverter.swift */; };
|
||||
OBJ_58 /* gltf_scenekitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* gltf_scenekitTests.swift */; };
|
||||
OBJ_60 /* glTFSceneKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "gltf_scenekit::gltf_scenekit::Product" /* glTFSceneKit.framework */; };
|
||||
OBJ_67 /* GLTF+SceneKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* GLTF+SceneKit.swift */; };
|
||||
|
@ -194,16 +180,12 @@
|
|||
D27BDF82203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GLTFKHRDracoMeshCompressionExtension.swift; sourceTree = "<group>"; };
|
||||
D287556B2077CABB009B7FA9 /* GLTF+CompressedTexture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GLTF+CompressedTexture.swift"; sourceTree = "<group>"; };
|
||||
D287E3F4209602EB00EBCFD1 /* TextureStorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextureStorageManager.swift; sourceTree = "<group>"; };
|
||||
D2A67BFF20ADB91B007523AF /* Draco.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Draco.swift; path = Draco/Draco/Draco.swift; sourceTree = "<group>"; };
|
||||
D2A67C0220ADB92F007523AF /* libdraco.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdraco.a; path = Draco/libdraco.a; sourceTree = "<group>"; };
|
||||
D2A67C0420ADB93E007523AF /* libdraco_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdraco_ios.a; path = Draco/libdraco_ios.a; sourceTree = "<group>"; };
|
||||
D2A67C0B20ADB9D0007523AF /* glTFSceneKit-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "glTFSceneKit-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
D2AC651D205D578A0091D5E0 /* GLTF_3D4MCompressedTextureExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GLTF_3D4MCompressedTextureExtension.swift; sourceTree = "<group>"; };
|
||||
D2AFCFBF208F1DE00048A9DA /* GLTF+Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GLTF+Animation.swift"; sourceTree = "<group>"; };
|
||||
D2AFCFC2208F2A010048A9DA /* GLTF+Material.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GLTF+Material.swift"; sourceTree = "<group>"; };
|
||||
D2AFCFD1208F6F010048A9DA /* GLTFResourceLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLTFResourceLoader.swift; sourceTree = "<group>"; };
|
||||
D2B0650D214FE0CC00309D6F /* glTFSceneKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = glTFSceneKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D2B0651E214FEC7200309D6F /* libdraco_tvos.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdraco_tvos.a; path = Draco/libdraco_tvos.a; sourceTree = "<group>"; };
|
||||
OBJ_11 /* GLTF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLTF.swift; sourceTree = "<group>"; };
|
||||
OBJ_12 /* GLTFAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLTFAccessor.swift; sourceTree = "<group>"; };
|
||||
OBJ_13 /* GLTFAccessorSparse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLTFAccessorSparse.swift; sourceTree = "<group>"; };
|
||||
|
@ -245,7 +227,6 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
D2A67C0520ADB93E007523AF /* libdraco_ios.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -253,7 +234,6 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
D2B0651F214FEC7200309D6F /* libdraco_tvos.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -269,7 +249,6 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
D2A67C0320ADB92F007523AF /* libdraco.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -279,9 +258,6 @@
|
|||
D27BDF7B203DCE6E0036F3A7 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2B0651E214FEC7200309D6F /* libdraco_tvos.a */,
|
||||
D2A67C0420ADB93E007523AF /* libdraco_ios.a */,
|
||||
D2A67C0220ADB92F007523AF /* libdraco.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -363,7 +339,6 @@
|
|||
children = (
|
||||
OBJ_6 /* Package.swift */,
|
||||
D2A67C0B20ADB9D0007523AF /* glTFSceneKit-Bridging-Header.h */,
|
||||
D2A67BFF20ADB91B007523AF /* Draco.swift */,
|
||||
OBJ_7 /* Sources */,
|
||||
OBJ_40 /* Tests */,
|
||||
OBJ_43 /* Dependencies */,
|
||||
|
@ -513,6 +488,7 @@
|
|||
LastUpgradeCheck = 1020;
|
||||
TargetAttributes = {
|
||||
D2750CEA2077B7AC00F6198D = {
|
||||
LastSwiftMigration = 1130;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
D2B064DA214FE0CC00309D6F = {
|
||||
|
@ -609,12 +585,10 @@
|
|||
D2AFCFC4208F2A010048A9DA /* GLTF+Material.swift in Sources */,
|
||||
D2750CF82077B7AC00F6198D /* GLTFAsset.swift in Sources */,
|
||||
D2750CF92077B7AC00F6198D /* GLTFBuffer.swift in Sources */,
|
||||
D2A67C0120ADB91B007523AF /* Draco.swift in Sources */,
|
||||
D2750CFA2077B7AC00F6198D /* GLTFBufferView.swift in Sources */,
|
||||
D2750CFB2077B7AC00F6198D /* GLTFCamera.swift in Sources */,
|
||||
D2AFCFD3208F6F010048A9DA /* GLTFResourceLoader.swift in Sources */,
|
||||
D2750CFC2077B7AC00F6198D /* GLTFCameraOrthographic.swift in Sources */,
|
||||
49A51D24234601B7002B5D24 /* GLTFConverter.swift in Sources */,
|
||||
D2750CFD2077B7AC00F6198D /* GLTFCameraPerspective.swift in Sources */,
|
||||
D2750CFE2077B7AC00F6198D /* GLTFImage.swift in Sources */,
|
||||
D2750CFF2077B7AC00F6198D /* GLTFMaterial.swift in Sources */,
|
||||
|
@ -622,14 +596,13 @@
|
|||
D2750D012077B7AC00F6198D /* GLTFMaterialOcclusionTextureInfo.swift in Sources */,
|
||||
D2750D022077B7AC00F6198D /* GLTFMaterialPBRMetallicRoughness.swift in Sources */,
|
||||
D2750D032077B7AC00F6198D /* GLTFMesh.swift in Sources */,
|
||||
D2750D042077B7AC00F6198D /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */,
|
||||
D2C6215723B4F77F00EA739E /* GLTFConverter.swift in Sources */,
|
||||
D2750D052077B7AC00F6198D /* GLTFMeshPrimitive.swift in Sources */,
|
||||
D2750D062077B7AC00F6198D /* GLTFNode.swift in Sources */,
|
||||
D287556E2077CB1B009B7FA9 /* GLTF+CompressedTexture.swift in Sources */,
|
||||
D2750D072077B7AC00F6198D /* JSONCodingKeys.swift in Sources */,
|
||||
D2750D082077B7AC00F6198D /* GLTFSampler.swift in Sources */,
|
||||
D2750D092077B7AC00F6198D /* GLTFScene.swift in Sources */,
|
||||
D2180CE0208745A7005353CF /* GLTF+Draco.swift in Sources */,
|
||||
D2750D0A2077B7AC00F6198D /* GLTFSkin.swift in Sources */,
|
||||
D287E3F6209602EB00EBCFD1 /* TextureStorageManager.swift in Sources */,
|
||||
D2750D0B2077B7AC00F6198D /* GLTFTexture.swift in Sources */,
|
||||
|
@ -658,12 +631,10 @@
|
|||
D2B064E9214FE0CC00309D6F /* GLTF+Material.swift in Sources */,
|
||||
D2B064EA214FE0CC00309D6F /* GLTFAsset.swift in Sources */,
|
||||
D2B064EB214FE0CC00309D6F /* GLTFBuffer.swift in Sources */,
|
||||
D2B064EC214FE0CC00309D6F /* Draco.swift in Sources */,
|
||||
D2B064ED214FE0CC00309D6F /* GLTFBufferView.swift in Sources */,
|
||||
D2B064EE214FE0CC00309D6F /* GLTFCamera.swift in Sources */,
|
||||
D2B064EF214FE0CC00309D6F /* GLTFResourceLoader.swift in Sources */,
|
||||
D2B064F0214FE0CC00309D6F /* GLTFCameraOrthographic.swift in Sources */,
|
||||
49A51D25234601B7002B5D24 /* GLTFConverter.swift in Sources */,
|
||||
D2B064F1214FE0CC00309D6F /* GLTFCameraPerspective.swift in Sources */,
|
||||
D2B064F2214FE0CC00309D6F /* GLTFImage.swift in Sources */,
|
||||
D2B064F3214FE0CC00309D6F /* GLTFMaterial.swift in Sources */,
|
||||
|
@ -671,14 +642,13 @@
|
|||
D2B064F5214FE0CC00309D6F /* GLTFMaterialOcclusionTextureInfo.swift in Sources */,
|
||||
D2B064F6214FE0CC00309D6F /* GLTFMaterialPBRMetallicRoughness.swift in Sources */,
|
||||
D2B064F7214FE0CC00309D6F /* GLTFMesh.swift in Sources */,
|
||||
D2B064F8214FE0CC00309D6F /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */,
|
||||
D2C6215823B4F78000EA739E /* GLTFConverter.swift in Sources */,
|
||||
D2B064F9214FE0CC00309D6F /* GLTFMeshPrimitive.swift in Sources */,
|
||||
D2B064FA214FE0CC00309D6F /* GLTFNode.swift in Sources */,
|
||||
D2B064FB214FE0CC00309D6F /* GLTF+CompressedTexture.swift in Sources */,
|
||||
D2B064FC214FE0CC00309D6F /* JSONCodingKeys.swift in Sources */,
|
||||
D2B064FD214FE0CC00309D6F /* GLTFSampler.swift in Sources */,
|
||||
D2B064FE214FE0CC00309D6F /* GLTFScene.swift in Sources */,
|
||||
D2B064FF214FE0CC00309D6F /* GLTF+Draco.swift in Sources */,
|
||||
D2B06500214FE0CC00309D6F /* GLTFSkin.swift in Sources */,
|
||||
D2B06501214FE0CC00309D6F /* TextureStorageManager.swift in Sources */,
|
||||
D2B06502214FE0CC00309D6F /* GLTFTexture.swift in Sources */,
|
||||
|
@ -691,8 +661,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
49A51D26234601B7002B5D24 /* GLTFConverter.swift in Sources */,
|
||||
49A51D1C2345E8BB002B5D24 /* GLTFConverter+Geometry.swift in Sources */,
|
||||
OBJ_58 /* gltf_scenekitTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -717,12 +685,10 @@
|
|||
D2AFCFC3208F2A010048A9DA /* GLTF+Material.swift in Sources */,
|
||||
OBJ_77 /* GLTFAsset.swift in Sources */,
|
||||
OBJ_78 /* GLTFBuffer.swift in Sources */,
|
||||
D2A67C0020ADB91B007523AF /* Draco.swift in Sources */,
|
||||
OBJ_79 /* GLTFBufferView.swift in Sources */,
|
||||
OBJ_80 /* GLTFCamera.swift in Sources */,
|
||||
D2AFCFD2208F6F010048A9DA /* GLTFResourceLoader.swift in Sources */,
|
||||
OBJ_81 /* GLTFCameraOrthographic.swift in Sources */,
|
||||
49A51D23234601B7002B5D24 /* GLTFConverter.swift in Sources */,
|
||||
OBJ_82 /* GLTFCameraPerspective.swift in Sources */,
|
||||
OBJ_83 /* GLTFImage.swift in Sources */,
|
||||
OBJ_84 /* GLTFMaterial.swift in Sources */,
|
||||
|
@ -730,14 +696,13 @@
|
|||
OBJ_86 /* GLTFMaterialOcclusionTextureInfo.swift in Sources */,
|
||||
OBJ_87 /* GLTFMaterialPBRMetallicRoughness.swift in Sources */,
|
||||
OBJ_88 /* GLTFMesh.swift in Sources */,
|
||||
D27BDF83203F0BEF0036F3A7 /* GLTFKHRDracoMeshCompressionExtension.swift in Sources */,
|
||||
D2C6215623B4F77E00EA739E /* GLTFConverter.swift in Sources */,
|
||||
OBJ_89 /* GLTFMeshPrimitive.swift in Sources */,
|
||||
OBJ_90 /* GLTFNode.swift in Sources */,
|
||||
D287556D2077CB16009B7FA9 /* GLTF+CompressedTexture.swift in Sources */,
|
||||
D2157142204035E3009E9D16 /* JSONCodingKeys.swift in Sources */,
|
||||
OBJ_91 /* GLTFSampler.swift in Sources */,
|
||||
OBJ_92 /* GLTFScene.swift in Sources */,
|
||||
D2180CDF208745A6005353CF /* GLTF+Draco.swift in Sources */,
|
||||
OBJ_93 /* GLTFSkin.swift in Sources */,
|
||||
D287E3F5209602EB00EBCFD1 /* TextureStorageManager.swift in Sources */,
|
||||
OBJ_94 /* GLTFTexture.swift in Sources */,
|
||||
|
@ -760,7 +725,6 @@
|
|||
D2750D112077B7AC00F6198D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
BITCODE_GENERATION_MODE = bitcode;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
|
@ -768,19 +732,9 @@
|
|||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekit_ios_Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
|
@ -795,7 +749,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "glTFSceneKit-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGET_NAME = glTFSceneKit;
|
||||
VALID_ARCHS = arm64;
|
||||
|
@ -805,26 +759,15 @@
|
|||
D2750D122077B7AC00F6198D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekit_ios_Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
|
@ -839,7 +782,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "glTFSceneKit-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGET_NAME = glTFSceneKit;
|
||||
VALID_ARCHS = arm64;
|
||||
|
@ -855,19 +798,9 @@
|
|||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/glTFSceneKit_tvos.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
|
@ -899,19 +832,9 @@
|
|||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Draco",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/glTFSceneKit_tvos.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
|
@ -1036,27 +959,12 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/.build/checkouts/**",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekitTests_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.build/checkouts/**";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-lc++",
|
||||
"-ldraco",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
PRODUCT_NAME = glTFSceneKitTests;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "glTFSceneKit-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGET_NAME = gltf_scenekitTests;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -1065,27 +973,12 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/.build/checkouts/**",
|
||||
);
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekitTests_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.build/checkouts/**";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-lc++",
|
||||
"-ldraco",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
PRODUCT_NAME = glTFSceneKitTests;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "glTFSceneKit-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGET_NAME = gltf_scenekitTests;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -1093,7 +986,6 @@
|
|||
OBJ_64 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
|
@ -1103,8 +995,7 @@
|
|||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekit_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACH_O_TYPE = mh_dylib;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
|
@ -1128,7 +1019,6 @@
|
|||
OBJ_65 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
|
@ -1138,8 +1028,7 @@
|
|||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = glTFSceneKit.xcodeproj/gltf_scenekit_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Draco";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACH_O_TYPE = mh_dylib;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
OTHER_LDFLAGS = (
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue