Add a game module

The game module contains for now a internal loop to allow the game to
always update at 16ms and a simple scene graph to place object in a 3d
world. A camera class is defined to be able to view the world.
This commit is contained in:
Renaud Casenave-Péré 2014-11-10 16:35:21 +09:00
parent 75f40f6be8
commit d4fd7ecc55
3 changed files with 163 additions and 0 deletions

100
src/game/game-object.lisp Normal file
View file

@ -0,0 +1,100 @@
#|
This file is a part of stoe project.
Copyright (c) 2014 Renaud Casenave-Péré (renaud@casenave-pere.fr)
|#
(in-package :cl-user)
(defpackage stoe.game.game-object
(:use :cl)
(:nicknames :game-object :go)
(:shadow :position)
(:export :node
:parent
:children
:attach
:detach
:walk
:position
:direction
:make-object
:update-trans-matrix
:make-camera
:view
:update-view))
(in-package :stoe.game.game-object)
(defclass scene-node ()
((parent :initform nil :reader parent)
(children :initform nil :reader children))
(:documentation "Base class for a node in the scene graph."))
(defgeneric attach (scene-node parent)
(:documentation "Attach a new node to the scene graph to be rendered."))
(defmethod attach ((scene-node scene-node) (parent scene-node))
(with-slots (children) parent
(with-slots ((new-parent parent)) scene-node
(push scene-node children)
(setf new-parent parent))))
(defgeneric detach (scene-node)
(:documentation "Detach a node from the scene graph to prevent it and its
children from being rendered."))
(defmethod detach ((scene-node scene-node))
(with-slots (parent) scene-node
(with-slots (children) parent
(setf children (remove scene-node children))
(setf parent nil))))
(defun walk (fun node)
"Walk through the scene graph and apply `fun' at each node."
(with-slots (children) node
(loop for child in children
do (progn
(apply fun child)
(walk fun child)))))
(defclass game-object (scene-node)
((position :initarg :position :accessor position :type 'f3:float3)
(direction :initarg :direction :accessor direction :type 'q:quaternion)
(trans-matrix :initform (f44:mat-ident) :accessor trans-matrix :type 'f44:float44)
(components :initform nil))
(:documentation "Base class for all objects existing in the game world."))
(defclass camera (game-object)
((fovy :initarg :fovy :accessor fovy)
(aspect :initarg :aspect :accessor aspect)
(near :initarg :near :accessor near)
(far :initarg :far :accessor far)
(projection :initarg :projection :accessor projection :type 'f44:float44)
(view :accessor view :type 'f44:float44))
(:documentation "Base class for a camera representing a view of the game world."))
(defun make-object (&key (pos (v:vec 0 0 0)) (dir (q:from-axis-and-angle (v:vec 0 0 1) 0)) mesh)
(let ((obj (make-instance 'game-object :position pos :direction dir)))
(when mesh
(with-slots (components) obj
(push mesh components)))
obj))
(defun update-trans-matrix (node)
(setf (trans-matrix node) (m:* (trans-matrix (parent node))
(geom:mat-trans (position node))
(q:to-float44 (direction node)))))
(defun make-camera (fovy aspect near far)
(let ((camera (make-instance 'camera :position (v:vec 0 0 2)
:direction (q:from-axis-and-angle (v:vec 0 0 1) 0)
:fovy fovy
:aspect aspect
:near near
:far far
:projection (geom:make-persp-matrix fovy aspect near far))))
(update-view camera)
camera))
(defun update-view (camera)
"Compute the world to view matrix from the position and the direction of `camera'."
(with-accessors ((pos go:position) (dir go:direction) (view view)) camera
(setf view (m:* (m::transpose (q:to-float44 dir)) (geom:mat-trans (v:- pos))))))

59
src/game/game.lisp Normal file
View file

@ -0,0 +1,59 @@
#|
This file is a part of stoe project.
Copyright (c) 2014 Renaud Casenave-Péré (renaud@casenave-pere.fr)
|#
(in-package :cl-user)
(defpackage stoe.game
(:use :cl)
(:nicknames :game)
(:export :get-world-origin
:get-current-camera))
(in-package :stoe.game)
(defconstant +loop-step-time+ 16000.0
"The length of one game loop frame.")
(defvar *last-frame-remaining-time* 0.0
"The game loop advance +loop-step-time+ at a time but when the delta time doesn't correspond
we need to keep the remaining time.")
(defvar *current-camera* nil
"The camera used to render the scene.")
(defvar *world-origin* nil
"The origin node of the scene.")
(defun initialize (&optional argv)
"Initialize the game module."
(declare (ignore argv))
(format t "Initialize Game module~%")
(input:initialize)
(setf *world-origin* (go:make-object))
(setf *current-camera* (go:make-camera 90 (/ 16 9) 1.0 1000.0))
(go:attach *current-camera* *world-origin*))
(defun finalize ()
"Finalize the game module."
(setf *current-camera* nil)
(setf *world-origin* nil)
(input:finalize))
(defun update (delta-time)
"Update the game module.
Advance the world by `delta-time', +loop-step-time+ at a time."
(setf delta-time (+ delta-time *last-frame-remaining-time*))
(loop while (> delta-time +loop-step-time+)
do (progn
(when *current-camera*
(go:update-view *current-camera*))
(input:update +loop-step-time+)
(decf delta-time +loop-step-time+)))
(setf *last-frame-remaining-time* delta-time))
(modules:register-initialize-fun #'initialize)
(modules:register-finalize-fun #'finalize)
(modules:register-update-fun #'update)
(defun get-world-origin () *world-origin*)
(defun get-current-camera () *current-camera*)

View file

@ -49,6 +49,10 @@
:depends-on ("thread" "containers" "utils"))
(:file "file"
:depends-on ("jobs"))
(:module "game"
:components
((:file "game-object")
(:file "game")))
(:module "render"
:components
((:file "gl-utils")