diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 43e3918..dbcedf5 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,5 +1,5 @@
Changelog for Photon
-$Id: CHANGELOG.txt,v 1.8 2005/08/14 07:40:12 cozman Exp $
+$Id: CHANGELOG.txt,v 1.9 2005/08/16 06:32:39 cozman Exp $
! : Major Changes (potentially breaking existing code)
+ : New Features
@@ -8,14 +8,19 @@ $Id: CHANGELOG.txt,v 1.8 2005/08/14 07:40:12 cozman Exp $
0.0.2
! Removed InputListener, opting to move features into State class.
- ! Changed Application::getElapsedTime to Application::getTimeDelta
+ ! Dropped reliance on Kernel, and added main loop to Application, Kernel
+ access now available through Application's getUpdateKernel() and
+ getRenderKernel().
+ Code::Blocks and Dev-C++ support
+ Addition of a State stack allowing for applications to fall back to the
previous state when done with a particular state.
+ Notification of mouse wheel events added.
+ Addition of a State test/example program.
- + Addition of Application::setTimeDeltaMode ability to set how time delta
- is calculated. (actual/average currently supported).
+ + Addition of Application::setFrameTimeSmoothing ability to set how elapsed
+ time is calculated. (actual/average currently supported).
+ + Addition of fixed time stepping option in Application/State system.
+ + Addition of code to allow control of depth testing via
+ Application::setDepthTestParams
* Fixed X11 fullscreen mode
* Removed ALUT dependencies by adding custom WAV loading code
* Mouse move events now give relative position.
diff --git a/SConstruct b/SConstruct
index 91140a5..fc17a8b 100644
--- a/SConstruct
+++ b/SConstruct
@@ -5,7 +5,7 @@
# James Turk (jpt2433@rit.edu)
#
# Version:
-# $Id: SConstruct,v 1.21 2005/08/10 05:56:27 cozman Exp $
+# $Id: SConstruct,v 1.22 2005/08/16 06:32:39 cozman Exp $
import os,os.path
import glob
@@ -52,11 +52,11 @@ def BuildSuperHeader(target = None, source = None, env = None):
SuperHeaderAction = Action(BuildSuperHeader)
-# Configure the environment (Check libraries):
+# Configure the environment
env = Environment(ENV = os.environ, MSVS_VERSION = 7.0)
-env.Append(CPPPATH='include', CPPFLAGS='-Wall')
+env.Append(CPPPATH='include', CPPFLAGS=['-Wall', '-fmessage-length=0'])
env.ParseConfig('freetype-config --cflags')
-# Configure
+# Configure (Check Libraries)
if not env.GetOption('clean'):
conf = Configure(env)
if not conf.CheckLibWithHeader(OGL_LIB, 'GL/gl.h', 'C++'):
@@ -105,10 +105,11 @@ for test_src in test_srcs:
tests.append(env.Program(test_name, source=test_src, LIBPATH='./lib',
LIBS=['photon','glfw',OAL_LIB,OGL_LIB,GLU_LIB,
'physfs','corona','freetype']))
-env.Alias('test',tests)
+env.Alias('tests',tests)
# Visual C++ Projects
-msvc = env.MSVSProject(target = 'msvc/photon' + env['MSVSPROJECTSUFFIX'],
+if(os.name == 'nt'):
+ msvc = env.MSVSProject(target = 'msvc/photon' + env['MSVSPROJECTSUFFIX'],
srcs = getFilesMulti(SRC_DIRS, '*.cpp'), incs = INC_FILES,
buildtarget = lib, variant = 'Release')
-env.Alias('msvc',msvc)
+ env.Alias('msvc',msvc)
diff --git a/codeblocks/photon.cbp b/codeblocks/photon.cbp
index 4ebfb5f..cfc8ea3 100644
--- a/codeblocks/photon.cbp
+++ b/codeblocks/photon.cbp
@@ -222,12 +222,6 @@
-
-
-
-
-
-
@@ -332,9 +326,5 @@
-
-
-
-
diff --git a/include/Application.hpp b/include/Application.hpp
index 0530b05..d7ae208 100644
--- a/include/Application.hpp
+++ b/include/Application.hpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Application.hpp,v 1.19 2005/08/14 07:40:13 cozman Exp $
+// $Id: Application.hpp,v 1.20 2005/08/16 06:32:39 cozman Exp $
#ifndef PHOTON_APPLICATION_HPP
#define PHOTON_APPLICATION_HPP
@@ -20,18 +20,10 @@
#include "types.hpp"
#include "util/VersionInfo.hpp"
#include "State.hpp"
-#include "Task.hpp"
#include "Kernel.hpp"
#include "audio/AudioCore.hpp"
#include "util/Singleton.hpp"
-enum TimeDeltaMode
-{
- TDM_ACTUAL,
- TDM_AVERAGE,
- TDM_FIXED
-};
-
namespace photon
{
@@ -39,7 +31,7 @@ namespace photon
// Photon main class, contains functions that control creation of the display,
// setting the OpenGL view, input handling, timing, and management.
//
-// Class is a and therefore should be accessed through
+// Application is a and therefore should be accessed through
// Application::getInstance(). (Application Singleton is created/destroyed
// automatically)
class Application : public util::Singleton
@@ -54,6 +46,47 @@ public:
// Function: ~Application
// Default destructor, shuts down dependencies.
virtual ~Application();
+
+// Group: General
+public:
+ // Function: run
+ // Runs application until a quit is requested either via the operating
+ // system (ex. Alt-F4) or through a call to .
+ //
+ // Should not be called before a has been set and a display has
+ // been created via .
+ void run();
+
+ // called by run while !quit()
+ void update();
+
+ // Function: quit
+ // Sets Quit flag, terminating application.
+ void quit();
+
+ // Function: getUpdateKernel
+ // Access the application's update , registered with this
+ // kernel are executed after the current .
+ //
+ // Returns:
+ // Reference to "Update Kernel"
+ Kernel& getUpdateKernel();
+
+ // Function: getRenderKernel
+ // Access the application's render , registered with this
+ // kernel are executed after the current .
+ //
+ // Returns:
+ // Reference to "Render Kernel"
+ Kernel& getRenderKernel();
+
+ // Function: isActive
+ // Checks if application is active, which on most systems simply means it
+ // has focus.
+ //
+ // Returns:
+ // True if application is active, false otherwise.
+ bool isActive();
// Group: Window
public:
@@ -111,14 +144,6 @@ public:
// Returns:
// Height of display in pixels.
uint getDisplayHeight();
-
- // Function: isActive
- // Checks if application is active, which on most systems simply means it
- // has focus.
- //
- // Returns:
- // True if application is active, false otherwise.
- bool isActive();
// Group: Ortho
public:
@@ -154,6 +179,8 @@ public:
// Function: setPerspectiveView
// Creates a viewport with a given 3D perspective inside of a rectangular
// portion of the screen.
+ //
+ // Note that (true) will be called as a side effect.
//
// Parameters:
// x - X coord for top left corner of new viewport.
@@ -168,6 +195,8 @@ public:
// Function: setPerspectiveView
// Sets entire screen as current viewport with a given 3D perspective.
+ //
+ // Note that (true) will be called as a side effect.
//
// Parameters:
// fovy - The y axis field of view angle, in degrees.
@@ -192,13 +221,23 @@ public:
void setOrthoProjection(scalar width, scalar height);
// Function: setPerspectiveProjection
- // Sets a perspective projection matrix.
+ // Sets a perspective projection matrix.
+ //
+ // Note that (true) will be called as a side effect.
//
// Parameters:
// fovy - The y axis field of view angle, in degrees.
// zNear - Distance from viewer to near clipping plane.
// zFar - Distance from viewer to far clipping plane.
void setPerspectiveProjection(scalar fovy, scalar zNear, scalar zFar);
+
+ // Function: setDepthTestMode
+ // Toggle depth testing and clearing of depth buffer.
+ //
+ // Parameters:
+ // enable - if true, testing/clearing depth buffer is enabled, if false
+ // testing and clearing of depth buffer will be disabled.
+ void setDepthTestMode(bool enable);
// Group: Input
public:
@@ -262,10 +301,43 @@ public:
// been running.
scalar getTime();
- void setTimeDeltaMode(TimeDeltaMode mode, int numFrames=0);
- void setTimeDeltaMode(TimeDeltaMode mode, scalar fixedStep);
- double getTimeDelta();
+ // Function: setFrameTimeSmoothing
+ // Sets number of frames used to smooth the timeDelta so that minor jitters
+ // in frramerate do not cause severe stuttering.
+ //
+ // Parameters:
+ // numFrames - Number of frames to average, setting to <= 1 turns off
+ // smoothing of timeDelta. (Recommend something >= 50, <= 500)
+ void setFrameTimeSmoothing(int numFrames);
+
+ // Function: getElapsedTime
+ // Finds the amount of time passed between frames, or average of a number
+ // of frames if has been called.
+ //
+ // Returns:
+ // Time (or average time) between frames.
+ double getElapsedTime();
+
+ // Function: getFramerate
+ // Gets number of frames per second the application is currently processing
+ // subject to smoothing by .
+ //
+ // Returns:
+ // Calculated number of frames per second.
double getFramerate();
+
+ // Function: setFixedUpdateStep
+ // Sets a fixed timestep to be used in calls to the current
+ // update method. This allows stability in physics systems.
+ //
+ // Parameters:
+ // enable - if true, will enable fixed timestepping (if false will disable)
+ // fixedStep - the timestep to use for the fixed step, ignored if disabling
+ // maxStep - if somehow the update portion get signifigantly behind the
+ // render portion, to avoid executing a large number of steps
+ // (hanging the program) an optional maxStep can be specified.
+ // [default = 5*fixedStep]
+ void setFixedUpdateStep(bool enable, scalar fixedStep, scalar maxStep=0);
// Group: State Management
public:
@@ -323,92 +395,41 @@ private:
util::VersionInfo initPhysFS(const std::string& arg0);
util::VersionInfo initGLFW();
void initOpenGL();
-
-// Task Classes
-private:
- // UpdateTask, does the updating work of Application, registered as a Task
- // so that user need not call something akin to Application::update() every
- // frame
- class UpdateTask : public Task
- {
-
- friend class Application;
-
- public:
- UpdateTask();
-
- void update();
-
- private:
- int mouseX_;
- int mouseY_;
- bool active_;
- bool timerPaused_;
- bool unpauseOnActive_;
- scalar lastPause_;
- scalar pausedTime_;
- scalar secPerFrame_;
- scalar lastUpdate_;
- std::valarray frameTimes_;
- };
-
- // VideoTask, does the updating work of OpenGL
- class VideoTask : public Task
- {
- public:
- VideoTask();
-
- void update();
- };
-
- // StateUpdate, calls State::update
- class StateUpdate : public Task
- {
-
- friend class Application;
-
- public:
- StateUpdate();
- void update();
-
- private:
- StatePtr state_;
- };
-
- // StateRender, calls State::render
- class StateRender : public Task
- {
-
- friend class Application;
-
- public:
- StateRender();
- void update();
-
- private:
- StatePtr state_;
- };
// Data members
private:
// version number for photon
util::VersionInfo photonVer_;
- // Application info
+ // display variables
uint displayWidth_;
uint displayHeight_;
uint viewportWidth_;
uint viewportHeight_;
+ GLbitfield clearFlags_;
- TimeDeltaMode timeDeltaMode_;
-
- // tasks
- shared_ptr updateTask_;
- shared_ptr stateUpdate_;
- shared_ptr stateRender_;
-
// input system variables
static std::vector pressedKeys_;
+ int mouseX_;
+ int mouseY_;
+
+ // timing variables
+ bool active_;
+ bool timerPaused_;
+ bool unpauseOnActive_;
+ scalar lastPause_;
+ scalar pausedTime_;
+ scalar elapsedTime_;
+ scalar lastUpdate_;
+ scalar fixedTimeStep_;
+ scalar maxTimeStep_;
+ scalar timeAccumulator_;
+ std::valarray frameTimes_;
+
+ // other
+ bool quit_;
+ Kernel updateKernel_;
+ Kernel renderKernel_;
// state system
static std::stack stateStack_;
@@ -427,16 +448,9 @@ void Application::setState()
// clear stack
while(!stateStack_.empty())
{
- // pop then resume
- stateStack_.pop();
- if(!stateStack_.empty())
- {
- stateStack_.top()->onResume();
- }
+ popState();
}
stateStack_.push(newState); // make newState the only state on stack
-
- stateRender_->state_ = stateUpdate_->state_ = newState;
}
template
@@ -450,8 +464,6 @@ void Application::pushState()
stateStack_.top()->onPause();
}
stateStack_.push(newState); // push newState on top of stack
-
- stateRender_->state_ = stateUpdate_->state_ = newState;
}
diff --git a/include/Kernel.hpp b/include/Kernel.hpp
index a64a29e..9139067 100644
--- a/include/Kernel.hpp
+++ b/include/Kernel.hpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Kernel.hpp,v 1.3 2005/08/07 07:12:46 cozman Exp $
+// $Id: Kernel.hpp,v 1.4 2005/08/16 06:32:39 cozman Exp $
#ifndef PHOTON_KERNEL_HPP
#define PHOTON_KERNEL_HPP
@@ -13,7 +13,6 @@
#include
#include
-#include "util/Singleton.hpp"
#include "Task.hpp"
namespace photon
@@ -25,9 +24,8 @@ namespace photon
//
// To use Kernel:
// - (1) Add any tasks (should be derived from )
-// - (2) call
-// - (3) in order to avoid running forever, all tasks should eventually die
-class Kernel : public util::Singleton
+// - (2) Call step() every frame when task should update.
+class Kernel
{
// Group: (Con/De)structors
@@ -47,14 +45,11 @@ public:
//
// Each 'step' all tasks are run in order of their priorities, if two
// tasks have the same priority, they are run in the order they were added.
- void step();
-
- // Function: run
- // Runs tasks until all tasks are asleep or dead.
//
- // See Also:
- //
- void run();
+ // Parameters:
+ // timeDelta - The time elapsed between frames, possibly fixed via
+ // .
+ void step(scalar timeDelta);
// Group: Task Management
public:
diff --git a/include/State.hpp b/include/State.hpp
index 31039ef..77f97ef 100644
--- a/include/State.hpp
+++ b/include/State.hpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: State.hpp,v 1.3 2005/08/12 06:26:00 cozman Exp $
+// $Id: State.hpp,v 1.4 2005/08/16 06:32:39 cozman Exp $
#ifndef PHOTON_STATE_HPP
#define PHOTON_STATE_HPP
@@ -52,7 +52,7 @@ public:
// All of a state's logic should go in update, it is called every frame
// before the rendering process begins. Nothing should be drawn to the
// screen within update because it will be cleared before it is shown.
- virtual void update() { };
+ virtual void update(scalar timeDelta) { };
// Function: render
// The only required member of State, anything that should be drawn to the
diff --git a/include/Task.hpp b/include/Task.hpp
index c6e1d65..0bb6faa 100644
--- a/include/Task.hpp
+++ b/include/Task.hpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Task.hpp,v 1.4 2005/08/07 07:12:46 cozman Exp $
+// $Id: Task.hpp,v 1.5 2005/08/16 06:32:39 cozman Exp $
#ifndef PHOTON_TASK_HPP
#define PHOTON_TASK_HPP
@@ -16,29 +16,25 @@
namespace photon
{
+
+// Title: Task
-// Type: TaskPriority
+// Enum: PriorityLevel
+// Enumeration defining priority of a Task.
+//
+// Values:
+// PRI_LOWEST - Lowest priority available.
+// PRI_LOW - Lower-than-usual priority.
+// PRI_NORMAL - Normal priority, suitable for most tasks.
+// PRI_HIGH - Lower-than-usual priority.
+// PRI_HIGHEST - Highest priority available.
enum PriorityLevel
{
- PRI_APP_UPDATE, // special priority for Application updating (first)
-
- // user-level priorities all lie in logic area
PRI_HIGHEST,
PRI_HIGH,
PRI_NORMAL,
PRI_LOW,
- PRI_LOWEST,
-
- PRI_VIDEO_UPDATE, // special priority for clearing screen/video buffers
- PRI_RENDER // special priority for rendering (right after clear)
-
- // It is also important that no priorities fall between render/app update
- // so that flow of applications is:
- // 1) Update & SwapBuffers
- // 2) Logic & User Level tasks
- // 3) Clear screen/buffers
- // 4) Render
- // 5) SwapBuffers again (see step 1)
+ PRI_LOWEST
};
// Class: Task
@@ -68,7 +64,11 @@ public:
// Function: update
// Pure virtual, every child task must overload it's own update(), when a
// task is active this is called every 'frame.'
- virtual void update()=0;
+ //
+ // Parameters:
+ // timeDelta - The time elapsed between frames, possibly fixed via
+ // .
+ virtual void update(scalar timeDelta)=0;
// Function: onStart
// Virtual function, overload to define behavior when the task is started.
diff --git a/include/entrypoint.hpp b/include/entrypoint.hpp
index 6dca1ef..d2e4afb 100644
--- a/include/entrypoint.hpp
+++ b/include/entrypoint.hpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: entrypoint.hpp,v 1.9 2005/08/10 21:22:33 cozman Exp $
+// $Id: entrypoint.hpp,v 1.10 2005/08/16 06:32:39 cozman Exp $
#ifndef PHOTON_ENTRYPOINT_HPP
@@ -29,16 +29,19 @@
//
// int PhotonMain(const StrVec& args)
// {
+// // get Application singleton
+// Application& app(Application::getInstance);
+//
// // create window
-// Application::getInstance().createDisplay(800,600,32,0,0,false);
+// app.createDisplay(800,600,32,0,0,false);
//
// // set current state
-// Application::getInstance().setState();
+// app.setState();
//
-// // can also add any tasks here
+// // can also add any tasks before running
//
// // run until finished
-// Kernel::getInstance().run();
+// app.run();
//
// return 0;
// }
diff --git a/ndoc/Menu.txt b/ndoc/Menu.txt
index 48dcdb7..d26ba85 100644
--- a/ndoc/Menu.txt
+++ b/ndoc/Menu.txt
@@ -32,7 +32,6 @@ Group: photon:: {
File: Application (Application.hpp)
File: Basic Types (types.hpp)
File: Exception/Error Types (exceptions.hpp)
- File: InputListener (InputListener.hpp)
File: Kernel (Kernel.hpp)
File: Log (Log.hpp)
File: Logging Utilities (LogSink.hpp)
@@ -40,7 +39,7 @@ Group: photon:: {
File: ResourceManaged (ResourceManaged.hpp)
File: ResourceManager (ResourceManager.hpp)
File: State (State.hpp)
- File: Task.hpp (Task.hpp)
+ File: Task (Task.hpp)
Group: Audio:: {
@@ -86,6 +85,5 @@ Group: Index {
Function Index: Functions
Type Index: Types
Variable Index: Variables
- File Index: Files
} # Group: Index
diff --git a/photon.mm b/photon.mm
index a2f407b..427e35c 100644
--- a/photon.mm
+++ b/photon.mm
@@ -5,65 +5,141 @@
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
-
+
@@ -80,8 +156,9 @@
+
-
+
@@ -99,14 +176,19 @@
-
+
+
+
+
+
+
+
+
-
-
-
+
diff --git a/src/Application.cpp b/src/Application.cpp
index a34b7d4..102ceae 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Application.cpp,v 1.24 2005/08/14 07:40:13 cozman Exp $
+// $Id: Application.cpp,v 1.25 2005/08/16 06:32:39 cozman Exp $
#include "Application.hpp"
@@ -16,10 +16,7 @@
#include
#include "exceptions.hpp"
-#include "Log.hpp"
-#include "Kernel.hpp"
#include "Application.hpp"
-#include "audio/AudioCore.hpp"
#include "util/filesys/filesys.hpp"
#include
@@ -35,38 +32,134 @@ std::stack Application::stateStack_;
Application::Application(const std::string& arg0) :
photonVer_(0,0,1), // this is the current version
- displayWidth_(0), displayHeight_(0),
- viewportWidth_(0), viewportHeight_(0),
- timeDeltaMode_(TDM_ACTUAL),
- updateTask_(new UpdateTask()),
- stateUpdate_(new StateUpdate()),
- stateRender_(new StateRender())
+ displayWidth_(0), displayHeight_(0), viewportWidth_(0), viewportHeight_(0),
+ clearFlags_(GL_COLOR_BUFFER_BIT),
+ mouseX_(0), mouseY_(0),
+ active_(false), timerPaused_(false), unpauseOnActive_(false),
+ lastPause_(0), pausedTime_(0), elapsedTime_(0), lastUpdate_(0),
+ fixedTimeStep_(0), maxTimeStep_(0), timeAccumulator_(0), frameTimes_(0),
+ quit_(true)
{
util::VersionInfo physfsReq(1,0,0); // requires PhysFS 1.0.0
util::ensureVersion("PhysFS", initPhysFS(arg0), physfsReq);
util::VersionInfo glfwReq(2,4,2); // requires GLFW 2.4.2
util::ensureVersion("GLFW", initGLFW(), glfwReq);
-
- new Kernel; // create Kernel before it is used
-
- Kernel::getInstance().addTask(updateTask_); // add updater task
- Kernel::getInstance().addTask(stateUpdate_); // add state updater task
- Kernel::getInstance().addTask(stateRender_); // add state renderer task
}
Application::~Application()
{
- if(displayWidth_ && displayHeight_)
- {
- glfwCloseWindow(); //close GLFW window
- }
-
glfwTerminate(); // shutdown GLFW
PHYSFS_deinit(); // shutdown PhysFS
+}
+
+void Application::run()
+{
+ while(!quit_)
+ {
+ update();
+ }
+}
+
+void Application::update()
+{
+ // update glfw state
+ glfwGetMousePos(&mouseX_, &mouseY_);
+
+ // quit on window closing or Alt-F4/Alt-X
+ if(!glfwGetWindowParam(GLFW_OPENED) ||
+ ( (glfwGetKey(GLFW_KEY_LALT) || glfwGetKey(GLFW_KEY_RALT)) &&
+ (glfwGetKey(GLFW_KEY_F4) || glfwGetKey('X')) ) )
+ {
+ quit();
+ }
+
+ // hold active-state
+ active_ = (glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE);
- Kernel::destroy(); // destroy Kernel on way out
+ //automatically pause/unpause app timer on focus
+ scalar curTime( getTime() );
+ if(!active_ && !timerPaused_)
+ {
+ timerPaused_ = true;
+ lastPause_ = curTime;
+ unpauseOnActive_ = true;
+ }
+ else if(active_ && unpauseOnActive_)
+ {
+ timerPaused_ = false;
+ //pausedTime_ += curTime - lastPause_;
+ unpauseOnActive_ = false;
+ }
+
+ // keep track of time between frames
+ static uint frameIndex(0);
+
+ if(++frameIndex >= frameTimes_.size())
+ {
+ frameIndex = 0;
+ }
+ elapsedTime_ = frameTimes_[frameIndex] = curTime-lastUpdate_;
+
+ lastUpdate_ = curTime;
+
+ if(!stateStack_.empty() && !quit_)
+ {
+ if(fixedTimeStep_ > 0)
+ {
+ if(elapsedTime_ > maxTimeStep_)
+ {
+ elapsedTime_ = maxTimeStep_;
+ }
+
+ timeAccumulator_ += elapsedTime_;
+
+ while(timeAccumulator_ >= fixedTimeStep_)
+ {
+ stateStack_.top()->update(fixedTimeStep_);
+ updateKernel_.step(fixedTimeStep_);
+ timeAccumulator_ -= fixedTimeStep_;
+ }
+ }
+ else
+ {
+ stateStack_.top()->update(elapsedTime_);
+ updateKernel_.step(elapsedTime_);
+ }
+ }
+
+ // render step
+ if(!stateStack_.empty() && !quit_)
+ {
+ // clear everything before rendering
+ glClear(clearFlags_);
+ stateStack_.top()->render();
+ renderKernel_.step(fixedTimeStep_);
+ glfwSwapBuffers(); // swap buffers after rendering
+ }
+}
+
+void Application::quit()
+{
+ glfwCloseWindow(); //close GLFW window
+
+ quit_ = true;
+}
+
+Kernel& Application::getUpdateKernel()
+{
+ return updateKernel_;
+}
+
+Kernel& Application::getRenderKernel()
+{
+ return renderKernel_;
+}
+
+bool Application::isActive()
+{
+ return active_;
}
// Window //////////////////////////////////////////////////////////////////////
@@ -93,15 +186,16 @@ void Application::createDisplay(uint width, uint height,
initOpenGL();
setOrthoView();
+ setDepthTestMode(false);
- Kernel::getInstance().addTask(TaskPtr(new VideoTask()));
-
// register the callbacks (after a window is open)
glfwSetKeyCallback(Application::keyCallback);
//glfwSetCharCallback(Application::charCallback);
glfwSetMouseButtonCallback(Application::mouseButtonCallback);
glfwSetMousePosCallback(Application::mouseMoveCallback);
glfwSetMouseWheelCallback(Application::mouseWheelCallback);
+
+ quit_ = false;
}
void Application::createDisplay(uint width, uint height, uint bpp,
@@ -150,11 +244,6 @@ uint Application::getDisplayHeight()
return displayHeight_;
}
-bool Application::isActive()
-{
- return updateTask_->active_;
-}
-
// Ortho ///////////////////////////////////////////////////////////////////////
void Application::setOrthoView(int x, int y, int viewWidth, int viewHeight,
@@ -217,9 +306,12 @@ void Application::setOrthoProjection(scalar width, scalar height)
//back to modelview
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
+
+ setDepthTestMode(true);
}
-void Application::setPerspectiveProjection(scalar fovy, scalar zNear, scalar zFar)
+void Application::setPerspectiveProjection(scalar fovy, scalar zNear,
+ scalar zFar)
{
GLdouble ratio = static_cast(viewportWidth_) /
static_cast(viewportHeight_);
@@ -234,6 +326,21 @@ void Application::setPerspectiveProjection(scalar fovy, scalar zNear, scalar zFa
glLoadIdentity();
}
+void Application::setDepthTestMode(bool enable)
+{
+ if(enable)
+ {
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+ clearFlags_ |= GL_DEPTH_BUFFER_BIT;
+ }
+ else
+ {
+ glDisable(GL_DEPTH_TEST);
+ clearFlags_ &= ~GL_DEPTH_BUFFER_BIT;
+ }
+}
+
// Input ///////////////////////////////////////////////////////////////////////
bool Application::keyPressed(KeyCode key)
{
@@ -252,12 +359,12 @@ bool Application::mouseButtonPressed(MouseButton button)
int Application::getMouseX()
{
- return updateTask_->mouseX_;
+ return mouseX_;
}
int Application::getMouseY()
{
- return updateTask_->mouseY_;
+ return mouseY_;
}
int Application::getMouseWheelPos()
@@ -269,59 +376,53 @@ int Application::getMouseWheelPos()
scalar Application::getTime()
{
- return glfwGetTime() - updateTask_->pausedTime_;
+ return glfwGetTime() - pausedTime_;
}
-void Application::setTimeDeltaMode(TimeDeltaMode mode, int numFrames)
+void Application::setFrameTimeSmoothing(int numFrames)
{
- // if the mode is fixed should have speficied a scalar fixedStep
- if(mode == TDM_FIXED)
- {
- throw PreconditionException("setTimeDeltaMode called without fixedStep"
- "but with TDM_FIXED mode.");
- }
- // if the mode is average should have at least two frames to average
- if(mode == TDM_AVERAGE && numFrames <= 1)
- {
- throw PreconditionException("setTimeDeltaMode called with TDM_AVERAGE"
- "but numFrames <= 1");
- }
+ if(numFrames <= 1)
+ numFrames = 0;
- timeDeltaMode_ = mode;
- updateTask_->frameTimes_.resize(numFrames);
+ frameTimes_.resize(numFrames);
}
-void Application::setTimeDeltaMode(TimeDeltaMode mode, scalar fixedStep)
+double Application::getElapsedTime()
{
- // if the mode is not fixed, should have specified a non-scalar numFrames
- if(mode != TDM_FIXED)
+ if(frameTimes_.size() == 0)
{
- throw PreconditionException("setTimeDeltaMode called with fixedStep but"
- "mode not TDM_FIXED.");
+ return elapsedTime_;
}
-
- timeDeltaMode_ = mode;
- //fixedStep_ = fixedStep;
-}
-
-double Application::getTimeDelta()
-{
- switch(timeDeltaMode_)
+ else
{
- case TDM_ACTUAL:
- return updateTask_->secPerFrame_;
- case TDM_AVERAGE:
- return updateTask_->frameTimes_.sum()/updateTask_->frameTimes_.size();
- case TDM_FIXED:
- return 0.01;//fixedStep_;
- default:
- return 0;
+ return frameTimes_.sum()/frameTimes_.size();
}
}
double Application::getFramerate()
{
- return 1/getTimeDelta();
+ return 1/getElapsedTime();
+}
+
+void Application::setFixedUpdateStep(bool enable, scalar fixedStep,
+ scalar maxStep)
+{
+ if(!enable)
+ {
+ fixedTimeStep_ = -1; // set to < 0, disabling fixed timestepping
+ }
+ else
+ {
+ fixedTimeStep_ = fixedStep;
+ if(maxStep <= 0)
+ {
+ maxTimeStep_ = 5*fixedTimeStep_;
+ }
+ else
+ {
+ maxTimeStep_ = maxStep;
+ }
+ }
}
// States //////////////////////////////////////////////////////////////////////
@@ -331,7 +432,7 @@ void Application::popState()
// check for underflow
if(stateStack_.empty())
{
- throw PreconditionException("Attempt to popState without at least 2 "
+ throw PreconditionException("Attempt to popState without at least one "
"states on stack.");
}
@@ -342,7 +443,6 @@ void Application::popState()
if(!stateStack_.empty())
{
stateStack_.top()->onResume();
- stateRender_->state_ = stateUpdate_->state_ = stateStack_.top();
}
}
@@ -478,10 +578,6 @@ void Application::initOpenGL()
// Set smooth shading.
glShadeModel(GL_SMOOTH);
- // Setup depth checking.
- //glDepthFunc(GL_LEQUAL);
- //glEnable(GL_DEPTH_TEST);
-
//setup hints
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
@@ -494,95 +590,9 @@ void Application::initOpenGL()
glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
-}
-
-// Tasks ///////////////////////////////////////////////////////////////////////
-
-Application::UpdateTask::UpdateTask() :
- Task("Application::UpdateTask", PRI_APP_UPDATE),
- mouseX_(0), mouseY_(0),
- active_(false), timerPaused_(false),
- unpauseOnActive_(false), lastPause_(0), pausedTime_(0),
- secPerFrame_(0), lastUpdate_(0), frameTimes_(0)
-{
-}
-
-void Application::UpdateTask::update()
-{
- static uint frameIndex(0);
- scalar curTime( glfwGetTime() - pausedTime_ );
-
- // keep track of time between frames
- if(++frameIndex >= frameTimes_.size())
- {
- frameIndex = 0;
- }
- secPerFrame_ = frameTimes_[frameIndex] = curTime-lastUpdate_;
-
- lastUpdate_ = curTime;
-
- glfwSwapBuffers();
-
- // update the display here instead of Application (since it belongs to glfw)
-
- glfwGetMousePos(&mouseX_, &mouseY_);
-
- // quit on window closing or Alt-F4/Alt-X
- if(!glfwGetWindowParam(GLFW_OPENED) ||
- ( (glfwGetKey(GLFW_KEY_LALT) || glfwGetKey(GLFW_KEY_RALT)) &&
- (glfwGetKey(GLFW_KEY_F4) || glfwGetKey('X')) ) )
- {
- Kernel::getInstance().killAllTasks();
- }
-
- // hold active-state
- active_ = (glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE);
-
- // automatically pause/unpause app timer on focus
- if(!active_ && !timerPaused_)
- {
- timerPaused_ = true;
- lastPause_ = curTime;
- unpauseOnActive_ = true;
- }
- else if(active_ && unpauseOnActive_)
- {
- timerPaused_ = false;
- pausedTime_ += curTime - lastPause_;
- unpauseOnActive_ = false;
- }
-}
-
-Application::VideoTask::VideoTask() :
- Task("Application::VideoTask", PRI_VIDEO_UPDATE)
-{
-}
-
-void Application::VideoTask::update()
-{
- // TODO: clear depth/stencil if requested
- glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-}
-
-Application::StateUpdate::StateUpdate() :
- Task("Application::StateUpdate", PRI_NORMAL)
-{
-}
-
-void Application::StateUpdate::update()
-{
- state_->update();
-}
-
-Application::StateRender::StateRender() :
- Task("Application::StateRender", PRI_RENDER)
-{
-}
-
-void Application::StateRender::update()
-{
- state_->render();
+ // depth testing enabled by default
+ setDepthTestMode(false);
}
}
diff --git a/src/Kernel.cpp b/src/Kernel.cpp
index e8a00b7..16e540b 100644
--- a/src/Kernel.cpp
+++ b/src/Kernel.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Kernel.cpp,v 1.4 2005/08/07 07:12:47 cozman Exp $
+// $Id: Kernel.cpp,v 1.5 2005/08/16 06:32:39 cozman Exp $
#include "Kernel.hpp"
@@ -25,7 +25,7 @@ Kernel::~Kernel()
killAllTasks();
}
-void Kernel::step()
+void Kernel::step(scalar timeDelta)
{
std::list::iterator it;
@@ -37,7 +37,7 @@ void Kernel::step()
// only update alive, non-paused tasks
if(task->isAlive() && !task->isPaused())
{
- task->update();
+ task->update(timeDelta);
}
}
@@ -58,15 +58,6 @@ void Kernel::step()
}
}
}
-
-void Kernel::run()
-{
- // loop on activeTasks
- while(!tasks_.empty())
- {
- step();
- }
-}
void Kernel::addTask(TaskPtr task)
{
diff --git a/test/Audio_test.cpp b/test/Audio_test.cpp
index bf0f735..d0e1b3d 100644
--- a/test/Audio_test.cpp
+++ b/test/Audio_test.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Audio_test.cpp,v 1.14 2005/08/12 06:26:00 cozman Exp $
+// $Id: Audio_test.cpp,v 1.15 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
@@ -184,19 +184,16 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
- // initialize audio core
- Application::getInstance().initAudioCore();
+ Application& app(Application::getInstance());
+
+ app.createDisplay(800,600,32,0,0,false); // create window
+ app.initAudioCore(); // initialize audio core
// be sure to add FPSDisplayTask
- Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
+ //Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
- // register state and make active
- Application::getInstance().setState();
-
- // run until finished
- Kernel::getInstance().run();
+ app.setState(); // register state and make active
+ app.run(); // run until finished
return 0;
}
diff --git a/test/Font_test.cpp b/test/Font_test.cpp
index 6583afc..13559ef 100644
--- a/test/Font_test.cpp
+++ b/test/Font_test.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Font_test.cpp,v 1.11 2005/08/10 21:22:33 cozman Exp $
+// $Id: Font_test.cpp,v 1.12 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
@@ -49,17 +49,15 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
+ Application& app(Application::getInstance());
+
+ app.createDisplay(800,600,32,0,0,false); // create window
// be sure to add FPSDisplayTask
- Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
+ //Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
- // set current state
- Application::getInstance().setState();
-
- // run until finished
- Kernel::getInstance().run();
+ app.setState(); // register state and make active
+ app.run(); // run until finished
return 0;
}
diff --git a/test/Image_test.cpp b/test/Image_test.cpp
index ac01091..4f2c0f8 100644
--- a/test/Image_test.cpp
+++ b/test/Image_test.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Image_test.cpp,v 1.11 2005/08/10 21:22:33 cozman Exp $
+// $Id: Image_test.cpp,v 1.12 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
@@ -60,17 +60,15 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
+ Application& app(Application::getInstance());
+
+ app.createDisplay(800,600,32,0,0,false); // create window
// be sure to add FPSDisplayTask
- Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
+ //Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
- // set current state
- Application::getInstance().setState();
-
- // run until finished
- Kernel::getInstance().run();
+ app.setState(); // register state and make active
+ app.run(); // run until finished
return 0;
}
diff --git a/test/Input_test.cpp b/test/Input_test.cpp
index 58fe284..b498d62 100644
--- a/test/Input_test.cpp
+++ b/test/Input_test.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Input_test.cpp,v 1.10 2005/08/12 06:26:00 cozman Exp $
+// $Id: Input_test.cpp,v 1.11 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
@@ -119,17 +119,15 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
+ Application& app(Application::getInstance());
+
+ app.createDisplay(800,600,32,0,0,false); // create window
// be sure to add FPSDisplayTask
- Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
+ //Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
- // set current state
- Application::getInstance().setState();
-
- // run until finished
- Kernel::getInstance().run();
+ app.setState(); // register state and make active
+ app.run(); // run until finished
return 0;
}
diff --git a/test/Pen_test.cpp b/test/Pen_test.cpp
index d15b8ce..5f02619 100644
--- a/test/Pen_test.cpp
+++ b/test/Pen_test.cpp
@@ -5,7 +5,7 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: Pen_test.cpp,v 1.8 2005/08/10 21:22:33 cozman Exp $
+// $Id: Pen_test.cpp,v 1.9 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
@@ -73,17 +73,15 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
+ Application& app(Application::getInstance());
+
+ app.createDisplay(800,600,32,0,0,false); // create window
// be sure to add FPSDisplayTask
- Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
+ //Kernel::getInstance().addTask(TaskPtr(new FPSDisplayTask()));
- // set current state
- Application::getInstance().setState();
-
- // run until finished
- Kernel::getInstance().run();
+ app.setState(); // register state and make active
+ app.run(); // run until finished
return 0;
}
diff --git a/test/State_test.cpp b/test/State_test.cpp
index 18af780..f7edbe9 100644
--- a/test/State_test.cpp
+++ b/test/State_test.cpp
@@ -5,17 +5,15 @@
// James Turk (jpt2433@rit.edu)
//
// Version:
-// $Id: State_test.cpp,v 1.3 2005/08/14 07:40:13 cozman Exp $
+// $Id: State_test.cpp,v 1.4 2005/08/16 06:32:39 cozman Exp $
#include "photon.hpp"
using namespace photon;
-#include "FPSDisplayTask.hpp" // used to display FPS in title bar
-
-#include
class Demo2D : public State
{
+// basic struct used to represent a bouncing robot head
struct MovingRect
{
math::Rect pos;
@@ -27,22 +25,26 @@ public:
{
util::RandGen rand;
+ // load the robot image
video::Image::addResource("robo","data/robo.png");
-
roboImg.open("robo");
+ // create 5 robots
robots.resize(5);
+ // initialize all the robot MovingRects
for(std::vector::iterator robot( robots.begin() );
robot != robots.end();
++robot)
{
+ // randomly position robots
robot->pos.moveTo(math::Point2(
rand.genRand(0.,800-roboImg.getWidth()),
rand.genRand(0.,600-roboImg.getHeight())));
+ // set size to image size
robot->pos.resize(roboImg.getWidth(), roboImg.getHeight());
- // generates -400 or +400
+ // generates -400 or +400 initial velocity
robot->vel.x = rand.genRandSign()*400;
robot->vel.y = rand.genRandSign()*400;
}
@@ -50,6 +52,7 @@ public:
Application::getInstance().setOrthoView();
}
+ // leave the 2D test if the user presses escape
void onKeyPress(KeyCode key)
{
if(key == KEY_ESC)
@@ -58,17 +61,20 @@ public:
}
}
- void update()
+ void update(scalar timeDelta)
{
+ // loop through and update all active robots
for(std::vector::iterator robot( robots.begin() );
robot != robots.end();
++robot)
{
- math::Vector2 vel(robot->vel *
- Application::getInstance().getTimeDelta());
+ // calculate velocity
+ math::Vector2 vel(robot->vel * timeDelta);
+ // move
robot->pos.moveRel(vel.x, vel.y);
+ // bounce if robot tries to go off screen
if(robot->pos.getLeft() < 0 || robot->pos.getRight() > 800)
{
robot->vel.x *= -1;
@@ -94,6 +100,7 @@ public:
void render()
{
+ // loop through robots, drawing each
for(std::vector::iterator robot( robots.begin() );
robot != robots.end();
++robot)
@@ -117,11 +124,6 @@ public:
video::Image::addResource("robo","data/robo.png");
Application::getInstance().setPerspectiveView(45.0, 1.0, 100.0);
-
- glShadeModel(GL_SMOOTH); // smooth shading
- glClearDepth(1.0f); // set clear depth
- glEnable(GL_DEPTH_TEST); // enable depth testing
- glDepthFunc(GL_LEQUAL);
// create the list to display a box
glNewList(boxList, GL_COMPILE);
@@ -168,9 +170,11 @@ public:
~Demo3D()
{
+ // free lists on exit of 3D demo
glDeleteLists(boxList, 1);
}
+ // leave the 3D test if the user presses escape
void onKeyPress(KeyCode key)
{
if(key == KEY_ESC)
@@ -179,17 +183,17 @@ public:
}
}
- void update()
+ void update(scalar timeDelta)
{
- scalar dt = Application::getInstance().getTimeDelta();
-
- xRot += 30*dt;
- yRot += 40*dt;
- zRot += 50*dt;
+ // rotate the cube on all 3 axes
+ xRot += 30*timeDelta;
+ yRot += 40*timeDelta;
+ zRot += 50*timeDelta;
}
void render()
{
+ // draw the cube each frame
glLoadIdentity();
glTranslatef(0.0f,0.0f,-5.0f);
glRotated(xRot,1.0f,0.0f,0.0f);
@@ -208,6 +212,7 @@ private:
class Menu : public State
{
+// simple Menu::Item class used for a mouse-based menu
struct Item
{
std::string text;
@@ -218,31 +223,35 @@ public:
Menu() :
app(Application::getInstance())
{
+ // load the fonts
video::Font::addResource("menufont","FreeMono.ttf",64);
font.open("menufont");
font.setColor(video::Color(255, 128, 0));
+ // name the menuItems
menuItems[0].text = "2D Demo";
menuItems[1].text = "3D Demo";
menuItems[2].text = "Quit";
+ // generate the bounding rectangles for the menuItems
const photon::uint ySkip(font.getHeight() + 20);
scalar curY(100);
scalar width;
for(int i=0; i < 3; ++i)
{
+ // find width to center text
width = font.calcStringWidth(menuItems[i].text);
- menuItems[i].rect.moveRel((app.getDisplayWidth() - width) / 2, curY);
+ menuItems[i].rect.moveRel((app.getDisplayWidth()-width) / 2, curY);
menuItems[i].rect.resize(width, font.getHeight());
- curY += ySkip;
+ curY += ySkip; // move down (don't write text on top of itself)
}
app.setOrthoView();
- app.setTimeDeltaMode(TDM_AVERAGE, 250);
}
void onMouseButtonPress(MouseButton button)
{
+ // handle mouse clicks inside bounding rectangles
if(menuItems[0].rect.contains(math::Point2(app.getMouseX(),
app.getMouseY())))
{
@@ -256,7 +265,7 @@ public:
else if(menuItems[2].rect.contains(math::Point2(app.getMouseX(),
app.getMouseY())))
{
- Kernel::getInstance().killAllTasks();
+ app.quit();
}
}
@@ -265,8 +274,10 @@ public:
video::Color c( font.getColor() );
video::Pen p;
+ // draw the menu items
for(int i=0; i < 3; ++i)
{
+ // change color to white if mouse is within it's bounding rect
if(menuItems[i].rect.contains(math::Point2(app.getMouseX(),
app.getMouseY())))
{
@@ -284,8 +295,10 @@ public:
void onResume()
{
+ // when resumed, select a new random color
font.setColor(video::Color(rand.genRand(0,255), rand.genRand(0,255),
rand.genRand(0,255)));
+ // return to orthoView, 3D demo might have put us in perspective
app.setOrthoView();
}
@@ -299,17 +312,16 @@ private:
int PhotonMain(const StrVec& args)
{
- // create window
- Application::getInstance().createDisplay(800,600,32,0,0,false);
+ Application& app(Application::getInstance());
+ app.createDisplay(800,600,32,0,0,false); // create window
+ app.setFixedUpdateStep(true, .01);
+
// add archives to search path
util::filesys::addToSearchPath("data/fonts.zip");
- // set current state
- Application::getInstance().setState