MetalByTutorials/13-procedural-generation/final/procedural/Procedural/Scene.swift

138 lines
4.2 KiB
Swift

//
/**
* Copyright (c) 2019 Razeware LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
* distribute, sublicense, create a derivative work, and/or sell copies of the
* Software in any work that is designed, intended, or marketed for pedagogical or
* instructional purposes related to programming, coding, application development,
* or information technology. Permission for such use, copying, modification,
* merger, publication, distribution, sublicensing, creation of derivative works,
* or sale is expressly withheld.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import Foundation
import CoreGraphics
class Scene {
let inputController = InputController()
let physicsController = PhysicsController()
var skybox: Skybox?
var sceneSize: CGSize
var cameras = [Camera()]
var currentCameraIndex = 0
var camera: Camera {
return cameras[currentCameraIndex]
}
init(sceneSize: CGSize) {
self.sceneSize = sceneSize
setupScene()
sceneSizeWillChange(to: sceneSize)
}
let rootNode = Node()
var renderables: [Renderable] = []
var uniforms = Uniforms()
var fragmentUniforms = FragmentUniforms()
func setupScene() {
// override this to add objects to the scene
}
private func updatePlayer(deltaTime: Float) {
guard let node = inputController.player else { return }
let holdPosition = node.position
let holdRotation = node.rotation
inputController.updatePlayer(deltaTime: deltaTime)
if physicsController.checkCollisions() && !updateCollidedPlayer() {
node.position = holdPosition
node.rotation = holdRotation
}
}
func updateCollidedPlayer() -> Bool {
// override this
return false
}
final func update(deltaTime: Float) {
updatePlayer(deltaTime: deltaTime)
uniforms.projectionMatrix = camera.projectionMatrix
uniforms.viewMatrix = camera.viewMatrix
fragmentUniforms.cameraPosition = camera.position
updateScene(deltaTime: deltaTime)
update(nodes: rootNode.children, deltaTime: deltaTime)
}
private func update(nodes: [Node], deltaTime: Float) {
nodes.forEach { node in
node.update(deltaTime: deltaTime)
update(nodes: node.children, deltaTime: deltaTime)
}
}
func updateScene(deltaTime: Float) {
// override this to update your scene
}
final func add(node: Node, parent: Node? = nil, render: Bool = true) {
if let parent = parent {
parent.add(childNode: node)
} else {
rootNode.add(childNode: node)
}
guard render == true,
let renderable = node as? Renderable else {
return
}
renderables.append(renderable)
}
final func remove(node: Node) {
if let parent = node.parent {
parent.remove(childNode: node)
} else {
for child in node.children {
child.parent = nil
}
node.children = []
}
guard node is Renderable,
let index = (renderables.firstIndex {
$0 as? Node === node
}) else { return }
renderables.remove(at: index)
}
func sceneSizeWillChange(to size: CGSize) {
for camera in cameras {
camera.aspect = Float(size.width / size.height)
}
sceneSize = size
}
}