From d4fd7ecc55bd40501e704c66b31cec815edd8cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20Casenave-P=C3=A9r=C3=A9?= Date: Mon, 10 Nov 2014 16:35:21 +0900 Subject: [PATCH] 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. --- src/game/game-object.lisp | 100 ++++++++++++++++++++++++++++++++++++++ src/game/game.lisp | 59 ++++++++++++++++++++++ stoe.asd | 4 ++ 3 files changed, 163 insertions(+) create mode 100644 src/game/game-object.lisp create mode 100644 src/game/game.lisp diff --git a/src/game/game-object.lisp b/src/game/game-object.lisp new file mode 100644 index 0000000..805ea7b --- /dev/null +++ b/src/game/game-object.lisp @@ -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)))))) diff --git a/src/game/game.lisp b/src/game/game.lisp new file mode 100644 index 0000000..a97bbd9 --- /dev/null +++ b/src/game/game.lisp @@ -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*) diff --git a/stoe.asd b/stoe.asd index 30a2056..171c833 100644 --- a/stoe.asd +++ b/stoe.asd @@ -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")