277 lines
9.0 KiB
Swift
277 lines
9.0 KiB
Swift
import Foundation
|
|
import simd
|
|
import JolkEngine
|
|
|
|
|
|
@main struct Program
|
|
{
|
|
static func main()
|
|
{
|
|
Application(
|
|
application: CavesOfJolk(),
|
|
config: ApplicationConfiguration(
|
|
resizable: true,
|
|
vSync: .on,
|
|
windowWidth: 1280,
|
|
windowHeight: 720,
|
|
bundle: Bundle.module)
|
|
).run()
|
|
}
|
|
}
|
|
|
|
class CavesOfJolk: ApplicationImplementation
|
|
{
|
|
private var aspect: Float = 0
|
|
private var drawEdges = false
|
|
|
|
var env = Environment()
|
|
var worldMesh = RenderMesh.empty, cube = RenderMesh.empty, suzanne = RenderMesh.empty, toybox = RenderMesh.empty
|
|
var texture = Texture2D.empty, jolkTex = Texture2D.empty, suzanneDiffuse = Texture2D.empty
|
|
|
|
private lazy var world = Collision()
|
|
private lazy var colin = Colin()
|
|
private lazy var jolkCube = JolkCube(position: .init(0, 1, -4))
|
|
private var lightTheta: Float = 0
|
|
|
|
private var frameCount = 0
|
|
private var frameTimer: Float = 1
|
|
|
|
func create(render: inout Renderer)
|
|
{
|
|
render.clearColour = XnaColour.CornflowerBlue
|
|
|
|
env.setFog(
|
|
colour: XnaColour.CornflowerBlue,
|
|
//mode: .depth, falloff: .range(type: .linear, start: 4, end: 38))
|
|
//mode: .depth, falloff: .range(type: .linear, start: 1.25, end: 24.5))
|
|
//mode: .distance, falloff: .factor(type: .exp2, density: 0.1))
|
|
mode: .depth, falloff: .factor(type: .exp2, density: 0.0222))
|
|
|
|
env.addAmbientLight(colour: XnaColour.DarkSlateGray.lighten(by: -0.666))
|
|
env.addDirectionalLight(
|
|
direction: -Vec3f(1, -1, -1).normalised,
|
|
colour: XnaColour.BlanchedAlmond
|
|
.lighten(by: -0.02)
|
|
.mix(with: XnaColour.LightSlateGray, 0.5)
|
|
.mix(with: XnaColour.White, 0.125))
|
|
env.addPointLight(
|
|
position: Vec3f(3, 0.33, -5),
|
|
colour: XnaColour.Green.mix(with: XnaColour.Gray, 0.5),
|
|
intensity: 2)
|
|
env.addPointLight(
|
|
position: Vec3f(5.5, 0.33, -6),
|
|
colour: XnaColour.Red.mix(with: XnaColour.Gray, 0.5),
|
|
intensity: 2)
|
|
env.addPointLight(
|
|
position: Vec3f(4, 0.33, -7),
|
|
colour: XnaColour.Blue.mix(with: XnaColour.Gray, 0.5),
|
|
intensity: 2)
|
|
}
|
|
|
|
func loadContent(content: inout ContentManager) throws
|
|
{
|
|
try loadWorld(&content)
|
|
cube = try content.create(mesh: .init(
|
|
vertices: [
|
|
.init(position: Vec3f(-1, -1, 1), normal: .forward, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f( 1, -1, 1), normal: .forward, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f(-1, 1, 1), normal: .forward, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f( 1, 1, 1), normal: .forward, texCoord: Vec2f(1, 1)),
|
|
.init(position: Vec3f( 1, -1, 1), normal: .right, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f( 1, -1, -1), normal: .right, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f( 1, 1, 1), normal: .right, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f( 1, 1, -1), normal: .right, texCoord: Vec2f(1, 1)),
|
|
.init(position: Vec3f( 1, -1, -1), normal: .back, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f(-1, -1, -1), normal: .back, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f( 1, 1, -1), normal: .back, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f(-1, 1, -1), normal: .back, texCoord: Vec2f(1, 1)),
|
|
.init(position: Vec3f(-1, -1, -1), normal: .left, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f(-1, -1, 1), normal: .left, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f(-1, 1, -1), normal: .left, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f(-1, 1, 1), normal: .left, texCoord: Vec2f(1, 1)),
|
|
.init(position: Vec3f(-1, -1, -1), normal: .down, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f( 1, -1, -1), normal: .down, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f(-1, -1, 1), normal: .down, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f( 1, -1, 1), normal: .down, texCoord: Vec2f(1, 1)),
|
|
.init(position: Vec3f(-1, 1, 1), normal: .up, texCoord: Vec2f(0, 0)),
|
|
.init(position: Vec3f( 1, 1, 1), normal: .up, texCoord: Vec2f(1, 0)),
|
|
.init(position: Vec3f(-1, 1, -1), normal: .up, texCoord: Vec2f(0, 1)),
|
|
.init(position: Vec3f( 1, 1, -1), normal: .up, texCoord: Vec2f(1, 1))
|
|
],
|
|
indices: [
|
|
0, 1, 2, 2, 1, 3,
|
|
4, 5, 6, 6, 5, 7,
|
|
8, 9, 10, 10, 9, 11,
|
|
12, 13, 14, 14, 13, 15,
|
|
16, 17, 18, 18, 17, 19,
|
|
20, 21, 22, 22, 21, 23 ]))
|
|
suzanne = try content.load("suzanne.g3db")
|
|
toybox = try content.load("toybox.g3db")
|
|
|
|
|
|
let linearMipped = Texture2DParameters(
|
|
minFilter: .linear, magFilter: .linear,
|
|
wrapMode: .repeating,
|
|
mipMode: .linear)
|
|
let linearClamp = Texture2DParameters(
|
|
minFilter: .linear, magFilter: .linear,
|
|
wrapMode: .clampEdge)
|
|
|
|
texture = try content.load("cobblestone.png", params: linearMipped)
|
|
jolkTex = try content.load("jolkmeup.jpg", params: linearClamp)
|
|
suzanneDiffuse = try content.load("suzanne_diffuse.png", params: linearMipped)
|
|
}
|
|
|
|
func resize(width: Int, height: Int)
|
|
{
|
|
aspect = Float(width) / Float(height)
|
|
print(aspect)
|
|
}
|
|
|
|
func update(deltaTime: Float)
|
|
{
|
|
colin.update(deltaTime: deltaTime, world: world)
|
|
jolkCube.update(deltaTime: deltaTime, world: world)
|
|
|
|
lightTheta += deltaTime
|
|
|
|
frameCount += 1
|
|
frameTimer -= deltaTime
|
|
if frameTimer <= 0
|
|
{
|
|
print("FPS: \(frameCount)")
|
|
frameCount = 0
|
|
frameTimer += 1.0
|
|
}
|
|
}
|
|
|
|
private func loadWorld(_ content: inout ContentManager) throws
|
|
{
|
|
//let world: Mesh = try content.load("World.obj")
|
|
let obj = try ObjReader.read(url: try content.getResource("World.obj"))
|
|
let mesh: Mesh = try ObjLoader.read(model: obj)
|
|
worldMesh = try content.create(mesh: mesh)
|
|
|
|
if let collision = obj.objects["Collision3D"]
|
|
{
|
|
world.build(obj: obj, collision: collision)
|
|
}
|
|
}
|
|
|
|
/*
|
|
if let collision = world.subMeshes["Collision"]
|
|
{
|
|
edges.reserveCapacity(collision.length / 6)
|
|
let get2dVertex =
|
|
{ i in
|
|
let vertex = world.vertices[Int(world.indices[i])]
|
|
let position = Vec2f(vertex.position.x, vertex.position.z)
|
|
let normal = Vec2f(vertex.normal.x, vertex.normal.z)
|
|
return (position, normal)
|
|
}
|
|
for i in stride(from: collision.start, to: collision.start + collision.length, by: 3)
|
|
{
|
|
let (v0p, v0n) = get2dVertex(i)
|
|
let (v1p, v1n) = get2dVertex(i + 1)
|
|
let (v2p, v2n) = get2dVertex(i + 2)
|
|
|
|
let (p0, p1) = if v0p == v2p || v1p == v2p { (v0p, v1p) }
|
|
else if v0p == v1p { (v0p, v2p) }
|
|
else { throw NSError() }
|
|
|
|
let edge = Edge(p: p0.lerp(p1, 0.5), n: (v0n + v1n + v2n).normalised, w: (p0 - p1).len)
|
|
if !edges.contains(where: { edge.p == $0.p && edge.w == $0.w })
|
|
{
|
|
edges.append(edge)
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
func draw(render: inout Renderer, deltaTime: Float)
|
|
{
|
|
// Setup camera
|
|
render.setProjection(matrix:
|
|
.perspective(fovY: Float.rad(fromDeg: colin.fov), aspect: aspect, zNear: 0.1, zFar: 100))
|
|
render.setView(matrix: colin.transform)
|
|
|
|
// Update point lights
|
|
var theta = Vec3f(repeating: lightTheta) * Vec3f(0.12, 0.011, 0.056) * 6
|
|
var i = env.lights.startIndex
|
|
while i != env.lights.endIndex
|
|
{
|
|
if case .point(let colour, _, _) = env.lights[i]
|
|
{
|
|
env.lights[i] = .point(
|
|
colour: colour,
|
|
position: Vec3f(
|
|
4 + 6 * cos(theta[0]),
|
|
0.33 + 0.33 * sin(theta[1]),
|
|
-6 + 3 * sin(theta[0] * 2)),
|
|
intensity: 3.1 + 1.53 * cos(theta[2]))
|
|
|
|
let spacing = 0.5 * Float.pi * (2 / 3.0)
|
|
theta += spacing * Vec3f(1, 0.98, 0.5566)
|
|
}
|
|
i = env.lights.index(after: i)
|
|
}
|
|
|
|
// Draw world
|
|
render.setMaterial(Material(
|
|
specular: XnaColour.BlanchedAlmond.mix(with: .black, 0.12),
|
|
texture: texture.id))
|
|
render.draw(mesh: worldMesh, environment: env)
|
|
|
|
// Draw jolked up shit
|
|
render.setMaterial(Material(
|
|
specular: XnaColour.Gray,
|
|
gloss: 20.0,
|
|
texture: jolkTex.id))
|
|
render.draw(mesh: cube, model: jolkCube.transform, environment: env)
|
|
|
|
render.setMaterial(Material(texture: suzanneDiffuse.id))
|
|
render.draw(mesh: suzanne, model: .translate(.up + Vec3f(3.0, 0.0, -3.5) * 2.5), environment: env)
|
|
|
|
render.draw(mesh: toybox,
|
|
model: .translate(Vec3f(6.0, 0.667, -3.5) * Vec3f(2.5, 1, 2.5))
|
|
* .rotate(y: lightTheta * 0.5) * .scale(scalar: 0.25), environment: env)
|
|
|
|
if Input.instance.keyPressed(.c) { drawEdges = !drawEdges }
|
|
if drawEdges
|
|
{
|
|
/*
|
|
var lines = [Line](
|
|
repeating: .init(from: .init(), to: .init(), colour: .zero),
|
|
count: edges.count * 3)
|
|
var i: Int = 0
|
|
for edge in edges
|
|
{
|
|
let tangent = Vec2f(edge.n.y, -edge.n.x)
|
|
let a = edge.p - tangent * edge.w * 0.5
|
|
let b = edge.p + tangent * edge.w * 0.5
|
|
|
|
lines[i] = Line(
|
|
from: Vec3f(a.x, 0, a.y),
|
|
to: Vec3f(b.x, 0, b.y),
|
|
colour: Colour(r: 0.1, g: 0.9, b: 0.1))
|
|
lines[i + 1] = Line(
|
|
from: Vec3f(edge.p.x, 0, edge.p.y),
|
|
to: Vec3f(edge.p.x + edge.n.x * 0.25, 0, edge.p.y + edge.n.y * 0.25),
|
|
colour: Colour(r: 0.9, g: 0.1, b: 0.1))
|
|
|
|
let deltaPos = Vec2f(colin.position.x, colin.position.z) - edge.p
|
|
let something = tangent * deltaPos.cross(edge.n)
|
|
lines[i + 2] = Line(
|
|
from: Vec3f(edge.p.x + something.x, 0.0, edge.p.y + something.y),
|
|
to: Vec3f(edge.p.x + something.x + edge.n.x * 0.5, 0.0, edge.p.y + something.y + edge.n.y * 0.5),
|
|
colour: XnaColour.Azure)
|
|
i += 3
|
|
}
|
|
render.drawGizmos(lines: lines)
|
|
*/
|
|
|
|
world.draw(render, position: colin.position)
|
|
}
|
|
}
|
|
}
|