From 24a99e8731c2c3453a7cb76d4dc9f772545b1ce2 Mon Sep 17 00:00:00 2001 From: cxgeorge <> Date: Tue, 29 Jan 2002 20:42:39 +0000 Subject: [PATCH] *** empty log message *** --- panda/src/framework/framework_multimon.cxx | 1746 ++++++++++++++++++++ 1 file changed, 1746 insertions(+) create mode 100644 panda/src/framework/framework_multimon.cxx diff --git a/panda/src/framework/framework_multimon.cxx b/panda/src/framework/framework_multimon.cxx new file mode 100644 index 0000000000..19b79f119a --- /dev/null +++ b/panda/src/framework/framework_multimon.cxx @@ -0,0 +1,1746 @@ +// Filename: framework.cxx +// Created by: cary (25Mar99) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + +// We need to include bitMask.h first to avoid a VC++ compiler bug +// related to 2 parameter templates +#include "bitMask.h" + +#include "framework.h" +#include "config_framework.h" + +#include "pystub.h" +#include "time.h" +// Since framework.cxx includes pystub.h, no program that links with +// framework needs to do so. No Python code should attempt to link +// with libframework.so. + +#include "cullTraverser.h" +#include "appTraverser.h" +#include "directRenderTraverser.h" +#include "mouse.h" +#include "mouseWatcher.h" +#include "buttonThrower.h" +#include "keyboardButton.h" +#include "eventHandler.h" +#include "throw_event.h" +#include "camera.h" +#include "geom.h" +#include "geomprimitives.h" +#include "renderRelation.h" +#include "dataRelation.h" +#include "geomNode.h" +#include "namedNode.h" +#include "pt_NamedNode.h" +#include "colorTransition.h" +#include "renderModeTransition.h" +#include "materialTransition.h" +#include "dataGraphTraversal.h" +#include "trackball.h" +#include "driveInterface.h" +#include "transform2sg.h" +#include "texture.h" +#include "texturePool.h" +#include "textureTransition.h" +#include "interactiveGraphicsPipe.h" +#include "noninteractiveGraphicsPipe.h" +#include "graphicsWindow.h" +#include "plist.h" +#include "lightTransition.h" +#include "materialTransition.h" +#include "animControl.h" +#include "animControlCollection.h" +#include "auto_bind.h" +#include "ambientLight.h" +#include "directionalLight.h" +#include "pointLight.h" +#include "spotlight.h" +#include "dconfig.h" +#include "cullFaceTransition.h" +#include "pruneTransition.h" +#include "dftraverser.h" +#include "renderBuffer.h" +#include "loader.h" +#include "fogTransition.h" +#include "clockObject.h" +#include "compose_matrix.h" +#include "notify.h" +#include "nullTransitionWrapper.h" +#include "nullLevelState.h" +#include "sceneGraphReducer.h" +#include "textNode.h" +#include "depthTestTransition.h" +#include "depthWriteTransition.h" +#include "orthographicLens.h" +#include "transparencyTransition.h" +#include "bamReader.h" +#include "collisionRay.h" +#include "collisionNode.h" +#include "collisionTraverser.h" +#include "collisionHandlerFloor.h" +#include "nodePath.h" +#include "multiplexStream.h" +#include "dSearchPath.h" +#include "camera.h" +#include "perspectiveLens.h" +#include "wdxGraphicsPipe.h" +#include "wdxGraphicsWindow.h" + +#ifdef USE_IPC +#include "ipc_file.h" +#include "ipc_mutex.h" +#include "ipc_thread.h" +#endif + +Configure(framework); + +ConfigureFn(framework) { +} + +int NumWindows=1; + +AppTraverser *app_traverser; +PT_NamedNode data_root; +PT_NamedNode root; +PT(GeomNode) geomnode; +PT_NamedNode render_top; +PT_NamedNode render; +NodeRelation *render_arc; +PT(MouseAndKeyboard) mak; +PT(MouseWatcher) mouse_watcher; +PT(Trackball) trackball; +PT(DriveInterface) drive_interface; +PT_NamedNode camera_top; +NodeRelation *camera_top_arc; + +static Node *current_trackball = NULL; +static Node *alt_trackball = NULL; + +Texture* ttex; +//PT(GraphicsPipe) main_pipe; +PT(GraphicsPipe) rib_pipe; +//PT(GraphicsWindow) main_win; +PT(GraphicsWindow) rib_win; +RenderRelation* first_arc; +PT(wdxGraphicsWindow) main_window; +wdxGraphicsWindowGroup *pWinGrp; + +PT_NamedNode lights; + +PT(AmbientLight) light; +PT(DirectionalLight) dlight; +bool have_dlight = false; +PT(PointLight) plight; +PT(Spotlight) slight; + +PT(Material) material; + +PT(Fog) fog; + +// Framerate vars + +PT_NamedNode framerate_top; +RenderRelation *framerate_arc = (RenderRelation*)0L; +PT_NamedNode framerate_node; + +PT(GraphicsLayer) framerate_layer; +PT(TextFont) framerate_font; +PT(TextNode) framerate_text; + +Loader loader; + +EventHandler event_handler(EventQueue::get_global_event_queue()); + +std::string chan_config = "single"; + +static double start_time = 0.0; +static int start_frame_count = 0; + +void (*extra_display_func)() = NULL; +void (*define_keys)(EventHandler&) = NULL; +void (*extra_overrides_func)(ChanCfgOverrides&, std::string&) = NULL; +void (*first_init)() = NULL; +void (*additional_idle)() = NULL; + +#ifdef USE_IPC +static bool forked_draw = framework.GetBool("fork-draw", false); +static mutex run_render; +static bool render_running = true; +static bool quit_draw = false; +static thread* draw_thread; +#endif + +static CollisionTraverser *col_trans = NULL; +static CollisionHandlerFloor *col_handler = NULL; +static CollisionNode *ray_node = NULL; + +class GeomNorms : public GeomLine +{ +public: + GeomNorms(void) : GeomLine() {} + virtual Geom *explode() const { + return new GeomNorms(*this); + } + + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + GeomLine::init_type(); + register_type(_type_handle, "GeomNorms", + GeomLine::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +TypeHandle GeomNorms::_type_handle; + +// Since the Normal*Traversers don't care about state, we don't need +// to accumulate the RenderTransitions, so it will template on +// NullTransition. +class NormalAddTraverser : + public TraverserVisitor { +public: + NormalAddTraverser(GraphicsStateGuardian *gsg) : _gsg(gsg) {} + bool reached_node(Node*, NullTransitionWrapper&, NullLevelState&); + + // No need to declare a forward_arc() function that simply returns + // true; this is the default behavior. + +public: + GraphicsStateGuardian *_gsg; +}; + +bool NormalAddTraverser:: +reached_node(Node *node, NullTransitionWrapper &, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNorms *gn = new GeomNorms; + GeomNode *geom = DCAST(GeomNode, node); + int vert_count = 0; + int i; + for (i = 0; i < geom->get_num_geoms(); i++) { + dDrawable *d = geom->get_geom(i); + if (d->is_of_type(Geom::get_class_type())) { + Geom *g = DCAST(Geom, d); + for (int j=0; jget_num_prims(); ++j) + vert_count += g->get_length(j); + } + } + if (vert_count > 0) { + PTA_Vertexf verts=PTA_Vertexf::empty_array(2 * vert_count); + for (i = 0; i < geom->get_num_geoms(); i++) { + dDrawable *d = geom->get_geom(i); + if (d->is_of_type(Geom::get_class_type())) { + PTA_Vertexf lverts; + PTA_ushort iverts; + Geom *g = DCAST(Geom, d); + g->get_coords(lverts, iverts); + int vert_idx = 0; + if (g->get_binding(G_NORMAL) == G_OFF) { + for (int j=0; jget_num_prims(); ++j) { + for (int k=0; kget_length(j); ++k, ++vert_idx) { + verts[2 * vert_idx] = lverts[vert_idx]; + verts[(2 * vert_idx) + 1] = lverts[vert_idx]; + } + } + } else { + PTA_Normalf lnorms; + PTA_ushort inorms; + GeomBindType nbond; + g->get_normals(lnorms, nbond, inorms); + for (int j=0; jget_num_prims(); ++j) { + for (int k=0; kget_length(j); ++k, ++vert_idx) { + verts[2 * vert_idx] = lverts[vert_idx]; + verts[(2 * vert_idx) + 1] = lverts[vert_idx] + + lnorms[vert_idx]; + } + } + } + } + } + gn->set_num_prims(vert_count); + gn->set_coords(verts); + } + geom->add_geom(gn); + } + return true; +} + +class NormalDelTraverser : + public TraverserVisitor { +public: + NormalDelTraverser(GraphicsStateGuardian *gsg) : _gsg(gsg) {} + bool reached_node(Node*, NullTransitionWrapper&, NullLevelState&); +public: + GraphicsStateGuardian *_gsg; +}; + +bool NormalDelTraverser:: +reached_node(Node *node, NullTransitionWrapper &, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNode *geom = DCAST(GeomNode, node); + int i, j; + do { + for (i = 0, j = -1; + i < geom->get_num_geoms(); + ++i) { + if (geom->get_geom(i)->is_of_type(GeomNorms::get_class_type())) { + j = i; + } + } + if (j != -1) { + geom->remove_geom(j); + } + } while (j != -1); + } + return true; +} + +//hacks. the callback architecture should be changed to make this more straightfwd +static wdxGraphicsWindow *g_pCurRenderWin=NULL; +static bool g_bDoAppTraversal=false; +void render_frame(void){ //GraphicsPipe *pipe) { + + // I think we want to traverse only once/frame, so use flag + if(g_bDoAppTraversal) + app_traverser->traverse(render_top); + g_bDoAppTraversal=false; + +/* int num_windows = pipe->get_num_windows(); + for (int w = 0; w < num_windows; w++) { ????? + GraphicsWindow *win = pipe->get_window(w); + + } +*/ + + g_pCurRenderWin->get_gsg()->render_frame(); +} + +// to be used with new display callback system +class DisplayCallback : public GraphicsWindow::Callback { + public: + virtual void draw(bool) { + render_frame(/*main_pipe*/); + if (extra_display_func != NULL) + extra_display_func(); + } +}; + +// to be used with old GLUT callback system +void display_func( void ) { + render_frame(/*main_pipe*/); + if (extra_display_func != NULL) + extra_display_func(); +} + +void set_lighting(bool enabled) { + if (enabled) { + // Enable the lights on the initial state. + PT(LightTransition) la = new LightTransition; + la->set_on(light.p()); + + if (have_dlight) { + la->set_on(dlight.p()); + } + render_arc->set_transition(la); + + } else { + // Remove the lights from the initial state. + render_arc->clear_transition(LightTransition::get_class_type()); + } +} + +// to be used with new display callback system +class IdleCallback : public GraphicsWindow::Callback { + public: + virtual void idle(void) { + // Initiate the data traversal, to send device data down its + // respective pipelines. + traverse_data_graph(data_root); + + // Perform the collision traversal, if we have a collision + // traverser standing by. + if (col_trans != (CollisionTraverser *)NULL) { + col_trans->traverse(render_top); + } + + // Throw any events generated recently. + event_handler.process_events(); + + if (additional_idle != NULL) { + (*additional_idle)(); + } + } +}; + +// to be used with old GLUT callback system +void idle_func( void ) +{ + // Initiate the data traversal, to send device data down its + // respective pipelines. + traverse_data_graph(data_root); + + // Throw any events generated recently. + event_handler.process_events(); +} + +void unpause_draw(void); + +void event_esc(CPT_Event ev) { +#ifdef USE_IPC + if (forked_draw) { + quit_draw = true; + unpause_draw(); + mutex_lock m(run_render); + } +#endif + + 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; + } + + // The Escape key was pressed. Exit the application. + + rib_pipe = NULL; + rib_win = NULL; + +#ifdef DO_PSTATS + if (PStatClient::is_connected()) { + framework_cat.info() << "Disconnecting from stats host" << endl; + PStatClient::disconnect(); + } +#endif + + if(ev->get_name()=="q") { + // if app ever exits using exit() without close_window, hopefully this will work + framework_cat.debug() << "Testing unsafe, no close_window(), direct exit() of framework\n"; + } else { + delete pWinGrp; // calls close_window + pWinGrp=NULL; + framework_cat.debug() << "Exiting framework\n"; + } + +// main_win = NULL; +// main_pipe = NULL; + + exit(0); +} + +void event_f(CPT_Event) { + 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; + } +} + +void event_S(CPT_Event) { +#ifdef DO_PSTATS + framework_cat.info() << "Connecting to stats host" << endl; + PStatClient::connect(); +#else + framework_cat.error() << "Stats host not supported." << endl; +#endif +} + +void event_A(CPT_Event) { +#ifdef DO_PSTATS + if (PStatClient::is_connected()) { + framework_cat.info() << "Disconnecting from stats host" << endl; + PStatClient::disconnect(); + } else { + framework_cat.error() << "Stats host is already disconnected." << endl; + } +#else + framework_cat.error() << "Stats host not supported." << endl; +#endif +} +/* +void setup_framerate(void) { + if (framerate_top != (NamedNode*)0L) + return; + + framerate_top = new NamedNode("framerate_top"); + framerate_node = new NamedNode("framerate"); + framerate_arc = new RenderRelation(framerate_top, framerate_node); + + // Setup some overrides to turn off certain properties which we probably + // won't need for 2-d objects. + framerate_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1); + framerate_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1); + framerate_arc->set_transition(new LightTransition(LightTransition::all_off()), 1); + framerate_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1); + framerate_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1); + + // create a 2-d camera. + PT(Camera) cam2d = new Camera("framerate_cam"); + new RenderRelation(framerate_node, cam2d); + cam2d->set_scene(framerate_top); + PT(Lens) lens = new OrthographicLens; + lens->set_film_size(2.0); + cam2d->set_lens(lens); + + // Now create a new layer + // eventually this should be done through chanconfig' + GraphicsChannel *chan = main_win->get_channel(0); + nassertv(chan != (GraphicsChannel*)0L); + + framerate_layer = chan->make_layer(); + nassertv(framerate_layer != (GraphicsLayer *)0L); + framerate_layer->set_active(true); + + DisplayRegion *dr = framerate_layer->make_display_region(); + nassertv(dr != (DisplayRegion *)0L); + dr->set_camera(cam2d); + + // load the font + PT_Node font_model = loader.load_sync("cmtt12"); + + if (font_model != (NamedNode *)0L) { + framerate_font = new TextFont(font_model); + framerate_text = new TextNode("framerate_text"); + new RenderRelation(framerate_node, framerate_text); + + LMatrix4f mat = LMatrix4f::scale_mat(0.05) * + LMatrix4f::translate_mat(-0.95, 0.0, 0.95); + + framerate_text->set_transform(mat); + framerate_text->set_font(framerate_font); + framerate_text->set_card_color(0.5, 0.5, 0.5, 0.5); + framerate_text->set_card_as_margin(0.5, 0.5, 0.2, 0.2); + framerate_text->set_frame_color(1., 0., 0., 1.); + framerate_text->set_frame_as_margin(0.5, 0.5, 0.2, 0.2); + framerate_text->set_align(TM_ALIGN_LEFT); + framerate_text->set_text_color(1., 1., 1., 1.); + framerate_text->set_text("blah"); + } +} +*/ +void handle_framerate(void) { + static bool first_time = true; + static int buffer_count; + static int buffer_size = framework.GetInt("framerate-buffer", 60); + static double *prev_times = (double*)0L; + static double *deltas = (double*)0L; + + if (framerate_layer == (GraphicsLayer*)0L) + return; + + if (!framerate_layer->is_active()) { + first_time = true; + return; + } + + double now = ClockObject::get_global_clock()->get_frame_time(); + + if (first_time) { + if (prev_times == (double*)0L) { + prev_times = new double[buffer_size]; + deltas = new double[buffer_size]; + } + buffer_count = 0; + prev_times[buffer_count++] = now; + first_time = false; + } else if (buffer_count < buffer_size) { + deltas[buffer_count-1] = now - prev_times[buffer_count-1]; + prev_times[buffer_count++] = now; + } else { + deltas[buffer_size-1] = now - prev_times[buffer_size-1]; + double delta = 0.; + for (int i=0; iset_text(os.str()); + + // now roll everything down one + for (int j=0; jset_active(is_on); + is_on = !is_on; +} +*/ +void event_t(CPT_Event) { + // The "t" key was pressed. Toggle the showing of textures. + static bool textures_enabled = true; + + textures_enabled = !textures_enabled; + if (textures_enabled) { + // Remove the override from the initial state. + render_arc->clear_transition(TextureTransition::get_class_type()); + } else { + // Set an override on the initial state to disable texturing. + TextureTransition *ta = new TextureTransition; + ta->set_priority(100); + render_arc->set_transition(ta); + } +} + +void event_l(CPT_Event) { + // The "l" key was pressed. Toggle lighting. + static bool lighting_enabled = false; + + lighting_enabled = !lighting_enabled; + set_lighting(lighting_enabled); +} + +void event_w(CPT_Event) { + // The "w" key was pressed. Toggle wireframe mode. + static bool wireframe_mode = false; + + wireframe_mode = !wireframe_mode; + if (!wireframe_mode) { + // Set the normal, filled mode on the render arc. + RenderModeTransition *rma = new RenderModeTransition(RenderModeProperty::M_filled); + CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_clockwise); + render_arc->set_transition(rma); + render_arc->set_transition(cfa); + + } else { + // Set the initial state up for wireframe mode. + RenderModeTransition *rma = new RenderModeTransition(RenderModeProperty::M_wireframe); + CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_none); + render_arc->set_transition(rma); + render_arc->set_transition(cfa); + } +} + +void event_b(CPT_Event) { + // The 'b' key was pressed. Toggle backface culling. + static bool backface_mode = false; + + backface_mode = !backface_mode; + if (backface_mode) { + material->set_twoside(true); + CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_none); + render_arc->set_transition(cfa); + } else { + material->set_twoside(false); + CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_clockwise); + render_arc->set_transition(cfa); + } +} + +void event_R(CPT_Event) { + /* The "R" key was pressed. Dump a RIB file. + if (rib_win == (GraphicsWindow*)0L) + return; + nout << "Writing RIB frame " << rib_win->get_frame_number() << "\n"; + render_frame(rib_pipe); + */ +} + +void event_grave(CPT_Event) { + GraphicsStateGuardian *gsg = main_window->get_gsg(); + const RenderBuffer &rb = gsg->get_render_buffer(RenderBuffer::T_front); + + // We simply grab the first DisplayRegion on the first Layer on the + // window's main channel. + GraphicsChannel *channel = main_window->get_channel(0); + nassertv(channel != (GraphicsChannel *)NULL); + + if (channel->get_num_layers() == 0) { + nout << "Channel has no layers!\n"; + return; + } + GraphicsLayer *layer = channel->get_layer(0); + nassertv(layer != (GraphicsLayer *)NULL); + + if (layer->get_num_drs() == 0) { + nout << "Layer has no display regions!\n"; + return; + } + DisplayRegion *dr = layer->get_dr(0); + nassertv(dr != (DisplayRegion *)NULL); + + int width = dr->get_pixel_width(); + int height = dr->get_pixel_height(); + + PixelBuffer p(width, height, 3, 1, PixelBuffer::T_unsigned_byte, + PixelBuffer::F_rgb); + + nout << "Capturing frame.\n"; + + p.copy(gsg, dr, rb); + ostringstream s; + s << "frame" << ClockObject::get_global_clock()->get_frame_count() << ".pnm"; + Filename filename = s.str(); + p.write(filename); + + cerr << "Wrote " << filename << "\n"; +} + +void event_n(CPT_Event) { + static bool normals_on = false; + + normals_on = !normals_on; + + // do i do this for every window?? bugbug + for(int j=0;j_windows.size();j++) + if (normals_on) { + NormalAddTraverser trav(pWinGrp->_windows[j]->get_gsg()); + df_traverse(render, trav, NullTransitionWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + } else { + NormalDelTraverser trav(pWinGrp->_windows[j]->get_gsg()); + df_traverse(render, trav, NullTransitionWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + } + +} + +void event_C(CPT_Event) { + static bool showing_collision_solids = false; + + showing_collision_solids = !showing_collision_solids; + if (showing_collision_solids) { + // So we'll break down and use the NodePath interface in + // framework. We haven't used it here before, but it's such a + // splendid interface; why not use it? + NodePath render_path(render); + render_path.show_collision_solids(); + + } else { + NodePath render_path(render); + render_path.hide_collision_solids(); + } +} + +void event_N(CPT_Event) { + nout << "Reducing scene graph.\n"; + SceneGraphReducer gr(RenderRelation::get_class_type()); + gr.apply_transitions(first_arc); + int num_reduced = gr.flatten(root, true); + nout << "Removed " << num_reduced << " arcs.\n"; +} + +// switch_trackball() is a local function to fiddle with the dgraph +// arcs to make a different trackball be in control of the mouse. +static void +switch_trackball(Node *trackball) { + if (current_trackball != NULL) { + remove_child(mouse_watcher, current_trackball, + DataRelation::get_class_type()); + } + current_trackball = trackball; + if (current_trackball != NULL) { + new DataRelation(mouse_watcher, current_trackball); + } +} + +// set_alt_trackball() should be called by user code to change the +// alternate trackball that is in effect when the user presses "c". +void +set_alt_trackball(Node *tb) { + if (tb == NULL) { + switch_trackball(trackball); + } else { + alt_trackball = tb; + switch_trackball(alt_trackball); + } +} + + +static void +start_drive() { + // Extract the current position from the trackball. + LMatrix4f mat = trackball->get_trans_mat(); + LPoint3f scale, hpr, xyz; + decompose_matrix(mat, scale, hpr, xyz); + if (hpr[2] > 90) { + hpr[0] += 180.0; + } + hpr[1] = 0.0; + hpr[2] = 0.0; + + drive_interface->set_pos(xyz); + drive_interface->set_hpr(hpr); + + // Make sure the ray-downcaster is set, so we maintain a constant + // height above the ground. + if (col_trans == (CollisionTraverser *)NULL) { + ray_node = new CollisionNode("ray"); + ray_node->set_into_collide_mask(0); + ray_node->set_from_collide_mask(drive_mask); + + NodeRelation *arc = new RenderRelation(camera_top, ray_node); + + ray_node->add_solid(new CollisionRay(LPoint3f(0.0, 0.0, 0.0), + LVector3f::down())); + arc->set_transition(new PruneTransition); + + col_trans = new CollisionTraverser; + col_handler = new CollisionHandlerFloor; + col_handler->set_offset(drive_height); + col_trans->add_collider(ray_node, col_handler); + col_handler->add_collider(ray_node, drive_interface); + } +} + +static void +stop_drive() { + // Extract the current position from the drive interface and + // restore it to the trackball. + LPoint3f xyz = drive_interface->get_pos(); + LPoint3f hpr = drive_interface->get_hpr(); + LPoint3f scale(1.0, 1.0, 1.0); + LMatrix4f mat; + compose_matrix(mat, scale, hpr, xyz); + trackball->set_mat(invert(mat)); + trackball->reset_origin_here(); +} + +void event_c(CPT_Event) { + // "c" key pressed: change to alternate controls. + + if (current_trackball == trackball) { + if (alt_trackball != NULL) { + switch_trackball(alt_trackball); + } + } else { + if (current_trackball == drive_interface) { + stop_drive(); + } + switch_trackball(trackball); + } +} + +void event_D(CPT_Event) { + // "D" key pressed: toggle drive controls. + if (current_trackball == drive_interface) { + stop_drive(); + switch_trackball(trackball); + + } else { + start_drive(); + set_alt_trackball(drive_interface); + } +} + +// sample code to verify and pick a new fullscreen size dynamically +#define NUMWINDOWSIZES 5 +static int cur_winsize_idx=0; +static unsigned int window_sizearr[NUMWINDOWSIZES*2] = + {640,480, 1024,768, 800,600, 454,656, 1280,1024}; + +void event_3(CPT_Event) { + do { + cur_winsize_idx++; + cur_winsize_idx %= NUMWINDOWSIZES; + // skip over the ones marked as bad (0) + } while(window_sizearr[cur_winsize_idx*2]==0); + + for(int j=0;j_windows.size();j++) + pWinGrp->_windows[j]->resize(window_sizearr[cur_winsize_idx*2],window_sizearr[cur_winsize_idx*2+1]); +} + +void event_p(CPT_Event) { + // "p" key pressed: print pos, hpr + LPoint3f xyz; + LPoint3f hpr; + + if (current_trackball == trackball) { + xyz = trackball->get_pos(); + hpr = trackball->get_hpr(); + } else if (current_trackball == drive_interface) { + xyz = drive_interface->get_pos(); + hpr = drive_interface->get_hpr(); + } + + printf("current pos, hpr: %f %f %f %f %f %f\n",xyz[0],xyz[1],xyz[2],hpr[0],hpr[1],hpr[2]); +} + +void event_P(CPT_Event) { + // "P" key pressed: set pos, hpr + LPoint3f xyz; + LPoint3f hpr; + + printf("input new pos, hpr in fmt: f f f f f f\n"); + scanf("%f %f %f %f %f %f",&xyz[0],&xyz[1],&xyz[2],&hpr[0],&hpr[1],&hpr[2]); + + if (current_trackball == trackball) { + trackball->set_pos(xyz); + trackball->set_hpr(hpr); + } else if (current_trackball == drive_interface) { + drive_interface->set_pos(xyz); + drive_interface->set_hpr(hpr); + + } +} + +void event_g(CPT_Event) { + // "g" key pressed: toggle fog. + static bool fog_mode = false; + + fog_mode = !fog_mode; + if (fog_mode) { + FogTransition *fa = new FogTransition(fog); + render_arc->set_transition(fa); + } else { + FogTransition *fa = new FogTransition; + render_arc->set_transition(fa); + } +} + +#ifdef USE_IPC +/* +void pause_draw(void) { + if (!render_running) + return; + run_render.lock(); + render_running = false; + framework_cat.info() << "draw thread paused" << endl; +} + +void unpause_draw(void) { + if (render_running) + return; + run_render.unlock(); + render_running = true; + framework_cat.info() << "draw thread continuing" << endl; +} + +void draw_loop(void*) { + for (;!quit_draw;) { + mutex_lock m(run_render); + main_win->update(); + handle_framerate(); + } + framework_cat.info() << "draw thread exiting" << endl; +} + +void event_x(CPT_Event) { + // "x" key pressed: pause/unpause draw + if (!forked_draw) + return; + if (render_running) + pause_draw(); + else + unpause_draw(); +} +*/ +#endif + +#define RANDFRAC (rand()/(float)(RAND_MAX)) + +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; +} gridded_file_info; + +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; + +// making these fns to get around ridiculous VC++ matrix inlining bugs at Opt2 +static void move_gridded_stuff(GriddedMotionType gridmotiontype,gridded_file_info *InfoArr, RenderRelation **pRRptrArr, 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; + } + pRRptrArr[i]->set_transition(new TransformTransition(xfm_mat)); + } +} + +int framework_main(int argc, char *argv[]) { + pystub(); + + /* + // The first thing we should do is to set up a multiplexing Notify. + MultiplexStream *mstream = new MultiplexStream; + Notify::ptr()->set_ostream_ptr(mstream, true); + mstream->add_standard_output(); + mstream->add_system_debug(); + + string framework_notify_output = framework.GetString("framework-notify-output", ""); + if (!framework_notify_output.empty()) { + if (!mstream->add_file(framework_notify_output)) { + framework_cat.error() + << "Unable to open " << framework_notify_output << " for output.\n"; + } else { + framework_cat.info() + << "Sending Notify output to " << framework_notify_output << "\n"; + } + } + */ + + GeomNorms::init_type(); + +#ifndef DEBUG + // This just makes sure that no one changed the value of a + // _type_handle member after the type was registered. It shouldn't + // ever happen. If it did, most likely two classes are sharing the + // same _type_handle variable for some reason. + TypeRegistry::reregister_types(); +#endif + + app_traverser = new AppTraverser(RenderRelation::get_class_type()); + + // Allow the specification of multiple files on the command + // line. This is handy, for instance, to load up both a character + // and its animation file. + + typedef pvector Files; + Files files; + Files gridded_files; + + if (first_init != NULL) + first_init(); + + Files *pFileCollection = &files; + + int gridrepeats=1; + GriddedMotionType gridmotiontype = None; + + // bool bRotateGriddedObjs = false; + // bool bMoveGriddedObjs = false; + + for (int a = 1; a < argc; a++) { + if ((argv[a] != (char*)0L) && ((argv[a])[0] != '-') && + ((argv[a])[0] != '+') && ((argv[a])[0] != '#')) + pFileCollection->push_back(Filename::from_os_specific(argv[a])); + else switch((argv[a])[1]) { + case 'r': + gridmotiontype = Rotation; + break; + + case 'm': + gridmotiontype = LinearMotion; + break; + + case 'g': { + pFileCollection = &gridded_files; + + char *pStr=(argv[a])+2; + if (*pStr != '\0') { + gridrepeats=atoi(pStr); + if(gridrepeats<1) + gridrepeats=1; + + } + break; + } + } + } + +#if 0 + // load display modules + GraphicsPipe::resolve_modules(); + + framework_cat.info() << "Known pipe types:" << endl; + GraphicsPipe::get_factory().write_types(framework_cat.info(false), 2); + + // Create a window + main_pipe = GraphicsPipe::get_factory(). + make_instance(InteractiveGraphicsPipe::get_class_type()); + + if (main_pipe == (GraphicsPipe*)0L) { + framework_cat.error() + << "No interactive pipe is available! Check your Configrc!\n"; + exit(1); + } + + framework_cat.info() + << "Opened a '" << main_pipe->get_type().get_name() << "' interactive graphics pipe." << endl; + + rib_pipe = NULL; +#endif +#if 0 + rib_pipe = GraphicsPipe::get_factory(). + make_instance(NoninteractiveGraphicsPipe::get_class_type()); + + if (rib_pipe == (GraphicsPipe*)0L) + framework_cat.info() + << "Did not open a non-interactive graphics pipe, features related" + << " to that will\nbe disabled." << endl; + else + framework_cat.info() + << "Opened a '" << rib_pipe->get_type().get_name() + << "' non-interactive graphics pipe." << endl; +#endif + + // Create the render node + render_top = new NamedNode("render_top"); + render = new NamedNode("render"); + render_arc = new RenderRelation(render_top, render); + + camera_top = new NamedNode("camera_top"); + camera_top_arc = new RenderRelation(render, camera_top); + + // bypass chancfg stuff and do this directly + + int NumWindows=framework.GetInt("num-windows", 1); + + GraphicsWindow::Properties *pWinProps = new GraphicsWindow::Properties[NumWindows]; + memset(pWinProps,0,NumWindows*sizeof(GraphicsWindow::Properties)); + + pWinProps[0]._title=framework.GetString("win-title", "PandaWin"); + pWinProps[0]._xsize=framework.GetInt("win-width", 640); + pWinProps[0]._ysize=framework.GetInt("win-height", 480); + pWinProps[0]._fullscreen=framework.GetBool("fullscreen", true); + pWinProps[0]._border=!framework.GetBool("no-border", false); + pWinProps[0]._want_depth_bits=1; + pWinProps[0]._want_color_bits=1; + pWinProps[0]._mask = W_RGBA | W_DOUBLE | W_DEPTH; + + int jjj; + for(jjj=1;jjj_windows[jjj]->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(Camera) camera = new Camera; + PT(Lens) lens = new PerspectiveLens; + lens->set_film_size(pWinProps->_xsize,pWinProps->_ysize); + camera->set_lens(lens); + dr->set_camera(camera); + + // Window setup is complete. Now we just need to make a scene graph + // for the camera to render. + camera->set_scene(render_top); + new RenderRelation(camera_top,camera); + } + + main_window=pWinGrp->_windows[0]; + + delete [] pWinProps; // dont need these anymore + pWinProps=NULL; + +#if 0 + ChanCfgOverrides override; + + // need to find a better way to differentiate unsigned int from regular + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE))); + override.setField(ChanCfgOverrides::Title, "Demo"); + + std::string conf = framework.GetString("chan-config", chan_config); + if (extra_overrides_func != NULL) + extra_overrides_func(override, conf); + + ChanConfig chanConfig = ChanConfig(main_pipe, conf, render_top, override); + main_win = chanConfig.get_win(); + assert(main_win != (GraphicsWindow*)0L); + camera_top = chanConfig.get_group_node(0); + + camera_top->set_name("camera_top"); + for(int group_node_index=1;group_node_indexset_name("rib_cameras"); + for(int rib_group_node_index=1; + rib_group_node_indexset_constant_attenuation( 2.0 ); + plight->set_linear_attenuation( 1.0 ); + plight->set_quadratic_attenuation( 0.5 ); + slight = new Spotlight( "spot" ); + new RenderRelation( lights, light ); +#if 0 + new RenderRelation( lights, dlight ); + new RenderRelation( lights, plight ); + new RenderRelation( lights, slight ); +#endif + + // Turn on culling. + CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_clockwise); + render_arc->set_transition(cfa); + + // Set up a default material. + material = new Material; + material->set_ambient( Colorf( 1, 1, 1, 1 ) ); + MaterialTransition *ma = new MaterialTransition(material); + render_arc->set_transition(ma); + + // Set up a default fog + fog = new Fog; + + // Create the data graph root. + data_root = new NamedNode( "data" ); + + // Create a mouse and put it in the data graph. + mak = new MouseAndKeyboard( main_window, 0 ); + new DataRelation(data_root, mak); + + // Create a MouseWatcher underneath the mouse, so we can have some + // 2-d control effects. + mouse_watcher = new MouseWatcher("mouse_watcher"); + new DataRelation(mak, mouse_watcher); + mouse_watcher->set_button_down_pattern("mw-%r-%b"); + mouse_watcher->set_button_up_pattern("mw-%r-%b-up"); + mouse_watcher->set_enter_pattern("mw-in-%r"); + mouse_watcher->set_leave_pattern("mw-out-%r"); + + // Create a trackball to handle the mouse input. + trackball = new Trackball("trackball"); + trackball->set_pos(LVector3f::forward() * 50.0); + + // Also create a drive interface. The user might switch to this + // later. + drive_interface = new DriveInterface("drive_interface"); + + new DataRelation(mouse_watcher, trackball); + current_trackball = trackball; + + // Connect the trackball output to the camera's transform. + PT(Transform2SG) tball2cam = new Transform2SG("tball2cam"); +// tball2cam->set_arc(arc1); + tball2cam->set_arc(camera_top_arc); + new DataRelation(trackball, tball2cam); + + PT(Transform2SG) drive2cam = new Transform2SG("drive2cam"); +// drive2cam->set_arc(arc1); + drive2cam->set_arc(camera_top_arc); + new DataRelation(drive_interface, drive2cam); + + // Create a ButtonThrower to throw events from the keyboard. + PT(ButtonThrower) et = new ButtonThrower("kb-events"); + ModifierButtons mods; + mods.add_button(KeyboardButton::shift()); + mods.add_button(KeyboardButton::control()); + mods.add_button(KeyboardButton::alt()); + et->set_modifier_buttons(mods); + new DataRelation(mouse_watcher, et); + + root = new NamedNode("root"); + first_arc = new RenderRelation(render, root, 100); + + ////// for gridded stuff + PT_Node *pNodeArr=NULL; + RenderRelation **pRRptrArr=NULL; + gridded_file_info *InfoArr=NULL; + int gridded_files_size=0; + ////////////////// + + if (files.empty() && gridded_files.empty() && framework.GetBool("have-omnitriangle", true)) { + // The user did not specify a model file to load. Create some + // default geometry. + + 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); + + geomnode = new GeomNode; + geomnode->add_geom(geom.p()); + RenderRelation *arc = new RenderRelation(root, geomnode, 10); + first_arc = arc; + + Texture *tex = TexturePool::load_texture("rock-floor.rgb"); + if (tex != (Texture *)NULL) { + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + arc->set_transition(new TextureTransition(tex)); + } + + } else { + // Load up some geometry from one or more files. + DSearchPath local_path("."); + + + Files::const_iterator fi; + for (fi = files.begin(); fi != files.end(); ++fi) { + Filename filename = (*fi); + + // 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_Node node = loader.load_sync(filename); + + if (node == (Node *)NULL) { + framework_cat.error() << "Unable to load file " << filename << "\n"; + continue; + } + + new RenderRelation(root, node); + } + + if(!gridded_files.empty()) { + + typedef RenderRelation *RenderRelationPtr; + + gridded_files_size= gridded_files.size(); + pNodeArr = new PT_Node[gridded_files.size()*gridrepeats]; + pRRptrArr = new RenderRelationPtr[gridded_files.size()*gridrepeats]; + InfoArr = new gridded_file_info[gridded_files.size()*gridrepeats]; + + int j=0; + + for (fi = gridded_files.begin(); fi != gridded_files.end(); (++fi),j++) { + Filename filename = (*fi); + + filename.resolve_filename(local_path); + + pNodeArr[j] = loader.load_sync(filename); + + if (pNodeArr[j] == (Node *)NULL) { + framework_cat.error() << "Unable to load file " << filename << "\n"; + j--; + gridded_files_size--; + continue; + } + } + + gridwidth=1; + while(gridwidth*gridwidth < gridded_files_size*gridrepeats) { + gridwidth++; + } + + grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0; + wander_area_pos_offset = -max(fabs(grid_pos_offset),MIN_WANDERAREA_DIMENSION/2.0); + + float xpos = grid_pos_offset; + float ypos = grid_pos_offset; + int filenum=0; + + srand( (unsigned)time( NULL ) ); + + double now = ClockObject::get_global_clock()->get_frame_time(); + + for(int passnum=0;passnum0) { + // cant directly instance characters due to LOD problems, + // must copy using copy_subgraph for now + + pNodeArr[filenum] = pNodeArr[j]->copy_subgraph(RenderRelation::get_class_type()); + } + + pRRptrArr[filenum] = new RenderRelation(root, pNodeArr[filenum]); + + 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 + + InfoArr[filenum].starttime = now; + + InfoArr[filenum].xcenter=xpos; + InfoArr[filenum].ycenter=ypos; + InfoArr[filenum].ang1=RANDFRAC * 360.0; + InfoArr[filenum].ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL; + + InfoArr[filenum].ang2=RANDFRAC * 360.0; + InfoArr[filenum].ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL; + + InfoArr[filenum].radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS; + + if(RANDFRAC>0.5) { + InfoArr[filenum].ang1_vel=-InfoArr[filenum].ang1_vel; + } + + if(RANDFRAC>0.5) { + InfoArr[filenum].ang2_vel=-InfoArr[filenum].ang2_vel; + } + + // xforms happen left to right + LVector2f new_center = LVector2f(InfoArr[filenum].radius,0.0) * + LMatrix3f::rotate_mat(InfoArr[filenum].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(InfoArr[filenum].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)) + + InfoArr[filenum].vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL; + + InfoArr[filenum].xstart=xpos; + InfoArr[filenum].ystart=ypos; + + InfoArr[filenum].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + InfoArr[filenum].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; + + InfoArr[filenum].starttime = now; + + float xdel = InfoArr[filenum].xdel = InfoArr[filenum].xend-InfoArr[filenum].xstart; + float ydel = InfoArr[filenum].ydel = InfoArr[filenum].yend-InfoArr[filenum].ystart; + + InfoArr[filenum].endtime = csqrt(xdel*xdel+ydel*ydel)/InfoArr[filenum].vel; + + InfoArr[filenum].timedel = InfoArr[filenum].endtime - InfoArr[filenum].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,InfoArr[filenum].rotmat); + + LVector3f translate_vec(xpos, ypos, 0.0); + LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec); + + xfm_mat = InfoArr[filenum].rotmat * tmat2; + } else { + LVector3f translate_vec(xpos, ypos, 0.0); + xfm_mat = LMatrix4f::translate_mat(translate_vec); + } + + pRRptrArr[filenum]->set_transition(new TransformTransition(xfm_mat)); + + if(((filenum+1) % gridwidth) == 0) { + xpos= -gridwidth*GRIDCELLSIZE/2.0; + ypos+=GRIDCELLSIZE; + } else { + xpos+=GRIDCELLSIZE; + } + } + } + + } + + // If we happened to load up both a character file and its + // matching animation file, attempt to bind them together now. + AnimControlCollection anim_controls; + auto_bind(root, anim_controls, ~0); + anim_controls.loop_all(true); + } + + // Now prepare all the textures with the GSG. + NodePath render_path(render); + + for(jjj=0;jjj_windows[jjj]; + render_path.prepare_scene(pWin->get_gsg()); + + // sample code to verify and pick a new fullscreen size dynamically + if(pWin->verify_window_sizes(NUMWINDOWSIZES,window_sizearr)==0) { + framework_cat.error() << "None of the potential new fullscreen sizes are valid\n"; + exit(1); + } + + if (framework.Defined("clear-value")) { + float cf = framework.GetFloat("clear-value", 0.0f); + Colorf c(cf, cf, cf, 1.0f); + pWin->get_gsg()->set_color_clear_value(c); + } + } + + // Set up keyboard events. + event_handler.add_hook("escape", event_esc); + event_handler.add_hook("q", event_esc); + event_handler.add_hook("3", event_3); + event_handler.add_hook("f", event_f); +// event_handler.add_hook("shift-F", event_f_full); + event_handler.add_hook("t", event_t); + event_handler.add_hook("l", event_l); + event_handler.add_hook("w", event_w); + event_handler.add_hook("b", event_b); + event_handler.add_hook("shift-R", event_R); + event_handler.add_hook("`", event_grave); + event_handler.add_hook("n", event_n); + event_handler.add_hook("c", event_c); + event_handler.add_hook("shift-D", event_D); + event_handler.add_hook("g", event_g); + event_handler.add_hook("shift-C", event_C); + event_handler.add_hook("shift-N", event_N); + event_handler.add_hook("shift-S", event_S); + event_handler.add_hook("shift-A", event_A); + event_handler.add_hook("p", event_p); + event_handler.add_hook("shift-P", event_P); + +#ifdef USE_IPC + event_handler.add_hook("x", event_x); +#endif + + if (define_keys != NULL) + define_keys(event_handler); + + // Now that we've called define_keys() (which might or might not + // set have_dlight), we can turn on lighting. + set_lighting(false); + + // 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(); + start_time = ClockObject::get_global_clock()->get_frame_time(); + start_frame_count = ClockObject::get_global_clock()->get_frame_count(); + + DisplayCallback dcb; + IdleCallback icb; + + for(jjj=0;jjj_windows[jjj]; + pWin->set_draw_callback(&dcb); + pWin->set_idle_callback(&icb); + } + +#if 0 + if (!pWinGrp->_windows[jjj]->supports_update()) { + framework_cat.info() + << "Window type " << main_win->get_type() + << " supports only the glut-style main loop interface.\n"; + + pWin->register_draw_function(display_func); + pWin->register_idle_function(idle_func); + pWin->main_loop(); + + } else +#endif + + { +#ifdef USE_IPC + if (forked_draw) { + framework_cat.info() << "forking draw thread" << endl; + draw_thread = thread::create(draw_loop); + for (;;) + icb.idle(); + } else +#endif + { + while(1) { + g_bDoAppTraversal=true; + for(jjj=0;jjj_windows[jjj]; + g_pCurRenderWin->update(); + } + ClockObject::get_global_clock()->tick(); + throw_event("NewFrame"); + + handle_framerate(); + + if((!gridded_files.empty()) && gridmotiontype) { + move_gridded_stuff(gridmotiontype,InfoArr, pRRptrArr, gridded_files_size*gridrepeats); + } + } + } + } + + if(!gridded_files.empty()) { + delete [] pNodeArr; + delete [] pRRptrArr; + delete [] InfoArr; + } + return 1; +}