Compare commits

...

24 Commits

Author SHA1 Message Date
Volodymyr Boichentsov 3a043b41c6
Update Package.swift 2019-12-27 23:14:25 +00:00
Volodymyr Boichentsov 8512600e7d
Merge pull request #8 from 3D4Medical/feature/version-2
Feature/version 2
2019-12-27 22:55:04 +00:00
Volodymyr Boichentsov a8916f6e60
Create swift.yml 2019-12-27 22:52:57 +00:00
Volodymyr Boichentsov 0bef3b8633
Merge pull request #7 from sakrist/feature/version-2
Fixes in version 2
2019-12-27 22:49:56 +00:00
Volodymyr Boichentsov 46fd54f726 update package file 2019-12-27 22:42:11 +00:00
Volodymyr Boichentsov 31dea75e7c extended simple test 2019-12-27 22:14:11 +00:00
Volodymyr Boichentsov 8c02951c65 add "available(OSX 10.12, iOS 10.0, *)" 2019-12-27 10:46:38 +00:00
Volodymyr Boichentsov 9a4685ba98 check on define DRACO for draco 2019-12-27 10:35:41 +00:00
Volodymyr Boichentsov 8e65bf0f64 remove draco include 2019-12-27 10:26:28 +00:00
Volodymyr Boichentsov 8c0d86d542 fix simple tests 2019-12-27 10:04:59 +00:00
Volodymyr Boichentsov 956583145d exclude draco from linkage 2019-12-03 23:31:19 +00:00
Volodymyr Boichentsov bcb7294eb1 Draco as macros 2019-12-03 22:31:03 +00:00
Sergey Novikov 19c3089f7e lint fixes 2019-12-03 11:41:39 +00:00
Sergey Novikov f6c9d76e03 error saving for callback 2019-12-02 14:31:38 +00:00
Sergey Novikov af4c4926c5 fix for recursion 2019-11-28 11:34:54 +00:00
Sergey Novikov 463a27bc55 checking existing node in scene of no 2019-11-27 09:29:32 +00:00
Sergey Novikov 6b5faed6f6 optional scene in delegate methods 2019-11-14 17:44:13 +00:00
Sergey Novikov e69d2f12ff public api for scn method 2019-11-13 17:03:53 +00:00
Sergey Novikov 9e0a0a4dcb public methods for ver 2 cache 2019-11-12 15:52:34 +00:00
Sergey Novikov 0aed670c0c remove callback for textures, root node for converter, white colour for empty material 2019-11-08 13:16:39 +00:00
Sergey Novikov aacc3a5ad0 remove async version 2019-11-06 13:53:26 +00:00
Sergey Novikov f9575a721c async loading. failed 2019-11-06 10:50:38 +00:00
Sergey Novikov e49ed1f3b9 stash for new version. 2019-11-05 17:15:55 +00:00
Sergey Novikov ed9d49fc09 extend required extensions 2019-10-24 17:34:57 +01:00
52 changed files with 1193 additions and 1237 deletions

15
.github/workflows/swift.yml vendored Normal file
View File

@ -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

4
.gitignore vendored
View File

@ -1,6 +1,4 @@
.DS_Store
/.build
/Packages
gltf_scenekit.xcodeproj/project.xcworkspace/xcuserdata
gltf_scenekit.xcodeproj/xcuserdata
glTFSceneKit.xcodeproj/xcuserdata
**/xcuserdata

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "Draco"]
path = Draco
url = https://github.com/3d4medical/DracoSwiftPackage.git

1
Draco

@ -1 +0,0 @@
Subproject commit 10caef92253b10439b3bc560ecef4a79804f612a

View File

@ -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
}

View File

@ -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"

View File

@ -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.
}
}
```

View File

@ -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 {
}
}
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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 {
}
}
}

View File

@ -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) }
}

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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 {
}
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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()
}()
}

View File

@ -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 {
}
}
}

View File

@ -2,5 +2,5 @@ import XCTest
@testable import gltf_scenekitTests
XCTMain([
testCase(gltf_scenekitTests.allTests),
testCase(gltf_scenekitTests.allTests)
])

View File

@ -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)
}
}
}

View File

@ -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 = (