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