diff --git a/panda/src/framework/Sources.pp b/panda/src/framework/Sources.pp index c43dae6cdd..4a4f502c6b 100644 --- a/panda/src/framework/Sources.pp +++ b/panda/src/framework/Sources.pp @@ -1,38 +1,20 @@ #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ dtoolutil:c dtoolbase:c dtool:m -#begin static_lib_target +#begin lib_target #define TARGET framework + #define BUILDING_DLL BUILDING_FRAMEWORK #define LOCAL_LIBS \ - putil collide loader sgmanip chan text chancfg cull \ + pgraph putil collide loader sgmanip chan text chancfg cull \ pnmimage pnmimagetypes event #define SOURCES \ - config_framework.cxx config_framework.h framework.cxx framework.h + config_framework.cxx config_framework.h \ + pandaFramework.cxx pandaFramework.I pandaFramework.h \ + windowFramework.cxx windowFramework.I windowFramework.h #define INSTALL_HEADERS \ - framework.h - -#end static_lib_target - - -#if $[HAVE_DX] - // USE_DX ensures DirectX dirs are on include&link lines - #define USE_DX yes - - #begin static_lib_target - #define TARGET framework_multimon - #define LOCAL_LIBS \ - putil collide loader sgmanip chan text chancfg cull \ - pnmimage pnmimagetypes event wdxdisplay - - #define SOURCES \ - config_framework.cxx config_framework.h framework_multimon.cxx framework.h - - #define INSTALL_HEADERS \ - framework.h - - #end static_lib_target -#endif - + pandaFramework.I pandaFramework.h \ + windowFramework.I windowFramework.h +#end lib_target diff --git a/panda/src/framework/config_framework.cxx b/panda/src/framework/config_framework.cxx index 9871bbb14e..e88f266fd0 100644 --- a/panda/src/framework/config_framework.cxx +++ b/panda/src/framework/config_framework.cxx @@ -26,7 +26,6 @@ NotifyCategoryDef(framework, ""); ConfigureFn(config_framework) { } -// This is the height above the ground your eye should maintain while -// driving using the "D" interface. -const double drive_height = config_framework.GetDouble("drive-height", 6.0); -const CollideMask drive_mask = config_framework.GetInt("drive-mask", ~0); +const int win_width = config_framework.GetInt("win-width", 640); +const int win_height = config_framework.GetInt("win-height", 480); +const bool fullscreen = config_framework.GetBool("fullscreen", false); diff --git a/panda/src/framework/config_framework.h b/panda/src/framework/config_framework.h index 5f7bbe5150..18f7366259 100644 --- a/panda/src/framework/config_framework.h +++ b/panda/src/framework/config_framework.h @@ -19,14 +19,14 @@ #ifndef CONFIG_FRAMEWORK_H #define CONFIG_FRAMEWORK_H -#include -#include -#include +#include "pandabase.h" +#include "notifyCategoryProxy.h" -NotifyCategoryDecl(framework, EXPCL_EMPTY, EXPCL_EMPTY); +NotifyCategoryDecl(framework, EXPCL_FRAMEWORK, EXPCL_FRAMEWORK); // Configure variables for framework package. -extern const double drive_height; -extern const CollideMask drive_mask; +extern const int win_width; +extern const int win_height; +extern const bool fullscreen; #endif diff --git a/panda/src/framework/pandaFramework.I b/panda/src/framework/pandaFramework.I new file mode 100644 index 0000000000..114d31873f --- /dev/null +++ b/panda/src/framework/pandaFramework.I @@ -0,0 +1,139 @@ +// Filename: pandaFramework.I +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_graphics_engine +// Access: Public +// Description: Returns the GraphicsEngine that is used to render all +// the windows in the framework. Normally there's no +// reason for user code to mess with this. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsEngine *PandaFramework:: +get_graphics_engine() { + return &_engine; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_data_root +// Access: Public +// Description: Returns the root of the data graph. This is the +// graph of nodes that is traversed to control the +// inputs from user devices like the mouse and keyboard. +//////////////////////////////////////////////////////////////////// +INLINE const qpNodePath &PandaFramework:: +get_data_root() const { + return _data_root; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_event_handler +// Access: Public +// Description: Returns the EventHandler object that serves events in +// the framework. This is primarily used to dispatch on +// keypresses and such. +//////////////////////////////////////////////////////////////////// +INLINE EventHandler &PandaFramework:: +get_event_handler() { + return _event_handler; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_window_title +// Access: Public +// Description: Specifies the title that is set for all subsequently +// created windows. +//////////////////////////////////////////////////////////////////// +INLINE void PandaFramework:: +set_window_title(const string &title) { + _window_title = title; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_num_windows +// Access: Public +// Description: Returns the number of windows that are currently +// open. +//////////////////////////////////////////////////////////////////// +INLINE int PandaFramework:: +get_num_windows() const { + return _windows.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_window +// Access: Public +// Description: Returns the nth window currently open. +//////////////////////////////////////////////////////////////////// +INLINE WindowFramework *PandaFramework:: +get_window(int n) const { + nassertr(n >= 0 && n < (int)_windows.size(), NULL); + return _windows[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_wireframe +// Access: Public +// Description: Returns the current state of the wireframe flag. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaFramework:: +get_wireframe() const { + return _wireframe_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_texture +// Access: Public +// Description: Returns the current state of the texture flag. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaFramework:: +get_texture() const { + return _texture_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_two_sided +// Access: Public +// Description: Returns the current state of the two_sided flag. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaFramework:: +get_two_sided() const { + return _two_sided_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_lighting +// Access: Public +// Description: Returns the current state of the lighting flag. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaFramework:: +get_lighting() const { + return _lighting_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_exit_flag +// Access: Public +// Description: Sets the flag that indicates it is time for the +// application to exit. The application will actually +// exit at the end of the current frame. +//////////////////////////////////////////////////////////////////// +INLINE void PandaFramework:: +set_exit_flag() { + _exit_flag = true; +} diff --git a/panda/src/framework/pandaFramework.cxx b/panda/src/framework/pandaFramework.cxx new file mode 100644 index 0000000000..2ebd2320bc --- /dev/null +++ b/panda/src/framework/pandaFramework.cxx @@ -0,0 +1,530 @@ +// Filename: pandaFramework.cxx +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pandaFramework.h" +#include "clockObject.h" +#include "pStatClient.h" +#include "eventQueue.h" +#include "qpdataGraphTraverser.h" +#include "interactiveGraphicsPipe.h" +#include "config_framework.h" + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PandaFramework:: +PandaFramework() : + _event_handler(EventQueue::get_global_event_queue()) +{ + _is_open = false; + _made_default_pipe = false; + _data_root = qpNodePath("data"); + _window_title = "Panda"; + _start_time = 0.0; + _frame_count = 0; + _wireframe_enabled = false; + _texture_enabled = true; + _two_sided_enabled = false; + _lighting_enabled = false; + _default_keys_enabled = false; + _exit_flag = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PandaFramework:: +~PandaFramework() { + if (_is_open) { + close_framework(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::open_framework +// Access: Public +// Description: Should be called once at the beginning of the +// application to initialize Panda (and the framework) +// for use. The command-line arguments should be passed +// in so Panda can remove any arguments that it +// recognizes as special control parameters. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +open_framework(int &argc, char **&argv) { + if (_is_open) { + return; + } + + _is_open = true; + + reset_frame_rate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::close_framework +// Access: Public +// Description: Should be called at the end of an application to +// close Panda. This is optional, as the destructor +// will do the same thing. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +close_framework() { + if (!_is_open) { + return; + } + + close_all_windows(); + // We should define this function on GraphicsEngine. + // _engine.remove_all_windows(); + _event_handler.remove_all_hooks(); + + _is_open = false; + _made_default_pipe = false; + _default_pipe.clear(); + + _start_time = 0.0; + _frame_count = 0; + _wireframe_enabled = false; + _two_sided_enabled = false; + _lighting_enabled = false; + _default_keys_enabled = false; + _exit_flag = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_default_pipe +// Access: Public +// Description: Returns the default pipe. This is the GraphicsPipe +// that all windows in the framework will be created on, +// unless otherwise specified in open_window(). It is +// usually the primary graphics interface on the local +// machine. +// +// If the default pipe has not yet been created, this +// creates it. +// +// The return value is the default pipe, or NULL if no +// default pipe could be created. +//////////////////////////////////////////////////////////////////// +GraphicsPipe *PandaFramework:: +get_default_pipe() { + nassertr(_is_open, NULL); + if (!_made_default_pipe) { + make_default_pipe(); + _made_default_pipe = true; + } + return _default_pipe; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_default_window_props +// Access: Public, Virtual +// Description: Fills in the indicated window properties structure +// according to the normal window properties for this +// application. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +get_default_window_props(GraphicsWindow::Properties &props) { + props._xorg = 0; + props._yorg = 0; + props._xsize = win_width; + props._ysize = win_height; + props._fullscreen = fullscreen; + props._title = _window_title; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::open_window +// Access: Public +// Description: Opens a new window, using the default parameters. +// Returns the new WindowFramework if successful, or +// NULL if not. +//////////////////////////////////////////////////////////////////// +WindowFramework *PandaFramework:: +open_window(GraphicsPipe *pipe) { + nassertr(_is_open, NULL); + + GraphicsWindow::Properties props; + get_default_window_props(props); + + return open_window(props, pipe); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::open_window +// Access: Public +// Description: Opens a new window using the indicated properties. +// (You may initialize the properties to their default +// values by calling get_default_window_props() first.) +// +// Returns the new WindowFramework if successful, or +// NULL if not. +//////////////////////////////////////////////////////////////////// +WindowFramework *PandaFramework:: +open_window(const GraphicsWindow::Properties &props, GraphicsPipe *pipe) { + if (pipe == (GraphicsPipe *)NULL) { + pipe = get_default_pipe(); + if (pipe == (GraphicsPipe *)NULL) { + // Can't get a pipe. + return NULL; + } + } + + nassertr(_is_open, NULL); + WindowFramework *wf = make_window_framework(); + _windows.push_back(wf); + + GraphicsWindow *win = wf->open_window(props, pipe); + if (win != (GraphicsWindow *)NULL) { + _engine.add_window(win); + } + + return wf; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::close_window +// Access: Public +// Description: Closes the nth window and removes it from the list. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +close_window(int n) { + nassertv(n >= 0 && n < (int)_windows.size()); + WindowFramework *wf = _windows[n]; + + GraphicsWindow *win = wf->get_graphics_window(); + if (win != (GraphicsWindow *)NULL) { + _engine.remove_window(win); + } + + wf->close_window(); + delete wf; + _windows.erase(_windows.begin() + n); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::close_all_windows +// Access: Public +// Description: Closes all currently open windows and empties the +// list of windows. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +close_all_windows() { + Windows::iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + WindowFramework *wf = (*wi); + + GraphicsWindow *win = wf->get_graphics_window(); + if (win != (GraphicsWindow *)NULL) { + _engine.remove_window(win); + } + + wf->close_window(); + delete wf; + } + + _windows.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::report_frame_rate +// Access: Public +// Description: Reports the currently measured average frame rate to +// the indicated ostream. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +report_frame_rate(ostream &out) const { + double now = ClockObject::get_global_clock()->get_frame_time(); + double delta = now - _start_time; + + int frame_count = ClockObject::get_global_clock()->get_frame_count(); + int num_frames = frame_count - _frame_count; + if (num_frames > 0) { + out << num_frames << " frames in " << delta << " seconds.\n"; + double fps = ((double)num_frames) / delta; + out << fps << " fps average (" << 1000.0 / fps << "ms)\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::reset_frame_rate +// Access: Public +// Description: Resets the frame rate computation. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +reset_frame_rate() { + _start_time = ClockObject::get_global_clock()->get_frame_time(); + _frame_count = ClockObject::get_global_clock()->get_frame_count(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_wireframe +// Access: Public +// Description: Sets the wireframe state on all windows. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +set_wireframe(bool enable) { + Windows::iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + WindowFramework *wf = (*wi); + wf->set_wireframe(enable); + } + + _wireframe_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_texture +// Access: Public +// Description: Sets the texture state on all windows. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +set_texture(bool enable) { + Windows::iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + WindowFramework *wf = (*wi); + wf->set_texture(enable); + } + + _texture_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_two_sided +// Access: Public +// Description: Sets the two_sided state on all windows. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +set_two_sided(bool enable) { + Windows::iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + WindowFramework *wf = (*wi); + wf->set_two_sided(enable); + } + + _two_sided_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::set_lighting +// Access: Public +// Description: Sets the lighting state on all windows. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +set_lighting(bool enable) { + Windows::iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + WindowFramework *wf = (*wi); + wf->set_lighting(enable); + } + + _lighting_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::enable_default_keys +// Access: Public +// Description: Sets callbacks on the event handler to handle all of +// the normal viewer keys, like t to toggle texture, ESC +// or q to quit, etc. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +enable_default_keys() { + if (!_default_keys_enabled) { + do_enable_default_keys(); + _default_keys_enabled = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::do_frame +// Access: Public, Virtual +// Description: Renders one frame and performs all associated +// processing. Returns true if we should continue +// rendering, false if we should exit. This is normally +// called only from main_loop(). +//////////////////////////////////////////////////////////////////// +bool PandaFramework:: +do_frame() { + nassertr(_is_open, false); + qpDataGraphTraverser dg_trav; + dg_trav.traverse(_data_root.node()); + _event_handler.process_events(); + _engine.render_frame(); + + return !_exit_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::main_loop +// Access: Public +// Description: Called to yield control to the panda framework. This +// function does not return until set_exit_flag() has +// been called. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +main_loop() { + while (do_frame()) { + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::make_window_framework +// Access: Protected, Virtual +// Description: Creates a new WindowFramework object. This is +// provided as a hook so derived PandaFramework classes +// can create custom WindowFramework objects. +//////////////////////////////////////////////////////////////////// +WindowFramework *PandaFramework:: +make_window_framework() { + return new WindowFramework(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::make_default_pipe +// Access: Protected, Virtual +// Description: Creates the default GraphicsPipe that will contain +// all windows that are not opened on a specific pipe. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +make_default_pipe() { + // We use the GraphicsPipe factory to make us a renderable pipe + // without knowing exactly what kind of pipes we have available. We + // don't care, so long as we can render to it interactively. + + // This depends on the shared library or libraries (DLL's to you + // Windows folks) that have been loaded in at runtime from the + // load-display Configrc variable. + GraphicsPipe::resolve_modules(); + + nout << "Known pipe types:" << endl; + GraphicsPipe::get_factory().write_types(nout, 2); + + _default_pipe = GraphicsPipe::get_factory(). + make_instance(InteractiveGraphicsPipe::get_class_type()); + + if (_default_pipe == (GraphicsPipe*)NULL) { + nout << "No interactive pipe is available! Check your Configrc!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::do_enable_default_keys +// Access: Protected, Virtual +// Description: The implementation of enable_default_keys(). +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +do_enable_default_keys() { + _event_handler.add_hook("escape", event_esc, this); + _event_handler.add_hook("q", event_esc, this); + _event_handler.add_hook("f", event_f, this); + _event_handler.add_hook("w", event_w, this); + _event_handler.add_hook("t", event_t, this); + _event_handler.add_hook("b", event_b, this); + _event_handler.add_hook("l", event_l, this); + _event_handler.add_hook("shift-s", event_S, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_esc +// Access: Protected, Static +// Description: Default handler for ESC or q key: exit the +// application. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_esc(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->_exit_flag = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_f +// Access: Protected, Static +// Description: Default handler for f key: report and reset frame +// rate. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_f(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->report_frame_rate(nout); + self->reset_frame_rate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_w +// Access: Protected, Static +// Description: Default handler for w key: toggle wireframe. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_w(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->set_wireframe(!self->get_wireframe()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_t +// Access: Protected, Static +// Description: Default handler for t key: toggle texture. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_t(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->set_texture(!self->get_texture()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_b +// Access: Protected, Static +// Description: Default handler for b key: toggle backface (two-sided +// rendering). +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_b(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->set_two_sided(!self->get_two_sided()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_l +// Access: Protected, Static +// Description: Default handler for l key: toggle lighting. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_l(CPT_Event, void *data) { + PandaFramework *self = (PandaFramework *)data; + self->set_lighting(!self->get_lighting()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::event_S +// Access: Protected, Static +// Description: Default handler for shift-S key: activate stats. +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +event_S(CPT_Event, void *data) { +#ifdef DO_PSTATS + nout << "Connecting to stats host" << endl; + PStatClient::connect(); +#else + nout << "Stats host not supported." << endl; +#endif +} diff --git a/panda/src/framework/pandaFramework.h b/panda/src/framework/pandaFramework.h new file mode 100644 index 0000000000..45398e3794 --- /dev/null +++ b/panda/src/framework/pandaFramework.h @@ -0,0 +1,131 @@ +// Filename: pandaFramework.h +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDAFRAMEWORK_H +#define PANDAFRAMEWORK_H + +#include "pandabase.h" + +#include "windowFramework.h" + +#include "qpnodePath.h" +#include "eventHandler.h" +#include "graphicsPipe.h" +#include "graphicsEngine.h" +#include "graphicsWindow.h" +#include "pointerTo.h" + +#include "pvector.h" + +//////////////////////////////////////////////////////////////////// +// Class : PandaFramework +// Description : This class serves to provide a high-level framework +// for basic applications that use Panda in simple ways +// (like opening a window to view models, etc.). +//////////////////////////////////////////////////////////////////// +class EXPCL_FRAMEWORK PandaFramework { +public: + PandaFramework(); + virtual ~PandaFramework(); + + void open_framework(int &argc, char **&argv); + void close_framework(); + + GraphicsPipe *get_default_pipe(); + INLINE GraphicsEngine *get_graphics_engine(); + INLINE const qpNodePath &get_data_root() const; + INLINE EventHandler &get_event_handler(); + + INLINE void set_window_title(const string &title); + virtual void get_default_window_props(GraphicsWindow::Properties &props); + + WindowFramework *open_window(GraphicsPipe *pipe = NULL); + WindowFramework *open_window(const GraphicsWindow::Properties &props, + GraphicsPipe *pipe = NULL); + + INLINE int get_num_windows() const; + INLINE WindowFramework *get_window(int n) const; + void close_window(int n); + void close_all_windows(); + + void report_frame_rate(ostream &out) const; + void reset_frame_rate(); + + void set_wireframe(bool enable); + void set_texture(bool enable); + void set_two_sided(bool enable); + void set_lighting(bool enable); + + INLINE bool get_wireframe() const; + INLINE bool get_texture() const; + INLINE bool get_two_sided() const; + INLINE bool get_lighting() const; + + void enable_default_keys(); + + virtual bool do_frame(); + void main_loop(); + + INLINE void set_exit_flag(); + +protected: + virtual WindowFramework *make_window_framework(); + virtual void make_default_pipe(); + virtual void do_enable_default_keys(); + + static void event_esc(CPT_Event, void *data); + static void event_f(CPT_Event, void *data); + static void event_w(CPT_Event, void *data); + static void event_t(CPT_Event, void *data); + static void event_b(CPT_Event, void *data); + static void event_l(CPT_Event, void *data); + static void event_S(CPT_Event, void *data); + + +private: + bool _is_open; + bool _made_default_pipe; + + string _window_title; + + PT(GraphicsPipe) _default_pipe; + GraphicsEngine _engine; + + qpNodePath _data_root; + EventHandler _event_handler; + + typedef pvector Windows; + Windows _windows; + + // For counting frame rate. + double _start_time; + int _frame_count; + + bool _wireframe_enabled; + bool _texture_enabled; + bool _two_sided_enabled; + bool _lighting_enabled; + + bool _default_keys_enabled; + + bool _exit_flag; +}; + +#include "pandaFramework.I" + +#endif diff --git a/panda/src/framework/windowFramework.I b/panda/src/framework/windowFramework.I new file mode 100644 index 0000000000..881a86867c --- /dev/null +++ b/panda/src/framework/windowFramework.I @@ -0,0 +1,104 @@ +// Filename: windowFramework.I +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_panda_framework +// Access: Public +// Description: Returns a pointer to the associated PandaFramework +// object. +//////////////////////////////////////////////////////////////////// +INLINE PandaFramework *WindowFramework:: +get_panda_framework() const { + return _panda_framework; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_graphics_window +// Access: Public +// Description: Returns a pointer to the underlying GraphicsWindow +// object. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsWindow *WindowFramework:: +get_graphics_window() const { + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_num_cameras +// Access: Public +// Description: Returns the number of 3-d cameras associated with the +// window. A window usually has only one camera, but it +// may have multiple cameras if there are multiple +// display regions within the window. +//////////////////////////////////////////////////////////////////// +INLINE int WindowFramework:: +get_num_cameras() const { + return _cameras.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_camera +// Access: Public +// Description: Returns the nth camera associated with the window. +//////////////////////////////////////////////////////////////////// +INLINE qpCamera *WindowFramework:: +get_camera(int n) const { + nassertr(n >= 0 && n < (int)_cameras.size(), NULL); + return _cameras[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_wireframe +// Access: Public +// Description: Returns the current state of the wireframe flag. +//////////////////////////////////////////////////////////////////// +INLINE bool WindowFramework:: +get_wireframe() const { + return _wireframe_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_texture +// Access: Public +// Description: Returns the current state of the texture flag. +//////////////////////////////////////////////////////////////////// +INLINE bool WindowFramework:: +get_texture() const { + return _texture_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_two_sided +// Access: Public +// Description: Returns the current state of the two_sided flag. +//////////////////////////////////////////////////////////////////// +INLINE bool WindowFramework:: +get_two_sided() const { + return _two_sided_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_lighting +// Access: Public +// Description: Returns the current state of the lighting flag. +//////////////////////////////////////////////////////////////////// +INLINE bool WindowFramework:: +get_lighting() const { + return _lighting_enabled; +} diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx new file mode 100644 index 0000000000..a188e6f2e7 --- /dev/null +++ b/panda/src/framework/windowFramework.cxx @@ -0,0 +1,520 @@ +// Filename: windowFramework.cxx +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "windowFramework.h" +#include "pandaFramework.h" +#include "mouseAndKeyboard.h" +#include "qpbuttonThrower.h" +#include "qptrackball.h" +#include "qptransform2sg.h" +#include "dSearchPath.h" +#include "filename.h" +#include "loader.h" +#include "keyboardButton.h" +#include "geomTri.h" +#include "texturePool.h" +#include "textureAttrib.h" +#include "perspectiveLens.h" +#include "auto_bind.h" +#include "ambientLight.h" +#include "directionalLight.h" +#include "lightAttrib.h" + +// This number is chosen arbitrarily to override any settings in model +// files. +static const int override_priority = 100; + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +WindowFramework:: +WindowFramework(PandaFramework *panda_framework) : + _panda_framework(panda_framework) +{ + _alight = (AmbientLight *)NULL; + _dlight = (DirectionalLight *)NULL; + _got_keyboard = false; + _got_trackball = false; + _got_lights = false; + _wireframe_enabled = false; + _texture_enabled = true; + _two_sided_enabled = false; + _lighting_enabled = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::Destructor +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +WindowFramework:: +~WindowFramework() { + close_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::open_window +// Access: Protected +// Description: Opens the actual window. This is normally called +// only from PandaFramework::open_window(). +//////////////////////////////////////////////////////////////////// +GraphicsWindow *WindowFramework:: +open_window(const GraphicsWindow::Properties &props, GraphicsPipe *pipe) { + nassertr(_window == (GraphicsWindow *)NULL, _window); + + _window = pipe->make_window(props); + + // Set up a 3-d camera for the window by default. + make_camera(); + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::close_window +// Access: Protected +// Description: Closes the window. This is normally called +// from PandaFramework::close_window(). +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +close_window() { + _window.clear(); + _camera_group.remove_node(); + _render.remove_node(); + _render_2d.remove_node(); + _mouse.remove_node(); + + _alight = (AmbientLight *)NULL; + _dlight = (DirectionalLight *)NULL; + _got_keyboard = false; + _got_trackball = false; + _got_lights = false; + + _wireframe_enabled = false; + _texture_enabled = true; + _two_sided_enabled = false; + _lighting_enabled = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_camera_group +// Access: Public +// Description: Returns the node above the collection of 3-d cameras +// in the scene graph. This node may be moved around to +// represent the viewpoint. +//////////////////////////////////////////////////////////////////// +const qpNodePath &WindowFramework:: +get_camera_group() { + if (_camera_group.is_empty()) { + _camera_group = get_render().attach_new_node("camera_group"); + } + return _camera_group; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_render +// Access: Public +// Description: Returns the root of the 3-d scene graph. +//////////////////////////////////////////////////////////////////// +const qpNodePath &WindowFramework:: +get_render() { + if (_render.is_empty()) { + _render = qpNodePath("render"); + + // This is maybe here temporarily, and maybe not. + _render.set_two_sided(0); + } + return _render; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_render_2d +// Access: Public +// Description: Returns the root of the 2-d scene graph. +//////////////////////////////////////////////////////////////////// +const qpNodePath &WindowFramework:: +get_render_2d() { + if (_render_2d.is_empty()) { + _render_2d = qpNodePath("render_2d"); + } + return _render_2d; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::get_mouse +// Access: Public +// Description: Returns the node in the data graph corresponding to +// the mouse associated with this window. +//////////////////////////////////////////////////////////////////// +const qpNodePath &WindowFramework:: +get_mouse() { + if (_mouse.is_empty()) { + qpNodePath data_root = _panda_framework->get_data_root(); + qpMouseAndKeyboard *mouse_node = + new qpMouseAndKeyboard(_window, 0, "mouse"); + _mouse = data_root.attach_new_node(mouse_node); + } + return _mouse; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::enable_keyboard +// Access: Public +// Description: Creates a ButtonThrower to listen to button presses +// and throw them as events. +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +enable_keyboard() { + if (_got_keyboard) { + return; + } + + qpNodePath mouse = get_mouse(); + + PT(qpButtonThrower) bt = new qpButtonThrower("kb-events"); + ModifierButtons mods; + mods.add_button(KeyboardButton::shift()); + mods.add_button(KeyboardButton::control()); + mods.add_button(KeyboardButton::alt()); + bt->set_modifier_buttons(mods); + mouse.attach_new_node(bt); + + _got_keyboard = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::setup_trackball +// Access: Public +// Description: Sets up the mouse to trackball around the camera. +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +setup_trackball() { + if (_got_trackball) { + return; + } + + qpNodePath mouse = get_mouse(); + qpNodePath camera = get_camera_group(); + + PT(qpTrackball) trackball = new qpTrackball("trackball"); + trackball->set_pos(LVector3f::forward() * 50.0); + mouse.attach_new_node(trackball); + + PT(qpTransform2SG) tball2cam = new qpTransform2SG("tball2cam"); + tball2cam->set_node(camera.node()); + trackball->add_child(tball2cam); + + _got_trackball = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::load_models +// Access: Public +// Description: Loads up all the model files listed in the indicated +// argument list. If first_arg is supplied, it is the +// first argument in the list to consider. +// +// Returns true if all models loaded successfully, or +// false if at least one of them had an error. +//////////////////////////////////////////////////////////////////// +bool WindowFramework:: +load_models(const qpNodePath &parent, int argc, char *argv[], int first_arg) { + pvector files; + + for (int i = first_arg; i < argc && argv[i] != (char *)NULL; i++) { + files.push_back(Filename::from_os_specific(argv[i])); + } + + return load_models(parent, files); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::load_models +// Access: Public +// Description: Loads up all the model files listed in the indicated +// argument list. +// +// Returns true if all models loaded successfully, or +// false if at least one of them had an error. +//////////////////////////////////////////////////////////////////// +bool WindowFramework:: +load_models(const qpNodePath &parent, const pvector &files) { + bool all_ok = true; + + qpNodePath render = get_render(); + + pvector::const_iterator fi; + for (fi = files.begin(); fi != files.end(); ++fi) { + const Filename &filename = (*fi); + qpNodePath model = load_model(parent, filename); + if (model.is_empty()) { + all_ok = false; + } + } + + return all_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::load_model +// Access: Public +// Description: Loads up the indicated model and returns the new +// NodePath, or the empty NodePath if the model could +// not be loaded. +//////////////////////////////////////////////////////////////////// +qpNodePath WindowFramework:: +load_model(const qpNodePath &parent, Filename filename) { + nout << "Loading " << filename << "\n"; + + // First, we always try to resolve a filename from the current + // directory. This means a local filename will always be found + // before the model path is searched. + DSearchPath local_path("."); + filename.resolve_filename(local_path); + + Loader loader; + PT(PandaNode) node = loader.qpload_sync(filename); + if (node == (PandaNode *)NULL) { + nout << "Unable to load " << filename << "\n"; + return qpNodePath::not_found(); + } + + return parent.attach_new_node(node); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::load_default_model +// Access: Public +// Description: Loads our favorite blue triangle. This is intended +// to provide some default geometry to have *something* +// to look at for testing, when no other models are +// provided. +//////////////////////////////////////////////////////////////////// +qpNodePath WindowFramework:: +load_default_model(const qpNodePath &parent) { + PTA_Vertexf coords; + PTA_TexCoordf uvs; + PTA_Normalf norms; + PTA_Colorf colors; + PTA_ushort cindex; + + coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0)); + uvs.push_back(TexCoordf(0.0, 0.0)); + uvs.push_back(TexCoordf(1.0, 0.0)); + uvs.push_back(TexCoordf(0.0, 1.0)); + norms.push_back(Normalf::back()); + colors.push_back(Colorf(0.5, 0.5, 1.0, 1.0)); + cindex.push_back(0); + cindex.push_back(0); + cindex.push_back(0); + + PT(GeomTri) geom = new GeomTri; + geom->set_num_prims(1); + geom->set_coords(coords); + geom->set_texcoords(uvs, G_PER_VERTEX); + geom->set_normals(norms, G_PER_PRIM); + geom->set_colors(colors, G_PER_VERTEX, cindex); + + CPT(RenderState) state = RenderState::make_empty(); + Texture *tex = TexturePool::load_texture("rock-floor.rgb"); + if (tex != (Texture *)NULL) { + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + state = state->add_attrib(TextureAttrib::make(tex)); + } + + qpGeomNode *geomnode = new qpGeomNode("tri"); + geomnode->add_geom(geom, state); + + return parent.attach_new_node(geomnode); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::loop_animations +// Access: Public +// Description: Looks for characters and their matching animation +// files in the scene graph; binds and loops any +// matching animations found. +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +loop_animations() { + // If we happened to load up both a character file and its matching + // animation file, attempt to bind them together now and start the + // animations looping. + auto_bind(get_render().node(), _anim_controls, ~0); + _anim_controls.loop_all(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::set_wireframe +// Access: Public +// Description: Forces wireframe state (true) or restores default +// rendering (false). +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +set_wireframe(bool enable) { + if (enable == _wireframe_enabled) { + return; + } + + qpNodePath render = get_render(); + + if (enable) { + render.set_render_mode_wireframe(override_priority); + render.set_two_sided(true, override_priority); + } else { + render.clear_render_mode(); + if (!_two_sided_enabled) { + render.clear_two_sided(); + } + } + + _wireframe_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::set_texture +// Access: Public +// Description: Forces textures off (false) or restores default +// rendering (true). +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +set_texture(bool enable) { + if (enable == _texture_enabled) { + return; + } + + qpNodePath render = get_render(); + + if (!enable) { + render.set_texture_off(override_priority); + } else { + render.clear_texture(); + } + + _texture_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::set_two_sided +// Access: Public +// Description: Forces two-sided rendering (true) or restores default +// rendering (false). +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +set_two_sided(bool enable) { + if (enable == _two_sided_enabled) { + return; + } + + qpNodePath render = get_render(); + + if (enable) { + render.set_two_sided(true, override_priority); + } else { + if (!_wireframe_enabled) { + render.clear_two_sided(); + } + } + + _two_sided_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::set_lighting +// Access: Public +// Description: Turns lighting on (true) or off (false). +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +set_lighting(bool enable) { + if (enable == _lighting_enabled) { + return; + } + + qpNodePath render = get_render(); + + if (enable) { + if (!_got_lights) { + setup_lights(); + } + render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, + _alight, _dlight)); + } else { + render.node()->clear_attrib(LightAttrib::get_class_type()); + } + + _lighting_enabled = enable; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::make_camera +// Access: Protected +// Description: Makes a new 3-d camera for the window. +//////////////////////////////////////////////////////////////////// +PT(qpCamera) WindowFramework:: +make_camera() { + // Get the first channel on the window. This will be the only + // channel on non-SGI hardware. + PT(GraphicsChannel) channel = _window->get_channel(0); + + // Make a layer on the channel to hold our display region. + PT(GraphicsLayer) layer = channel->make_layer(); + + // And create a display region that covers the entire window. + PT(DisplayRegion) dr = layer->make_display_region(); + + // Finally, we need a camera to associate with the display region. + PT(qpCamera) camera = new qpCamera("camera"); + qpNodePath camera_np = get_camera_group().attach_new_node(camera); + _cameras.push_back(camera); + + PT(Lens) lens = new PerspectiveLens; + lens->set_film_size(_window->get_width(), _window->get_height()); + camera->set_lens(lens); + camera->set_scene(get_render()); + dr->set_qpcamera(camera_np); + + return camera; +} + +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::setup_lights +// Access: Protected +// Description: Makes light nodes and attaches them to the camera for +// viewing the scene. +//////////////////////////////////////////////////////////////////// +void WindowFramework:: +setup_lights() { + if (_got_lights) { + return; + } + + qpNodePath camera_group = get_camera_group(); + qpNodePath light_group = camera_group.attach_new_node("lights"); + + _alight = new AmbientLight("ambient"); + _alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f)); + _dlight = new DirectionalLight("directional"); + light_group.attach_new_node(_alight); + light_group.attach_new_node(_dlight); + + _got_lights = true; +} diff --git a/panda/src/framework/windowFramework.h b/panda/src/framework/windowFramework.h new file mode 100644 index 0000000000..358964b8af --- /dev/null +++ b/panda/src/framework/windowFramework.h @@ -0,0 +1,117 @@ +// Filename: windowFramework.h +// Created by: drose (02Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINDOWFRAMEWORK_H +#define WINDOWFRAMEWORK_H + +#include "pandabase.h" +#include "qpnodePath.h" +#include "qpcamera.h" +#include "graphicsWindow.h" +#include "animControlCollection.h" +#include "filename.h" +#include "pointerTo.h" +#include "pvector.h" + +class PandaFramework; +class AmbientLight; +class DirectionalLight; + +//////////////////////////////////////////////////////////////////// +// Class : WindowFramework +// Description : This encapsulates the data that is normally +// associated with a single window that we've opened. +//////////////////////////////////////////////////////////////////// +class EXPCL_FRAMEWORK WindowFramework { +protected: + WindowFramework(PandaFramework *panda_framework); + virtual ~WindowFramework(); + + GraphicsWindow *open_window(const GraphicsWindow::Properties &props, + GraphicsPipe *pipe); + void close_window(); + +public: + INLINE PandaFramework *get_panda_framework() const; + INLINE GraphicsWindow *get_graphics_window() const; + const qpNodePath &get_camera_group(); + + INLINE int get_num_cameras() const; + INLINE qpCamera *get_camera(int n) const; + + const qpNodePath &get_render(); + const qpNodePath &get_render_2d(); + const qpNodePath &get_mouse(); + + void enable_keyboard(); + void setup_trackball(); + + bool load_models(const qpNodePath &parent, + int argc, char *argv[], int first_arg = 1); + bool load_models(const qpNodePath &parent, + const pvector &files); + qpNodePath load_model(const qpNodePath &parent, Filename filename); + qpNodePath load_default_model(const qpNodePath &parent); + void loop_animations(); + + void set_wireframe(bool enable); + void set_texture(bool enable); + void set_two_sided(bool enable); + void set_lighting(bool enable); + + INLINE bool get_wireframe() const; + INLINE bool get_texture() const; + INLINE bool get_two_sided() const; + INLINE bool get_lighting() const; + +protected: + PT(qpCamera) make_camera(); + void setup_lights(); + +private: + PandaFramework *_panda_framework; + PT(GraphicsWindow) _window; + + qpNodePath _camera_group; + typedef pvector< PT(qpCamera) > Cameras; + Cameras _cameras; + + qpNodePath _render; + qpNodePath _render_2d; + AnimControlCollection _anim_controls; + + qpNodePath _mouse; + + AmbientLight *_alight; + DirectionalLight *_dlight; + + bool _got_keyboard; + bool _got_trackball; + bool _got_lights; + + bool _wireframe_enabled; + bool _texture_enabled; + bool _two_sided_enabled; + bool _lighting_enabled; + + friend class PandaFramework; +}; + +#include "windowFramework.I" + +#endif diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index ea98a8fbfe..97df45a58d 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -21,8 +21,37 @@ /* See dtoolsymbols.h for a rant on the purpose of this file. */ +/* Note that the symbols declared in this file appear in alphabetical + order. Also note that we must use C-style comments only here, not + C++-style comments, since this file is occasionally included by a C + file. */ + #if defined(WIN32_VC) && !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) +#ifdef BUILDING_FRAMEWORK + #define EXPCL_FRAMEWORK __declspec(dllexport) + #define EXPTP_FRAMEWORK +#else + #define EXPCL_FRAMEWORK __declspec(dllimport) + #define EXPTP_FRAMEWORK extern +#endif + +#ifdef BUILDING_LINUX_AUDIO + #define EXPCL_LINUX_AUDIO __declspec(dllexport) + #define EXPTP_LINUX_AUDIO +#else + #define EXPCL_LINUX_AUDIO __declspec(dllimport) + #define EXPTP_LINUX_AUDIO extern +#endif + +#ifdef BUILDING_MILES_AUDIO + #define EXPCL_MILES_AUDIO __declspec(dllexport) + #define EXPTP_MILES_AUDIO +#else + #define EXPCL_MILES_AUDIO __declspec(dllimport) + #define EXPTP_MILES_AUDIO extern +#endif + #ifdef BUILDING_PANDA #define EXPCL_PANDA __declspec(dllexport) #define EXPTP_PANDA @@ -31,6 +60,14 @@ #define EXPTP_PANDA extern #endif +#ifdef BUILDING_PANDACR + #define EXPCL_PANDACR __declspec(dllexport) + #define EXPTP_PANDACR +#else + #define EXPCL_PANDACR __declspec(dllimport) + #define EXPTP_PANDACR extern +#endif + #ifdef BUILDING_PANDADX #define EXPCL_PANDADX __declspec(dllexport) #define EXPTP_PANDADX @@ -71,14 +108,6 @@ #define EXPTP_PANDAGL extern #endif -#ifdef BUILDING_PANDACR - #define EXPCL_PANDACR __declspec(dllexport) - #define EXPTP_PANDACR -#else - #define EXPCL_PANDACR __declspec(dllimport) - #define EXPTP_PANDACR extern -#endif - #ifdef BUILDING_PANDAGLUT #define EXPCL_PANDAGLUT __declspec(dllexport) #define EXPTP_PANDAGLUT @@ -111,27 +140,23 @@ #define EXPTP_SHADER extern #endif -#ifdef BUILDING_MILES_AUDIO - #define EXPCL_MILES_AUDIO __declspec(dllexport) - #define EXPTP_MILES_AUDIO -#else - #define EXPCL_MILES_AUDIO __declspec(dllimport) - #define EXPTP_MILES_AUDIO extern -#endif - -#ifdef BUILDING_LINUX_AUDIO - #define EXPCL_LINUX_AUDIO __declspec(dllexport) - #define EXPTP_LINUX_AUDIO -#else - #define EXPCL_LINUX_AUDIO __declspec(dllimport) - #define EXPTP_LINUX_AUDIO extern -#endif - #else /* !WIN32_VC */ +#define EXPCL_FRAMEWORK +#define EXPTP_FRAMEWORK + +#define EXPCL_LINUX_AUDIO +#define EXPTP_LINUX_AUDIO + +#define EXPCL_MILES_AUDIO +#define EXPTP_MILES_AUDIO + #define EXPCL_PANDA #define EXPTP_PANDA +#define EXPCL_PANDACR +#define EXPTP_PANDACR + #define EXPCL_PANDADX #define EXPTP_PANDADX @@ -147,9 +172,6 @@ #define EXPCL_PANDAGL #define EXPTP_PANDAGL -#define EXPCL_PANDACR -#define EXPTP_PANDACR - #define EXPCL_PANDAGLUT #define EXPTP_PANDAGLUT @@ -162,15 +184,6 @@ #define EXPCL_SHADER #define EXPTP_SHADER -#define EXPCL_MILES_AUDIO -#define EXPTP_MILES_AUDIO - -#define EXPCL_LINUX_AUDIO -#define EXPTP_LINUX_AUDIO - -#define EXPCL_FRAMEWORK -#define EXPTP_FRAMEWORK - #endif /* WIN32_VC */ #if defined(WIN32_VC) && !defined(CPPPARSER) diff --git a/panda/src/testbed/Sources.pp b/panda/src/testbed/Sources.pp index 9cb4b69bf1..a6adbb4bd3 100644 --- a/panda/src/testbed/Sources.pp +++ b/panda/src/testbed/Sources.pp @@ -10,21 +10,12 @@ #if $[LINK_ALL_STATIC] // If we're statically linking, we need to explicitly link with // at least one graphics renderer. - #define LOCAL_LIBS pandacr pandagl pandadx $[LOCAL_LIBS] + #define LOCAL_LIBS pandagl pandadx $[LOCAL_LIBS] // And we might like to have the egg loader available. #define LOCAL_LIBS pandaegg $[LOCAL_LIBS] #endif -#define UNIX_SYS_LIBS m - -#begin bin_target - #define TARGET demo - - #define SOURCES \ - demo.cxx - -#end bin_target #begin bin_target #define TARGET pview @@ -34,138 +25,8 @@ #end bin_target #begin test_bin_target - #define TARGET open_window + #define TARGET pgrid #define SOURCES \ - open_window.cxx - - #define LOCAL_LIBS $[LOCAL_LIBS] pandacr pandagl pandadx -#end test_bin_target - -#if $[HAVE_DX] - #begin test_bin_target - #define TARGET demo_multimon - - #define SOURCES \ - demo.cxx - - #define LOCAL_LIBS $[LOCAL_LIBS] pandadx framework_multimon - #end test_bin_target -#endif - -#begin test_bin_target - #define TARGET downloader - - #define SOURCES \ - downloader_test.cxx - - #define USE_ZLIB yes - #define TARGET_IF_ZLIB yes - -#end test_bin_target - -#begin test_bin_target - #define TARGET herc - - #define SOURCES \ - herc.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET loader - - #define SOURCES \ - loader_test.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET lod - - #define SOURCES \ - lod_test.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET min_herc - - #define SOURCES \ - min_herc.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET min_shader - - #define SOURCES \ - min_shader.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET panda - - #define SOURCES \ - panda.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET shader - - #define SOURCES \ - shader_test.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET test_eggWrite - - #define SOURCES \ - test_eggWrite.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET test_particles - - #define SOURCES \ - test_particles.cxx - #define LOCAL_LIBS $[LOCAL_LIBS] physics particlesystem - -#end test_bin_target - -#begin test_bin_target - #define TARGET test_sprite_particles - - #define SOURCES \ - test_sprite_particles.cxx - #define LOCAL_LIBS $[LOCAL_LIBS] physics particlesystem - -#end test_bin_target - -#begin test_bin_target - #define TARGET test_recparticles - - #define SOURCES \ - test_recparticles.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET text - - #define SOURCES \ - text_test.cxx - -#end test_bin_target - -#begin test_bin_target - #define TARGET vrpn_demo - - #define SOURCES \ - vrpn_demo.cxx - + pgrid.cxx #end test_bin_target diff --git a/panda/src/testbed/pgrid.cxx b/panda/src/testbed/pgrid.cxx new file mode 100644 index 0000000000..4d3e8f35ac --- /dev/null +++ b/panda/src/testbed/pgrid.cxx @@ -0,0 +1,429 @@ +// Filename: pgrid.cxx +// Created by: drose (03Apr02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pandaFramework.h" +#include "pandaNode.h" +#include "transformState.h" +#include "clockObject.h" +#include "string_utils.h" +#include "pvector.h" + +#include + + +#define RANDFRAC (rand()/(float)(RAND_MAX)) + +class GriddedFilename { +public: + Filename _filename; + int _count; + qpNodePath _model; +}; +typedef pvector GriddedFilenames; + +typedef struct { + // for rot moving + float xcenter,ycenter; + float xoffset,yoffset; + float ang1,ang1_vel; + float ang2,ang2_vel; + + float radius; + + // for moving + float xstart,ystart; + float xend,yend; + float xdel,ydel,timedel; + double starttime,endtime; + double vel; + LMatrix4f rotmat; + + PandaNode *node; +} gridded_file_info; + +typedef pvector GriddedInfoArray; + +typedef enum {None,Rotation,LinearMotion} GriddedMotionType; + +#define GRIDCELLSIZE 5.0 +static int gridwidth; // cells/side + +#define MIN_WANDERAREA_DIMENSION 120.0 + +static float grid_pos_offset; // origin of grid +static float wander_area_pos_offset; + +static GriddedMotionType gridmotiontype = None; + + +// making these fns to get around ridiculous VC++ matrix inlining bugs at Opt2 +static void +move_gridded_stuff(GriddedMotionType gridmotiontype, + gridded_file_info *InfoArr, int size) { + double now = ClockObject::get_global_clock()->get_frame_time(); + + LMatrix4f tmat1,tmat2,xfm_mat; + + for(int i = 0; i < size; i++) { + double time_delta = (now-InfoArr[i].starttime); +#define DO_FP_MODULUS(VAL,MAXVAL) \ + {if(VAL > MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL); VAL=VAL-idivresult*MAXVAL;} else \ + if(VAL < -MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL); VAL=VAL+idivresult*MAXVAL;}} + + // probably should use panda lerps for this stuff, but I dont understand how + + if(gridmotiontype==Rotation) { + + InfoArr[i].ang1=time_delta*InfoArr[i].ang1_vel; + DO_FP_MODULUS(InfoArr[i].ang1,360.0); + InfoArr[i].ang2=time_delta*InfoArr[i].ang2_vel; + DO_FP_MODULUS(InfoArr[i].ang2,360.0); + + // xforms happen left to right + LVector2f new_center = LVector2f(InfoArr[i].radius,0.0) * + LMatrix3f::rotate_mat(InfoArr[i].ang1); + + LVector3f translate_vec(InfoArr[i].xcenter+new_center._v.v._0, + InfoArr[i].ycenter+new_center._v.v._1, + 0.0); + + const LVector3f rotation_axis(0.0, 0.0, 1.0); + + tmat1 = LMatrix4f::rotate_mat_normaxis(InfoArr[i].ang2,rotation_axis); + tmat2 = LMatrix4f::translate_mat(translate_vec); + xfm_mat = tmat1 * tmat2; + } else { + + float xpos,ypos; + + if(now>InfoArr[i].endtime) { + InfoArr[i].starttime = now; + + xpos = InfoArr[i].xstart = InfoArr[i].xend; + ypos = InfoArr[i].ystart = InfoArr[i].yend; + + InfoArr[i].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + InfoArr[i].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + + float xdel = InfoArr[i].xdel = InfoArr[i].xend-InfoArr[i].xstart; + float ydel = InfoArr[i].ydel = InfoArr[i].yend-InfoArr[i].ystart; + + InfoArr[i].endtime = now + csqrt(xdel*xdel+ydel*ydel)/InfoArr[i].vel; + InfoArr[i].timedel = InfoArr[i].endtime - InfoArr[i].starttime; + + const LVector3f rotate_axis(0.0, 0.0, 1.0); + + float ang = rad_2_deg(atan2(-xdel,ydel)); + + InfoArr[i].rotmat= LMatrix4f::rotate_mat_normaxis(ang,rotate_axis); + } else { + float timefrac= time_delta/InfoArr[i].timedel; + + xpos = InfoArr[i].xdel*timefrac+InfoArr[i].xstart; + ypos = InfoArr[i].ydel*timefrac+InfoArr[i].ystart; + } + + LVector3f translate_vec(xpos, ypos, 0.0); + LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec); + + xfm_mat = InfoArr[i].rotmat * tmat2; + } + InfoArr[i].node->set_transform(TransformState::make_mat(xfm_mat)); + } +} + +bool +get_command_line_opts(int &argc, char **&argv) { + // Use getopt() to decode the optional command-line parameters. + // extern char *optarg; + extern int optind; + const char *options = "rm"; + int flag = getopt(argc, argv, options); + while (flag != EOF) { + switch (flag) { + case 'r': + gridmotiontype = Rotation; + break; + + case 'm': + gridmotiontype = LinearMotion; + break; + + case '?': + nout << "Invalid parameter.\n"; + return false; + } + + flag = getopt(argc, argv, options); + } + + argv += (optind - 1); + argc -= (optind - 1); + + return true; +} + +void +get_command_line_filenames(int argc, char *argv[], + pvector &static_filenames, + GriddedFilenames &gridded_filenames) { + for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) { + const string &arg = argv[i]; + size_t comma = arg.find(','); + if (comma == string::npos) { + // No comma in the filename, so it must be an ordinary static file. + static_filenames.push_back(Filename::from_os_specific(arg)); + + } else { + // A comma in the filename indicates a gridded file. The syntax + // is filename,count where count represents the number of times + // the file is repeated. + string name = arg.substr(0, comma); + string count_str = arg.substr(comma + 1); + int count; + if (!string_to_int(count_str, count)) { + nout << "Ignoring invalid number: " << count_str << "\n"; + count = 1; + } else if (count <= 0) { + nout << "Ignoring inappropriate number: " << count << "\n"; + count = 1; + } + + GriddedFilename gf; + gf._filename = Filename::from_os_specific(name); + gf._count = count; + gridded_filenames.push_back(gf); + } + } +} + +void +load_gridded_models(WindowFramework *window, + GriddedFilenames &filenames, + GriddedInfoArray &info_arr) { + // Load up all the files indicated in the list of gridded filenames + // and store them in the given vector. + + // First, load up each model from disk once, and store them all + // separate from the scene graph. Also count up the total number of + // models we'll be putting in the grid. + int grid_count = 0; + qpNodePath models("models"); + GriddedFilenames::iterator fi; + for (fi = filenames.begin(); fi != filenames.end(); ++fi) { + GriddedFilename &gf = (*fi); + gf._model = window->load_model(models, gf._filename); + if (!gf._model.is_empty()) { + grid_count += gf._count; + } + } + + info_arr.clear(); + info_arr.reserve(grid_count); + + // Compute the integer square root of grid_count, so that we put our + // models in a nice square grid. + + gridwidth=1; + while(gridwidth*gridwidth < grid_count) { + gridwidth++; + } + + grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0; + wander_area_pos_offset = -max(fabs(grid_pos_offset),MIN_WANDERAREA_DIMENSION/2.0); + + // Now walk through the list again, copying models into the scene + // graph as we go. + + float xpos = grid_pos_offset; + float ypos = grid_pos_offset; + + srand( (unsigned)time( NULL ) ); + double now = ClockObject::get_global_clock()->get_frame_time(); + + int model_count = 0; + int passnum = 0; + bool loaded_any; + + qpNodePath render = window->get_render(); + do { + loaded_any = false; + + for (fi = filenames.begin(); fi != filenames.end(); ++fi) { + const GriddedFilename &gf = (*fi); + if (!gf._model.is_empty() && gf._count > passnum) { + loaded_any = true; + // Copy this model into the scene graph, and assign it a + // position on the grid. + + string model_name = format_string(++model_count); + qpNodePath model = render.attach_new_node(model_name); + gf._model.copy_to(model); + + gridded_file_info info; + info.node = model.node(); + + LMatrix4f xfm_mat,tmat1,tmat2; + + if(gridmotiontype==Rotation) { + +#define MIN_REVOLUTION_ANGVEL 30 +#define MAX_REVOLUTION_ANGVEL 60 + +#define MIN_ROTATION_ANGVEL 30 +#define MAX_ROTATION_ANGVEL 600 + +#define MAX_RADIUS 4.0*GRIDCELLSIZE +#define MIN_RADIUS 0.1*GRIDCELLSIZE + + info.starttime = now; + + info.xcenter=xpos; + info.ycenter=ypos; + info.ang1=RANDFRAC * 360.0; + info.ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL; + + info.ang2=RANDFRAC * 360.0; + info.ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL; + + info.radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS; + + if(RANDFRAC>0.5) { + info.ang1_vel=-info.ang1_vel; + } + + if(RANDFRAC>0.5) { + info.ang2_vel=-info.ang2_vel; + } + + // xforms happen left to right + LVector2f new_center = LVector2f(info.radius,0.0) * + LMatrix3f::rotate_mat(info.ang1); + + const LVector3f rotate_axis(0.0, 0.0, 1.0); + + LVector3f translate_vec(xpos+new_center._v.v._0, + ypos+new_center._v.v._1, + 0.0); + + LMatrix4f::rotate_mat_normaxis(info.ang2,rotate_axis,tmat1); + tmat2 = LMatrix4f::translate_mat(translate_vec); + xfm_mat = tmat1 * tmat2; + } else if(gridmotiontype==LinearMotion) { + +#define MIN_VEL 2.0 +#define MAX_VEL (fabs(wander_area_pos_offset)) + + info.vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL; + + info.xstart=xpos; + info.ystart=ypos; + + info.xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + info.yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + + info.starttime = now; + + float xdel = info.xdel = info.xend-info.xstart; + float ydel = info.ydel = info.yend-info.ystart; + + info.endtime = csqrt(xdel*xdel+ydel*ydel)/info.vel; + + info.timedel = info.endtime - info.starttime; + + const LVector3f rotate_axis(0.0, 0.0, 1.0); + float ang = rad_2_deg(atan2(-xdel,ydel)); + + LMatrix4f::rotate_mat_normaxis(ang,rotate_axis,info.rotmat); + + LVector3f translate_vec(xpos, ypos, 0.0); + LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec); + + xfm_mat = info.rotmat * tmat2; + } else { + LVector3f translate_vec(xpos, ypos, 0.0); + xfm_mat = LMatrix4f::translate_mat(translate_vec); + } + + info.node->set_transform(TransformState::make_mat(xfm_mat)); + + info_arr.push_back(info); + + if((model_count % gridwidth) == 0) { + xpos= -gridwidth*GRIDCELLSIZE/2.0; + ypos+=GRIDCELLSIZE; + } else { + xpos+=GRIDCELLSIZE; + } + } + } + + passnum++; + } while (loaded_any); + + // Finally, remove the source models we loaded up. Not a real big deal. + for (fi = filenames.begin(); fi != filenames.end(); ++fi) { + GriddedFilename &gf = (*fi); + if (!gf._model.is_empty()) { + gf._model.remove_node(); + } + } +} + +int +main(int argc, char *argv[]) { + PandaFramework framework; + vector_string args; + framework.open_framework(argc, argv); + framework.set_window_title("Gridded Object Viewer"); + + if (!get_command_line_opts(argc, argv)) { + return (1); + } + + // Extract the remaining arguments into two lists of files: those + // with a grid parameter, and those without. + pvector static_filenames; + GriddedFilenames gridded_filenames; + get_command_line_filenames(argc, argv, static_filenames, gridded_filenames); + + WindowFramework *window = framework.open_window(); + if (window != (WindowFramework *)NULL) { + // We've successfully opened a window. + + window->enable_keyboard(); + window->setup_trackball(); + window->load_models(window->get_render(), static_filenames); + + GriddedInfoArray info_arr; + load_gridded_models(window, gridded_filenames, info_arr); + + window->loop_animations(); + + framework.enable_default_keys(); + + while (framework.do_frame()) { + if (!info_arr.empty() && gridmotiontype) { + move_gridded_stuff(gridmotiontype, &info_arr[0], info_arr.size()); + } + } + } + + framework.report_frame_rate(nout); + return (0); +} diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index 104bd3d1dc..b781c2df24 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -16,453 +16,27 @@ // //////////////////////////////////////////////////////////////////// -#include "graphicsWindow.h" -#include "graphicsChannel.h" -#include "graphicsLayer.h" -#include "graphicsEngine.h" -#include "graphicsPipe.h" -#include "interactiveGraphicsPipe.h" -#include "displayRegion.h" -#include "qpcamera.h" -#include "perspectiveLens.h" -#include "pandaNode.h" -#include "textureAttrib.h" -#include "clockObject.h" -#include "qpgeomNode.h" -#include "geomTri.h" -#include "texture.h" -#include "texturePool.h" -#include "dSearchPath.h" -#include "loader.h" -#include "auto_bind.h" -#include "pStatClient.h" -#include "notify.h" -#include "qpnodePath.h" -#include "cullBinManager.h" -#include "mouseAndKeyboard.h" -#include "qpbuttonThrower.h" -#include "qptrackball.h" -#include "qptransform2sg.h" -#include "modifierButtons.h" -#include "keyboardButton.h" -#include "event.h" -#include "eventQueue.h" -#include "eventHandler.h" -#include "qpdataGraphTraverser.h" -#include "lightAttrib.h" -#include "ambientLight.h" -#include "directionalLight.h" - -// Use dconfig to read a few Configrc variables. -Configure(config_pview); -ConfigureFn(config_pview) { -} - -static const int win_width = config_pview.GetInt("win-width", 640); -static const int win_height = config_pview.GetInt("win-height", 480); - -// As long as this is true, the main loop will continue running. -bool run_flag = true; - -// These are referenced in both event_w() and in event_b(). -bool wireframe = false; -bool two_sided = false; -bool lighting = false; - - -// These are used by report_frame_rate(). -double start_time = 0.0; -int start_frame_count = 0; - -// A priority number high enough to override any model file settings. -static const int override_priority = 100; - -// This is the main scene graph. -qpNodePath render; - -// And this is the camera node. -qpNodePath camera_np; - -void -report_frame_rate() { - double now = ClockObject::get_global_clock()->get_frame_time(); - double delta = now - start_time; - - int frame_count = ClockObject::get_global_clock()->get_frame_count(); - int num_frames = frame_count - start_frame_count; - if (num_frames > 0) { - nout << endl << num_frames << " frames in " << delta << " seconds" << endl; - double x = ((double)num_frames) / delta; - nout << x << " fps average (" << 1000.0 / x << "ms)" << endl; - - // Reset the frame rate counter for the next press of 'f'. - start_time = now; - start_frame_count = frame_count; - } -} - -PT(GraphicsPipe) -make_pipe() { - // We use the GraphicsPipe factory to make us a renderable pipe - // without knowing exactly what kind of pipes we have available. We - // don't care, so long as we can render to it interactively. - - // This depends on the shared library or libraries (DLL's to you - // Windows folks) that have been loaded in at runtime from the - // load-display Configrc variable. - GraphicsPipe::resolve_modules(); - - nout << "Known pipe types:" << endl; - GraphicsPipe::get_factory().write_types(nout, 2); - - PT(GraphicsPipe) pipe; - pipe = GraphicsPipe::get_factory(). - make_instance(InteractiveGraphicsPipe::get_class_type()); - - if (pipe == (GraphicsPipe*)0L) { - nout << "No interactive pipe is available! Check your Configrc!\n"; - exit(1); - } - - return pipe; -} - -PT(GraphicsWindow) -make_window(GraphicsPipe *pipe, GraphicsEngine *engine) { - // Now create a window on that pipe. - GraphicsWindow::Properties window_prop; - window_prop._xorg = 0; - window_prop._yorg = 0; - window_prop._xsize = win_width; - window_prop._ysize = win_height; - window_prop._title = "Panda Viewer"; - // window_prop._fullscreen = true; - - PT(GraphicsWindow) window = pipe->make_window(window_prop); - engine->add_window(window); - - return window; -} - -PT(qpCamera) -make_camera(GraphicsWindow *window) { - // Get the first channel on the window. This will be the only - // channel on non-SGI hardware. - PT(GraphicsChannel) channel = window->get_channel(0); - - // Make a layer on the channel to hold our display region. - PT(GraphicsLayer) layer = channel->make_layer(); - - // And create a display region that covers the entire window. - PT(DisplayRegion) dr = layer->make_display_region(); - - // Finally, we need a camera to associate with the display region. - PT(qpCamera) camera = new qpCamera("camera"); - PT(Lens) lens = new PerspectiveLens; - lens->set_film_size(win_width, win_height); - camera->set_lens(lens); - dr->set_qpcamera(qpNodePath(camera)); - - return camera; -} - - -void -make_default_geometry(PandaNode *parent) { - PTA_Vertexf coords; - PTA_TexCoordf uvs; - PTA_Normalf norms; - PTA_Colorf colors; - PTA_ushort cindex; - - coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0)); - coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0)); - coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0)); - uvs.push_back(TexCoordf(0.0, 0.0)); - uvs.push_back(TexCoordf(1.0, 0.0)); - uvs.push_back(TexCoordf(0.0, 1.0)); - norms.push_back(Normalf::back()); - colors.push_back(Colorf(0.5, 0.5, 1.0, 1.0)); - cindex.push_back(0); - cindex.push_back(0); - cindex.push_back(0); - - PT(GeomTri) geom = new GeomTri; - geom->set_num_prims(1); - geom->set_coords(coords); - geom->set_texcoords(uvs, G_PER_VERTEX); - geom->set_normals(norms, G_PER_PRIM); - geom->set_colors(colors, G_PER_VERTEX, cindex); - - CPT(RenderState) state = RenderState::make_empty(); - Texture *tex = TexturePool::load_texture("rock-floor.rgb"); - if (tex != (Texture *)NULL) { - tex->set_minfilter(Texture::FT_linear); - tex->set_magfilter(Texture::FT_linear); - state = state->add_attrib(TextureAttrib::make(tex)); - } - - qpGeomNode *geomnode = new qpGeomNode("tri"); - parent->add_child(geomnode); - geomnode->add_geom(geom, state); -} - -void -get_models(PandaNode *parent, int argc, char *argv[]) { - if (argc < 2) { - // In the absence of any models on the command line, load up a - // default triangle so we at least have something to look at. - make_default_geometry(parent); - - } else { - Loader loader; - DSearchPath local_path("."); - - for (int i = 1; i < argc; i++) { - Filename filename = argv[i]; - - nout << "Loading " << filename << "\n"; - - // First, we always try to resolve a filename from the current - // directory. This means a local filename will always be found - // before the model path is searched. - filename.resolve_filename(local_path); - - PT(PandaNode) node = loader.qpload_sync(filename); - if (node == (PandaNode *)NULL) { - nout << "Unable to load " << filename << "\n"; - - } else { - node->ls(nout, 0); - parent->add_child(node); - } - } - } -} - -PandaNode * -setup_mouse(PandaNode *data_root, GraphicsWindow *window) { - qpMouseAndKeyboard *mouse = new qpMouseAndKeyboard(window, 0, "mouse"); - data_root->add_child(mouse); - - // Create a ButtonThrower to throw events from the keyboard. - PT(qpButtonThrower) bt = new qpButtonThrower("kb-events"); - ModifierButtons mods; - mods.add_button(KeyboardButton::shift()); - mods.add_button(KeyboardButton::control()); - mods.add_button(KeyboardButton::alt()); - bt->set_modifier_buttons(mods); - mouse->add_child(bt); - - return mouse; -} - -void -setup_trackball(PandaNode *mouse, qpCamera *camera) { - PT(qpTrackball) trackball = new qpTrackball("trackball"); - trackball->set_pos(LVector3f::forward() * 50.0); - mouse->add_child(trackball); - - PT(qpTransform2SG) tball2cam = new qpTransform2SG("tball2cam"); - tball2cam->set_node(camera); - trackball->add_child(tball2cam); -} - -void -event_esc(CPT_Event) { - // The Escape or q key was pressed. Exit the application. - run_flag = false; -} - -void -event_f(CPT_Event) { - // 'f' : report frame rate. - report_frame_rate(); -} - -void -event_t(CPT_Event) { - // 't' : toggle texture. - static bool texture_off = false; - - texture_off = !texture_off; - if (texture_off) { - nout << "Disabling texturing.\n"; - render.set_texture_off(override_priority); - } else { - nout << "Enabling texturing.\n"; - render.clear_texture(); - } -} - -void -event_w(CPT_Event) { - // 'w' : toggle wireframe. - wireframe = !wireframe; - if (wireframe) { - nout << "Setting wireframe mode.\n"; - render.set_render_mode_wireframe(override_priority); - render.set_two_sided(true, override_priority); - } else { - nout << "Clearing wireframe mode.\n"; - render.clear_render_mode(); - if (!two_sided) { - render.clear_two_sided(); - } - } -} - -void -event_b(CPT_Event) { - // 'b' : toggle backfacing. - two_sided = !two_sided; - if (two_sided) { - nout << "Showing back-facing polygons.\n"; - render.set_two_sided(true, override_priority); - } else { - nout << "Hiding back-facing polygons.\n"; - if (!wireframe) { - render.clear_two_sided(); - } - } -} - -void -event_s(CPT_Event) { - // 's' : toggle state sorting by putting everything into the 'unsorted' bin. - static bool sorting_off = false; - - sorting_off = !sorting_off; - if (sorting_off) { - nout << "Disabling state sorting.\n"; - render.set_bin("unsorted", 0, override_priority); - } else { - nout << "Enabling state sorting.\n"; - render.clear_bin(); - } -} - -void -event_S(CPT_Event) { - // shift 'S' : active PStats. -#ifdef DO_PSTATS - nout << "Connecting to stats host" << endl; - PStatClient::connect(); -#else - nout << "Stats host not supported." << endl; -#endif -} - -void -event_A(CPT_Event) { - // shift 'A' : deactive PStats. -#ifdef DO_PSTATS - if (PStatClient::is_connected()) { - nout << "Disconnecting from stats host" << endl; - PStatClient::disconnect(); - } else { - nout << "Stats host is already disconnected." << endl; - } -#else - nout << "Stats host not supported." << endl; -#endif -} - -void -event_l(CPT_Event) { - // 'l' : toggle lighting. - lighting = !lighting; - if (lighting) { - nout << "Enabling lighting.\n"; - static bool lights_created = false; - - static PT(AmbientLight) alight; - static PT(DirectionalLight) dlight; - - if (!lights_created) { - alight = new AmbientLight("ambient"); - alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f)); - dlight = new DirectionalLight("directional"); - camera_np.attach_new_node(alight); - camera_np.attach_new_node(dlight); - - lights_created = true; - } - - // Enable lights on the top node. - render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, alight, dlight)); - - } else { - nout << "Disabling lighting.\n"; - // Remove the lights from the top node. - render.node()->clear_attrib(LightAttrib::get_class_type()); - } -} +#include "pandaFramework.h" int main(int argc, char *argv[]) { - // First, we need a GraphicsPipe, before we can open a window. - PT(GraphicsPipe) pipe = make_pipe(); + PandaFramework framework; + framework.open_framework(argc, argv); + framework.set_window_title("Panda Viewer"); - // We also need a GraphicsEngine to manage the rendering process. - GraphicsEngine *engine = new GraphicsEngine; + WindowFramework *window = framework.open_window(); + if (window != (WindowFramework *)NULL) { + // We've successfully opened a window. - // Now open a window and get a camera. - PT(GraphicsWindow) window = make_window(pipe, engine); - PT(qpCamera) camera = make_camera(window); + window->enable_keyboard(); + window->setup_trackball(); + window->load_models(window->get_render(), argc, argv); + window->loop_animations(); - // Now we just need to make a scene graph for the camera to render. - render = qpNodePath("render"); - camera_np = render.attach_new_node(camera); - camera->set_scene(render); + framework.enable_default_keys(); + framework.main_loop(); + } - // This is maybe here temporarily, and maybe not. - render.set_two_sided(0); - - // Set up a data graph for tracking user input. - PT(PandaNode) data_root = new PandaNode("data_root"); - PandaNode *mouse = setup_mouse(data_root, window); - setup_trackball(mouse, camera); - qpDataGraphTraverser dg_trav; - - // Use an event handler to manage keyboard events. - EventHandler event_handler(EventQueue::get_global_event_queue()); - event_handler.add_hook("escape", event_esc); - event_handler.add_hook("q", event_esc); - event_handler.add_hook("f", event_f); - event_handler.add_hook("t", event_t); - event_handler.add_hook("w", event_w); - event_handler.add_hook("b", event_b); - event_handler.add_hook("s", event_s); - event_handler.add_hook("shift-s", event_S); - event_handler.add_hook("shift-a", event_A); - event_handler.add_hook("l", event_l); - - - // Put something in the scene graph to look at. - get_models(render.node(), argc, argv); - - // If we happened to load up both a character file and its matching - // animation file, attempt to bind them together now and start the - // animations looping. - AnimControlCollection anim_controls; - auto_bind(render.node(), anim_controls, ~0); - anim_controls.loop_all(true); - - - // Tick the clock once so we won't count the time spent loading up - // files, above, in our frame rate average. - ClockObject::get_global_clock()->tick(); - - // This is our main update loop. Loop here until someone - // (e.g. event_esc) sets run_flag to false. - while (run_flag) { - dg_trav.traverse(data_root); - event_handler.process_events(); - engine->render_frame(); - } - - report_frame_rate(); - delete engine; + framework.report_frame_rate(nout); return (0); }