The Skung Cave commit
This commit is contained in:
@ -4,7 +4,7 @@ import OrderedCollections
|
||||
|
||||
class G3DbLoader: LoaderProtocol
|
||||
{
|
||||
typealias T = Mesh
|
||||
typealias T = Mesh<VertexPositionNormalTexcoord>
|
||||
|
||||
func load(url: URL) -> T?
|
||||
{
|
||||
@ -12,6 +12,8 @@ class G3DbLoader: LoaderProtocol
|
||||
else { return Optional.none }
|
||||
return try? reader.read()
|
||||
}
|
||||
|
||||
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
|
||||
}
|
||||
|
||||
|
||||
@ -27,15 +29,15 @@ fileprivate struct G3DbReader
|
||||
self.reader = UBJsonReader(file: file)
|
||||
}
|
||||
|
||||
mutating func read() throws -> Mesh
|
||||
mutating func read() throws -> Mesh<VertexPositionNormalTexcoord>
|
||||
{
|
||||
let model = try readModel()
|
||||
|
||||
let mesh = model.meshes[0]
|
||||
|
||||
var vertices = [Mesh.Vertex]()
|
||||
var vertices = [VertexPositionNormalTexcoord]()
|
||||
var indices = [UInt16]()
|
||||
var subMeshes = OrderedDictionary<String, Mesh.SubMesh>()
|
||||
var subMeshes = OrderedDictionary<String, Mesh<VertexPositionNormalTexcoord>.SubMesh>()
|
||||
|
||||
let attributeSize =
|
||||
{ (attrib: G3DAttribute) in
|
||||
|
@ -16,6 +16,8 @@ struct NSImageLoader: LoaderProtocol
|
||||
return image
|
||||
}
|
||||
|
||||
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
|
||||
|
||||
static func loadImage(url: URL) throws -> Image
|
||||
{
|
||||
try autoreleasepool
|
||||
|
@ -2,10 +2,9 @@ import Foundation
|
||||
import OrderedCollections
|
||||
|
||||
|
||||
/*
|
||||
extension ObjMaterial
|
||||
{
|
||||
func convert() -> Material
|
||||
func convert(content: UnsafeMutablePointer<ContentManager>? = nil) -> Material
|
||||
{
|
||||
var m = Material()
|
||||
m.diffuse = self.diffuse.setAlpha(self.alpha)
|
||||
@ -14,37 +13,61 @@ extension ObjMaterial
|
||||
m.specular = self.specular
|
||||
m.specularExp = self.specularExp
|
||||
}
|
||||
if let content = content
|
||||
{
|
||||
if let albedo = self.diffuseMap
|
||||
{
|
||||
let filter = albedo.flags.contains(.blendHoriz) || albedo.flags.contains(.blendVert)
|
||||
m.texture = (try? content.pointee.load(albedo.path,
|
||||
params: Texture2DParameters(
|
||||
minFilter: filter ? .linear : .point,
|
||||
magFilter: filter ? .linear : .point,
|
||||
wrapMode: albedo.flags.contains(.clamp) ? .clampBorder : .repeating,
|
||||
mipMode: .linear)))?.id ?? RenderTexture2D.empty
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public struct ObjLoader: LoaderProtocol
|
||||
{
|
||||
public typealias T = Mesh
|
||||
typealias V = VertexPositionNormalColourTexcoord
|
||||
public typealias T = Mesh<VertexPositionNormalColourTexcoord>
|
||||
|
||||
public init() {}
|
||||
|
||||
public func load(url: URL) -> T?
|
||||
{
|
||||
try? Self.read(url: url)
|
||||
return try? Self.read(model: try ObjReader.read(url: url), content: nil)
|
||||
}
|
||||
|
||||
private static func read(url: URL) throws -> Mesh
|
||||
public func load(url: URL, content: inout ContentManager) -> T?
|
||||
{
|
||||
return try read(model: try ObjReader.read(url: url))
|
||||
return try? Self.read(model: try ObjReader.read(url: url), content: &content)
|
||||
}
|
||||
|
||||
public static func read(model: ObjModel) throws -> Mesh
|
||||
public static func read(model: ObjModel, content: UnsafeMutablePointer<ContentManager>?) throws -> T
|
||||
{
|
||||
var subMeshes: OrderedDictionary<String, Mesh.SubMesh> = .init()
|
||||
var vertices = [Mesh.Vertex]()
|
||||
var subMeshes = OrderedDictionary<String, T.SubMesh>()
|
||||
var materials = OrderedDictionary<String, Material>()
|
||||
var vertices = [V]()
|
||||
var indices = [UInt16]()
|
||||
|
||||
for (key, mtl) in model.materials
|
||||
{
|
||||
materials[key] = mtl.convert(content: content)
|
||||
}
|
||||
|
||||
let readIndex =
|
||||
{ (v: ObjModel.Index) -> UInt16 in
|
||||
let vertex = Mesh.Vertex(
|
||||
let colour = model.colours.isEmpty ? .white : Colour(
|
||||
r: model.colours[v.p][0],
|
||||
g: model.colours[v.p][1],
|
||||
b: model.colours[v.p][2]).linear
|
||||
let vertex = V(
|
||||
position: model.positions[v.p],
|
||||
colour: colour,
|
||||
normal: model.normals[v.n],
|
||||
texCoord: model.texCoords[v.t])
|
||||
if let index = vertices.firstIndex(of: vertex)
|
||||
@ -61,32 +84,39 @@ public struct ObjLoader: LoaderProtocol
|
||||
}
|
||||
}
|
||||
|
||||
for mesh in model.objects
|
||||
for object in model.objects.filter({ $0.key != "Collision3D" })
|
||||
{
|
||||
let start = indices.count
|
||||
for face: ObjModel.Face in mesh.value.faces
|
||||
var id = 0
|
||||
for mesh: ObjModel.Mesh in object.value.meshes
|
||||
{
|
||||
switch face
|
||||
let start = indices.count
|
||||
for face: ObjModel.Face in mesh.faces
|
||||
{
|
||||
case .triangle(let v1, let v2, let v3):
|
||||
for v in [ v1, v2, v3 ] { _ = readIndex(v) }
|
||||
case .quad(let v1, let v2, let v3, let v4):
|
||||
let n1 = readIndex(v1)
|
||||
_ = readIndex(v2)
|
||||
indices.append(readIndex(v3))
|
||||
_ = readIndex(v4)
|
||||
indices.append(n1)
|
||||
case .ngon(_): fallthrough
|
||||
default: break
|
||||
switch face
|
||||
{
|
||||
case .triangle(let v1, let v2, let v3):
|
||||
for v in [ v1, v2, v3 ] { _ = readIndex(v) }
|
||||
case .quad(let v1, let v2, let v3, let v4):
|
||||
let n1 = readIndex(v1)
|
||||
_ = readIndex(v2)
|
||||
indices.append(readIndex(v3))
|
||||
_ = readIndex(v4)
|
||||
indices.append(n1)
|
||||
case .ngon(_): fallthrough
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
let length = indices.count - start
|
||||
if length > 0
|
||||
{
|
||||
subMeshes[mesh.key] = .init(start: start, length: length)
|
||||
let length = indices.count - start
|
||||
if length > 0
|
||||
{
|
||||
subMeshes["\(object.key)_\(id)"] = .init(
|
||||
start: start, length: length,
|
||||
material: materials.index(forKey: mesh.material) ?? -1)
|
||||
}
|
||||
id += 1
|
||||
}
|
||||
}
|
||||
|
||||
return Mesh(vertices: vertices, indices: indices, subMeshes: subMeshes)
|
||||
return Mesh(vertices: vertices, indices: indices, subMeshes: subMeshes, materials: materials.values.map { $0 })
|
||||
}
|
||||
}
|
||||
|
@ -8,25 +8,46 @@ public struct ObjReader
|
||||
var file = try ObjDocumentReader(filePath: url)
|
||||
|
||||
var model = ObjModel()
|
||||
var materials = Dictionary<String, ObjMaterial>()
|
||||
var name: String? = nil
|
||||
var object = ObjModel.Object()
|
||||
var mesh = ObjModel.Mesh()
|
||||
|
||||
file.string(tag: "mtllib") { s in
|
||||
let mats = try ObjMtlLoader.read(url: url.deletingLastPathComponent().appendingPathComponent(s[0]))
|
||||
materials.merge(mats, uniquingKeysWith: { (_, new) in new } )
|
||||
model.materials.merge(mats, uniquingKeysWith: { (_, new) in new } )
|
||||
}
|
||||
file.string(tag: "o", count: 1) { s in
|
||||
if !object.faces.isEmpty
|
||||
if !mesh.faces.isEmpty { object.meshes.append(mesh) }
|
||||
if !object.meshes.isEmpty
|
||||
{
|
||||
model.objects[name!] = object
|
||||
object = .init()
|
||||
mesh = .init()
|
||||
}
|
||||
name = String(s[0])
|
||||
}
|
||||
file.float(tag: "v", count: 3) { v in model.positions.append(Vec3f(v[0], v[1], v[2])) }
|
||||
file.float(tag: "v") { v in
|
||||
if v.count == 6
|
||||
{
|
||||
if model.colours.isEmpty && !model.positions.isEmpty
|
||||
{
|
||||
for _ in 0..<model.positions.count
|
||||
{
|
||||
model.colours.append(.one)
|
||||
}
|
||||
}
|
||||
model.colours.append(Vec3f(v[3], v[4], v[5]))
|
||||
}
|
||||
else if !model.colours.isEmpty && v.count == 3
|
||||
{
|
||||
model.colours.append(.one)
|
||||
}
|
||||
else if v.count != 3 { throw ObjLoaderError.badTagParameters }
|
||||
model.positions.append(Vec3f(v[0], v[1], v[2]))
|
||||
}
|
||||
file.float(tag: "vn", count: 3) { v in model.normals.append(Vec3f(v[0], v[1], v[2])) }
|
||||
file.float(tag: "vt", count: 2) { v in model.texCoords.append(Vec2f(v[0], v[1])) }
|
||||
file.int(tag: "l") { i in mesh.faces.append(.line(p1: i[0], p2: i[1])) }
|
||||
file.raw(tag: "f") { raw in
|
||||
let readIndex =
|
||||
{ (raw: Substring) in
|
||||
@ -40,14 +61,14 @@ public struct ObjReader
|
||||
if raw.count == 3
|
||||
{
|
||||
for raw in raw { _ = try readIndex(raw) }
|
||||
object.faces.append(.triangle(
|
||||
mesh.faces.append(.triangle(
|
||||
v1: try readIndex(raw[raw.startIndex]),
|
||||
v2: try readIndex(raw[raw.startIndex + 1]),
|
||||
v3: try readIndex(raw[raw.startIndex + 2])))
|
||||
}
|
||||
else if raw.count == 4
|
||||
{
|
||||
object.faces.append(.quad(
|
||||
mesh.faces.append(.quad(
|
||||
v1: try readIndex(raw[raw.startIndex]),
|
||||
v2: try readIndex(raw[raw.startIndex + 1]),
|
||||
v3: try readIndex(raw[raw.startIndex + 2]),
|
||||
@ -55,17 +76,25 @@ public struct ObjReader
|
||||
}
|
||||
else if raw.count >= 5
|
||||
{
|
||||
object.faces.append(.ngon(try raw.map { try readIndex($0) }))
|
||||
mesh.faces.append(.ngon(try raw.map { try readIndex($0) }))
|
||||
}
|
||||
else { throw ObjLoaderError.badTagParameters }
|
||||
}
|
||||
file.int(tag: "l") { i in object.faces.append(.line(p1: i[0], p2: i[1])) }
|
||||
file.string(tag: "usemtl", count: 1) { s in
|
||||
if !mesh.faces.isEmpty
|
||||
{
|
||||
object.meshes.append(mesh)
|
||||
mesh = .init()
|
||||
}
|
||||
mesh.material = s[0]
|
||||
}
|
||||
// file.int(tag: "s", count: 1) { i in } //TODO: Smoothing groups
|
||||
|
||||
try file.read()
|
||||
|
||||
if !object.faces.isEmpty
|
||||
{
|
||||
model.objects[name!] = object
|
||||
}
|
||||
if !mesh.faces.isEmpty { object.meshes.append(mesh) }
|
||||
if !object.meshes.isEmpty { model.objects[name!] = object }
|
||||
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user