diff --git a/.gitignore b/.gitignore index b7923b22be..416d087716 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /built_x64 /built -/samples /thirdparty diff --git a/contrib/src/ai/aiBehaviors.cxx b/contrib/src/ai/aiBehaviors.cxx index da17488c8d..f629c0599c 100644 --- a/contrib/src/ai/aiBehaviors.cxx +++ b/contrib/src/ai/aiBehaviors.cxx @@ -18,7 +18,7 @@ static const float _PI = 3.14; AIBehaviors::AIBehaviors() { - _steering_force = LVecBase3f(0.0, 0.0, 0.0); + _steering_force = LVecBase3(0.0, 0.0, 0.0); _behaviors_flags = _behaviors_flags & _none; _previous_conflict = false; _conflict = false; @@ -67,7 +67,7 @@ bool AIBehaviors::is_conflict() { } if(is_on(_flee)) { - LVecBase3f dirn = _flee_force; + LVecBase3 dirn = _flee_force; dirn.normalize(); _flee_force = _steering_force.length() * dirn * _flee_obj->_flee_weight; } @@ -77,7 +77,7 @@ bool AIBehaviors::is_conflict() { } if(is_on(_evade)) { - LVecBase3f dirn = _evade_force; + LVecBase3 dirn = _evade_force; dirn.normalize(); _evade_force = _steering_force.length() * dirn * _evade_obj->_evade_weight; } @@ -111,9 +111,9 @@ bool AIBehaviors::is_conflict() { ///////////////////////////////////////////////////////////////////////////////////////////////////// -void AIBehaviors::accumulate_force(string force_type, LVecBase3f force) { +void AIBehaviors::accumulate_force(string force_type, LVecBase3 force) { - LVecBase3f old_force; + LVecBase3 old_force; if(force_type == "seek") { old_force = _seek_force; @@ -169,8 +169,8 @@ void AIBehaviors::accumulate_force(string force_type, LVecBase3f force) { ////////////////////////////////////////////////////////////////////////////////////////////// -LVecBase3f AIBehaviors::calculate_prioritized() { - LVecBase3f force; +LVecBase3 AIBehaviors::calculate_prioritized() { + LVecBase3 force; if(is_on(_seek)) { if(_conflict) { @@ -299,13 +299,13 @@ LVecBase3f AIBehaviors::calculate_prioritized() { if(is_on(_arrival)) { if(_seek_obj != NULL) { - LVecBase3f dirn = _steering_force; + LVecBase3 dirn = _steering_force; dirn.normalize(); _steering_force = ((_steering_force.length() - _arrival_force.length()) * dirn); } if(_pursue_obj != NULL) { - LVecBase3f dirn = _steering_force; + LVecBase3 dirn = _steering_force; dirn.normalize(); _steering_force = ((_steering_force.length() - _arrival_force.length()) * _arrival_obj->_arrival_direction); } @@ -639,7 +639,7 @@ void AIBehaviors::resume_ai(string ai_type) { // Function : seek // Description : This function activates seek and makes an object of the Seek class. // This is the function we want the user to call for seek to be done. -// This function is overloaded to accept a NodePath or an LVecBase3f. +// This function is overloaded to accept a NodePath or an LVecBase3. ///////////////////////////////////////////////////////////////////////////////// @@ -648,7 +648,7 @@ void AIBehaviors::seek(NodePath target_object, float seek_wt) { turn_on("seek"); } -void AIBehaviors::seek(LVecBase3f pos, float seek_wt) { +void AIBehaviors::seek(LVecBase3 pos, float seek_wt) { _seek_obj = new Seek(_ai_char, pos, seek_wt); turn_on("seek"); } @@ -657,7 +657,7 @@ void AIBehaviors::seek(LVecBase3f pos, float seek_wt) { // // Function : flee // Description : This function activates flee_activate and creates an object of the Flee class. -// This function is overloaded to accept a NodePath or an LVecBase3f. +// This function is overloaded to accept a NodePath or an LVecBase3. ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -668,7 +668,7 @@ void AIBehaviors::flee(NodePath target_object, double panic_distance, double rel turn_on("flee_activate"); } -void AIBehaviors::flee(LVecBase3f pos, double panic_distance, double relax_distance, float flee_wt) { +void AIBehaviors::flee(LVecBase3 pos, double panic_distance, double relax_distance, float flee_wt) { _flee_obj = new Flee(_ai_char, pos, panic_distance, relax_distance, flee_wt); _flee_list.insert(_flee_list.end(), *_flee_obj); @@ -769,38 +769,38 @@ void AIBehaviors::flock_activate() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f AIBehaviors::do_flock() { +LVecBase3 AIBehaviors::do_flock() { //! Initialize variables required to compute the flocking force on the ai char. unsigned int neighbor_count = 0; - LVecBase3f separation_force = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f alignment_force = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f cohesion_force = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f avg_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f total_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f avg_center_of_mass = LVecBase3f(0.0, 0.0, 0.0); - LVecBase3f total_center_of_mass = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3 separation_force = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 alignment_force = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 cohesion_force = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 avg_neighbor_heading = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 total_neighbor_heading = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 avg_center_of_mass = LVecBase3(0.0, 0.0, 0.0); + LVecBase3 total_center_of_mass = LVecBase3(0.0, 0.0, 0.0); //! Loop through all the other AI units in the flock to check if they are neigbours. for(unsigned int i = 0; i < _flock_group->_ai_char_list.size(); i++) { if(_flock_group->_ai_char_list[i]->_name != _ai_char->_name) { //! Using visibilty cone to detect neighbors. - LVecBase3f dist_vect = _flock_group->_ai_char_list[i]->_ai_char_np.get_pos() - _ai_char->_ai_char_np.get_pos(); - LVecBase3f ai_char_heading = _ai_char->get_velocity(); + LVecBase3 dist_vect = _flock_group->_ai_char_list[i]->_ai_char_np.get_pos() - _ai_char->_ai_char_np.get_pos(); + LVecBase3 ai_char_heading = _ai_char->get_velocity(); ai_char_heading.normalize(); //! Check if the current unit is a neighbor. if(dist_vect.dot(ai_char_heading) > ((dist_vect.length()) * (ai_char_heading.length()) * cos(_flock_group->_flock_vcone_angle * (_PI / 180))) && (dist_vect.length() < _flock_group->_flock_vcone_radius)) { //! Separation force calculation. - LVecBase3f ai_char_to_units = _ai_char->_ai_char_np.get_pos() - _flock_group->_ai_char_list[i]->_ai_char_np.get_pos(); + LVecBase3 ai_char_to_units = _ai_char->_ai_char_np.get_pos() - _flock_group->_ai_char_list[i]->_ai_char_np.get_pos(); float to_units_dist = ai_char_to_units.length(); ai_char_to_units.normalize(); separation_force += (ai_char_to_units / to_units_dist); //! Calculating the total heading and center of mass of all the neighbors. - LVecBase3f neighbor_heading = _flock_group->_ai_char_list[i]->get_velocity(); + LVecBase3 neighbor_heading = _flock_group->_ai_char_list[i]->get_velocity(); neighbor_heading.normalize(); total_neighbor_heading += neighbor_heading; total_center_of_mass += _flock_group->_ai_char_list[i]->_ai_char_np.get_pos(); @@ -814,7 +814,7 @@ LVecBase3f AIBehaviors::do_flock() { if(neighbor_count > 0) { //! Alignment force calculation avg_neighbor_heading = total_neighbor_heading / neighbor_count; - LVector3f ai_char_heading = _ai_char->get_velocity(); + LVector3 ai_char_heading = _ai_char->get_velocity(); ai_char_heading.normalize(); avg_neighbor_heading -= ai_char_heading; avg_neighbor_heading.normalize(); @@ -822,7 +822,7 @@ LVecBase3f AIBehaviors::do_flock() { //! Cohesion force calculation avg_center_of_mass = total_center_of_mass / neighbor_count; - LVecBase3f cohesion_dir = avg_center_of_mass - _ai_char->_ai_char_np.get_pos(); + LVecBase3 cohesion_dir = avg_center_of_mass - _ai_char->_ai_char_np.get_pos(); cohesion_dir.normalize(); cohesion_force = cohesion_dir * _ai_char->_movt_force; } @@ -830,7 +830,7 @@ LVecBase3f AIBehaviors::do_flock() { _flock_done = true; turn_off("flock"); turn_on("flock_activate"); - return(LVecBase3f(0.0, 0.0, 0.0)); + return(LVecBase3(0.0, 0.0, 0.0)); } //! Calculate the resultant force on the ai character by taking into account the separation, alignment and cohesion @@ -885,7 +885,7 @@ void AIBehaviors::path_follow(float follow_wt) { ///////////////////////////////////////////////////////////////////////////////// -void AIBehaviors::add_to_path(LVecBase3f pos) { +void AIBehaviors::add_to_path(LVecBase3 pos) { _path_follow_obj->add_to_path(pos); } @@ -923,7 +923,7 @@ void AIBehaviors::init_path_find(const char* navmesh_filename) { /////////////////////////////////////////////////////////////////////////////////////// -void AIBehaviors::path_find_to(LVecBase3f pos, string type) { +void AIBehaviors::path_find_to(LVecBase3 pos, string type) { _path_find_obj->path_find(pos, type); } @@ -1331,56 +1331,56 @@ switch(char_to_int(ai_type)) { if (is_on(_seek)) { _behaviors_flags ^= _seek; } - _seek_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _seek_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 2: { if (is_on(_flee)) { _behaviors_flags ^= _flee; } - _flee_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _flee_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 3: { if(is_on(_pursue)) { _behaviors_flags ^= _pursue; } - _pursue_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _pursue_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 4: { if(is_on(_evade)) { _behaviors_flags ^= _evade; } - _evade_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _evade_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 5: { if (is_on(_arrival)) { _behaviors_flags ^= _arrival; } - _arrival_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _arrival_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 6: { if(is_on(_flock)) { _behaviors_flags ^= _flock; } - _flock_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _flock_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 7: { if(is_on(_wander)) { _behaviors_flags ^= _wander; } - _wander_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _wander_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 8: { if(is_on(_obstacle_avoidance)) { _behaviors_flags ^= _obstacle_avoidance; } - _obstacle_avoidance_force = LVecBase3f(0.0f, 0.0f, 0.0f); + _obstacle_avoidance_force = LVecBase3(0.0f, 0.0f, 0.0f); break; } case 9:{ diff --git a/contrib/src/ai/aiBehaviors.h b/contrib/src/ai/aiBehaviors.h index 45a2761023..edba504662 100644 --- a/contrib/src/ai/aiBehaviors.h +++ b/contrib/src/ai/aiBehaviors.h @@ -73,41 +73,41 @@ public: Flock *_flock_group; int _behaviors_flags; - LVecBase3f _steering_force; + LVecBase3 _steering_force; Seek *_seek_obj; - LVecBase3f _seek_force; + LVecBase3 _seek_force; Flee *_flee_obj; - LVecBase3f _flee_force; + LVecBase3 _flee_force; //! This list is used if the ai character needs to flee from multiple onjects. ListFlee _flee_list; ListFlee::iterator _flee_itr; Pursue *_pursue_obj; - LVecBase3f _pursue_force; + LVecBase3 _pursue_force; Evade *_evade_obj; - LVecBase3f _evade_force; + LVecBase3 _evade_force; //! This list is used if the ai character needs to evade from multiple onjects. ListEvade _evade_list; ListEvade::iterator _evade_itr; Arrival *_arrival_obj; - LVecBase3f _arrival_force; + LVecBase3 _arrival_force; //! Since Flock is a collective behavior the variables are declared within the AIBehaviors class. float _flock_weight; - LVecBase3f _flock_force; + LVecBase3 _flock_force; bool _flock_done; Wander * _wander_obj; - LVecBase3f _wander_force; + LVecBase3 _wander_force; ObstacleAvoidance *_obstacle_avoidance_obj; - LVecBase3f _obstacle_avoidance_force; + LVecBase3 _obstacle_avoidance_force; PathFollow *_path_follow_obj; @@ -127,20 +127,20 @@ public: bool is_conflict(); - void accumulate_force(string force_type, LVecBase3f force); - LVecBase3f calculate_prioritized(); + void accumulate_force(string force_type, LVecBase3 force); + LVecBase3 calculate_prioritized(); void flock_activate(); - LVecBase3f do_flock(); + LVecBase3 do_flock(); int char_to_int(string ai_type); PUBLISHED: void seek(NodePath target_object, float seek_wt = 1.0); - void seek(LVecBase3f pos, float seek_wt = 1.0); + void seek(LVecBase3 pos, float seek_wt = 1.0); void flee(NodePath target_object, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0); - void flee(LVecBase3f pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0); + void flee(LVecBase3 pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0); void pursue(NodePath target_object, float pursue_wt = 1.0); @@ -155,12 +155,12 @@ PUBLISHED: void obstacle_avoidance(float feeler_length = 1.0); void path_follow(float follow_wt); - void add_to_path(LVecBase3f pos); + void add_to_path(LVecBase3 pos); void start_follow(string type = "normal"); // should have different function names. void init_path_find(const char* navmesh_filename); - void path_find_to(LVecBase3f pos, string type = "normal"); + void path_find_to(LVecBase3 pos, string type = "normal"); void path_find_to(NodePath target, string type = "normal"); void add_static_obstacle(NodePath obstacle); void add_dynamic_obstacle(NodePath obstacle); diff --git a/contrib/src/ai/aiCharacter.cxx b/contrib/src/ai/aiCharacter.cxx index e5a95005aa..fffa7ac2bb 100644 --- a/contrib/src/ai/aiCharacter.cxx +++ b/contrib/src/ai/aiCharacter.cxx @@ -23,8 +23,8 @@ AICharacter::AICharacter(string model_name, NodePath model_np, double mass, doub _max_force = max_force; _movt_force = movt_force; - _velocity = LVecBase3f(0.0, 0.0, 0.0); - _steering_force = LVecBase3f(0.0, 0.0, 0.0); + _velocity = LVecBase3(0.0, 0.0, 0.0); + _steering_force = LVecBase3(0.0, 0.0, 0.0); _steering = new AIBehaviors(); _steering->_ai_char = this; @@ -43,48 +43,43 @@ AICharacter::~AICharacter() { // This also makes the character look at the direction of the force. ///////////////////////////////////////////////////////////////////////////////////////// - -void AICharacter::update() { - - if(!_steering->is_off(_steering->_none)) { - - LVecBase3f old_pos = _ai_char_np.get_pos(); - - LVecBase3f steering_force = _steering->calculate_prioritized(); - - LVecBase3f acceleration = steering_force / _mass; +void AICharacter:: +update() { + if (!_steering->is_off(_steering->_none)) { + LVecBase3 old_pos = _ai_char_np.get_pos(); + LVecBase3 steering_force = _steering->calculate_prioritized(); + LVecBase3 acceleration = steering_force / _mass; _velocity = acceleration; - LVecBase3f direction = _steering->_steering_force; + LVecBase3 direction = _steering->_steering_force; direction.normalize(); _ai_char_np.set_pos(old_pos + _velocity) ; - if(steering_force.length() > 0) { + if (steering_force.length() > 0) { _ai_char_np.look_at(old_pos + (direction * 5)); _ai_char_np.set_h(_ai_char_np.get_h() + 180); _ai_char_np.set_p(-_ai_char_np.get_p()); _ai_char_np.set_r(-_ai_char_np.get_r()); } - } - else { - _steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_seek_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_flee_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_evade_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_flock_force = LVecBase3f(0.0, 0.0, 0.0); - _steering->_wander_force = LVecBase3f(0.0, 0.0, 0.0); + } else { + _steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_seek_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_flee_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_evade_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_flock_force = LVecBase3(0.0, 0.0, 0.0); + _steering->_wander_force = LVecBase3(0.0, 0.0, 0.0); } } -LVecBase3f AICharacter::get_velocity() { +LVecBase3 AICharacter::get_velocity() { return _velocity; } -void AICharacter::set_velocity(LVecBase3f velocity) { +void AICharacter::set_velocity(LVecBase3 velocity) { _velocity = velocity; } diff --git a/contrib/src/ai/aiCharacter.h b/contrib/src/ai/aiCharacter.h index 87a7925dda..f7af68a372 100644 --- a/contrib/src/ai/aiCharacter.h +++ b/contrib/src/ai/aiCharacter.h @@ -39,8 +39,8 @@ class EXPCL_PANDAAI AICharacter { public: double _mass; double _max_force; - LVecBase3f _velocity; - LVecBase3f _steering_force; + LVecBase3 _velocity; + LVecBase3 _steering_force; string _name; double _movt_force; unsigned int _ai_char_flock_id; @@ -51,7 +51,7 @@ class EXPCL_PANDAAI AICharacter { bool _pf_guide; void update(); - void set_velocity(LVecBase3f vel); + void set_velocity(LVecBase3 vel); void set_char_render(NodePath render); NodePath get_char_render(); @@ -59,7 +59,7 @@ PUBLISHED: double get_mass(); void set_mass(double m); - LVecBase3f get_velocity(); + LVecBase3 get_velocity(); double get_max_force(); void set_max_force(double max_force); diff --git a/contrib/src/ai/aiNode.cxx b/contrib/src/ai/aiNode.cxx index 00c1fa57a3..713d349f7e 100644 --- a/contrib/src/ai/aiNode.cxx +++ b/contrib/src/ai/aiNode.cxx @@ -14,7 +14,7 @@ #include "aiNode.h" -AINode::AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) { +AINode::AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) { for (int i = 0; i < 8; ++i) { _neighbours[i] = NULL; } diff --git a/contrib/src/ai/aiNode.h b/contrib/src/ai/aiNode.h index 77f6036500..d0af520b88 100644 --- a/contrib/src/ai/aiNode.h +++ b/contrib/src/ai/aiNode.h @@ -62,7 +62,7 @@ public: int _grid_x, _grid_y; // Position of the node in 3D space. - LVecBase3f _position; + LVecBase3 _position; // Dimensions of each face / cell on the mesh. // Height is given in case of expansion to a 3d mesh. Currently @@ -77,7 +77,7 @@ public: AINode *_next; PUBLISHED: - AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h); + AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h); ~AINode(); bool contains(float x, float y); diff --git a/contrib/src/ai/aiPathFinder.cxx b/contrib/src/ai/aiPathFinder.cxx index 2857b21062..789b587a7d 100644 --- a/contrib/src/ai/aiPathFinder.cxx +++ b/contrib/src/ai/aiPathFinder.cxx @@ -36,7 +36,7 @@ void PathFinder::find_path(Node *src_node, Node *dest_node) { // Add a dummy node as the first element of the open list with score = -1. // Inorder to implement a binary heap the index of the elements should never be 0. - Node *_dummy_node = new Node(-1, -1, LVecBase3f(0.0, 0.0, 0.0), 0, 0, 0); + Node *_dummy_node = new Node(-1, -1, LVecBase3(0.0, 0.0, 0.0), 0, 0, 0); _dummy_node->_status = _dummy_node->open; _dummy_node->_score = -1; _open_list.push_back(_dummy_node); @@ -379,7 +379,7 @@ void PathFinder::remove_from_clist(int r, int c) { ///////////////////////////////////////////////////////////////////////////////////////// -Node* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size) { +Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size) { int size = grid_size; float x = pos[0]; float y = pos[1]; diff --git a/contrib/src/ai/aiPathFinder.h b/contrib/src/ai/aiPathFinder.h index c1c167ad54..eb2f8de70b 100644 --- a/contrib/src/ai/aiPathFinder.h +++ b/contrib/src/ai/aiPathFinder.h @@ -23,7 +23,7 @@ typedef vector NodeArray; typedef vector NavMesh; -Node* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size); +Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/contrib/src/ai/arrival.cxx b/contrib/src/ai/arrival.cxx index 329955aba1..14b84030d4 100644 --- a/contrib/src/ai/arrival.cxx +++ b/contrib/src/ai/arrival.cxx @@ -36,8 +36,8 @@ Arrival::~Arrival() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Arrival::do_arrival() { - LVecBase3f direction_to_target; +LVecBase3 Arrival::do_arrival() { + LVecBase3 direction_to_target; double distance; if(_arrival_type) { @@ -52,22 +52,22 @@ LVecBase3f Arrival::do_arrival() { _arrival_direction.normalize(); if(int(distance) == 0) { - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); - _ai_char->_steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); + _ai_char->_steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0); if(_ai_char->_steering->_seek_obj != NULL) { _ai_char->_steering->turn_off("arrival"); _ai_char->_steering->turn_on("arrival_activate"); } _arrival_done = true; - return(LVecBase3f(0.0, 0.0, 0.0)); + return(LVecBase3(0.0, 0.0, 0.0)); } else { _arrival_done = false; } double u = _ai_char->get_velocity().length(); - LVecBase3f desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass(); + LVecBase3 desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass(); if(_ai_char->_steering->_seek_obj != NULL) { return(desired_force); @@ -85,7 +85,7 @@ LVecBase3f Arrival::do_arrival() { } cout<<"Arrival works only with seek and pursue"<_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render)); } diff --git a/contrib/src/ai/arrival.h b/contrib/src/ai/arrival.h index 69c0709e7a..788db0c1dd 100644 --- a/contrib/src/ai/arrival.h +++ b/contrib/src/ai/arrival.h @@ -27,9 +27,9 @@ public: AICharacter *_ai_char; NodePath _arrival_target; - LVecBase3f _arrival_target_pos; + LVecBase3 _arrival_target_pos; double _arrival_distance; - LVecBase3f _arrival_direction; + LVecBase3 _arrival_direction; bool _arrival_done; // This flag specifies if the arrival behavior is being used with seek or pursue behavior. @@ -39,7 +39,7 @@ public: Arrival(AICharacter *ai_ch, double distance = 10.0); ~Arrival(); - LVecBase3f do_arrival(); + LVecBase3 do_arrival(); void arrival_activate(); }; diff --git a/contrib/src/ai/evade.cxx b/contrib/src/ai/evade.cxx index 149ab4fbf4..c40f920ca9 100644 --- a/contrib/src/ai/evade.cxx +++ b/contrib/src/ai/evade.cxx @@ -42,23 +42,23 @@ Evade::~Evade() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Evade::do_evade() { +LVecBase3 Evade::do_evade() { assert(_evade_target && "evade target not assigned"); _evade_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _evade_target.get_pos(_ai_char->_window_render); double distance = _evade_direction.length(); _evade_direction.normalize(); - LVecBase3f desired_force = _evade_direction * _ai_char->_movt_force; + LVecBase3 desired_force = _evade_direction * _ai_char->_movt_force; if(distance > (_evade_distance + _evade_relax_distance)) { if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_evade) == _ai_char->_steering->_evade) { - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); } _ai_char->_steering->turn_off("evade"); _ai_char->_steering->turn_on("evade_activate"); _evade_done = true; - return(LVecBase3f(0.0, 0.0, 0.0)); + return(LVecBase3(0.0, 0.0, 0.0)); } else { _evade_done = false; diff --git a/contrib/src/ai/evade.h b/contrib/src/ai/evade.h index 088548ea28..4de79149fa 100644 --- a/contrib/src/ai/evade.h +++ b/contrib/src/ai/evade.h @@ -28,7 +28,7 @@ public: NodePath _evade_target; float _evade_weight; - LVecBase3f _evade_direction; + LVecBase3 _evade_direction; double _evade_distance; double _evade_relax_distance; bool _evade_done; @@ -38,7 +38,7 @@ public: double relax_distance, float evade_wt); ~Evade(); - LVecBase3f do_evade(); + LVecBase3 do_evade(); void evade_activate(); }; diff --git a/contrib/src/ai/flee.cxx b/contrib/src/ai/flee.cxx index af0b2a0548..b1579aeedb 100644 --- a/contrib/src/ai/flee.cxx +++ b/contrib/src/ai/flee.cxx @@ -29,7 +29,7 @@ Flee::Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance, _flee_activate_done = false; } -Flee::Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance, +Flee::Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance, double relax_distance, float flee_wt){ _ai_char = ai_ch; @@ -57,10 +57,10 @@ Flee::~Flee() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Flee::do_flee() { - LVecBase3f dirn; +LVecBase3 Flee::do_flee() { + LVecBase3 dirn; double distance; - LVecBase3f desired_force; + LVecBase3 desired_force; dirn = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_present_pos; distance = dirn.length(); @@ -68,12 +68,12 @@ LVecBase3f Flee::do_flee() { if(distance > (_flee_distance + _flee_relax_distance)) { if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_flee) == _ai_char->_steering->_flee) { - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); } _flee_done = true; _ai_char->_steering->turn_off("flee"); _ai_char->_steering->turn_on("flee_activate"); - return(LVecBase3f(0.0, 0.0, 0.0)); + return(LVecBase3(0.0, 0.0, 0.0)); } else { return(desired_force); @@ -90,7 +90,7 @@ LVecBase3f Flee::do_flee() { ///////////////////////////////////////////////////////////////////////////////// void Flee::flee_activate() { - LVecBase3f dirn; + LVecBase3 dirn; double distance; _flee_activate_done = false; diff --git a/contrib/src/ai/flee.h b/contrib/src/ai/flee.h index fc3e86c931..0d63b47baa 100644 --- a/contrib/src/ai/flee.h +++ b/contrib/src/ai/flee.h @@ -26,23 +26,23 @@ class EXPCL_PANDAAI Flee { public: AICharacter *_ai_char; - LVecBase3f _flee_position; + LVecBase3 _flee_position; float _flee_weight; - LVecBase3f _flee_direction; + LVecBase3 _flee_direction; double _flee_distance; double _flee_relax_distance; - LVecBase3f _flee_present_pos; + LVecBase3 _flee_present_pos; bool _flee_done; bool _flee_activate_done; Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0); - Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance = 10.0, + Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0); ~Flee(); - LVecBase3f do_flee(); + LVecBase3 do_flee(); void flee_activate(); }; diff --git a/contrib/src/ai/meshNode.cxx b/contrib/src/ai/meshNode.cxx index 0dfdf49ac6..9d119bbb66 100644 --- a/contrib/src/ai/meshNode.cxx +++ b/contrib/src/ai/meshNode.cxx @@ -1,7 +1,7 @@ #include "meshNode.h" -Node::Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) { +Node::Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) { for(int i = 0; i < 8; ++i) { _neighbours[i] = NULL; } diff --git a/contrib/src/ai/meshNode.h b/contrib/src/ai/meshNode.h index bf2db9680f..b9e1421558 100644 --- a/contrib/src/ai/meshNode.h +++ b/contrib/src/ai/meshNode.h @@ -47,7 +47,7 @@ public: int _grid_x, _grid_y; // Position of the node in 3D space. - LVecBase3f _position; + LVecBase3 _position; // Dimensions of each face / cell on the mesh. // Height is given in case of expansion to a 3d mesh. Currently not used. @@ -58,7 +58,7 @@ public: // Note: The data in this member is discarded when mesh data is written into navmesh.csv file. Node *_next; - Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h); + Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h); ~Node(); bool contains(float x, float y); diff --git a/contrib/src/ai/obstacleAvoidance.cxx b/contrib/src/ai/obstacleAvoidance.cxx index 7354636194..9cb9deed9c 100644 --- a/contrib/src/ai/obstacleAvoidance.cxx +++ b/contrib/src/ai/obstacleAvoidance.cxx @@ -35,15 +35,15 @@ bool ObstacleAvoidance::obstacle_detection() { // Calculate the volume of the AICharacter with respect to render PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds(); CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere(); - LVecBase3f avoidance(0.0, 0.0, 0.0); + LVecBase3 avoidance(0.0, 0.0, 0.0); double distance = 0x7fff ; double expanded_radius; - LVecBase3f to_obstacle; - LVecBase3f prev_avoidance; + LVecBase3 to_obstacle; + LVecBase3 prev_avoidance; for(unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) { PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds(); CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere(); - LVecBase3f near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos(); + LVecBase3 near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos(); // Check if it's the nearest obstacle, If so initialize as the nearest obstacle if((near_obstacle.length() < distance) && (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) { _nearest_obstacle = _ai_char->_world->_obstacles[i]; @@ -51,13 +51,13 @@ bool ObstacleAvoidance::obstacle_detection() { expanded_radius = bsphere->get_radius() + np_sphere->get_radius(); } } - LVecBase3f feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + LVecBase3 feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward()); feeler.normalize(); feeler *= (expanded_radius + np_sphere->get_radius()) ; to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos(); - LVector3f line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); - LVecBase3f project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared(); - LVecBase3f perp = project - to_obstacle; + LVector3 line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward()); + LVecBase3 project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared(); + LVecBase3 perp = project - to_obstacle; // If the nearest obstacle will collide with our AICharacter then send obstacle detection as true if((_nearest_obstacle) && (perp.length() < expanded_radius - np_sphere->get_radius()) && (project.length() < feeler.length())) { return true; @@ -90,27 +90,27 @@ void ObstacleAvoidance::obstacle_avoidance_activate() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f ObstacleAvoidance::do_obstacle_avoidance() { - LVecBase3f offset = _ai_char->get_node_path().get_pos() - _nearest_obstacle.get_pos(); +LVecBase3 ObstacleAvoidance::do_obstacle_avoidance() { + LVecBase3 offset = _ai_char->get_node_path().get_pos() - _nearest_obstacle.get_pos(); PT(BoundingVolume) bounds =_nearest_obstacle.get_bounds(); CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere(); PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds(); CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere(); double distance_needed = offset.length() - bsphere->get_radius() - np_sphere->get_radius(); if((obstacle_detection())) { - LVecBase3f direction = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + LVecBase3 direction = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward()); direction.normalize(); float forward_component = offset.dot(direction); - LVecBase3f projection = forward_component * direction; - LVecBase3f perpendicular_component = offset - projection; + LVecBase3 projection = forward_component * direction; + LVecBase3 perpendicular_component = offset - projection; double p = perpendicular_component.length(); perpendicular_component.normalize(); - LVecBase3f avoidance = perpendicular_component; + LVecBase3 avoidance = perpendicular_component; // The more closer the obstacle, the more force it generates avoidance = (avoidance * _ai_char->get_max_force() * _ai_char->_movt_force) / (p + 0.01); return avoidance; } _ai_char->_steering->turn_on("obstacle_avoidance_activate"); _ai_char->_steering->turn_off("obstacle_avoidance"); - return LVecBase3f(0, 0, 0); + return LVecBase3(0, 0, 0); } diff --git a/contrib/src/ai/obstacleAvoidance.h b/contrib/src/ai/obstacleAvoidance.h index 9df1c2bde1..787a417592 100644 --- a/contrib/src/ai/obstacleAvoidance.h +++ b/contrib/src/ai/obstacleAvoidance.h @@ -30,7 +30,7 @@ class EXPCL_PANDAAI ObstacleAvoidance { float _feeler; ObstacleAvoidance(AICharacter *ai_char, float feeler_length); - LVecBase3f do_obstacle_avoidance(); + LVecBase3 do_obstacle_avoidance(); ~ObstacleAvoidance(); void obstacle_avoidance_activate(); bool obstacle_detection(); diff --git a/contrib/src/ai/pathFind.cxx b/contrib/src/ai/pathFind.cxx index 65fa6f57d6..f4c8fd7d9f 100644 --- a/contrib/src/ai/pathFind.cxx +++ b/contrib/src/ai/pathFind.cxx @@ -29,7 +29,7 @@ void PathFind::create_nav_mesh(const char* navmesh_filename) { // Stage variables. int grid_x, grid_y; float l, w, h; - LVecBase3f position; + LVecBase3 position; // Variable to hold line data read from file. string line; @@ -75,7 +75,7 @@ void PathFind::create_nav_mesh(const char* navmesh_filename) { l = atof(fields[4].c_str()); w = atof(fields[5].c_str()); h = atof(fields[6].c_str()); - position = LVecBase3f(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str())); + position = LVecBase3(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str())); Node *stage_node = new Node(grid_x, grid_y, position, w, l, h); @@ -197,7 +197,7 @@ void PathFind::set_path_find(const char* navmesh_filename) { /////////////////////////////////////////////////////////////////////////////////////// -void PathFind::path_find(LVecBase3f pos, string type) { +void PathFind::path_find(LVecBase3 pos, string type) { if(type == "addPath") { if(_ai_char->_steering->_path_follow_obj) { _ai_char->_steering->remove_ai("pathfollow"); diff --git a/contrib/src/ai/pathFind.h b/contrib/src/ai/pathFind.h index a82142947b..cb9030e000 100644 --- a/contrib/src/ai/pathFind.h +++ b/contrib/src/ai/pathFind.h @@ -43,7 +43,7 @@ public: int _grid_size; NodePath _path_find_target; - LVecBase3f _prev_position; + LVecBase3 _prev_position; PT(GeomNode) _parent; LineSegs *_pen; vector _previous_obstacles; @@ -62,7 +62,7 @@ public: void clear_previous_obstacles(); void set_path_find(const char* navmesh_filename); - void path_find(LVecBase3f pos, string type = "normal"); + void path_find(LVecBase3 pos, string type = "normal"); void path_find(NodePath target, string type = "normal"); void add_obstacle_to_mesh(NodePath obstacle); void dynamic_avoid(NodePath obstacle); diff --git a/contrib/src/ai/pathFollow.cxx b/contrib/src/ai/pathFollow.cxx index 0e276eb5c4..43e155028b 100644 --- a/contrib/src/ai/pathFollow.cxx +++ b/contrib/src/ai/pathFollow.cxx @@ -20,7 +20,7 @@ PathFollow::~PathFollow() { ///////////////////////////////////////////////////////////////////////////////////////// -void PathFollow::add_to_path(LVecBase3f pos) { +void PathFollow::add_to_path(LVecBase3 pos) { _path.push_back(pos); } @@ -119,7 +119,7 @@ void PathFollow::do_follow() { bool PathFollow::check_if_possible() { Node* src = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _ai_char->_steering->_path_find_obj->_grid_size); - LVecBase3f _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render); + LVecBase3 _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render); Node* dst = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _prev_position, _ai_char->_steering->_path_find_obj->_grid_size); if(src && dst) { diff --git a/contrib/src/ai/pathFollow.h b/contrib/src/ai/pathFollow.h index 125ef98784..e2d09a27bc 100644 --- a/contrib/src/ai/pathFollow.h +++ b/contrib/src/ai/pathFollow.h @@ -13,7 +13,7 @@ class EXPCL_PANDAAI PathFollow { public: AICharacter *_ai_char; float _follow_weight; - vector _path; + vector _path; int _curr_path_waypoint; bool _start; NodePath _dummy; @@ -23,7 +23,7 @@ public: PathFollow(AICharacter *ai_ch, float follow_wt); ~PathFollow(); - void add_to_path(LVecBase3f pos); + void add_to_path(LVecBase3 pos); void start(string type); void do_follow(); bool check_if_possible(); diff --git a/contrib/src/ai/pursue.cxx b/contrib/src/ai/pursue.cxx index 259f5b340e..d14bb60514 100644 --- a/contrib/src/ai/pursue.cxx +++ b/contrib/src/ai/pursue.cxx @@ -37,17 +37,17 @@ Pursue::~Pursue() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Pursue::do_pursue() { +LVecBase3 Pursue::do_pursue() { assert(_pursue_target && "pursue target not assigned"); - LVecBase3f present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + LVecBase3 present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); double target_distance = (_pursue_target.get_pos(_ai_char->_window_render) - present_pos).length(); if(int(target_distance) == 0) { _pursue_done = true; - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); - _ai_char->_steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0); - return(LVecBase3f(0.0, 0.0, 0.0)); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); + _ai_char->_steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0); + return(LVecBase3(0.0, 0.0, 0.0)); } else { _pursue_done = false; @@ -56,6 +56,6 @@ LVecBase3f Pursue::do_pursue() { _pursue_direction = _pursue_target.get_pos(_ai_char->_window_render) - present_pos; _pursue_direction.normalize(); - LVecBase3f desired_force = _pursue_direction * _ai_char->_movt_force; + LVecBase3 desired_force = _pursue_direction * _ai_char->_movt_force; return(desired_force); } diff --git a/contrib/src/ai/pursue.h b/contrib/src/ai/pursue.h index cf8893cef3..e26d107129 100644 --- a/contrib/src/ai/pursue.h +++ b/contrib/src/ai/pursue.h @@ -28,12 +28,12 @@ public: NodePath _pursue_target; float _pursue_weight; - LVecBase3f _pursue_direction; + LVecBase3 _pursue_direction; bool _pursue_done; Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt); ~Pursue(); - LVecBase3f do_pursue(); + LVecBase3 do_pursue(); }; #endif diff --git a/contrib/src/ai/seek.cxx b/contrib/src/ai/seek.cxx index 6139c08398..f11b3cf21f 100644 --- a/contrib/src/ai/seek.cxx +++ b/contrib/src/ai/seek.cxx @@ -27,7 +27,7 @@ Seek::Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt) { _seek_done = false; } -Seek::Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt) { +Seek::Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt) { _ai_char = ai_ch; _seek_position = pos; @@ -51,16 +51,16 @@ Seek::~Seek() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Seek::do_seek() { +LVecBase3 Seek::do_seek() { double target_distance = (_seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length(); if(int(target_distance) == 0) { _seek_done = true; - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); _ai_char->_steering->turn_off("seek"); - return(LVecBase3f(0.0, 0.0, 0.0)); + return(LVecBase3(0.0, 0.0, 0.0)); } - LVecBase3f desired_force = _seek_direction * _ai_char->_movt_force; + LVecBase3 desired_force = _seek_direction * _ai_char->_movt_force; return(desired_force); } diff --git a/contrib/src/ai/seek.h b/contrib/src/ai/seek.h index c3f3ddc4a4..2d2cd998f4 100644 --- a/contrib/src/ai/seek.h +++ b/contrib/src/ai/seek.h @@ -26,16 +26,16 @@ class EXPCL_PANDAAI Seek { public: AICharacter *_ai_char; - LVecBase3f _seek_position; + LVecBase3 _seek_position; float _seek_weight; - LVecBase3f _seek_direction; + LVecBase3 _seek_direction; bool _seek_done; - LVecBase3f _seek_accum_force; + LVecBase3 _seek_accum_force; Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt = 1.0); - Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt = 1.0); + Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt = 1.0); ~Seek(); - LVecBase3f do_seek(); + LVecBase3 do_seek(); }; #endif diff --git a/contrib/src/ai/wander.cxx b/contrib/src/ai/wander.cxx index 804eb50105..48d72114f9 100644 --- a/contrib/src/ai/wander.cxx +++ b/contrib/src/ai/wander.cxx @@ -57,23 +57,23 @@ Wander::Wander(AICharacter *ai_ch, double wander_radius,int flag, double aoe, fl // default is XY axes switch(_flag) { case 0: { - _wander_target = LVecBase3f(_wander_radius * cos(theta), _wander_radius * sin(theta),0); + _wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0); break; } case 1: { - _wander_target = LVecBase3f(0, _wander_radius * cos(theta), _wander_radius * sin(theta)); + _wander_target = LVecBase3(0, _wander_radius * cos(theta), _wander_radius * sin(theta)); break; } case 2: { - _wander_target = LVecBase3f(_wander_radius * cos(theta), 0, _wander_radius * sin(theta)); + _wander_target = LVecBase3(_wander_radius * cos(theta), 0, _wander_radius * sin(theta)); break; } case 3: { - _wander_target = LVecBase3f(_wander_radius * sin(theta) * cos(si), _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta)); + _wander_target = LVecBase3(_wander_radius * sin(theta) * cos(si), _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta)); break; } default: { - _wander_target = LVecBase3f(_wander_radius * cos(theta), _wander_radius * sin(theta),0); + _wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0); break; } } @@ -91,52 +91,52 @@ Wander::~Wander() { ///////////////////////////////////////////////////////////////////////////////// -LVecBase3f Wander::do_wander() { - LVecBase3f present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render()); +LVecBase3 Wander::do_wander() { + LVecBase3 present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render()); // Create the random slices to enable random movement of wander for x,y,z respectively double time_slice_1 = random_clamped() * 1.5; double time_slice_2 = random_clamped() * 1.5; double time_slice_3 = random_clamped() * 1.5; switch(_flag) { case 0: { - _wander_target += LVecBase3f(time_slice_1, time_slice_2, 0); + _wander_target += LVecBase3(time_slice_1, time_slice_2, 0); break; } case 1: { - _wander_target += LVecBase3f(0, time_slice_1, time_slice_2); + _wander_target += LVecBase3(0, time_slice_1, time_slice_2); break; } case 2: { - _wander_target += LVecBase3f(time_slice_1, 0, time_slice_2); + _wander_target += LVecBase3(time_slice_1, 0, time_slice_2); break; } case 3: { - _wander_target += LVecBase3f(time_slice_1, time_slice_2, time_slice_3); + _wander_target += LVecBase3(time_slice_1, time_slice_2, time_slice_3); break; } default: { - _wander_target = LVecBase3f(time_slice_1, time_slice_2, 0); + _wander_target = LVecBase3(time_slice_1, time_slice_2, 0); } } _wander_target.normalize(); _wander_target *= _wander_radius; - LVecBase3f target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + LVecBase3 target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward()); target.normalize(); // Project wander target onto global space target = _wander_target + target; - LVecBase3f desired_target = present_pos + target; - LVecBase3f desired_force = desired_target - _ai_char->get_node_path().get_pos() ; + LVecBase3 desired_target = present_pos + target; + LVecBase3 desired_force = desired_target - _ai_char->get_node_path().get_pos() ; desired_force.normalize(); desired_force *= _ai_char->_movt_force; double distance = (present_pos - _init_pos).length(); if(_area_of_effect > 0 && distance > _area_of_effect) { - LVecBase3f direction = present_pos - _init_pos; + LVecBase3 direction = present_pos - _init_pos; direction.normalize(); desired_force = - direction * _ai_char->_movt_force; - LVecBase3f dirn = _ai_char->_steering->_steering_force; + LVecBase3 dirn = _ai_char->_steering->_steering_force; dirn.normalize(); - _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0); } return desired_force; } diff --git a/contrib/src/ai/wander.h b/contrib/src/ai/wander.h index 2ea89606eb..a760bbb15a 100644 --- a/contrib/src/ai/wander.h +++ b/contrib/src/ai/wander.h @@ -24,14 +24,14 @@ class EXPCL_PANDAAI Wander { public: AICharacter *_ai_char; double _wander_radius; - LVecBase3f _wander_target; + LVecBase3 _wander_target; float _wander_weight; int _flag; - LVecBase3f _init_pos; + LVecBase3 _init_pos; double _area_of_effect; Wander(AICharacter *ai_ch, double wander_radius, int flag, double aoe, float wander_weight); - LVecBase3f do_wander(); + LVecBase3 do_wander(); ~Wander(); }; diff --git a/direct/src/directscripts/Doxyfile.python b/direct/src/directscripts/Doxyfile.python index 81b68dc6b2..4bcfb58727 100644 --- a/direct/src/directscripts/Doxyfile.python +++ b/direct/src/directscripts/Doxyfile.python @@ -760,13 +760,13 @@ STRIP_CODE_COMMENTS = YES # then for each documented function all documented # functions referencing it will be listed. -REFERENCED_BY_RELATION = YES +REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. -REFERENCES_RELATION = YES +REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from diff --git a/direct/src/directscripts/extract_docs.py b/direct/src/directscripts/extract_docs.py index 2f76ed4a53..b6bb06bfeb 100644 --- a/direct/src/directscripts/extract_docs.py +++ b/direct/src/directscripts/extract_docs.py @@ -30,6 +30,7 @@ def comment(code): if empty_line: # New paragraph. comment += '\n\n' + empty_line = False elif comment: comment += '\n' comment += '/// ' + line @@ -56,15 +57,29 @@ def block_comment(code): line = line.rstrip() strline = line.lstrip('/ \t') - if reading_desc: - newlines.append('/// ' + line[min(indent, len(line) - len(strline)):]) - else: - # A "Description:" text starts the description. - if strline.startswith("Description"): - strline = strline[11:].lstrip(': \t') - indent = len(line) - len(strline) - reading_desc = True - newlines.append('/// ' + strline) + + if ':' in strline: + pre, post = strline.split(':', 1) + pre = pre.rstrip() + if pre == "Description": + strline = post.lstrip() + elif pre in ("Class", "Access", "Function", "Created by", "Enum"): + continue + + if strline or len(newlines) > 0: + newlines.append('/// ' + strline) + + #if reading_desc: + # newlines.append('/// ' + line[min(indent, len(line) - len(strline)):]) + #else: + # # A "Description:" text starts the description. + # if strline.startswith("Description"): + # strline = strline[11:].lstrip(': \t') + # indent = len(line) - len(strline) + # reading_desc = True + # newlines.append('/// ' + strline) + # else: + # print line newcode = '\n'.join(newlines) if len(newcode) > 0: @@ -73,6 +88,9 @@ def block_comment(code): return "" def translateFunctionName(name): + if name.startswith("__"): + return name + new = "" for i in name.split("_"): if new == "": @@ -85,32 +103,58 @@ def translateFunctionName(name): new += i[0].upper() + i[1:] return new -def translated_type_name(type): +def translateTypeName(name, mangle=True): + # Equivalent to C++ classNameFromCppName + class_name = "" + bad_chars = "!@#$%^&*()<>,.-=+~{}? " + next_cap = False + first_char = mangle + + for chr in name: + if (chr == '_' or chr == ' ') and mangle: + next_cap = True + elif chr in bad_chars: + if not mangle: + class_name += '_' + elif next_cap or first_char: + class_name += chr.upper() + next_cap = False + first_char = False + else: + class_name += chr + + return class_name + +def translated_type_name(type, scoped=True): + while interrogate_type_is_wrapped(type): + if interrogate_type_is_const(type): + return 'const ' + translated_type_name(interrogate_type_wrapped_type(type)) + else: + type = interrogate_type_wrapped_type(type) + + typename = interrogate_type_name(type) + if typename in ("PyObject", "_object"): + return "object" + if interrogate_type_is_atomic(type): token = interrogate_type_atomic_token(type) if token == 7: return 'str' + else: + return typename - typename = interrogate_type_name(type) - typename = typename.replace("< ", "").replace(" >", "") - return typename + typename = translateTypeName(typename) -def translateTypeSpec(name): - name = name.strip("* ") - name = name.replace("BitMask< unsigned int, 32 >", "BitMask32") - name = name.replace("atomic ", "") - name = name.replace("< ", "").replace(" >", "") - if name == '_object': - name = 'object' - elif name == '_typeobject': - name = 'type' - return name + if scoped and interrogate_type_is_nested(type): + return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename + else: + return typename def processElement(handle, element): if interrogate_element_has_comment(element): print >>handle, comment(interrogate_element_comment(element)) - print >>handle, translateTypeSpec(translated_type_name(interrogate_element_type(element))), + print >>handle, translated_type_name(interrogate_element_type(element)), print >>handle, interrogate_element_name(element) + ';' def processFunction(handle, function, isConstructor = False): @@ -120,11 +164,12 @@ def processFunction(handle, function, isConstructor = False): print >>handle, block_comment(interrogate_wrapper_comment(wrapper)) if not isConstructor: - if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0): - print >>handle, "static", + if interrogate_function_is_method(function): + if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0): + print >>handle, "static", if interrogate_wrapper_has_return_value(wrapper): - print >>handle, translateTypeSpec(translated_type_name(interrogate_wrapper_return_type(wrapper))), + print >>handle, translated_type_name(interrogate_wrapper_return_type(wrapper)), else: pass#print >>handle, "void", @@ -137,7 +182,7 @@ def processFunction(handle, function, isConstructor = False): if not interrogate_wrapper_parameter_is_this(wrapper, i_param): if not first: print >>handle, ",", - print >>handle, translateTypeSpec(translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param))), + print >>handle, translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)), if interrogate_wrapper_parameter_has_name(wrapper, i_param): print >>handle, interrogate_wrapper_parameter_name(wrapper, i_param), first = False @@ -145,7 +190,7 @@ def processFunction(handle, function, isConstructor = False): print >>handle, ");" def processType(handle, type): - typename = translated_type_name(type) + typename = translated_type_name(type, scoped=False) derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ] if interrogate_type_has_comment(type): @@ -157,7 +202,7 @@ def processType(handle, type): docstring = comment(interrogate_type_enum_value_comment(type, i_value)) if docstring: print >>handle, docstring - print >>handle, translateFunctionName(interrogate_type_enum_value_name(type, i_value)), "=", interrogate_type_enum_value(type, i_value), "," + print >>handle, interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), "," else: if interrogate_type_is_struct(type): classtype = "struct" @@ -192,38 +237,54 @@ def processType(handle, type): print >>handle, "};" +def processModule(handle, package): + print >>handle, "namespace %s {" % package + + if package != "core": + print >>handle, "using namespace core;" + + for i_type in xrange(interrogate_number_of_global_types()): + type = interrogate_get_global_type(i_type) + + if interrogate_type_has_module_name(type): + module_name = interrogate_type_module_name(type) + if "panda3d." + package == module_name: + processType(handle, type) + else: + print "Type %s has no module name" % typename + + for i_func in xrange(interrogate_number_of_global_functions()): + func = interrogate_get_global_function(i_func) + + if interrogate_function_has_module_name(func): + module_name = interrogate_function_module_name(func) + if "panda3d." + package == module_name: + processFunction(handle, func) + else: + print "Type %s has no module name" % typename + + print >>handle, "}" + + if __name__ == "__main__": handle = open("pandadoc.hpp", "w") print >>handle, comment("Panda3D modules that are implemented in C++.") - print >>handle, "namespace panda3d {}" + print >>handle, "namespace panda3d {" # Determine the path to the interrogatedb files interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "..", "..", "etc")) interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "input")) import panda3d.core + processModule(handle, "core") for lib in os.listdir(os.path.dirname(panda3d.__file__)): if lib.endswith(('.pyd', '.so')) and not lib.startswith('core.'): - __import__('panda3d.' + os.path.splitext(lib)[0]) + module_name = os.path.splitext(lib)[0] + __import__("panda3d." + module_name) + processModule(handle, module_name) - lastpkg = None - for i_type in xrange(interrogate_number_of_global_types()): - type = interrogate_get_global_type(i_type) - if interrogate_type_has_module_name(type): - package = interrogate_type_module_name(type) - if lastpkg != package: - if lastpkg is not None: - print >>handle, "}" - print >>handle, "namespace %s {" % package - lastpkg = package - - processType(handle, type) - else: - print "Type %s has no module name" % typename - - if lastpkg is not None: - print >>handle, "}" + print >>handle, "}" handle.close() diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py index 1f7f10c009..6a3fe5ab4e 100644 --- a/direct/src/filter/FilterManager.py +++ b/direct/src/filter/FilterManager.py @@ -294,7 +294,7 @@ class FilterManager(DirectObject): winprops = WindowProperties() winprops.setSize(xsize, ysize) - props = FrameBufferProperties(self.win.getFbProperties()) + props = FrameBufferProperties(FrameBufferProperties.getDefault()) props.setBackBuffers(0) props.setRgbColor(1) props.setDepthBits(depthbits) diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index 803cce5467..44cb182ab0 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -94,8 +94,9 @@ class DirectDialog(DirectFrame): ('text', '', None), ('text_align', TextNode.ALeft, None), ('text_scale', 0.06, None), - ('image', DGG.getDefaultDialogGeom(), None), - ('relief', None, None), + ('image', None, None), + ('relief', DGG.RAISED, None), + ('borderWidth', (0.01, 0.01), None), ('buttonTextList', [], DGG.INITOPT), ('buttonGeomList', [], DGG.INITOPT), ('buttonImageList', [], DGG.INITOPT), @@ -210,10 +211,16 @@ class DirectDialog(DirectFrame): bounds = self.stateNodePath[0].getTightBounds() if image: image.reparentTo(self.stateNodePath[0]) - l = bounds[0][0] - r = bounds[1][0] - b = bounds[0][2] - t = bounds[1][2] + if bounds is None: + l = 0 + r = 0 + b = 0 + t = 0 + else: + l = bounds[0][0] + r = bounds[1][0] + b = bounds[0][2] + t = bounds[1][2] # Center text and geom around origin # How far is center of text from origin? xOffset = -(l+r)*0.5 @@ -246,10 +253,16 @@ class DirectDialog(DirectFrame): bl = br = bb = bt = 0 for button in self.buttonList: bounds = button.stateNodePath[0].getTightBounds() - bl = min(bl, bounds[0][0]) - br = max(br, bounds[1][0]) - bb = min(bb, bounds[0][2]) - bt = max(bt, bounds[1][2]) + if bounds is None: + bl = 0 + br = 0 + bb = 0 + bt = 0 + else: + bl = min(bl, bounds[0][0]) + br = max(br, bounds[1][0]) + bb = min(bb, bounds[0][2]) + bt = max(bt, bounds[1][2]) bl -= bpad[0] br += bpad[0] bb -= bpad[1] @@ -303,6 +316,7 @@ class DirectDialog(DirectFrame): # reduce bottom by pad, button height and 2*button pad b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1] t = t + self['topPad'] + pad[1] + self['frameSize'] = (l, r, b, t) self['image_scale'] = (r - l, 1, t - b) # Center frame about text and buttons self['image_pos'] = ((l+r)*0.5, 0.0, (b+t)*0.5) diff --git a/direct/src/gui/DirectRadioButton.py b/direct/src/gui/DirectRadioButton.py index c96ba13dbb..c2cb423f0d 100755 --- a/direct/src/gui/DirectRadioButton.py +++ b/direct/src/gui/DirectRadioButton.py @@ -43,7 +43,7 @@ class DirectRadioButton(DirectButton): ('boxGeom', None, None), ('boxGeomColor', None, None), ('boxGeomScale', 1.0, None), - ('boxImage', loader.loadModel('models/gui/radio_button_gui'), None), + ('boxImage', None, None), ('boxImageScale', 1.0, None), ('boxImageColor', VBase4(1, 1, 1, 1), None), ('boxRelief', None, None), @@ -69,9 +69,11 @@ class DirectRadioButton(DirectButton): # Call option initialization functions self.initialiseoptions(DirectRadioButton) # After initialization with X giving it the correct size, put back space - if self['boxGeom'] == None: + if self['boxGeom'] is None: + if not 'boxRelief' in kw and self['boxImage'] is None: + self.indicator['relief'] = DGG.SUNKEN self.indicator['text'] = (' ', '*') - self.indicator['text_pos'] = (0, -.5) + self.indicator['text_pos'] = (0, -.25) else: self.indicator['text'] = (' ', ' ') diff --git a/direct/src/showbase/Transitions.py b/direct/src/showbase/Transitions.py index 7e3103a0d0..ca4919a48f 100644 --- a/direct/src/showbase/Transitions.py +++ b/direct/src/showbase/Transitions.py @@ -59,15 +59,12 @@ class Transitions: # Reload fade if its already been created if self.fade: - del self.fade + self.fade.destroy() self.fade = None self.loadFade() def loadFade(self): - if not self.fadeModel: - self.fadeModel = loader.loadModel(self.FadeModelName) - - if self.fade == None: + if self.fade is None: # We create a DirectFrame for the fade polygon, instead of # simply loading the polygon model and using it directly, # so that it will also obscure mouse events for objects @@ -80,10 +77,15 @@ class Transitions: image_scale = (4, 2, 2), state = DGG.NORMAL, ) + if not self.fadeModel: + # No fade model was given, so we make this the fade model. + self.fade["relief"] = DGG.FLAT + self.fade["frameSize"] = (-2, 2, -1, 1) + self.fade["frameColor"] = (0, 0, 0, 1) + self.fade.setTransparency(TransparencyAttrib.MAlpha) self.fade.setBin('unsorted', 0) self.fade.setColor(0,0,0,0) - def getFadeInIval(self, t=0.5, finishIval=None): """ Returns an interval without starting it. This is particularly useful in diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index ba5f324dda..366c2ea05c 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -4625,8 +4625,14 @@ write_function_instance(ostream &out, FunctionRemap *remap, format_specifiers += "h"; parameter_list += ", &" + param_name; } - expected_params += "int"; - only_pyobjects = false; + + extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += " && " + param_name + "_long != NULL"; + pexpr_string = "(" + type->get_local_name(&parser) + ")" + + "PyLong_AsLongLong(" + param_name + "_long)"; + extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; + expected_params += "long long"; + ++num_params; } else if (TypeManager::is_unsigned_integer(type)) { if (args_type == AT_single_arg) { diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 9e0d3bba7e..ee58c98e72 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -949,7 +949,7 @@ def CompileCxx(obj,src,opts): if PkgSkip("TOUCHINPUT") == 0: cmd += "/DWINVER=0x601 " cmd += "/Fo" + obj + " /nologo /c" - if (GetTargetArch() != 'x64' and PkgSkip("SSE2") == 0): + if GetTargetArch() != 'x64' and (not PkgSkip("SSE2") or 'SSE2' in opts): cmd += " /arch:SSE2" for x in ipath: cmd += " /I" + x for (opt,dir) in INCDIRECTORIES: @@ -1160,7 +1160,7 @@ def CompileCxx(obj,src,opts): if optlevel >= 4 or GetTarget() == "android": cmd += " -fno-rtti" - if PkgSkip("SSE2") == 0 and not arch.startswith("arm"): + if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm"): cmd += " -msse2" if optlevel >= 3: @@ -1705,7 +1705,7 @@ def RunGenPyCode(target, inputs, opts): if (PkgSkip("PYTHON") != 0): return - cmdstr = sys.executable + " " + cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " " if sys.version_info >= (2, 6): cmdstr += "-B " @@ -1729,7 +1729,7 @@ def RunGenPyCode(target, inputs, opts): def FreezePy(target, inputs, opts): assert len(inputs) > 0 # Make sure this function isn't called before genpycode is run. - cmdstr = sys.executable + " " + cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " " if sys.version_info >= (2, 6): cmdstr += "-B " @@ -1757,7 +1757,7 @@ def FreezePy(target, inputs, opts): def Package(target, inputs, opts): assert len(inputs) == 1 # Invoke the ppackage script. - command = sys.executable + " " + command = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " " if GetOptimizeOption(opts) >= 4: command += "-OO " @@ -3192,6 +3192,7 @@ if (not RUNTIME): OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA', 'ZLIB'] TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx') TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.cxx') + TargetAdd('p3pnmimage_convert_srgb_sse2.obj', opts=OPTS+['SSE2'], input='convert_srgb_sse2.cxx') OPTS=['DIR:panda/src/pnmimage', 'ZLIB'] IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"]) @@ -3621,6 +3622,7 @@ if (not RUNTIME): TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite2.obj') TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj') TargetAdd('libpanda.dll', input='p3pnmimage_composite2.obj') + TargetAdd('libpanda.dll', input='p3pnmimage_convert_srgb_sse2.obj') TargetAdd('libpanda.dll', input='p3text_composite1.obj') TargetAdd('libpanda.dll', input='p3text_composite2.obj') TargetAdd('libpanda.dll', input='p3tform_composite1.obj') diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 7fd660b8a6..9dc1ca470b 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -1018,7 +1018,7 @@ make_tet_mesh(BulletSoftBodyWorldInfo &info, const char *ele, const char *face, for (int i=0; i 0)|| - (fb_prop.get_back_buffers() > 0)|| - (fb_prop.get_accum_bits() > 0)|| - (fb_prop.get_multisamples() > 0)) { + if ((flags & BF_fb_props_optional) == 0) { + if (fb_prop.get_indexed_color() || + fb_prop.get_back_buffers() > 0 || + fb_prop.get_accum_bits() > 0) { return NULL; } } - // Early success - if we are sure that this buffer WILL - // meet specs, we can precertify it. - if ((cocoagsg != 0) && - (cocoagsg->is_valid()) && - (!cocoagsg->needs_reset()) && - (cocoagsg->_supports_framebuffer_object) && - (cocoagsg->_glDrawBuffers != 0) && - (fb_prop.is_basic())) { - precertify = true; + if (cocoagsg != NULL && cocoagsg->is_valid() && !cocoagsg->needs_reset()) { + if (!cocoagsg->_supports_framebuffer_object || + cocoagsg->_glDrawBuffers == NULL) { + return NULL; + } else { + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + precertify = true; + } } - return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); } diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 0143917956..978d88f256 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -418,19 +418,13 @@ ConfigVariableInt depth_bits ("depth-bits", 0, PRC_DESC("The minimum number of depth buffer bits requested.")); ConfigVariableInt color_bits -("color-bits", 0, - PRC_DESC("The minimum number of total color buffer bits requested. This " - "value is like red-bits + blue-bits + green-bits except Panda " - "won't care how the bits are divided up.")); -ConfigVariableInt red_bits -("red-bits", 0, - PRC_DESC("The minimum number of red color buffer bits requested.")); -ConfigVariableInt green_bits -("green-bits", 0, - PRC_DESC("The minimum number of green color buffer bits requested.")); -ConfigVariableInt blue_bits -("blue-bits", 0, - PRC_DESC("The minimum number of blue color buffer bits requested.")); +("color-bits", "", + PRC_DESC("The minimum number of total color buffer bits requested. If you " + "specify only one value, it will represent the total value for the " + "red, green and blue channels, and indicates you don't care how the " + "bits are divided up among the red, green and blue channels. If " + "you specify three values, it represents three separate red, green " + "and blue bit requirements.")); ConfigVariableInt alpha_bits ("alpha-bits", 0, PRC_DESC("The minimum number of alpha buffer bits requested.")); diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index 4da350f4e6..be71d0b63d 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -97,9 +97,6 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_srgb; extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_float; extern EXPCL_PANDA_DISPLAY ConfigVariableInt depth_bits; extern EXPCL_PANDA_DISPLAY ConfigVariableInt color_bits; -extern EXPCL_PANDA_DISPLAY ConfigVariableInt red_bits; -extern EXPCL_PANDA_DISPLAY ConfigVariableInt green_bits; -extern EXPCL_PANDA_DISPLAY ConfigVariableInt blue_bits; extern EXPCL_PANDA_DISPLAY ConfigVariableInt alpha_bits; extern EXPCL_PANDA_DISPLAY ConfigVariableInt stencil_bits; extern EXPCL_PANDA_DISPLAY ConfigVariableInt accum_bits; diff --git a/panda/src/display/frameBufferProperties.I b/panda/src/display/frameBufferProperties.I index d596427195..a6fc122af4 100644 --- a/panda/src/display/frameBufferProperties.I +++ b/panda/src/display/frameBufferProperties.I @@ -305,37 +305,42 @@ get_float_depth() const { INLINE void FrameBufferProperties:: set_depth_bits(int n) { _property[FBP_depth_bits] = n; - _specified[FBP_depth_bits] = true; + _specified |= (1 << FBP_depth_bits); } //////////////////////////////////////////////////////////////////// // Function: FrameBufferProperties::set_color_bits // Access: Published -// Description: +// Description: Sets the number of requested color bits as a single +// number that represents the sum of the individual +// numbers of red, green and blue bits. Panda won't +// care how the individual bits are divided up. +// +// See also set_rgba_bits, which allows you to specify +// requirements for the individual components. //////////////////////////////////////////////////////////////////// INLINE void FrameBufferProperties:: set_color_bits(int n) { _property[FBP_color_bits] = n; - _specified[FBP_color_bits] = true; + _specified |= (1 << FBP_color_bits); } //////////////////////////////////////////////////////////////////// // Function: FrameBufferProperties::set_rgba_bits // Access: Published -// Description: Sets all color bit requirements separately. +// Description: Convenience method for setting the red, green, blue +// and alpha bits in one go. //////////////////////////////////////////////////////////////////// INLINE void FrameBufferProperties:: set_rgba_bits(int r, int g, int b, int a) { _property[FBP_red_bits] = r; - _specified[FBP_red_bits] = true; _property[FBP_green_bits] = g; - _specified[FBP_green_bits] = true; _property[FBP_blue_bits] = b; - _specified[FBP_blue_bits] = true; _property[FBP_alpha_bits] = a; - _specified[FBP_alpha_bits] = true; _property[FBP_color_bits] = r + g + b; - _specified[FBP_color_bits] = true; + _specified |= (1 << FBP_color_bits) | (1 << FBP_red_bits) | + (1 << FBP_green_bits) | (1 << FBP_blue_bits) | + (1 << FBP_alpha_bits); } //////////////////////////////////////////////////////////////////// @@ -346,7 +351,7 @@ set_rgba_bits(int r, int g, int b, int a) { INLINE void FrameBufferProperties:: set_red_bits(int n) { _property[FBP_red_bits] = n; - _specified[FBP_red_bits] = true; + _specified |= (1 << FBP_red_bits); } //////////////////////////////////////////////////////////////////// @@ -357,7 +362,7 @@ set_red_bits(int n) { INLINE void FrameBufferProperties:: set_green_bits(int n) { _property[FBP_green_bits] = n; - _specified[FBP_green_bits] = true; + _specified |= (1 << FBP_green_bits); } //////////////////////////////////////////////////////////////////// @@ -368,7 +373,7 @@ set_green_bits(int n) { INLINE void FrameBufferProperties:: set_blue_bits(int n) { _property[FBP_blue_bits] = n; - _specified[FBP_blue_bits] = true; + _specified |= (1 << FBP_blue_bits); } //////////////////////////////////////////////////////////////////// @@ -379,7 +384,7 @@ set_blue_bits(int n) { INLINE void FrameBufferProperties:: set_alpha_bits(int n) { _property[FBP_alpha_bits] = n; - _specified[FBP_alpha_bits] = true; + _specified |= (1 << FBP_alpha_bits); } //////////////////////////////////////////////////////////////////// @@ -390,7 +395,7 @@ set_alpha_bits(int n) { INLINE void FrameBufferProperties:: set_stencil_bits(int n) { _property[FBP_stencil_bits] = n; - _specified[FBP_stencil_bits] = true; + _specified |= (1 << FBP_stencil_bits); } //////////////////////////////////////////////////////////////////// @@ -401,7 +406,7 @@ set_stencil_bits(int n) { INLINE void FrameBufferProperties:: set_accum_bits(int n) { _property[FBP_accum_bits] = n; - _specified[FBP_accum_bits] = true; + _specified |= (1 << FBP_accum_bits); } //////////////////////////////////////////////////////////////////// @@ -413,7 +418,7 @@ INLINE void FrameBufferProperties:: set_aux_rgba(int n) { nassertv(n < 4); _property[FBP_aux_rgba] = n; - _specified[FBP_aux_rgba] = true; + _specified |= (1 << FBP_aux_rgba); } //////////////////////////////////////////////////////////////////// @@ -425,7 +430,7 @@ INLINE void FrameBufferProperties:: set_aux_hrgba(int n) { nassertv(n < 4); _property[FBP_aux_hrgba] = n; - _specified[FBP_aux_hrgba] = true; + _specified |= (1 << FBP_aux_hrgba); } //////////////////////////////////////////////////////////////////// @@ -437,7 +442,7 @@ INLINE void FrameBufferProperties:: set_aux_float(int n) { nassertv(n < 4); _property[FBP_aux_float] = n; - _specified[FBP_aux_float] = true; + _specified |= (1 << FBP_aux_float); } //////////////////////////////////////////////////////////////////// @@ -448,7 +453,7 @@ set_aux_float(int n) { INLINE void FrameBufferProperties:: set_multisamples(int n) { _property[FBP_multisamples] = n; - _specified[FBP_multisamples] = true; + _specified |= (1 << FBP_multisamples); } //////////////////////////////////////////////////////////////////// @@ -460,7 +465,7 @@ set_multisamples(int n) { INLINE void FrameBufferProperties:: set_coverage_samples(int n) { _property[FBP_coverage_samples] = n; - _specified[FBP_coverage_samples] = true; + _specified |= (1 << FBP_coverage_samples); } //////////////////////////////////////////////////////////////////// @@ -471,7 +476,7 @@ set_coverage_samples(int n) { INLINE void FrameBufferProperties:: set_back_buffers(int n) { _property[FBP_back_buffers] = n; - _specified[FBP_back_buffers] = true; + _specified |= (1 << FBP_back_buffers); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/frameBufferProperties.cxx b/panda/src/display/frameBufferProperties.cxx index 8b38b4b3e1..d5cc37a97a 100644 --- a/panda/src/display/frameBufferProperties.cxx +++ b/panda/src/display/frameBufferProperties.cxx @@ -37,9 +37,9 @@ void FrameBufferProperties:: operator = (const FrameBufferProperties ©) { _flags_specified = copy._flags_specified; _flags = copy._flags; + _specified = copy._specified; for (int i = 0; i < FBP_COUNT; ++i) { - _specified[i] = copy._specified[i]; _property[i] = copy._property[i]; } } @@ -144,17 +144,24 @@ get_default() { if (depth_bits > 0) { default_props.set_depth_bits(depth_bits); } - if (color_bits > 0) { - default_props.set_color_bits(color_bits); - } - if (red_bits > 0) { - default_props.set_red_bits(red_bits); - } - if (green_bits > 0) { - default_props.set_green_bits(green_bits); - } - if (blue_bits > 0) { - default_props.set_blue_bits(blue_bits); + switch (color_bits.size()) { + case 0: + break; + case 1: + default_props.set_color_bits(color_bits[0]); + break; + case 3: + default_props.set_color_bits(color_bits[0] + color_bits[1] + color_bits[2]); + default_props.set_red_bits(color_bits[0]); + default_props.set_green_bits(color_bits[1]); + default_props.set_blue_bits(color_bits[2]); + break; + default: + default_props.set_color_bits(color_bits[0]); + display_cat.error() + << "Configuration variable color-bits takes either 1 or 3 values, not " + << color_bits.size() << "\n"; + break; } if (alpha_bits > 0) { default_props.set_alpha_bits(alpha_bits); @@ -189,10 +196,11 @@ operator == (const FrameBufferProperties &other) const { return false; } + if (_specified != other._specified) { + return false; + } + for (int i = 0; i < FBP_COUNT; ++i) { - if (_specified[i] != other._specified[i]) { - return false; - } if (_property[i] != other._property[i]) { return false; } @@ -214,9 +222,9 @@ clear() { _flags_specified = 0; for (int i = 0; i < FBP_COUNT; ++i) { - _specified[i] = 0; _property[i] = 0; } + _specified = 0; } //////////////////////////////////////////////////////////////////// @@ -232,9 +240,9 @@ add_properties(const FrameBufferProperties &other) { _flags |= other._flags & other._flags_specified; for (int i = 0; i < FBP_COUNT; ++i) { - if (other._specified[i]) { + if (other._specified & (1 << i)) { _property[i] = other._property[i]; - _specified[i] = true; + _specified |= (1 << i); } } } @@ -364,30 +372,18 @@ get_buffer_mask() const { //////////////////////////////////////////////////////////////////// bool FrameBufferProperties:: is_any_specified() const { - if (_flags_specified != 0) { - return true; - } - - for (int i = 0; i < FBP_COUNT; ++i) { - if (_specified[i]) { - return true; - } - } - return false; + return (_flags_specified | _specified) != 0; } //////////////////////////////////////////////////////////////////// // Function: FrameBufferProperties::set_all_specified // Access: Published -// Description: sets all the specified bits. +// Description: Marks all bits as having been specified. //////////////////////////////////////////////////////////////////// void FrameBufferProperties:: set_all_specified() { _flags_specified = FBF_all; - - for (int i = 0; i < FBP_COUNT; ++i) { - _specified[i] = true; - } + _specified = (1 << FBP_COUNT) - 1; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/frameBufferProperties.h b/panda/src/display/frameBufferProperties.h index 57ba098600..7e3861b847 100644 --- a/panda/src/display/frameBufferProperties.h +++ b/panda/src/display/frameBufferProperties.h @@ -67,7 +67,7 @@ private: }; int _property[FBP_COUNT]; - bool _specified[FBP_COUNT]; + int _specified; int _flags; int _flags_specified; diff --git a/panda/src/distort/nonlinearImager.cxx b/panda/src/distort/nonlinearImager.cxx index c64d9460ba..7a17a61b8f 100644 --- a/panda/src/distort/nonlinearImager.cxx +++ b/panda/src/distort/nonlinearImager.cxx @@ -27,7 +27,7 @@ //////////////////////////////////////////////////////////////////// // Function: NonlinearImager::Constructor // Access: Published -// Description: +// Description: //////////////////////////////////////////////////////////////////// NonlinearImager:: NonlinearImager() { @@ -38,7 +38,7 @@ NonlinearImager() { //////////////////////////////////////////////////////////////////// // Function: NonlinearImager::Destructor // Access: Published -// Description: +// Description: //////////////////////////////////////////////////////////////////// NonlinearImager:: ~NonlinearImager() { @@ -54,7 +54,7 @@ NonlinearImager:: //////////////////////////////////////////////////////////////////// // Function: NonlinearImager::add_screen // Access: Published -// This version of this method is deprecated and will +// Description: This version of this method is deprecated and will // soon be removed. Use the version that takes two // parameters instead. //////////////////////////////////////////////////////////////////// diff --git a/panda/src/doc/eggSyntax.txt b/panda/src/doc/eggSyntax.txt index 7cbf1b228f..4b57d91192 100644 --- a/panda/src/doc/eggSyntax.txt +++ b/panda/src/doc/eggSyntax.txt @@ -1476,6 +1476,10 @@ GROUPING ENTRIES its geometry is used to define the extent of the collision surface (unless the "descend" flag is given; see below). + It is now deprecated to use without "descend"; it will + become the default soon. You should always specify it for best + compatibility. + Valid types so far are: Plane @@ -1538,11 +1542,12 @@ GROUPING ENTRIES descend - Instead of creating only one collision object of the given type, - each group descended from this node that contains geometry will + Each group descended from this node that contains geometry will define a new collision object of the given type. The event name, if any, will also be inherited from the top node and - shared among all the collision objects. + shared among all the collision objects. This option will soon + be the default; it is suggested that it is always specified for + most compatibility. keep diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index ae3be83199..40ca88b6e7 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -2970,6 +2970,10 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group, make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode); } } + } else { + egg2pg_cat.warning() + << "Using without 'descend' is deprecated. 'descend' " + << "will become the default in a future version of Panda3D.\n"; } } diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 85b18b6f9d..4afda4b921 100755 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -41,7 +41,6 @@ CLP(CgShaderContext):: CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) { _glgsg = glgsg; _cg_program = 0; - _glsl_profile = false; nassertv(s->get_language() == Shader::SL_Cg); @@ -63,10 +62,6 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte release_resources(); } else { - if (cgGetProgramProfile(_cg_program) == CG_PROFILE_GLSLC) { - _glsl_profile = true; - } - cgGLLoadProgram(_cg_program); CGerror error = cgGetError(); if (error != CG_NO_ERROR) { @@ -351,14 +346,7 @@ issue_parameters(int altered) { if (altered & (_shader->_mat_spec[i]._dep[0] | _shader->_mat_spec[i]._dep[1])) { const LMatrix4 *val = _glgsg->fetch_specified_value(_shader->_mat_spec[i], altered); if (!val) continue; -#ifndef STDFLOAT_DOUBLE - // In this case, the data is already single-precision. - const PN_float32 *data = val->get_data(); -#else - // In this case, we have to convert it. - LMatrix4f valf = LCAST(PN_float32, *val); - const PN_float32 *data = valf.get_data(); -#endif + const PN_stdfloat *data = val->get_data(); CGparameter p = _cg_parameter_map[_shader->_mat_spec[i]._id._seqno]; switch (_shader->_mat_spec[i]._piece) { @@ -377,13 +365,13 @@ issue_parameters(int altered) { case Shader::SMP_row3x3: GLfv(cgGLSetParameter3)(p, data+12); continue; case Shader::SMP_upper3x3: { - LMatrix3f upper3 = val->get_upper_3(); + LMatrix3 upper3 = val->get_upper_3(); GLfc(cgGLSetMatrixParameter)(p, upper3.get_data()); continue; } case Shader::SMP_transpose3x3: { - LMatrix3f upper3 = val->get_upper_3(); + LMatrix3 upper3 = val->get_upper_3(); GLfr(cgGLSetMatrixParameter)(p, upper3.get_data()); continue; } @@ -412,7 +400,7 @@ disable_shader_vertex_arrays() { CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; if (p == 0) continue; - if (_glsl_profile && cgGetParameterBaseResource(p) == CG_ATTR0) { + if (cgGetParameterBaseResource(p) == CG_ATTR0) { int index = cgGetParameterResourceIndex(p); if (index >= 8) { _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8)); @@ -501,11 +489,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { num_values = GL_BGRA; } - // This is truly the most preposterous hack. When using the GLSL - // profiles, cgGLSetParameterPointer relies on the the driver mapping - // standard attributes to fixed indices (and breaking the spec doing - // so), which only the NVIDIA drivers do. Unbelievable. - if (_glsl_profile && cgGetParameterBaseResource(p) == CG_ATTR0) { + // cgGLSetParameterPointer is just stupidly bugged on every level. + // Sigh. This seems to work on both NVIDIA and AMD cards now. + if (cgGetParameterBaseResource(p) == CG_ATTR0) { int index = cgGetParameterResourceIndex(p); switch (index) { case 0: // gl_Vertex diff --git a/panda/src/glstuff/glCgShaderContext_src.h b/panda/src/glstuff/glCgShaderContext_src.h index b5d45d4aaa..16dd9bcdb2 100755 --- a/panda/src/glstuff/glCgShaderContext_src.h +++ b/panda/src/glstuff/glCgShaderContext_src.h @@ -52,7 +52,6 @@ public: private: CGprogram _cg_program; - bool _glsl_profile; pvector _cg_parameter_map; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 245fedb579..ced71cdddd 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -30,10 +30,10 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"), _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"), - _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples") + _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples"), + _requested_multisamples(0), + _requested_coverage_samples(0) { - CLP(GraphicsStateGuardian) *glgsg; - // A FBO doesn't have a back buffer. _draw_buffer_type = RenderBuffer::T_front; _screenshot_buffer_type = RenderBuffer::T_front; @@ -42,36 +42,6 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe, _fbo_multisample = 0; _initial_clear = true; _needs_rebuild = true; - DCAST_INTO_V(glgsg, _gsg); - - if (glgsg->get_supports_framebuffer_multisample() && glgsg->get_supports_framebuffer_blit()) { - _requested_multisamples = fb_prop.get_multisamples(); - } else { - _requested_multisamples = 0; - } - - if (glgsg->get_supports_framebuffer_multisample_coverage_nv() && glgsg->get_supports_framebuffer_blit()) { - _requested_coverage_samples = fb_prop.get_coverage_samples(); - // Note: Only 4 and 8 actual samples are supported by the extension, with 8 or 16 coverage samples. - if ((_requested_coverage_samples <= 8) && (_requested_coverage_samples > 0)) { - _requested_multisamples = 4; - _requested_coverage_samples = 8; - } else if (_requested_coverage_samples > 8) { - if (_requested_multisamples < 8) { - _requested_multisamples = 4; - } else { - _requested_multisamples = 8; - } - _requested_coverage_samples = 16; - } - - } else { - _requested_coverage_samples = 0; - } - - if (_requested_multisamples > glgsg->_max_fb_samples) { - _requested_multisamples = glgsg->_max_fb_samples; - } _rb_size_x = 0; _rb_size_y = 0; @@ -382,9 +352,9 @@ rebuild_bitplanes() { } } - // Decide whether we should use a depth stencil or just a regular depth attachment. - // If nothing was attached to either RTP_depth_stencil or RTP_depth, we use a - // depth-stencil renderbuffer. + // Decide whether we should use a depth stencil or just a regular depth + // attachment. If nothing was attached to either RTP_depth_stencil or + // RTP_depth, we use a depth-stencil renderbuffer. _use_depth_stencil = false; if (_gsg->get_supports_depth_stencil()) { if (attach[RTP_depth_stencil]) { @@ -396,19 +366,16 @@ rebuild_bitplanes() { // explicitly bound something to RTP_depth. _use_depth_stencil = false; - } else if (_fb_properties.get_float_depth()) { - // Let's not bother with a depth-stencil buffer - // if a float buffer was requested. - _use_depth_stencil = false; - - } else if (_fb_properties.get_depth_bits() > 24) { - // We can't give more than 24 depth bits with a depth-stencil buffer. - _use_depth_stencil = false; - } else if (_fb_properties.get_stencil_bits() > 0) { // The user requested stencil bits. Let's take the hint. _use_depth_stencil = true; + } else if (_fb_properties.get_depth_bits() > 24 || + _fb_properties.get_float_depth()) { + // 32-bit float depth is supported in conjunction with depth stencil, + // but it's a waste. Let's not do it unless the user requested stencil. + _use_depth_stencil = false; + } else if (_fb_properties.get_depth_bits() > 0) { // Let's use a depth stencil buffer by default, if a depth // buffer was requested. @@ -416,10 +383,9 @@ rebuild_bitplanes() { } } - // Knowing this, we can already be a tiny bit - // more accurate about the framebuffer properties. + // Knowing this, we can already be a tiny bit more accurate about the + // framebuffer properties. if (_use_depth_stencil) { - _fb_properties.set_depth_bits(24); _fb_properties.set_stencil_bits(8); } else { _fb_properties.set_stencil_bits(0); @@ -553,8 +519,7 @@ rebuild_bitplanes() { #endif // OPENGLES if (!_have_any_color) { - _fb_properties.set_color_bits(0); - _fb_properties.set_alpha_bits(0); + _fb_properties.set_rgba_bits(0, 0, 0, 0); } _initial_clear = false; @@ -699,8 +664,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, glGetTexLevelParameteriv(target, 0, GL_TEXTURE_BLUE_SIZE, &blue_size); glGetTexLevelParameteriv(target, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size); - _fb_properties.set_color_bits(red_size + green_size + blue_size); - _fb_properties.set_alpha_bits(alpha_size); + _fb_properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size); } #endif } @@ -717,6 +681,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, // No texture to bind. Instead, create a renderbuffer. // Choose a suitable renderbuffer format based on the requirements. #ifdef OPENGLES + // OpenGL ES case. GLuint gl_format = GL_RGBA4; switch (slot) { case RTP_depth_stencil: @@ -731,6 +696,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, gl_format = GL_DEPTH_COMPONENT16; } break; + //NB: we currently use RTP_stencil to store the right eye for stereo. //case RTP_stencil: // gl_format = GL_STENCIL_INDEX8; // break @@ -762,7 +728,8 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLuint gl_format = GL_RGBA; switch (slot) { case RTP_depth_stencil: - if (_fb_properties.get_float_depth()) { + if (_fb_properties.get_depth_bits() > 24 || + _fb_properties.get_float_depth()) { gl_format = GL_DEPTH32F_STENCIL8; } else { gl_format = GL_DEPTH24_STENCIL8; @@ -775,7 +742,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, gl_format = GL_DEPTH_COMPONENT32; } else if (_fb_properties.get_depth_bits() > 16) { gl_format = GL_DEPTH_COMPONENT24; - } else if (_fb_properties.get_depth_bits() > 8) { + } else if (_fb_properties.get_depth_bits() > 1) { gl_format = GL_DEPTH_COMPONENT16; } else { gl_format = GL_DEPTH_COMPONENT; @@ -903,8 +870,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_GREEN_SIZE_EXT, &green_size); glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_BLUE_SIZE_EXT, &blue_size); glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_ALPHA_SIZE_EXT, &alpha_size); - _fb_properties.set_color_bits(red_size + green_size + blue_size); - _fb_properties.set_alpha_bits(alpha_size); + _fb_properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size); } glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0); glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, attachpoint, @@ -976,7 +942,6 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, format = GL_DEPTH_COMPONENT32; break; default: - format = GL_DEPTH_COMPONENT; break; } } @@ -1269,14 +1234,12 @@ open_buffer() { // Describe the framebuffer properties of the FBO. // - // Unfortunately, we can't possibly predict which formats + // Unfortunately, we can't currently predict which formats // the implementation will allow us to use at this point, so // we'll just have to make some guesses and parrot the rest // of the properties back to the user. // When we actually attach the textures, we'll update the // properties more appropriately. - // This is probably safe, as we can usually bind just about - // any supported texture format to the FBO. // Rounding the depth bits is not spectacular, but at least we're // telling the user *something* about what we're going to get. @@ -1286,6 +1249,9 @@ open_buffer() { if (_fb_properties.get_color_bits() == 0 && _fb_properties.get_rgb_color()) { _fb_properties.set_color_bits(1); + _fb_properties.set_red_bits(1); + _fb_properties.set_green_bits(1); + _fb_properties.set_blue_bits(1); } // Actually, let's always get a colour buffer for now until we @@ -1294,18 +1260,28 @@ open_buffer() { _fb_properties.set_color_bits(1); } - if (_fb_properties.get_depth_bits() >= 32) { + if (_fb_properties.get_depth_bits() > 24) { _fb_properties.set_depth_bits(32); } else if (_fb_properties.get_depth_bits() > 16) { _fb_properties.set_depth_bits(24); - } else if (_fb_properties.get_depth_bits() > 8) { + } else { _fb_properties.set_depth_bits(16); } - // We're not going to get more than this, ever. + // We're not going to get more than this, ever. At least not until OpenGL + // introduces 64-bit texture formats. if (_fb_properties.get_color_bits() > 96) { _fb_properties.set_color_bits(96); } + if (_fb_properties.get_red_bits() > 32) { + _fb_properties.set_red_bits(32); + } + if (_fb_properties.get_green_bits() > 32) { + _fb_properties.set_green_bits(32); + } + if (_fb_properties.get_blue_bits() > 32) { + _fb_properties.set_blue_bits(32); + } if (_fb_properties.get_alpha_bits() > 32) { _fb_properties.set_alpha_bits(32); } @@ -1317,30 +1293,63 @@ open_buffer() { // We currently only support color formats this big as float. if (_fb_properties.get_color_bits() > 16 * 3) { - _fb_properties.set_color_bits(32 * 3); _fb_properties.set_float_color(true); - - if (_fb_properties.get_alpha_bits() > 0) { - _fb_properties.set_alpha_bits(32); - } } if (_fb_properties.get_srgb_color()) { - _fb_properties.set_color_bits(24); + // This is the only sRGB color format OpenGL supports. + _fb_properties.set_rgba_bits(8, 8, 8, + (_fb_properties.get_alpha_bits() > 0) ? 8 : 0); _fb_properties.set_float_color(false); - - if (_fb_properties.get_alpha_bits() > 0) { - _fb_properties.set_alpha_bits(32); - } } if (!_gsg->get_supports_depth_stencil()) { // At least we know we won't be getting stencil bits. _fb_properties.set_stencil_bits(0); + + } else if (_fb_properties.get_stencil_bits() > 0) { + // We don't currently support stencil-only targets. + _fb_properties.set_stencil_bits(8); + if (_fb_properties.get_depth_bits() < 24) { + _fb_properties.set_depth_bits(24); + } } + + // Accumulation buffers aren't supported for FBOs. _fb_properties.set_accum_bits(0); + if (glgsg->get_supports_framebuffer_multisample() && glgsg->get_supports_framebuffer_blit()) { + _requested_multisamples = _fb_properties.get_multisamples(); + } else { + _requested_multisamples = 0; + } + +#ifndef OPENGLES + if (glgsg->get_supports_framebuffer_multisample_coverage_nv() && glgsg->get_supports_framebuffer_blit()) { + _requested_coverage_samples = _fb_properties.get_coverage_samples(); + // Note: Only 4 and 8 actual samples are supported by the extension, with 8 or 16 coverage samples. + if ((_requested_coverage_samples <= 8) && (_requested_coverage_samples > 0)) { + _requested_multisamples = 4; + _requested_coverage_samples = 8; + } else if (_requested_coverage_samples > 8) { + if (_requested_multisamples < 8) { + _requested_multisamples = 4; + } else { + _requested_multisamples = 8; + } + _requested_coverage_samples = 16; + } + + } else { + _requested_coverage_samples = 0; + } +#endif + + if (_requested_multisamples > glgsg->_max_fb_samples) { + _requested_multisamples = glgsg->_max_fb_samples; + } _fb_properties.set_multisamples(_requested_multisamples); + _fb_properties.set_coverage_samples(_requested_coverage_samples); // Update aux settings to reflect the GL_MAX_DRAW_BUFFERS limit, // if we exceed it, that is. diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 33b48f1298..b2c09d9093 100755 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -1069,13 +1069,21 @@ issue_parameters(int altered) { case Shader::SMP_row3x3: _glgsg->_glUniform3fv(p, 1, data+12); continue; case Shader::SMP_upper3x3: { +#ifndef STDFLOAT_DOUBLE LMatrix3f upper3 = val->get_upper_3(); +#else + LMatrix3f upper3 = valf.get_upper_3(); +#endif _glgsg->_glUniformMatrix3fv(p, 1, false, upper3.get_data()); continue; } case Shader::SMP_transpose3x3: { +#ifndef STDFLOAT_DOUBLE LMatrix3f upper3 = val->get_upper_3(); +#else + LMatrix3f upper3 = valf.get_upper_3(); +#endif _glgsg->_glUniformMatrix3fv(p, 1, true, upper3.get_data()); continue; } diff --git a/panda/src/glxdisplay/glxGraphicsPipe.cxx b/panda/src/glxdisplay/glxGraphicsPipe.cxx index a4ec5b5409..508ca1a445 100644 --- a/panda/src/glxdisplay/glxGraphicsPipe.cxx +++ b/panda/src/glxdisplay/glxGraphicsPipe.cxx @@ -136,35 +136,28 @@ make_output(const string &name, // Second thing to try: a GLGraphicsBuffer if (retry == 1) { - if ((host==0)|| - (!gl_support_fbo)|| - ((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)) { + if (!gl_support_fbo || host == NULL || + (flags & (BF_require_parasite | BF_require_window)) != 0) { return NULL; } // Early failure - if we are sure that this buffer WONT // meet specs, we can bail out early. - int _fbo_multisample = 0; - if (!ConfigVariableBool("framebuffer-object-multisample", false, PRC_DESC("Enabled Multisample."))) { - _fbo_multisample = 16; - } - if ((flags & BF_fb_props_optional)==0) { - if ((fb_prop.get_indexed_color() > 0)|| - (fb_prop.get_back_buffers() > 0)|| - (fb_prop.get_accum_bits() > 0)|| - (fb_prop.get_multisamples() > _fbo_multisample)) { + if ((flags & BF_fb_props_optional) == 0) { + if (fb_prop.get_indexed_color() || + fb_prop.get_back_buffers() > 0 || + fb_prop.get_accum_bits() > 0) { return NULL; } } - // Early success - if we are sure that this buffer WILL - // meet specs, we can precertify it. - if ((posixgsg != 0) && - (posixgsg->is_valid()) && - (!posixgsg->needs_reset()) && - (posixgsg->_supports_framebuffer_object) && - (posixgsg->_glDrawBuffers != 0)&& - (fb_prop.is_basic())) { - precertify = true; + if (posixgsg != NULL && posixgsg->is_valid() && !posixgsg->needs_reset()) { + if (!posixgsg->_supports_framebuffer_object || + posixgsg->_glDrawBuffers == NULL) { + return NULL; + } else { + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + precertify = true; + } } return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index d44903e921..f5b3e8a051 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -41,6 +41,7 @@ #include "pbitops.h" #include "streamReader.h" #include "texturePeeker.h" +#include "convert_srgb.h" #ifdef HAVE_SQUISH #include @@ -131,46 +132,6 @@ struct DDSHeader { DDSCaps2 caps; }; -// This table is used for converting unsigned char texture values in an sRGB -// texture to linear RGB values, for use in mipmap generation. -static float srgb_to_lrgbf[256] = {0.000000f, 0.000304f, 0.000607f, 0.000911f, - 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, - 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f, - 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, - 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, - 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, - 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, - 0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, - 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f, - 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, - 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f, - 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, - 0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, - 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, - 0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, - 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f, - 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, - 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, - 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, - 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, - 0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, - 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386429f, 0.391572f, 0.396755f, 0.401978f, 0.407240f, - 0.412543f, 0.417885f, 0.423268f, 0.428690f, 0.434154f, 0.439657f, 0.445201f, - 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473531f, 0.479320f, 0.485150f, - 0.491021f, 0.496933f, 0.502886f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, - 0.533276f, 0.539479f, 0.545724f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, - 0.577580f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, - 0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, - 0.672443f, 0.679542f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, - 0.723055f, 0.730461f, 0.737910f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, - 0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f, - 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, - 0.947307f, 0.955973f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.000000f}; - //////////////////////////////////////////////////////////////////// // Function: Texture::Constructor // Access: Published @@ -4436,7 +4397,7 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const { switch (cdata->_component_type) { case T_unsigned_byte: { - LColorf scaled = cdata->_clear_color.fmin(LColorf(1)).fmax(LColorf::zero()); + LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero()); scaled *= 255; switch (cdata->_num_components) { case 2: @@ -4457,7 +4418,7 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const { case T_unsigned_short: { - LColorf scaled = cdata->_clear_color.fmin(LColorf(1)).fmax(LColorf::zero()); + LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero()); scaled *= 65535; switch (cdata->_num_components) { case 2: @@ -6990,7 +6951,13 @@ do_filter_2d_mipmap_pages(const CData *cdata, // We currently only support sRGB mipmap generation for // unsigned byte textures, due to our use of a lookup table. nassertv(cdata->_component_type == T_unsigned_byte); - filter_component = &filter_2d_unsigned_byte_srgb; + + if (has_sse2_sRGB_encode()) { + filter_component = &filter_2d_unsigned_byte_srgb_sse2; + } else { + filter_component = &filter_2d_unsigned_byte_srgb; + } + // Alpha is always linear. filter_alpha = &filter_2d_unsigned_byte; @@ -7140,7 +7107,13 @@ do_filter_3d_mipmap_level(const CData *cdata, // We currently only support sRGB mipmap generation for // unsigned byte textures, due to our use of a lookup table. nassertv(cdata->_component_type == T_unsigned_byte); - filter_component = &filter_3d_unsigned_byte_srgb; + + if (has_sse2_sRGB_encode()) { + filter_component = &filter_3d_unsigned_byte_srgb_sse2; + } else { + filter_component = &filter_3d_unsigned_byte_srgb; + } + // Alpha is always linear. filter_alpha = &filter_3d_unsigned_byte; @@ -7385,18 +7358,32 @@ filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q, void Texture:: filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q, size_t pixel_size, size_t row_size) { - float result = (srgb_to_lrgbf[q[0]] + - srgb_to_lrgbf[q[pixel_size]] + - srgb_to_lrgbf[q[row_size]] + - srgb_to_lrgbf[q[pixel_size + row_size]]) / 4.0f; + float result = (decode_sRGB_float(q[0]) + + decode_sRGB_float(q[pixel_size]) + + decode_sRGB_float(q[row_size]) + + decode_sRGB_float(q[pixel_size + row_size])); - // This is based on the formula out of the EXT_texture_sRGB - // specification, except the factors are multiplied with 255.0f. - if (result < 0.0031308f) { - *p = (unsigned char)(result * 3294.6f); - } else { - *p = (unsigned char)(269.025f * powf(result, 0.41666f) - 14.025f); - } + *p = encode_sRGB_uchar(result * 0.25f); + ++p; + ++q; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::filter_2d_unsigned_byte_srgb_sse2 +// Access: Public, Static +// Description: Averages a 2x2 block of pixel components into a +// single pixel component, for producing the next mipmap +// level. Increments p and q to the next component. +//////////////////////////////////////////////////////////////////// +void Texture:: +filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q, + size_t pixel_size, size_t row_size) { + float result = (decode_sRGB_float(q[0]) + + decode_sRGB_float(q[pixel_size]) + + decode_sRGB_float(q[row_size]) + + decode_sRGB_float(q[pixel_size + row_size])); + + *p = encode_sRGB_uchar_sse2(result * 0.25f); ++p; ++q; } @@ -7470,22 +7457,40 @@ filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q, void Texture:: filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q, size_t pixel_size, size_t row_size, size_t page_size) { - float result = (srgb_to_lrgbf[q[0]] + - srgb_to_lrgbf[q[pixel_size]] + - srgb_to_lrgbf[q[row_size]] + - srgb_to_lrgbf[q[pixel_size + row_size]] + - srgb_to_lrgbf[q[page_size]] + - srgb_to_lrgbf[q[pixel_size + page_size]] + - srgb_to_lrgbf[q[row_size + page_size]] + - srgb_to_lrgbf[q[pixel_size + row_size + page_size]]) / 8.0f; + float result = (decode_sRGB_float(q[0]) + + decode_sRGB_float(q[pixel_size]) + + decode_sRGB_float(q[row_size]) + + decode_sRGB_float(q[pixel_size + row_size]) + + decode_sRGB_float(q[page_size]) + + decode_sRGB_float(q[pixel_size + page_size]) + + decode_sRGB_float(q[row_size + page_size]) + + decode_sRGB_float(q[pixel_size + row_size + page_size])); - // This is based on the formula out of the EXT_texture_sRGB - // specification, except the factors are multiplied with 255.0f. - if (result < 0.0031308f) { - *p = (unsigned char)(result * 3294.6f); - } else { - *p = (unsigned char)(269.025f * powf(result, 0.41666f) - 14.025f); - } + *p = encode_sRGB_uchar(result * 0.125f); + ++p; + ++q; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::filter_3d_unsigned_byte_srgb_sse2 +// Access: Public, Static +// Description: Averages a 2x2x2 block of pixel components into a +// single pixel component, for producing the next mipmap +// level. Increments p and q to the next component. +//////////////////////////////////////////////////////////////////// +void Texture:: +filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q, + size_t pixel_size, size_t row_size, size_t page_size) { + float result = (decode_sRGB_float(q[0]) + + decode_sRGB_float(q[pixel_size]) + + decode_sRGB_float(q[row_size]) + + decode_sRGB_float(q[pixel_size + row_size]) + + decode_sRGB_float(q[page_size]) + + decode_sRGB_float(q[pixel_size + page_size]) + + decode_sRGB_float(q[row_size + page_size]) + + decode_sRGB_float(q[pixel_size + row_size + page_size])); + + *p = encode_sRGB_uchar_sse2(result * 0.125f); ++p; ++q; } diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index b957d62ff3..8dc1d1fcbc 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -752,6 +752,9 @@ private: static void filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q, size_t pixel_size, size_t row_size); + static void filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p, + const unsigned char *&q, + size_t pixel_size, size_t row_size); static void filter_2d_unsigned_short(unsigned char *&p, const unsigned char *&q, size_t pixel_size, size_t row_size); @@ -766,6 +769,10 @@ private: const unsigned char *&q, size_t pixel_size, size_t row_size, size_t page_size); + static void filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p, + const unsigned char *&q, + size_t pixel_size, size_t row_size, + size_t page_size); static void filter_3d_unsigned_short(unsigned char *&p, const unsigned char *&q, size_t pixel_size, size_t row_size, diff --git a/panda/src/grutil/pfmVizzer.cxx b/panda/src/grutil/pfmVizzer.cxx index 475d9d8fb5..1304d59bea 100644 --- a/panda/src/grutil/pfmVizzer.cxx +++ b/panda/src/grutil/pfmVizzer.cxx @@ -57,11 +57,11 @@ void PfmVizzer:: project(const Lens *lens, const PfmFile *undist_lut) { nassertv(_pfm.is_valid()); - static LMatrix4f to_uv(0.5f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.5f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.5f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f); - + static LMatrix4 to_uv(0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f); + for (int yi = 0; yi < _pfm.get_y_size(); ++yi) { for (int xi = 0; xi < _pfm.get_x_size(); ++xi) { if (!_pfm.has_point(xi, yi)) { @@ -79,8 +79,8 @@ project(const Lens *lens, const PfmFile *undist_lut) { } else { // Now the lens gives us coordinates in the range [-1, 1]. // Rescale these to [0, 1]. - LPoint3f uvw = film * to_uv; - + LPoint3f uvw = LCAST(float, film * to_uv); + if (undist_lut != NULL) { // Apply the undistortion map, if given. LPoint3f p2; @@ -88,7 +88,7 @@ project(const Lens *lens, const PfmFile *undist_lut) { uvw = p2; uvw[1] = 1.0 - uvw[1]; } - + p = uvw; } } diff --git a/panda/src/ode/odeRayGeom.I b/panda/src/ode/odeRayGeom.I index 314a7631e1..ef5ceb1760 100755 --- a/panda/src/ode/odeRayGeom.I +++ b/panda/src/ode/odeRayGeom.I @@ -11,6 +11,8 @@ // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// + + INLINE void OdeRayGeom:: set_length(dReal length) { dGeomRaySetLength(_id, length); diff --git a/panda/src/ode/odeTriMeshData.h b/panda/src/ode/odeTriMeshData.h index e126fcb8c5..4b9e6e03eb 100755 --- a/panda/src/ode/odeTriMeshData.h +++ b/panda/src/ode/odeTriMeshData.h @@ -62,6 +62,7 @@ PUBLISHED: // INLINE void get_buffer(unsigned char** buf, int* buf_len) const; // INLINE void set_buffer(unsigned char* buf); // INLINE void update(); + virtual void write(ostream &out = cout, unsigned int indent=0) const; void write_faces(ostream &out) const; @@ -90,6 +91,7 @@ public: const int* indices, int index_count, \ const int* normals); */ + INLINE void preprocess(); INLINE dTriMeshDataID get_id() const; diff --git a/panda/src/osxdisplay/osxGraphicsPipe.cxx b/panda/src/osxdisplay/osxGraphicsPipe.cxx index 594aed22a5..951c1bf3e1 100644 --- a/panda/src/osxdisplay/osxGraphicsPipe.cxx +++ b/panda/src/osxdisplay/osxGraphicsPipe.cxx @@ -412,37 +412,32 @@ make_output(const string &name, return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop, flags, gsg, host); } - + // Second thing to try: a GLGraphicsBuffer - + if (retry == 1) { - if (!osx_support_gl_buffer) { - return NULL; - } - if ((host==0)|| - ((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)) { + if (!osx_support_gl_buffer || !gl_support_fbo || host == NULL || + (flags & (BF_require_parasite | BF_require_window)) != 0) { return NULL; } // Early failure - if we are sure that this buffer WONT // meet specs, we can bail out early. - if ((flags & BF_fb_props_optional)==0) { - if ((fb_prop.get_indexed_color() > 0)|| - (fb_prop.get_back_buffers() > 0)|| - (fb_prop.get_accum_bits() > 0)|| - (fb_prop.get_multisamples() > 0)) { + if ((flags & BF_fb_props_optional) == 0) { + if (fb_prop.get_indexed_color() || + fb_prop.get_back_buffers() > 0 || + fb_prop.get_accum_bits() > 0) { return NULL; } } - // Early success - if we are sure that this buffer WILL - // meet specs, we can precertify it. - if ((osxgsg != 0) && - (osxgsg->is_valid()) && - (!osxgsg->needs_reset()) && - (osxgsg->_supports_framebuffer_object) && - (osxgsg->_glDrawBuffers != 0)&& - (fb_prop.is_basic())) { - precertify = true; + if (posixgsg != NULL && posixgsg->is_valid() && !posixgsg->needs_reset()) { + if (!posixgsg->_supports_framebuffer_object || + posixgsg->_glDrawBuffers == NULL) { + return NULL; + } else { + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + precertify = true; + } } return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); } diff --git a/panda/src/pgraph/cullBinManager.h b/panda/src/pgraph/cullBinManager.h index 6d29d9e1f4..3957359b50 100644 --- a/panda/src/pgraph/cullBinManager.h +++ b/panda/src/pgraph/cullBinManager.h @@ -99,7 +99,7 @@ private: class EXPCL_PANDA_PGRAPH BinDefinition { public: #ifndef NDEBUG - LColorf _flash_color; + LColor _flash_color; bool _flash_active; #endif bool _in_use; diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 162f08e03b..3a1f0c86de 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -721,13 +721,15 @@ get_user_bounds(int pipeline_stage, Thread *current_thread) const { //////////////////////////////////////////////////////////////////// INLINE void PandaNode:: mark_bounds_stale(int pipeline_stage, Thread *current_thread) const { - // It's important that we don't hold the lock during the call to - // force_bounds_stale(). + // We check whether it is already marked stale. If so, we don't have + // to make the call to force_bounds_stale(). bool is_stale_bounds; { CDStageReader cdata(_cycler, pipeline_stage, current_thread); - is_stale_bounds = (cdata->_last_bounds_update != cdata->_next_update); + is_stale_bounds = (cdata->_last_update != cdata->_next_update); } + // It's important that we don't hold the lock during the call to + // force_bounds_stale(). if (!is_stale_bounds) { ((PandaNode *)this)->force_bounds_stale(pipeline_stage, current_thread); } @@ -1833,7 +1835,7 @@ get_off_clip_planes() const { //////////////////////////////////////////////////////////////////// INLINE CPT(BoundingVolume) PandaNodePipelineReader:: get_bounds() const { - nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_external_bounds); + nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_external_bounds); return _cdata->_external_bounds; } @@ -1852,7 +1854,7 @@ get_bounds() const { //////////////////////////////////////////////////////////////////// INLINE int PandaNodePipelineReader:: get_nested_vertices() const { - nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_nested_vertices); + nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_nested_vertices); return _cdata->_nested_vertices; } diff --git a/panda/src/pnmimage/convert_srgb.I b/panda/src/pnmimage/convert_srgb.I new file mode 100644 index 0000000000..e841748fb4 --- /dev/null +++ b/panda/src/pnmimage/convert_srgb.I @@ -0,0 +1,175 @@ +// Filename: convert_srgb.I +// Created by: rdb (29Oct14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: decode_sRGB_float +// Description: Decodes the sRGB-encoded unsigned char value to +// a linearized float in the range 0-1. +//////////////////////////////////////////////////////////////////// +CONSTEXPR float decode_sRGB_float(unsigned char val) { + return to_linear_float_table[val]; +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_float +// Description: Decodes the sRGB-encoded floating-point value in +// the range 0-1 to a linearized float in the range +// 0-1. Inputs outside this range produce invalid +// results. +//////////////////////////////////////////////////////////////////// +INLINE float decode_sRGB_float(float val) { + return (val <= 0.04045f) + ? (val * (1.f / 12.92f)) + : cpow((val + 0.055f) * (1.f / 1.055f), 2.4f); +} + +//////////////////////////////////////////////////////////////////// +// Function: decode_sRGB_uchar +// Description: Decodes the sRGB-encoded unsigned char value to +// a linearized unsigned char value. +//////////////////////////////////////////////////////////////////// +CONSTEXPR unsigned char decode_sRGB_uchar(unsigned char val) { + return to_linear_uchar_table[val]; +} + +//////////////////////////////////////////////////////////////////// +// Function: decode_sRGB_uchar +// Description: Decodes the sRGB-encoded floating-point value in +// the range 0-1 to a linearized unsigned char value. +// Inputs outside this range are clamped. +//////////////////////////////////////////////////////////////////// +INLINE unsigned char decode_sRGB_uchar(float val) { + return (val <= 0.04045f) + ? (unsigned char)(max(0.f, val) * (255.f / 12.92f) + 0.5f) + : (unsigned char)(cpow((min(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f); +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_float +// Description: Encodes the linearized unsigned char value to an +// sRGB-encoded floating-point value in ther range 0-1. +//////////////////////////////////////////////////////////////////// +INLINE float +encode_sRGB_float(unsigned char val) { + // This seems like a very unlikely use case, so I didn't bother + // making a look-up table for this. + return (val == 0) ? 0 + : (1.055f * cpow((float)val * (1.f / 255.f), 0.41666f) - 0.055); +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_float +// Description: Encodes the linearized floating-point value in the +// range 0-1 to an sRGB-encoded float in the range +// 0-1. Inputs outside this range produce invalid +// results. +//////////////////////////////////////////////////////////////////// +INLINE float +encode_sRGB_float(float val) { + return (val < 0.0031308f) + ? (val * 12.92f) + : (1.055f * cpow(val, 0.41666f) - 0.055); +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_uchar +// Description: Encodes the linearized unsigned char value to an +// sRGB-encoded unsigned char value. +//////////////////////////////////////////////////////////////////// +CONSTEXPR unsigned char +encode_sRGB_uchar(unsigned char val) { + return to_srgb8_table[val]; +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_uchar +// Description: Encodes the linearized floating-point value in the +// range 0-1 to an sRGB-encoded unsigned char value. +// Inputs outside this range are clamped. +// +// When SSE2 support is known at compile time, this +// automatically uses an optimized version. Otherwise, +// it does not attempt runtime CPU detection. If you +// know that SSE2 is supported (ie. if the function +// has_sse2_sRGB_encode() returns true) you should +// call encode_sRGB_uchar_sse2 instead. +//////////////////////////////////////////////////////////////////// +INLINE unsigned char +encode_sRGB_uchar(float val) { +#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64) + // Use a highly optimized approximation that has more than enough + // accuracy for an unsigned char. + return encode_sRGB_uchar_sse2(val); +#else + return (val < 0.0031308f) + ? (unsigned char) (max(0.f, val) * 3294.6f + 0.5f) + : (unsigned char) (269.025f * cpow(min(val, 1.f), 0.41666f) - 13.525f); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_uchar +// Description: Encodes the linearized floating-point color value +// an sRGB-encoded xel in the range 0-255. +// +// When SSE2 support is known at compile time, this +// automatically uses an optimized version. Otherwise, +// it does not attempt runtime CPU detection. If you +// know that SSE2 is supported (ie. if the function +// has_sse2_sRGB_encode() returns true) you should +// call encode_sRGB_uchar_sse2 instead. +//////////////////////////////////////////////////////////////////// +INLINE void +encode_sRGB_uchar(const LColorf &color, xel &into) { +#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64) + // SSE2 support compiled-in; we're guaranteed to have it. + encode_sRGB_uchar_sse2(color, into); +#else + // Boring, slow, non-SSE2 version. + PPM_ASSIGN(into, + encode_sRGB_uchar(color[0]), + encode_sRGB_uchar(color[1]), + encode_sRGB_uchar(color[2])); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: encode_sRGB_uchar +// Description: Encodes the linearized floating-point color value +// an sRGB-encoded xel and alpha in the range 0-255. +// The alpha value is not sRGB-encoded. +// +// When SSE2 support is known at compile time, this +// automatically uses an optimized version. Otherwise, +// it does not attempt runtime CPU detection. If you +// know that SSE2 is supported (ie. if the function +// has_sse2_sRGB_encode() returns true) you should +// call encode_sRGB_uchar_sse2 instead. +//////////////////////////////////////////////////////////////////// +INLINE void +encode_sRGB_uchar(const LColorf &color, xel &into, xelval &into_alpha) { +#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64) + // SSE2 support compiled-in; we're guaranteed to have it. + encode_sRGB_uchar_sse2(color, into, into_alpha); +#else + // Boring, slow, non-SSE2 version. + PPM_ASSIGN(into, + encode_sRGB_uchar(color[0]), + encode_sRGB_uchar(color[1]), + encode_sRGB_uchar(color[2])); + + into_alpha = (xelval) (color[3] * 255.f + 0.5f); +#endif +} diff --git a/panda/src/pnmimage/convert_srgb.cxx b/panda/src/pnmimage/convert_srgb.cxx new file mode 100644 index 0000000000..ef27735d06 --- /dev/null +++ b/panda/src/pnmimage/convert_srgb.cxx @@ -0,0 +1,165 @@ +// Filename: convert_srgb.cxx +// Created by: rdb (13Nov14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "convert_srgb.h" + +#ifdef __GNUC__ +#include +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +// Lookup tables for converting from unsigned char formats. +ALIGN_64BYTE const +unsigned char to_srgb8_table[256] = { 0x00, 0x0d, 0x16, 0x1c, 0x22, 0x26, 0x2a, + 0x2e, 0x32, 0x35, 0x38, 0x3b, 0x3d, 0x40, 0x42, 0x45, 0x47, 0x49, 0x4b, 0x4d, + 0x4f, 0x51, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5d, 0x5f, 0x60, 0x62, 0x63, + 0x65, 0x66, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x73, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xaf, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb9, + 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, + 0xc2, 0xc2, 0xc3, 0xc4, 0xc4, 0xc5, 0xc5, 0xc6, 0xc7, 0xc7, 0xc8, 0xc8, 0xc9, + 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf, 0xd0, 0xd0, 0xd1, + 0xd1, 0xd2, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd9, 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6, + 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, + 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, + 0xff, 0xff}; + +ALIGN_64BYTE const +unsigned char to_linear_uchar_table[256] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, + 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, + 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, + 0x11, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, + 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5f, 0x60, 0x61, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, + 0x6f, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f, 0x80, + 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x92, 0x93, 0x95, + 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9f, 0xa1, 0xa3, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, + 0xad, 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4, + 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + 0xe0, 0xe2, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, + 0xfd, 0xff}; + +ALIGN_64BYTE +const float to_linear_float_table[256] = { 0, 0.000304f, 0.000607f, 0.000911f, + 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, + 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f, + 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, + 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, + 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, + 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, + 0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, + 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f, + 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, + 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f, + 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, + 0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, + 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, + 0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, + 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f, + 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, + 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, + 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, + 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, + 0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, + 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386429f, 0.391572f, 0.396755f, 0.401978f, 0.407240f, + 0.412543f, 0.417885f, 0.423268f, 0.428690f, 0.434154f, 0.439657f, 0.445201f, + 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473531f, 0.479320f, 0.485150f, + 0.491021f, 0.496933f, 0.502886f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, + 0.533276f, 0.539479f, 0.545724f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, + 0.577580f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, + 0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, + 0.672443f, 0.679542f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, + 0.723055f, 0.730461f, 0.737910f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, + 0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f, + 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, + 0.947307f, 0.955973f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.000000f}; + + +#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64) +// SSE2 support enabled at compile time. No runtime detection mechanism needed. +bool +has_sse2_sRGB_encode() { + return true; +} + +#else +// SSE2 support not guaranteed. Use a runtime detection mechanism. + +bool +has_sse2_sRGB_encode() { +#if defined(__GNUC__) + unsigned int a, b, c, d; + static const bool has_support = + (__get_cpuid(1, &a, &b, &c, &d) == 1 && (d & 0x04000000) != 0); + +#elif defined(_WIN32) + static const bool has_support = + (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != FALSE); + +#else + static const bool has_support = false; +#endif + + if (pnmimage_cat.is_debug()) { + static bool checked = false; + if (!checked) { +#if defined(__GNUC__) || defined(_WIN32) + if (has_support) { + pnmimage_cat.debug() + << "Runtime detection reports SSE2 instructions available: " + << "SSE2-optimized sRGB encoding routines enabled.\n"; + } else { + pnmimage_cat.debug() + << "Runtime detection reports SSE2 instructions unavailable: " + << "SSE2-optimized sRGB encoding routines disabled.\n"; + } +#else + pnmimage_cat.debug() + << "No runtime detection mechanism for SSE2 instructions available: " + << "SSE2-optimized sRGB encoding routines disabled.\n"; +#endif + checked = true; + } + } + + return has_support; +} + +#endif // __SSE2__ diff --git a/panda/src/pnmimage/convert_srgb.h b/panda/src/pnmimage/convert_srgb.h new file mode 100644 index 0000000000..6d59019d84 --- /dev/null +++ b/panda/src/pnmimage/convert_srgb.h @@ -0,0 +1,59 @@ +// Filename: convert_srgb.h +// Created by: rdb (13Nov14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONVERT_SRGB_H +#define CONVERT_SRGB_H + +#include "pandabase.h" +#include "luse.h" +#include "pnmimage_base.h" + +// The below functions can encode and decode sRGB colors in various +// representations. Some of them are implemented using look-up tables, +// some others using SSE2 intrinsics. +extern EXPCL_PANDA_PNMIMAGE const unsigned char to_srgb8_table[256]; +extern EXPCL_PANDA_PNMIMAGE const unsigned char to_linear_uchar_table[256]; +extern EXPCL_PANDA_PNMIMAGE const float to_linear_float_table[256]; + +EXPCL_PANDA_PNMIMAGE CONSTEXPR float decode_sRGB_float(unsigned char val); +EXPCL_PANDA_PNMIMAGE INLINE float decode_sRGB_float(float val); +EXPCL_PANDA_PNMIMAGE CONSTEXPR unsigned char decode_sRGB_uchar(unsigned char val); +EXPCL_PANDA_PNMIMAGE INLINE unsigned char decode_sRGB_uchar(float val); + +EXPCL_PANDA_PNMIMAGE INLINE float encode_sRGB_float(unsigned char val); +EXPCL_PANDA_PNMIMAGE INLINE float encode_sRGB_float(float val); +EXPCL_PANDA_PNMIMAGE CONSTEXPR unsigned char encode_sRGB_uchar(unsigned char val); +EXPCL_PANDA_PNMIMAGE INLINE unsigned char encode_sRGB_uchar(float val); + +// These functions convert more than one component in one go, +// which can be faster due to vectorization. +EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from, + xel &into); +EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from, + xel &into, xelval &into_alpha); + +// Use these functions if you know that SSE2 support is available. +// Otherwise, they will crash! +EXPCL_PANDA_PNMIMAGE unsigned char encode_sRGB_uchar_sse2(float val); +EXPCL_PANDA_PNMIMAGE void encode_sRGB_uchar_sse2(const LColorf &from, + xel &into); +EXPCL_PANDA_PNMIMAGE void encode_sRGB_uchar_sse2(const LColorf &from, + xel &into, xelval &into_alpha); + +// Use the following to find out if you can call either of the above. +EXPCL_PANDA_PNMIMAGE bool has_sse2_sRGB_encode(); + +#include "convert_srgb.I" + +#endif diff --git a/panda/src/pnmimage/convert_srgb_sse2.cxx b/panda/src/pnmimage/convert_srgb_sse2.cxx new file mode 100644 index 0000000000..8121eccc8d --- /dev/null +++ b/panda/src/pnmimage/convert_srgb_sse2.cxx @@ -0,0 +1,151 @@ +// Filename: convert_srgb_sse2.cxx +// Created by: rdb (13Nov14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +// This file should always be compiled with SSE2 support. These +// functions will only be called when SSE2 support is detected at +// run-time. + +#include "convert_srgb.h" +#include "luse.h" + +#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64) + +#include +#include + +static INLINE __m128i _encode_sRGB_sse2_mul255(__m128 val) { + // This an SSE2-based approximation of the sRGB encode function. + // It has a maximum error of around 0.001, which is by far small + // enough for a uchar. It is also at least 10x as fast as the + // original; up to 40x when taking advantage of vectorization. + // Note that the fourth float is only multiplied with 255. + + // Part of the code in this function is derived from: + // http://stackoverflow.com/a/6486630/2135754 + + // Clamp to 0-1 range. + val = _mm_max_ps(val, _mm_set1_ps(0.0f)); + val = _mm_min_ps(val, _mm_set1_ps(1.0f)); + + // Pre-multiply with constant factor to adjust for exp bias. + __m128 xf = _mm_mul_ps(val, _mm_set1_ps(6.3307e18f)); + + // Approximate logarithm by... casting! + xf = _mm_cvtepi32_ps(_mm_castps_si128(xf)); + + // Multiply 'logarithm' by power. + xf = _mm_mul_ps(xf, _mm_set1_ps(2.0f / 3.0f)); + + // Reverse operation of above: cast the other way. + xf = _mm_castsi128_ps(_mm_cvtps_epi32(xf)); + + // Make an overestimate and an underestimate. + __m128 xover = _mm_mul_ps(val, xf); + __m128 xunder = _mm_mul_ps(_mm_mul_ps(val, val), + _mm_rsqrt_ps(xf)); + + // Average the two factors, with a slight bias. + __m128 xavg = _mm_mul_ps(_mm_add_ps(xover, xunder), + _mm_set1_ps(0.5286098f)); + + // Take square root twice. Note that this is faster than + // the more expensive _mm_sqrt_ps instruction. + xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); + xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); + + // Bring it into the correct range. These factors are determined + // not on the basis of accuracy, but are chosen such that the + // decoder lookup table produces an equivalent result for any value. + xavg = _mm_mul_ps(xavg, _mm_set1_ps(269.122f)); + xavg = _mm_sub_ps(xavg, _mm_set1_ps(13.55f)); + + // Compute the linear section. This is also the path that + // the alpha channel takes, so we set the alpha multiplier + // to 255 (since alpha is not sRGB-converted). + __m128 lval = _mm_mul_ps(val, + _mm_set_ps(255.0f, 3294.6f, 3294.6f, 3294.6f)); + + lval = _mm_add_ps(lval, _mm_set1_ps(0.5f)); + + // Decide which version to return. Rig the alpha + // comparator to always fail so that the linear path + // is always chosen for alpha. + __m128 mask = _mm_cmpge_ps(val, + _mm_set_ps(2.0f, 0.0031308f, 0.0031308f, 0.0031308f)); + + // This is a non-branching way to return one or the other value. + return _mm_cvttps_epi32(_mm_or_ps( + _mm_and_ps(mask, xavg), + _mm_andnot_ps(mask, lval))); +} + +unsigned char +encode_sRGB_uchar_sse2(float val) { + // Running only a single component through this function is still + // way faster than the equivalent non-SSE2 version. + return (unsigned char) + _mm_extract_epi16(_encode_sRGB_sse2_mul255(_mm_set1_ps(val)), 0); +} + +void +encode_sRGB_uchar_sse2(const LColorf &color, xel &into) { +#ifdef LINMATH_ALIGN + __m128 vec = _mm_load_ps(color.get_data()); +#else + __m128 vec = _mm_loadu_ps(color.get_data()); +#endif + + __m128i vals = _encode_sRGB_sse2_mul255(vec); + into.r = _mm_extract_epi16(vals, 0); + into.g = _mm_extract_epi16(vals, 2); + into.b = _mm_extract_epi16(vals, 4); +} + +void +encode_sRGB_uchar_sse2(const LColorf &color, xel &into, xelval &into_alpha) { +#ifdef LINMATH_ALIGN + __m128 vec = _mm_load_ps(color.get_data()); +#else + __m128 vec = _mm_loadu_ps(color.get_data()); +#endif + + __m128i vals = _encode_sRGB_sse2_mul255(vec); + into.r = _mm_extract_epi16(vals, 0); + into.g = _mm_extract_epi16(vals, 2); + into.b = _mm_extract_epi16(vals, 4); + into_alpha = _mm_extract_epi16(vals, 6); +} + +#else +// Somehow we're still compiling this without SSE2 support. We'll +// still have to define these functions, but emit a warning that the +// build system isn't configured properly. +#warning convert_srgb_sse2.cxx is being compiled without SSE2 support! + +unsigned char +encode_sRGB_uchar_sse2(float val) { + return encode_sRGB_uchar(val); +} + +void +encode_sRGB_uchar_sse2(const LColorf &color, xel &into) { + encode_sRGB_uchar(color, into); +} + +void +encode_sRGB_uchar_sse2(const LColorf &color, xel &into, xelval &into_alpha) { + encode_sRGB_uchar(color, into, into_alpha); +} + +#endif diff --git a/panda/src/pnmimage/p3pnmimage_composite1.cxx b/panda/src/pnmimage/p3pnmimage_composite1.cxx index 0e290cbc95..85dfe7f0a1 100644 --- a/panda/src/pnmimage/p3pnmimage_composite1.cxx +++ b/panda/src/pnmimage/p3pnmimage_composite1.cxx @@ -1,4 +1,5 @@ #include "config_pnmimage.cxx" +#include "convert_srgb.cxx" #include "pfmFile.cxx" #include "pnm-image-filter.cxx" #include "pnmbitio.cxx" diff --git a/panda/src/rocket/rocketRenderInterface.h b/panda/src/rocket/rocketRenderInterface.h index 8f02fe4b23..bc1e64647e 100644 --- a/panda/src/rocket/rocketRenderInterface.h +++ b/panda/src/rocket/rocketRenderInterface.h @@ -63,7 +63,7 @@ private: // Hold the scissor settings and whether or not to enable scissoring. bool _enable_scissor; - LVecBase4f _scissor; + LVecBase4 _scissor; // These are temporarily filled in by render(). CullTraverser *_trav; diff --git a/panda/src/tinydisplay/zbuffer.h b/panda/src/tinydisplay/zbuffer.h index a4c8a2015c..46190ef4e6 100644 --- a/panda/src/tinydisplay/zbuffer.h +++ b/panda/src/tinydisplay/zbuffer.h @@ -75,7 +75,7 @@ typedef unsigned int ZPOINT; ((((unsigned int)(a) << 24) & 0xff000000) | (((unsigned int)(r) << 16) & 0xff0000) | (((unsigned int)(g) << 8) & 0xff00) | (unsigned int)(b)) #define SRGB_TO_PIXEL(r,g,b) \ - ((encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB10[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4])) + ((encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4])) #define SRGBA_TO_PIXEL(r,g,b,a) \ ((((unsigned int)(a) << 16) & 0xff000000) | (encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4])) diff --git a/panda/src/wgldisplay/wglGraphicsPipe.cxx b/panda/src/wgldisplay/wglGraphicsPipe.cxx index f4695c3f16..fbfa17a339 100644 --- a/panda/src/wgldisplay/wglGraphicsPipe.cxx +++ b/panda/src/wgldisplay/wglGraphicsPipe.cxx @@ -153,46 +153,32 @@ make_output(const string &name, return new wglGraphicsWindow(engine, this, name, fb_prop, win_prop, flags, gsg, host); } - + // Second thing to try: a GLGraphicsBuffer - + if (retry == 1) { - if ((!gl_support_fbo)|| - (host==0)|| - ((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)) { + if (!gl_support_fbo || host == NULL || + (flags & (BF_require_parasite | BF_require_window)) != 0) { return NULL; } // Early failure - if we are sure that this buffer WONT // meet specs, we can bail out early. - int _fbo_multisample = 0; - if (!ConfigVariableBool("framebuffer-object-multisample", false, PRC_DESC("Enabled Multisample."))) { - _fbo_multisample = 16; - } - if ((flags & BF_fb_props_optional)==0) { + if ((flags & BF_fb_props_optional) == 0) { if (fb_prop.get_indexed_color() || - (fb_prop.get_back_buffers() > 0)|| - (fb_prop.get_accum_bits() > 0)|| - (fb_prop.get_multisamples() > _fbo_multisample)) { + fb_prop.get_back_buffers() > 0 || + fb_prop.get_accum_bits() > 0) { return NULL; } } - if ((wglgsg != 0) && - (wglgsg->is_valid()) && - (!wglgsg->needs_reset()) && - !wglgsg->_supports_framebuffer_object) { - return NULL; - } - - // Early success - if we are sure that this buffer WILL - // meet specs, we can precertify it. - if ((wglgsg != 0) && - (wglgsg->is_valid()) && - (!wglgsg->needs_reset()) && - (wglgsg->_supports_framebuffer_object) && - (wglgsg->_glDrawBuffers != 0)&& - (fb_prop.is_basic())) { - precertify = true; + if (wglgsg != NULL && wglgsg->is_valid() && !wglgsg->needs_reset()) { + if (!wglgsg->_supports_framebuffer_object || + wglgsg->_glDrawBuffers == NULL) { + return NULL; + } else { + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + precertify = true; + } } return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host); diff --git a/pandatool/src/daeegg/daeCharacter.cxx b/pandatool/src/daeegg/daeCharacter.cxx index d3c091d7bf..aea226b9b6 100644 --- a/pandatool/src/daeegg/daeCharacter.cxx +++ b/pandatool/src/daeegg/daeCharacter.cxx @@ -320,7 +320,7 @@ build_table(EggTable *parent, FCDSceneNode* node, const pset &keys) { } // Quantize the FPS, otherwise Panda complains about FPS mismatches. - float fps = round(((keys.size() - 1) / timing_total) * 100) * 0.01f; + float fps = cfloor(((keys.size() - 1) / timing_total) * 100 + 0.5f) * 0.01f; xform->set_fps(fps); // Loop through the children joints diff --git a/pandatool/src/daeegg/daeCharacter.h b/pandatool/src/daeegg/daeCharacter.h index cd656eae06..4b9e86af73 100644 --- a/pandatool/src/daeegg/daeCharacter.h +++ b/pandatool/src/daeegg/daeCharacter.h @@ -45,7 +45,7 @@ public: _bind_pose(LMatrix4d::ident_mat()) {} LMatrix4d _bind_pose; - const PT(EggGroup) _group; + PT(EggGroup) _group; const FCDSceneNode *_scene_node; DaeCharacter *_character; }; diff --git a/pandatool/src/daeegg/daeToEggConverter.cxx b/pandatool/src/daeegg/daeToEggConverter.cxx index 8ff46b401b..88ae0c7027 100755 --- a/pandatool/src/daeegg/daeToEggConverter.cxx +++ b/pandatool/src/daeegg/daeToEggConverter.cxx @@ -228,7 +228,7 @@ convert_file(const Filename &filename) { Characters::iterator it; DaeCharacter *character; for (it = _characters.begin(); it != _characters.end(); ++it) { - DaeCharacter *character = *it; + character = *it; // Collect key frame timings. if (get_animation_convert() == AC_both || diff --git a/pandatool/src/daeegg/pre_fcollada_include.h b/pandatool/src/daeegg/pre_fcollada_include.h index a2497f5140..70c80ca324 100755 --- a/pandatool/src/daeegg/pre_fcollada_include.h +++ b/pandatool/src/daeegg/pre_fcollada_include.h @@ -22,6 +22,13 @@ #error You must include pre_fcollada_include.h before including FCollada.h! #endif +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + // FCollada expects LINUX to be defined on linux #ifdef IS_LINUX #ifndef LINUX diff --git a/samples/asteroids/main.py b/samples/asteroids/main.py new file mode 100755 index 0000000000..297ccf91e6 --- /dev/null +++ b/samples/asteroids/main.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman, and Greg Lindley +# Last Updated: 2015-03-13 +# +# This tutorial demonstrates the use of tasks. A task is a function that +# gets called once every frame. They are good for things that need to be +# updated very often. In the case of asteroids, we use tasks to update +# the positions of all the objects, and to check if the bullets or the +# ship have hit the asteroids. +# +# Note: This definitely a complicated example. Tasks are the cores of +# most games so it seemed appropriate to show what a full game in Panda +# could look like. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import TextNode, TransparencyAttrib +from panda3d.core import LPoint3, LVector3 +from direct.gui.OnscreenText import OnscreenText +from direct.task.Task import Task +from math import sin, cos, pi +from random import randint, choice, random +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Wait, Func +import sys + +# Constants that will control the behavior of the game. It is good to +# group constants like this so that they can be changed once without +# having to find everywhere they are used in code +SPRITE_POS = 55 # At default field of view and a depth of 55, the screen +# dimensions is 40x30 units +SCREEN_X = 20 # Screen goes from -20 to 20 on X +SCREEN_Y = 15 # Screen goes from -15 to 15 on Y +TURN_RATE = 360 # Degrees ship can turn in 1 second +ACCELERATION = 10 # Ship acceleration in units/sec/sec +MAX_VEL = 6 # Maximum ship velocity in units/sec +MAX_VEL_SQ = MAX_VEL ** 2 # Square of the ship velocity +DEG_TO_RAD = pi / 180 # translates degrees to radians for sin and cos +BULLET_LIFE = 2 # How long bullets stay on screen before removed +BULLET_REPEAT = .2 # How often bullets can be fired +BULLET_SPEED = 10 # Speed bullets move +AST_INIT_VEL = 1 # Velocity of the largest asteroids +AST_INIT_SCALE = 3 # Initial asteroid scale +AST_VEL_SCALE = 2.2 # How much asteroid speed multiplies when broken up +AST_SIZE_SCALE = .6 # How much asteroid scale changes when broken up +AST_MIN_SCALE = 1.1 # If and asteroid is smaller than this and is hit, +# it disapears instead of splitting up + + +# This helps reduce the amount of code used by loading objects, since all of +# the objects are pretty much the same. +def loadObject(tex=None, pos=LPoint3(0, 0), depth=SPRITE_POS, scale=1, + transparency=True): + # Every object uses the plane model and is parented to the camera + # so that it faces the screen. + obj = loader.loadModel("models/plane") + obj.reparentTo(camera) + + # Set the initial position and scale. + obj.setPos(pos.getX(), depth, pos.getY()) + obj.setScale(scale) + + # This tells Panda not to worry about the order that things are drawn in + # (ie. disable Z-testing). This prevents an effect known as Z-fighting. + obj.setBin("unsorted", 0) + obj.setDepthTest(False) + + if transparency: + # Enable transparency blending. + obj.setTransparency(TransparencyAttrib.MAlpha) + + if tex: + # Load and set the requested texture. + tex = loader.loadTexture("textures/" + tex) + obj.setTexture(tex, 1) + + return obj + + +# Macro-like function used to reduce the amount to code needed to create the +# on screen instructions +def genLabelText(text, i): + return OnscreenText(text=text, parent=base.a2dTopLeft, pos=(0.07, -.06 * i - 0.1), + fg=(1, 1, 1, 1), align=TextNode.ALeft, shadow=(0, 0, 0, 0.5), scale=.05) + + +class AsteroidsDemo(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This code puts the standard title and instruction text on screen + self.title = OnscreenText(text="Panda3D: Tutorial - Tasks", + parent=base.a2dBottomRight, scale=.07, + align=TextNode.ARight, pos=(-0.1, 0.1), + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 0.5)) + self.escapeText = genLabelText("ESC: Quit", 0) + self.leftkeyText = genLabelText("[Left Arrow]: Turn Left (CCW)", 1) + self.rightkeyText = genLabelText("[Right Arrow]: Turn Right (CW)", 2) + self.upkeyText = genLabelText("[Up Arrow]: Accelerate", 3) + self.spacekeyText = genLabelText("[Space Bar]: Fire", 4) + + # Disable default mouse-based camera control. This is a method on the + # ShowBase class from which we inherit. + self.disableMouse() + + # Load the background starfield. + self.setBackgroundColor((0, 0, 0, 1)) + self.bg = loadObject("stars.jpg", scale=146, depth=200, + transparency=False) + + # Load the ship and set its initial velocity. + self.ship = loadObject("ship.png") + self.setVelocity(self.ship, LVector3.zero()) + + # A dictionary of what keys are currently being pressed + # The key events update this list, and our task will query it as input + self.keys = {"turnLeft": 0, "turnRight": 0, + "accel": 0, "fire": 0} + + self.accept("escape", sys.exit) # Escape quits + # Other keys events set the appropriate value in our key dictionary + self.accept("arrow_left", self.setKey, ["turnLeft", 1]) + self.accept("arrow_left-up", self.setKey, ["turnLeft", 0]) + self.accept("arrow_right", self.setKey, ["turnRight", 1]) + self.accept("arrow_right-up", self.setKey, ["turnRight", 0]) + self.accept("arrow_up", self.setKey, ["accel", 1]) + self.accept("arrow_up-up", self.setKey, ["accel", 0]) + self.accept("space", self.setKey, ["fire", 1]) + + # Now we create the task. taskMgr is the task manager that actually + # calls the function each frame. The add method creates a new task. + # The first argument is the function to be called, and the second + # argument is the name for the task. It returns a task object which + # is passed to the function each frame. + self.gameTask = taskMgr.add(self.gameLoop, "gameLoop") + + # Stores the time at which the next bullet may be fired. + self.nextBullet = 0.0 + + # This list will stored fired bullets. + self.bullets = [] + + # Complete initialization by spawning the asteroids. + self.spawnAsteroids() + + # As described earlier, this simply sets a key in the self.keys dictionary + # to the given value. + def setKey(self, key, val): + self.keys[key] = val + + def setVelocity(self, obj, val): + obj.setPythonTag("velocity", val) + + def getVelocity(self, obj): + return obj.getPythonTag("velocity") + + def setExpires(self, obj, val): + obj.setPythonTag("expires", val) + + def getExpires(self, obj): + return obj.getPythonTag("expires") + + def spawnAsteroids(self): + # Control variable for if the ship is alive + self.alive = True + self.asteroids = [] # List that will contain our asteroids + + for i in range(10): + # This loads an asteroid. The texture chosen is random + # from "asteroid1.png" to "asteroid3.png". + asteroid = loadObject("asteroid%d.png" % (randint(1, 3)), + scale=AST_INIT_SCALE) + self.asteroids.append(asteroid) + + # This is kind of a hack, but it keeps the asteroids from spawning + # near the player. It creates the list (-20, -19 ... -5, 5, 6, 7, + # ... 20) and chooses a value from it. Since the player starts at 0 + # and this list doesn't contain anything from -4 to 4, it won't be + # close to the player. + asteroid.setX(choice(range(-SCREEN_X, -5) + range(5, SCREEN_X))) + # Same thing for Y, but from -15 to 15 + asteroid.setZ(choice(range(-SCREEN_Y, -5) + range(5, SCREEN_Y))) + + # Heading is a random angle in radians + heading = random() * 2 * pi + + # Converts the heading to a vector and multiplies it by speed to + # get a velocity vector + v = LVector3(sin(heading), 0, cos(heading)) * AST_INIT_VEL + self.setVelocity(self.asteroids[i], v) + + # This is our main task function, which does all of the per-frame + # processing. It takes in self like all functions in a class, and task, + # the task object returned by taskMgr. + def gameLoop(self, task): + # Get the time elapsed since the next frame. We need this for our + # distance and velocity calculations. + dt = globalClock.getDt() + + # If the ship is not alive, do nothing. Tasks return Task.cont to + # signify that the task should continue running. If Task.done were + # returned instead, the task would be removed and would no longer be + # called every frame. + if not self.alive: + return Task.cont + + # update ship position + self.updateShip(dt) + + # check to see if the ship can fire + if self.keys["fire"] and task.time > self.nextBullet: + self.fire(task.time) # If so, call the fire function + # And disable firing for a bit + self.nextBullet = task.time + BULLET_REPEAT + # Remove the fire flag until the next spacebar press + self.keys["fire"] = 0 + + # update asteroids + for obj in self.asteroids: + self.updatePos(obj, dt) + + # update bullets + newBulletArray = [] + for obj in self.bullets: + self.updatePos(obj, dt) # Update the bullet + # Bullets have an experation time (see definition of fire) + # If a bullet has not expired, add it to the new bullet list so + # that it will continue to exist. + if self.getExpires(obj) > task.time: + newBulletArray.append(obj) + else: + obj.removeNode() # Otherwise, remove it from the scene. + # Set the bullet array to be the newly updated array + self.bullets = newBulletArray + + # Check bullet collision with asteroids + # In short, it checks every bullet against every asteroid. This is + # quite slow. A big optimization would be to sort the objects left to + # right and check only if they overlap. Framerate can go way down if + # there are many bullets on screen, but for the most part it's okay. + for bullet in self.bullets: + # This range statement makes it step though the asteroid list + # backwards. This is because if an asteroid is removed, the + # elements after it will change position in the list. If you go + # backwards, the length stays constant. + for i in range(len(self.asteroids) - 1, -1, -1): + asteroid = self.asteroids[i] + # Panda's collision detection is more complicated than we need + # here. This is the basic sphere collision check. If the + # distance between the object centers is less than sum of the + # radii of the two objects, then we have a collision. We use + # lengthSquared() since it is faster than length(). + if ((bullet.getPos() - asteroid.getPos()).lengthSquared() < + (((bullet.getScale().getX() + asteroid.getScale().getX()) + * .5) ** 2)): + # Schedule the bullet for removal + self.setExpires(bullet, 0) + self.asteroidHit(i) # Handle the hit + + # Now we do the same collision pass for the ship + shipSize = self.ship.getScale().getX() + for ast in self.asteroids: + # Same sphere collision check for the ship vs. the asteroid + if ((self.ship.getPos() - ast.getPos()).lengthSquared() < + (((shipSize + ast.getScale().getX()) * .5) ** 2)): + # If there is a hit, clear the screen and schedule a restart + self.alive = False # Ship is no longer alive + # Remove every object in asteroids and bullets from the scene + for i in self.asteroids + self.bullets: + i.removeNode() + self.bullets = [] # Clear the bullet list + self.ship.hide() # Hide the ship + # Reset the velocity + self.setVelocity(self.ship, LVector3(0, 0, 0)) + Sequence(Wait(2), # Wait 2 seconds + Func(self.ship.setR, 0), # Reset heading + Func(self.ship.setX, 0), # Reset position X + # Reset position Y (Z for Panda) + Func(self.ship.setZ, 0), + Func(self.ship.show), # Show the ship + Func(self.spawnAsteroids)).start() # Remake asteroids + return Task.cont + + # If the player has successfully destroyed all asteroids, respawn them + if len(self.asteroids) == 0: + self.spawnAsteroids() + + return Task.cont # Since every return is Task.cont, the task will + # continue indefinitely + + # Updates the positions of objects + def updatePos(self, obj, dt): + vel = self.getVelocity(obj) + newPos = obj.getPos() + (vel * dt) + + # Check if the object is out of bounds. If so, wrap it + radius = .5 * obj.getScale().getX() + if newPos.getX() - radius > SCREEN_X: + newPos.setX(-SCREEN_X) + elif newPos.getX() + radius < -SCREEN_X: + newPos.setX(SCREEN_X) + if newPos.getZ() - radius > SCREEN_Y: + newPos.setZ(-SCREEN_Y) + elif newPos.getZ() + radius < -SCREEN_Y: + newPos.setZ(SCREEN_Y) + + obj.setPos(newPos) + + # The handler when an asteroid is hit by a bullet + def asteroidHit(self, index): + # If the asteroid is small it is simply removed + if self.asteroids[index].getScale().getX() <= AST_MIN_SCALE: + self.asteroids[index].removeNode() + # Remove the asteroid from the list of asteroids. + del self.asteroids[index] + else: + # If it is big enough, divide it up into little asteroids. + # First we update the current asteroid. + asteroid = self.asteroids[index] + newScale = asteroid.getScale().getX() * AST_SIZE_SCALE + asteroid.setScale(newScale) # Rescale it + + # The new direction is chosen as perpendicular to the old direction + # This is determined using the cross product, which returns a + # vector perpendicular to the two input vectors. By crossing + # velocity with a vector that goes into the screen, we get a vector + # that is orthagonal to the original velocity in the screen plane. + vel = self.getVelocity(asteroid) + speed = vel.length() * AST_VEL_SCALE + vel.normalize() + vel = LVector3(0, 1, 0).cross(vel) + vel *= speed + self.setVelocity(asteroid, vel) + + # Now we create a new asteroid identical to the current one + newAst = loadObject(scale=newScale) + self.setVelocity(newAst, vel * -1) + newAst.setPos(asteroid.getPos()) + newAst.setTexture(asteroid.getTexture(), 1) + self.asteroids.append(newAst) + + # This updates the ship's position. This is similar to the general update + # but takes into account turn and thrust + def updateShip(self, dt): + heading = self.ship.getR() # Heading is the roll value for this model + # Change heading if left or right is being pressed + if self.keys["turnRight"]: + heading += dt * TURN_RATE + self.ship.setR(heading % 360) + elif self.keys["turnLeft"]: + heading -= dt * TURN_RATE + self.ship.setR(heading % 360) + + # Thrust causes acceleration in the direction the ship is currently + # facing + if self.keys["accel"]: + heading_rad = DEG_TO_RAD * heading + # This builds a new velocity vector and adds it to the current one + # relative to the camera, the screen in Panda is the XZ plane. + # Therefore all of our Y values in our velocities are 0 to signify + # no change in that direction. + newVel = \ + LVector3(sin(heading_rad), 0, cos(heading_rad)) * ACCELERATION * dt + newVel += self.getVelocity(self.ship) + # Clamps the new velocity to the maximum speed. lengthSquared() is + # used again since it is faster than length() + if newVel.lengthSquared() > MAX_VEL_SQ: + newVel.normalize() + newVel *= MAX_VEL + self.setVelocity(self.ship, newVel) + + # Finally, update the position as with any other object + self.updatePos(self.ship, dt) + + # Creates a bullet and adds it to the bullet list + def fire(self, time): + direction = DEG_TO_RAD * self.ship.getR() + pos = self.ship.getPos() + bullet = loadObject("bullet.png", scale=.2) # Create the object + bullet.setPos(pos) + # Velocity is in relation to the ship + vel = (self.getVelocity(self.ship) + + (LVector3(sin(direction), 0, cos(direction)) * + BULLET_SPEED)) + self.setVelocity(bullet, vel) + # Set the bullet expiration time to be a certain amount past the + # current time + self.setExpires(bullet, time + BULLET_LIFE) + + # Finally, add the new bullet to the list + self.bullets.append(bullet) + +# We now have everything we need. Make an instance of the class and start +# 3D rendering +demo = AsteroidsDemo() +demo.run() diff --git a/samples/asteroids/models/plane.egg b/samples/asteroids/models/plane.egg new file mode 100644 index 0000000000..b82f1c0a74 --- /dev/null +++ b/samples/asteroids/models/plane.egg @@ -0,0 +1,39 @@ + { Y-Up } + + { + "maya2egg plane.mb plane.egg" +} + groundPlane_transform { +} + pPlane1 { + pPlaneShape1.verts { + 1 { + -0.5 -0.5 0 + { 0 0 -1 } + { 0 0 } + { 1 1 1 1 } + } + 2 { + -0.5 0.5 0 + { 0 0 -1 } + { 0 1 } + { 1 1 1 1 } + } + 3 { + 0.5 -0.5 0 + { 0 0 -1 } + { 1 0 } + { 1 1 1 1 } + } + 4 { + 0.5 0.5 0 + { 0 0 -1 } + { 1 1 } + { 1 1 1 1 } + } + } + { + { 0 0 -1 } + { 3 4 2 1 { pPlaneShape1.verts } } + } +} diff --git a/samples/asteroids/textures/asteroid1.png b/samples/asteroids/textures/asteroid1.png new file mode 100644 index 0000000000..f5b127e096 Binary files /dev/null and b/samples/asteroids/textures/asteroid1.png differ diff --git a/samples/asteroids/textures/asteroid2.png b/samples/asteroids/textures/asteroid2.png new file mode 100644 index 0000000000..6f61c89657 Binary files /dev/null and b/samples/asteroids/textures/asteroid2.png differ diff --git a/samples/asteroids/textures/asteroid3.png b/samples/asteroids/textures/asteroid3.png new file mode 100644 index 0000000000..0ad15afd2e Binary files /dev/null and b/samples/asteroids/textures/asteroid3.png differ diff --git a/samples/asteroids/textures/bullet.png b/samples/asteroids/textures/bullet.png new file mode 100644 index 0000000000..5af5129c62 Binary files /dev/null and b/samples/asteroids/textures/bullet.png differ diff --git a/samples/asteroids/textures/ship.png b/samples/asteroids/textures/ship.png new file mode 100644 index 0000000000..d7ee706447 Binary files /dev/null and b/samples/asteroids/textures/ship.png differ diff --git a/samples/asteroids/textures/stars.jpg b/samples/asteroids/textures/stars.jpg new file mode 100644 index 0000000000..b56c143569 Binary files /dev/null and b/samples/asteroids/textures/stars.jpg differ diff --git a/samples/ball-in-maze/main.py b/samples/ball-in-maze/main.py new file mode 100755 index 0000000000..1ea47aeb05 --- /dev/null +++ b/samples/ball-in-maze/main.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial shows how to detect and respond to collisions. It uses solids +# create in code and the egg files, how to set up collision masks, a traverser, +# and a handler, how to detect collisions, and how to dispatch function based +# on the collisions. All of this is put together to simulate a labyrinth-style +# game + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import CollisionTraverser, CollisionNode +from panda3d.core import CollisionHandlerQueue, CollisionRay +from panda3d.core import Material, LRotationf, NodePath +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import TextNode +from panda3d.core import LVector3, BitMask32 +from direct.gui.OnscreenText import OnscreenText +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.LerpInterval import LerpFunc +from direct.interval.FunctionInterval import Func, Wait +from direct.task.Task import Task +import sys + +# Some constants for the program +ACCEL = 70 # Acceleration in ft/sec/sec +MAX_SPEED = 5 # Max speed in ft/sec +MAX_SPEED_SQ = MAX_SPEED ** 2 # Squared to make it easier to use lengthSquared +# Instead of length + + +class BallInMazeDemo(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This code puts the standard title and instruction text on screen + self.title = \ + OnscreenText(text="Panda3D: Tutorial - Collision Detection", + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08, + shadow=(0, 0, 0, 0.5)) + self.instructions = \ + OnscreenText(text="Mouse pointer tilts the board", + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.05, -0.08), fg=(1, 1, 1, 1), scale=.06, + shadow=(0, 0, 0, 0.5)) + + self.accept("escape", sys.exit) # Escape quits + + # Disable default mouse-based camera control. This is a method on the + # ShowBase class from which we inherit. + self.disableMouse() + camera.setPosHpr(0, 0, 25, 0, -90, 0) # Place the camera + + # Load the maze and place it in the scene + self.maze = loader.loadModel("models/maze") + self.maze.reparentTo(render) + + # Most times, you want collisions to be tested against invisible geometry + # rather than every polygon. This is because testing against every polygon + # in the scene is usually too slow. You can have simplified or approximate + # geometry for the solids and still get good results. + # + # Sometimes you'll want to create and position your own collision solids in + # code, but it's often easier to have them built automatically. This can be + # done by adding special tags into an egg file. Check maze.egg and ball.egg + # and look for lines starting with . The part is brackets tells + # Panda exactly what to do. Polyset means to use the polygons in that group + # as solids, while Sphere tells panda to make a collision sphere around them + # Keep means to keep the polygons in the group as visable geometry (good + # for the ball, not for the triggers), and descend means to make sure that + # the settings are applied to any subgroups. + # + # Once we have the collision tags in the models, we can get to them using + # NodePath's find command + + # Find the collision node named wall_collide + self.walls = self.maze.find("**/wall_collide") + + # Collision objects are sorted using BitMasks. BitMasks are ordinary numbers + # with extra methods for working with them as binary bits. Every collision + # solid has both a from mask and an into mask. Before Panda tests two + # objects, it checks to make sure that the from and into collision masks + # have at least one bit in common. That way things that shouldn't interact + # won't. Normal model nodes have collision masks as well. By default they + # are set to bit 20. If you want to collide against actual visable polygons, + # set a from collide mask to include bit 20 + # + # For this example, we will make everything we want the ball to collide with + # include bit 0 + self.walls.node().setIntoCollideMask(BitMask32.bit(0)) + # CollisionNodes are usually invisible but can be shown. Uncomment the next + # line to see the collision walls + #self.walls.show() + + # We will now find the triggers for the holes and set their masks to 0 as + # well. We also set their names to make them easier to identify during + # collisions + self.loseTriggers = [] + for i in range(6): + trigger = self.maze.find("**/hole_collide" + str(i)) + trigger.node().setIntoCollideMask(BitMask32.bit(0)) + trigger.node().setName("loseTrigger") + self.loseTriggers.append(trigger) + # Uncomment this line to see the triggers + # trigger.show() + + # Ground_collide is a single polygon on the same plane as the ground in the + # maze. We will use a ray to collide with it so that we will know exactly + # what height to put the ball at every frame. Since this is not something + # that we want the ball itself to collide with, it has a different + # bitmask. + self.mazeGround = self.maze.find("**/ground_collide") + self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1)) + + # Load the ball and attach it to the scene + # It is on a root dummy node so that we can rotate the ball itself without + # rotating the ray that will be attached to it + self.ballRoot = render.attachNewNode("ballRoot") + self.ball = loader.loadModel("models/ball") + self.ball.reparentTo(self.ballRoot) + + # Find the collison sphere for the ball which was created in the egg file + # Notice that it has a from collision mask of bit 0, and an into collison + # mask of no bits. This means that the ball can only cause collisions, not + # be collided into + self.ballSphere = self.ball.find("**/ball") + self.ballSphere.node().setFromCollideMask(BitMask32.bit(0)) + self.ballSphere.node().setIntoCollideMask(BitMask32.allOff()) + + # No we create a ray to start above the ball and cast down. This is to + # Determine the height the ball should be at and the angle the floor is + # tilting. We could have used the sphere around the ball itself, but it + # would not be as reliable + self.ballGroundRay = CollisionRay() # Create the ray + self.ballGroundRay.setOrigin(0, 0, 10) # Set its origin + self.ballGroundRay.setDirection(0, 0, -1) # And its direction + # Collision solids go in CollisionNode + # Create and name the node + self.ballGroundCol = CollisionNode('groundRay') + self.ballGroundCol.addSolid(self.ballGroundRay) # Add the ray + self.ballGroundCol.setFromCollideMask( + BitMask32.bit(1)) # Set its bitmasks + self.ballGroundCol.setIntoCollideMask(BitMask32.allOff()) + # Attach the node to the ballRoot so that the ray is relative to the ball + # (it will always be 10 feet over the ball and point down) + self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol) + # Uncomment this line to see the ray + #self.ballGroundColNp.show() + + # Finally, we create a CollisionTraverser. CollisionTraversers are what + # do the job of walking the scene graph and calculating collisions. + # For a traverser to actually do collisions, you need to call + # traverser.traverse() on a part of the scene. Fortunately, ShowBase + # has a task that does this for the entire scene once a frame. By + # assigning it to self.cTrav, we designate that this is the one that + # it should call traverse() on each frame. + self.cTrav = CollisionTraverser() + + # Collision traversers tell collision handlers about collisions, and then + # the handler decides what to do with the information. We are using a + # CollisionHandlerQueue, which simply creates a list of all of the + # collisions in a given pass. There are more sophisticated handlers like + # one that sends events and another that tries to keep collided objects + # apart, but the results are often better with a simple queue + self.cHandler = CollisionHandlerQueue() + # Now we add the collision nodes that can create a collision to the + # traverser. The traverser will compare these to all others nodes in the + # scene. There is a limit of 32 CollisionNodes per traverser + # We add the collider, and the handler to use as a pair + self.cTrav.addCollider(self.ballSphere, self.cHandler) + self.cTrav.addCollider(self.ballGroundColNp, self.cHandler) + + # Collision traversers have a built in tool to help visualize collisions. + # Uncomment the next line to see it. + #self.cTrav.showCollisions(render) + + # This section deals with lighting for the ball. Only the ball was lit + # because the maze has static lighting pregenerated by the modeler + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.55, .55, .55, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 0, -1)) + directionalLight.setColor((0.375, 0.375, 0.375, 1)) + directionalLight.setSpecularColor((1, 1, 1, 1)) + self.ballRoot.setLight(render.attachNewNode(ambientLight)) + self.ballRoot.setLight(render.attachNewNode(directionalLight)) + + # This section deals with adding a specular highlight to the ball to make + # it look shiny. Normally, this is specified in the .egg file. + m = Material() + m.setSpecular((1, 1, 1, 1)) + m.setShininess(96) + self.ball.setMaterial(m, 1) + + # Finally, we call start for more initialization + self.start() + + def start(self): + # The maze model also has a locator in it for where to start the ball + # To access it we use the find command + startPos = self.maze.find("**/start").getPos() + # Set the ball in the starting position + self.ballRoot.setPos(startPos) + self.ballV = LVector3(0, 0, 0) # Initial velocity is 0 + self.accelV = LVector3(0, 0, 0) # Initial acceleration is 0 + + # Create the movement task, but first make sure it is not already + # running + taskMgr.remove("rollTask") + self.mainLoop = taskMgr.add(self.rollTask, "rollTask") + + # This function handles the collision between the ray and the ground + # Information about the interaction is passed in colEntry + def groundCollideHandler(self, colEntry): + # Set the ball to the appropriate Z value for it to be exactly on the + # ground + newZ = colEntry.getSurfacePoint(render).getZ() + self.ballRoot.setZ(newZ + .4) + + # Find the acceleration direction. First the surface normal is crossed with + # the up vector to get a vector perpendicular to the slope + norm = colEntry.getSurfaceNormal(render) + accelSide = norm.cross(LVector3.up()) + # Then that vector is crossed with the surface normal to get a vector that + # points down the slope. By getting the acceleration in 3D like this rather + # than in 2D, we reduce the amount of error per-frame, reducing jitter + self.accelV = norm.cross(accelSide) + + # This function handles the collision between the ball and a wall + def wallCollideHandler(self, colEntry): + # First we calculate some numbers we need to do a reflection + norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall + curSpeed = self.ballV.length() # The current speed + inVec = self.ballV / curSpeed # The direction of travel + velAngle = norm.dot(inVec) # Angle of incidance + hitDir = colEntry.getSurfacePoint(render) - self.ballRoot.getPos() + hitDir.normalize() + # The angle between the ball and the normal + hitAngle = norm.dot(hitDir) + + # Ignore the collision if the ball is either moving away from the wall + # already (so that we don't accidentally send it back into the wall) + # and ignore it if the collision isn't dead-on (to avoid getting caught on + # corners) + if velAngle > 0 and hitAngle > .995: + # Standard reflection equation + reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec + + # This makes the velocity half of what it was if the hit was dead-on + # and nearly exactly what it was if this is a glancing blow + self.ballV = reflectVec * (curSpeed * (((1 - velAngle) * .5) + .5)) + # Since we have a collision, the ball is already a little bit buried in + # the wall. This calculates a vector needed to move it so that it is + # exactly touching the wall + disp = (colEntry.getSurfacePoint(render) - + colEntry.getInteriorPoint(render)) + newPos = self.ballRoot.getPos() + disp + self.ballRoot.setPos(newPos) + + # This is the task that deals with making everything interactive + def rollTask(self, task): + # Standard technique for finding the amount of time since the last + # frame + dt = globalClock.getDt() + + # If dt is large, then there has been a # hiccup that could cause the ball + # to leave the field if this functions runs, so ignore the frame + if dt > .2: + return Task.cont + + # The collision handler collects the collisions. We dispatch which function + # to handle the collision based on the name of what was collided into + for i in range(self.cHandler.getNumEntries()): + entry = self.cHandler.getEntry(i) + name = entry.getIntoNode().getName() + if name == "wall_collide": + self.wallCollideHandler(entry) + elif name == "ground_collide": + self.groundCollideHandler(entry) + elif name == "loseTrigger": + self.loseGame(entry) + + # Read the mouse position and tilt the maze accordingly + if base.mouseWatcherNode.hasMouse(): + mpos = base.mouseWatcherNode.getMouse() # get the mouse position + self.maze.setP(mpos.getY() * -10) + self.maze.setR(mpos.getX() * 10) + + # Finally, we move the ball + # Update the velocity based on acceleration + self.ballV += self.accelV * dt * ACCEL + # Clamp the velocity to the maximum speed + if self.ballV.lengthSquared() > MAX_SPEED_SQ: + self.ballV.normalize() + self.ballV *= MAX_SPEED + # Update the position based on the velocity + self.ballRoot.setPos(self.ballRoot.getPos() + (self.ballV * dt)) + + # This block of code rotates the ball. It uses something called a quaternion + # to rotate the ball around an arbitrary axis. That axis perpendicular to + # the balls rotation, and the amount has to do with the size of the ball + # This is multiplied on the previous rotation to incrimentally turn it. + prevRot = LRotationf(self.ball.getQuat()) + axis = LVector3.up().cross(self.ballV) + newRot = LRotationf(axis, 45.5 * dt * self.ballV.length()) + self.ball.setQuat(prevRot * newRot) + + return Task.cont # Continue the task indefinitely + + # If the ball hits a hole trigger, then it should fall in the hole. + # This is faked rather than dealing with the actual physics of it. + def loseGame(self, entry): + # The triggers are set up so that the center of the ball should move to the + # collision point to be in the hole + toPos = entry.getInteriorPoint(render) + taskMgr.remove('rollTask') # Stop the maze task + + # Move the ball into the hole over a short sequence of time. Then wait a + # second and call start to reset the game + Sequence( + Parallel( + LerpFunc(self.ballRoot.setX, fromData=self.ballRoot.getX(), + toData=toPos.getX(), duration=.1), + LerpFunc(self.ballRoot.setY, fromData=self.ballRoot.getY(), + toData=toPos.getY(), duration=.1), + LerpFunc(self.ballRoot.setZ, fromData=self.ballRoot.getZ(), + toData=self.ballRoot.getZ() - .9, duration=.2)), + Wait(1), + Func(self.start)).start() + +# Finally, create an instance of our class and start 3d rendering +demo = BallInMazeDemo() +demo.run() diff --git a/samples/ball-in-maze/models/ball.egg.pz b/samples/ball-in-maze/models/ball.egg.pz new file mode 100644 index 0000000000..b2433a951a Binary files /dev/null and b/samples/ball-in-maze/models/ball.egg.pz differ diff --git a/samples/ball-in-maze/models/iron05.jpg b/samples/ball-in-maze/models/iron05.jpg new file mode 100644 index 0000000000..78d53bc163 Binary files /dev/null and b/samples/ball-in-maze/models/iron05.jpg differ diff --git a/samples/ball-in-maze/models/limba.jpg b/samples/ball-in-maze/models/limba.jpg new file mode 100644 index 0000000000..0725b7c1b8 Binary files /dev/null and b/samples/ball-in-maze/models/limba.jpg differ diff --git a/samples/ball-in-maze/models/maze.egg.pz b/samples/ball-in-maze/models/maze.egg.pz new file mode 100644 index 0000000000..a2be4a1287 Binary files /dev/null and b/samples/ball-in-maze/models/maze.egg.pz differ diff --git a/samples/boxing-robots/main.py b/samples/boxing-robots/main.py new file mode 100755 index 0000000000..20d59b02a3 --- /dev/null +++ b/samples/boxing-robots/main.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman, and Eddie Caanan +# Last Updated: 2015-03-13 +# +# This tutorial shows how to play animations on models aka "actors". +# It is based on the popular game of "Rock 'em Sock 'em Robots". + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import TextNode +from panda3d.core import LVector3 +from direct.gui.OnscreenText import OnscreenText +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Func, Wait +from direct.actor import Actor +from random import random +import sys + + +class BoxingRobotDemo(ShowBase): + # Macro-like function used to reduce the amount to code needed to create the + # on screen instructions + + def genLabelText(self, text, i): + return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.05, + pos=(0.1, - 0.1 -.07 * i), fg=(1, 1, 1, 1), + align=TextNode.ALeft) + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This code puts the standard title and instruction text on screen + self.title = OnscreenText(text="Panda3D: Tutorial - Actors", + parent=base.a2dBottomRight, style=1, + fg=(0, 0, 0, 1), pos=(-0.2, 0.1), + align=TextNode.ARight, scale=.09) + + self.escapeEventText = self.genLabelText("ESC: Quit", 0) + self.akeyEventText = self.genLabelText("[A]: Robot 1 Left Punch", 1) + self.skeyEventText = self.genLabelText("[S]: Robot 1 Right Punch", 2) + self.kkeyEventText = self.genLabelText("[K]: Robot 2 Left Punch", 3) + self.lkeyEventText = self.genLabelText("[L]: Robot 2 Right Punch", 4) + + # Set the camera in a fixed position + self.disableMouse() + camera.setPosHpr(14.5, -15.4, 14, 45, -14, 0) + self.setBackgroundColor(0, 0, 0) + + # Add lighting so that the objects are not drawn flat + self.setupLights() + + # Load the ring + self.ring = loader.loadModel('models/ring') + self.ring.reparentTo(render) + + # Models that use skeletal animation are known as Actors instead of models + # Instead of just one file, the have one file for the main model, and an + # additional file for each playable animation. + # They are loaded using Actor.Actor instead of loader.LoadModel. + # The constructor takes the location of the main object as with a normal model + # and a dictionary (A fancy python structure that is like a lookup table) + # that contains names for animations, and paths to the appropriate + # files + self.robot1 = Actor.Actor('models/robot', + {'leftPunch': 'models/robot_left_punch', + 'rightPunch': 'models/robot_right_punch', + 'headUp': 'models/robot_head_up', + 'headDown': 'models/robot_head_down'}) + + # Actors need to be positioned and parented like normal objects + self.robot1.setPosHprScale(-1, -2.5, 4, 45, 0, 0, 1.25, 1.25, 1.25) + self.robot1.reparentTo(render) + + # We'll repeat the process for the second robot. The only thing that changes + # here is the robot's color and position + self.robot2 = Actor.Actor('models/robot', + {'leftPunch': 'models/robot_left_punch', + 'rightPunch': 'models/robot_right_punch', + 'headUp': 'models/robot_head_up', + 'headDown': 'models/robot_head_down'}) + + # Set the properties of this robot + self.robot2.setPosHprScale(1, 1.5, 4, 225, 0, 0, 1.25, 1.25, 1.25) + self.robot2.setColor((.7, 0, 0, 1)) + self.robot2.reparentTo(render) + + # Now we define how the animated models will move. Animations are played + # through special intervals. In this case we use actor intervals in a + # sequence to play the part of the punch animation where the arm extends, + # call a function to check if the punch landed, and then play the part of the + # animation where the arm retracts + + # Punch sequence for robot 1's left arm + self.robot1.punchLeft = Sequence( + # Interval for the outstreched animation + self.robot1.actorInterval('leftPunch', startFrame=1, endFrame=10), + # Function to check if the punch was successful + Func(self.checkPunch, 2), + # Interval for the retract animation + self.robot1.actorInterval('leftPunch', startFrame=11, endFrame=32)) + + # Punch sequence for robot 1's right arm + self.robot1.punchRight = Sequence( + self.robot1.actorInterval('rightPunch', startFrame=1, endFrame=10), + Func(self.checkPunch, 2), + self.robot1.actorInterval('rightPunch', startFrame=11, endFrame=32)) + + # Punch sequence for robot 2's left arm + self.robot2.punchLeft = Sequence( + self.robot2.actorInterval('leftPunch', startFrame=1, endFrame=10), + Func(self.checkPunch, 1), + self.robot2.actorInterval('leftPunch', startFrame=11, endFrame=32)) + + # Punch sequence for robot 2's right arm + self.robot2.punchRight = Sequence( + self.robot2.actorInterval('rightPunch', startFrame=1, endFrame=10), + Func(self.checkPunch, 1), + self.robot2.actorInterval('rightPunch', startFrame=11, endFrame=32)) + + # We use the same techinique to create a sequence for when a robot is knocked + # out where the head pops up, waits a while, and then resets + + # Head animation for robot 1 + self.robot1.resetHead = Sequence( + # Interval for the head going up. Since no start or end frames were given, + # the entire animation is played. + self.robot1.actorInterval('headUp'), + Wait(1.5), + # The head down animation was animated a little too quickly, so this will + # play it at 75% of it's normal speed + self.robot1.actorInterval('headDown', playRate=.75)) + + # Head animation for robot 2 + self.robot2.resetHead = Sequence( + self.robot2.actorInterval('headUp'), + Wait(1.5), + self.robot2.actorInterval('headDown', playRate=.75)) + + # Now that we have defined the motion, we can define our key input. + # Each fist is bound to a key. When a key is pressed, self.tryPunch checks to + # make sure that the both robots have their heads down, and if they do it + # plays the given interval + self.accept('escape', sys.exit) + self.accept('a', self.tryPunch, [self.robot1.punchLeft]) + self.accept('s', self.tryPunch, [self.robot1.punchRight]) + self.accept('k', self.tryPunch, [self.robot2.punchLeft]) + self.accept('l', self.tryPunch, [self.robot2.punchRight]) + + # tryPunch will play the interval passed to it only if + # neither robot has 'resetHead' playing (a head is up) AND + # the punch interval passed to it is not already playing + def tryPunch(self, interval): + if (not self.robot1.resetHead.isPlaying() and + not self.robot2.resetHead.isPlaying() and + not interval.isPlaying()): + interval.start() + + # checkPunch will determine if a successful punch has been thrown + def checkPunch(self, robot): + if robot == 1: + # punch is directed to robot 1 + # if robot 1 is playing'resetHead', do nothing + if self.robot1.resetHead.isPlaying(): + return + # if robot 1 is not punching... + if (not self.robot1.punchLeft.isPlaying() and + not self.robot1.punchRight.isPlaying()): + # ...15% chance of successful hit + if random() > .85: + self.robot1.resetHead.start() + # Otherwise, only 5% chance of sucessful hit + elif random() > .95: + self.robot1.resetHead.start() + else: + # punch is directed to robot 2, same as above + if self.robot2.resetHead.isPlaying(): + return + if (not self.robot2.punchLeft.isPlaying() and + not self.robot2.punchRight.isPlaying()): + if random() > .85: + self.robot2.resetHead.start() + elif random() > .95: + self.robot2.resetHead.start() + + # This function sets up the lighting + def setupLights(self): + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.8, .8, .75, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 0, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + render.setLight(render.attachNewNode(ambientLight)) + render.setLight(render.attachNewNode(directionalLight)) + +demo = BoxingRobotDemo() +demo.run() diff --git a/samples/boxing-robots/models/ring.egg.pz b/samples/boxing-robots/models/ring.egg.pz new file mode 100644 index 0000000000..81ca6ca8dd Binary files /dev/null and b/samples/boxing-robots/models/ring.egg.pz differ diff --git a/samples/boxing-robots/models/robot.egg.pz b/samples/boxing-robots/models/robot.egg.pz new file mode 100644 index 0000000000..268c2cbb63 Binary files /dev/null and b/samples/boxing-robots/models/robot.egg.pz differ diff --git a/samples/boxing-robots/models/robot_head_down.egg.pz b/samples/boxing-robots/models/robot_head_down.egg.pz new file mode 100644 index 0000000000..ea9529a1d2 Binary files /dev/null and b/samples/boxing-robots/models/robot_head_down.egg.pz differ diff --git a/samples/boxing-robots/models/robot_head_up.egg.pz b/samples/boxing-robots/models/robot_head_up.egg.pz new file mode 100644 index 0000000000..c2678c835f Binary files /dev/null and b/samples/boxing-robots/models/robot_head_up.egg.pz differ diff --git a/samples/boxing-robots/models/robot_left_punch.egg.pz b/samples/boxing-robots/models/robot_left_punch.egg.pz new file mode 100644 index 0000000000..d7dc06405e Binary files /dev/null and b/samples/boxing-robots/models/robot_left_punch.egg.pz differ diff --git a/samples/boxing-robots/models/robot_right_punch.egg.pz b/samples/boxing-robots/models/robot_right_punch.egg.pz new file mode 100644 index 0000000000..372f3bbd4e Binary files /dev/null and b/samples/boxing-robots/models/robot_right_punch.egg.pz differ diff --git a/samples/bump-mapping/main.py b/samples/bump-mapping/main.py new file mode 100755 index 0000000000..f399dfd909 --- /dev/null +++ b/samples/bump-mapping/main.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# +# Bump mapping is a way of making polygonal surfaces look +# less flat. This sample uses normal mapping for all +# surfaces, and also parallax mapping for the column. +# +# This is a tutorial to show how to do normal mapping +# in panda3d using the Shader Generator. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import loadPrcFileData +from panda3d.core import WindowProperties +from panda3d.core import Filename, Shader +from panda3d.core import AmbientLight, PointLight +from panda3d.core import TextNode +from panda3d.core import LPoint3, LVector3 +from direct.task.Task import Task +from direct.actor.Actor import Actor +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.filter.CommonFilters import * +import sys +import os + + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, + shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, + pos=(0.08, -pos - 0.04), align=TextNode.ALeft) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), shadow=(0, 0, 0, 1)) + + +class BumpMapDemo(ShowBase): + + def __init__(self): + # Configure the parallax mapping settings (these are just the defaults) + loadPrcFileData("", "parallax-mapping-samples 3\n" + "parallax-mapping-scale 0.1") + + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # Check video card capabilities. + if not self.win.getGsg().getSupportsBasicShaders(): + addTitle("Bump Mapping: " + "Video driver reports that Cg shaders are not supported.") + return + + # Post the instructions + self.title = addTitle("Panda3D: Tutorial - Bump Mapping") + self.inst1 = addInstructions(0.06, "Press ESC to exit") + self.inst2 = addInstructions(0.12, "Move mouse to rotate camera") + self.inst3 = addInstructions(0.18, "Left mouse button: Move forwards") + self.inst4 = addInstructions(0.24, "Right mouse button: Move backwards") + self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off") + + # Load the 'abstract room' model. This is a model of an + # empty room containing a pillar, a pyramid, and a bunch + # of exaggeratedly bumpy textures. + + self.room = loader.loadModel("models/abstractroom") + self.room.reparentTo(render) + + # Make the mouse invisible, turn off normal mouse controls + self.disableMouse() + props = WindowProperties() + props.setCursorHidden(True) + self.win.requestProperties(props) + self.camLens.setFov(60) + + # Set the current viewing target + self.focus = LVector3(55, -55, 20) + self.heading = 180 + self.pitch = 0 + self.mousex = 0 + self.mousey = 0 + self.last = 0 + self.mousebtn = [0, 0, 0] + + # Start the camera control task: + taskMgr.add(self.controlCamera, "camera-task") + self.accept("escape", sys.exit, [0]) + self.accept("mouse1", self.setMouseBtn, [0, 1]) + self.accept("mouse1-up", self.setMouseBtn, [0, 0]) + self.accept("mouse2", self.setMouseBtn, [1, 1]) + self.accept("mouse2-up", self.setMouseBtn, [1, 0]) + self.accept("mouse3", self.setMouseBtn, [2, 1]) + self.accept("mouse3-up", self.setMouseBtn, [2, 0]) + self.accept("enter", self.toggleShader) + self.accept("j", self.rotateLight, [-1]) + self.accept("k", self.rotateLight, [1]) + self.accept("arrow_left", self.rotateCam, [-1]) + self.accept("arrow_right", self.rotateCam, [1]) + + # Add a light to the scene. + self.lightpivot = render.attachNewNode("lightpivot") + self.lightpivot.setPos(0, 0, 25) + self.lightpivot.hprInterval(10, LPoint3(360, 0, 0)).loop() + plight = PointLight('plight') + plight.setColor((1, 1, 1, 1)) + plight.setAttenuation(LVector3(0.7, 0.05, 0)) + plnp = self.lightpivot.attachNewNode(plight) + plnp.setPos(45, 0, 0) + self.room.setLight(plnp) + + # Add an ambient light + alight = AmbientLight('alight') + alight.setColor((0.2, 0.2, 0.2, 1)) + alnp = render.attachNewNode(alight) + self.room.setLight(alnp) + + # Create a sphere to denote the light + sphere = loader.loadModel("models/icosphere") + sphere.reparentTo(plnp) + + # Tell Panda that it should generate shaders performing per-pixel + # lighting for the room. + self.room.setShaderAuto() + + self.shaderenable = 1 + + def setMouseBtn(self, btn, value): + self.mousebtn[btn] = value + + def rotateLight(self, offset): + self.lightpivot.setH(self.lightpivot.getH() + offset * 20) + + def rotateCam(self, offset): + self.heading = self.heading - offset * 10 + + def toggleShader(self): + self.inst5.destroy() + if (self.shaderenable): + self.inst5 = addInstructions(0.30, "Enter: Turn bump maps On") + self.shaderenable = 0 + self.room.setShaderOff() + else: + self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off") + self.shaderenable = 1 + self.room.setShaderAuto() + + def controlCamera(self, task): + # figure out how much the mouse has moved (in pixels) + md = self.win.getPointer(0) + x = md.getX() + y = md.getY() + if self.win.movePointer(0, 100, 100): + self.heading = self.heading - (x - 100) * 0.2 + self.pitch = self.pitch - (y - 100) * 0.2 + if self.pitch < -45: + self.pitch = -45 + if self.pitch > 45: + self.pitch = 45 + self.camera.setHpr(self.heading, self.pitch, 0) + dir = self.camera.getMat().getRow3(1) + elapsed = task.time - self.last + if self.last == 0: + elapsed = 0 + if self.mousebtn[0]: + self.focus = self.focus + dir * elapsed * 30 + if self.mousebtn[1] or self.mousebtn[2]: + self.focus = self.focus - dir * elapsed * 30 + self.camera.setPos(self.focus - (dir * 5)) + if self.camera.getX() < -59.0: + self.camera.setX(-59) + if self.camera.getX() > 59.0: + self.camera.setX(59) + if self.camera.getY() < -59.0: + self.camera.setY(-59) + if self.camera.getY() > 59.0: + self.camera.setY(59) + if self.camera.getZ() < 5.0: + self.camera.setZ(5) + if self.camera.getZ() > 45.0: + self.camera.setZ(45) + self.focus = self.camera.getPos() + (dir * 5) + self.last = task.time + return Task.cont + +demo = BumpMapDemo() +demo.run() diff --git a/samples/bump-mapping/models/abstractroom.egg b/samples/bump-mapping/models/abstractroom.egg new file mode 100644 index 0000000000..b3f4d5179a --- /dev/null +++ b/samples/bump-mapping/models/abstractroom.egg @@ -0,0 +1,1671 @@ + { Y-Up } + + { + "maya2egg85 -o room.egg room.mb" +} + phong3SG.tref3 { + layingrock-n.jpg + format { rgba } + alpha-file { layingrock-h.jpg } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } + envtype { normal_height } +} + phong3SG { + layingrock-c.jpg + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + phong2SG.tref1 { + brick-n.jpg + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } + envtype { normal } +} + phong2SG { + brick-c.jpg + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + phong1SG.tref2 { + fieldstone-n.jpg + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } + envtype { normal } +} + phong1SG { + fieldstone-c.jpg + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + groundPlane_transform { +} + polySurface2 { + polySurfaceShape2.verts { + 0 { + -60 0 -60 + { + 3 3 + { 0 -3.83044e-009 -1 } + { -1 -3.83044e-009 0 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 1 { + -60 0 -60 + { + -0.5 -0.5 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 2 { + -60 0 -60 + { + 1.5 -0.5 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 3 { + -60 0 60 + { + 1.5 -0.5 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 4 { + -60 0 60 + { + -2 3 + { -5.15653e-008 4.13254e-011 -1 } + { -1 4.13254e-011 5.15653e-008 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 5 { + -60 0 60 + { + -0.5 -0.5 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 6 { + -60 50 -60 + { + -0.5 1.5 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 7 { + -60 50 -60 + { + 1.5 1.5 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 8 { + -60 50 60 + { + 1.5 1.5 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 9 { + -60 50 60 + { + -0.5 1.5 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 10 { + -37.5 0 25 + { + -1.49994 -1 + { 0 0 1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 11 { + -37.5 0 25 + { + 2.50006 -1 + { 0 0 1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 12 { + -37.5 50 25 + { + -1.49994 2 + { 0 0 1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 13 { + -37.5 50 25 + { + 2.50006 2 + { 0 0 1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 14 { + -37.5 50 25 + { + -0.541667 2.0625 + { 0 0 -1 } + { -1 0 0 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 15 { + -36.8882 0 21.1373 + { + 2.30006 -1 + { -0.309017 0 0.951057 } + { 0 1 0 } + } + { -0.951057 0 -0.309017 } + { 1 1 1 1 } + } + 16 { + -36.8882 0 28.8627 + { + -1.29994 -1 + { 0.309017 0 0.951057 } + { 0 1 0 } + } + { -0.951057 0 0.309017 } + { 1 1 1 1 } + } + 17 { + -36.8882 50 21.1373 + { + 2.30006 2 + { -0.309017 0 0.951057 } + { 0 1 0 } + } + { -0.951057 0 -0.309017 } + { 1 1 1 1 } + } + 18 { + -36.8882 50 21.1373 + { + -0.38072 2.03701 + { 6.17012e-006 0 -1 } + { -1 0 -6.17012e-006 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 19 { + -36.8882 50 28.8627 + { + -1.29994 2 + { 0.309017 0 0.951057 } + { 0 1 0 } + } + { -0.951057 0 0.309017 } + { 1 1 1 1 } + } + 20 { + -36.8882 50 28.8627 + { + -0.702613 2.03701 + { -6.17012e-006 0 -1 } + { -1 0 6.17012e-006 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 21 { + -35.1127 0 17.6527 + { + 2.10006 -1 + { -0.587785 0 0.809017 } + { 0 1 0 } + } + { -0.809017 0 -0.587785 } + { 1 1 1 1 } + } + 22 { + -35.1127 0 32.3473 + { + -1.09994 -1 + { 0.587785 0 0.809017 } + { 0 1 0 } + } + { -0.809017 0 0.587785 } + { 1 1 1 1 } + } + 23 { + -35.1127 50 17.6527 + { + 2.10006 2 + { -0.587785 0 0.809017 } + { 0 1 0 } + } + { -0.809017 0 -0.587785 } + { 1 1 1 1 } + } + 24 { + -35.1127 50 17.6527 + { + -0.235528 1.96303 + { -1.03074e-007 0 -1 } + { -1 0 1.03074e-007 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 25 { + -35.1127 50 32.3473 + { + -1.09994 2 + { 0.587785 0 0.809017 } + { 0 1 0 } + } + { -0.809017 0 0.587785 } + { 1 1 1 1 } + } + 26 { + -35.1127 50 32.3473 + { + -0.847805 1.96303 + { 1.03075e-007 0 -1 } + { -1 0 -1.03075e-007 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 27 { + -32.3473 0 14.8873 + { + 1.90006 -1 + { -0.809017 0 0.587786 } + { 0 1 0 } + } + { -0.587786 0 -0.809017 } + { 1 1 1 1 } + } + 28 { + -32.3473 0 35.1127 + { + -0.899936 -1 + { 0.809017 0 0.587785 } + { 0 1 0 } + } + { -0.587785 0 0.809017 } + { 1 1 1 1 } + } + 29 { + -32.3473 0 35.1127 + { + -0.96303 1.84781 + { 1.52608e-007 4.13254e-011 -1 } + { -1 4.13254e-011 -1.52608e-007 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 30 { + -32.3473 50 14.8873 + { + 1.90006 2 + { -0.809017 0 0.587786 } + { 0 1 0 } + } + { -0.587786 0 -0.809017 } + { 1 1 1 1 } + } + 31 { + -32.3473 50 14.8873 + { + -0.120303 1.84781 + { -8.0634e-006 0 -1 } + { -1 0 8.0634e-006 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 32 { + -32.3473 50 35.1127 + { + -0.899936 2 + { 0.809017 0 0.587785 } + { 0 1 0 } + } + { -0.587785 0 0.809017 } + { 1 1 1 1 } + } + 33 { + -28.8627 0 13.1118 + { + 1.70006 -1 + { -0.951057 0 0.309017 } + { 0 1 0 } + } + { -0.309017 0 -0.951057 } + { 1 1 1 1 } + } + 34 { + -28.8627 0 36.8882 + { + -0.699936 -1 + { 0.951057 0 0.309016 } + { 0 1 0 } + } + { -0.309016 0 0.951057 } + { 1 1 1 1 } + } + 35 { + -28.8627 50 13.1118 + { + 1.70006 2 + { -0.951057 0 0.309017 } + { 0 1 0 } + } + { -0.309017 0 -0.951057 } + { 1 1 1 1 } + } + 36 { + -28.8627 50 13.1118 + { + -0.0463246 1.70261 + { 3.81471e-007 0 -1 } + { -1 0 -3.81471e-007 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 37 { + -28.8627 50 36.8882 + { + -0.699936 2 + { 0.951057 0 0.309016 } + { 0 1 0 } + } + { -0.309016 0 0.951057 } + { 1 1 1 1 } + } + 38 { + -28.8627 50 36.8882 + { + -1.03701 1.70261 + { -3.81475e-007 0 -1 } + { -1 -4.89652e-008 3.81475e-007 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 39 { + -25 0 12.5 + { + 1.50006 -1 + { -1 0 -6.03476e-007 } + { 0 1 0 } + } + { 6.03476e-007 0 -1 } + { 1 1 1 1 } + } + 40 { + -25 0 37.5 + { + -0.499936 -1 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 41 { + -25 50 12.5 + { + -0.0208334 1.54167 + { 1.16912e-005 0 -1 } + { -1 0 -1.16912e-005 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 42 { + -25 50 12.5 + { + 1.50006 2 + { -1 0 -6.03476e-007 } + { 0 1 0 } + } + { 6.03476e-007 0 -1 } + { 1 1 1 1 } + } + 43 { + -25 50 37.5 + { + -0.499936 2 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 44 { + -25 50 37.5 + { + -1.0625 1.54167 + { -1.16911e-005 0 -1 } + { -1 -4.89652e-008 1.16911e-005 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 45 { + -21.1373 0 13.1118 + { + 1.30006 -1 + { -0.951057 0 -0.309017 } + { 0 1 0 } + } + { 0.309017 0 -0.951057 } + { 1 1 1 1 } + } + 46 { + -21.1373 0 36.8882 + { + -0.299936 -1 + { 0.951057 0 -0.309016 } + { 0 1 0 } + } + { 0.309016 0 0.951057 } + { 1 1 1 1 } + } + 47 { + -21.1373 50 13.1118 + { + -0.0463246 1.38072 + { -1.09281e-005 0 -1 } + { -1 0 1.09281e-005 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 48 { + -21.1373 50 13.1118 + { + 1.30006 2 + { -0.951057 0 -0.309017 } + { 0 1 0 } + } + { 0.309017 0 -0.951057 } + { 1 1 1 1 } + } + 49 { + -21.1373 50 36.8882 + { + -1.03701 1.38072 + { 1.09282e-005 0 -1 } + { -1 -4.89652e-008 -1.09282e-005 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 50 { + -21.1373 50 36.8882 + { + -0.299936 2 + { 0.951057 0 -0.309016 } + { 0 1 0 } + } + { 0.309016 0 0.951057 } + { 1 1 1 1 } + } + 51 { + -17.6527 0 14.8873 + { + 1.10006 -1 + { -0.809017 0 -0.587785 } + { 0 1 0 } + } + { 0.587785 0 -0.809017 } + { 1 1 1 1 } + } + 52 { + -17.6527 0 35.1127 + { + -0.0999364 -1 + { 0.809017 0 -0.587785 } + { 0 1 0 } + } + { 0.587785 0 0.809017 } + { 1 1 1 1 } + } + 53 { + -17.6527 50 14.8873 + { + -0.120304 1.23553 + { -7.25597e-007 0 -1 } + { -1 0 7.25597e-007 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 54 { + -17.6527 50 14.8873 + { + 1.10006 2 + { -0.809017 0 -0.587785 } + { 0 1 0 } + } + { 0.587785 0 -0.809017 } + { 1 1 1 1 } + } + 55 { + -17.6527 50 35.1127 + { + -0.96303 1.23553 + { 7.25593e-007 0 -1 } + { -1 -4.89652e-008 -7.25593e-007 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 56 { + -17.6527 50 35.1127 + { + -0.0999364 2 + { 0.809017 0 -0.587785 } + { 0 1 0 } + } + { 0.587785 0 0.809017 } + { 1 1 1 1 } + } + 57 { + -14.8873 0 17.6527 + { + -0.235528 1.1203 + { -5.45099e-007 4.13254e-011 -1 } + { -1 4.13254e-011 5.45099e-007 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 58 { + -14.8873 0 17.6527 + { + 0.900064 -1 + { -0.587785 0 -0.809017 } + { 0 1 0 } + } + { 0.809017 0 -0.587785 } + { 1 1 1 1 } + } + 59 { + -14.8873 0 32.3473 + { + 0.100064 -1 + { 0.587785 0 -0.809017 } + { 0 1 0 } + } + { 0.809017 0 0.587785 } + { 1 1 1 1 } + } + 60 { + -14.8873 50 17.6527 + { + 0.900064 2 + { -0.587785 0 -0.809017 } + { 0 1 0 } + } + { 0.809017 0 -0.587785 } + { 1 1 1 1 } + } + 61 { + -14.8873 50 32.3473 + { + -0.847805 1.1203 + { -1.79128e-006 0 -1 } + { -1 -4.89652e-008 1.79128e-006 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 62 { + -14.8873 50 32.3473 + { + 0.100064 2 + { 0.587785 0 -0.809017 } + { 0 1 0 } + } + { 0.809017 0 0.587785 } + { 1 1 1 1 } + } + 63 { + -13.1118 0 21.1373 + { + 0.700064 -1 + { -0.309017 0 -0.951057 } + { 0 1 0 } + } + { 0.951057 0 -0.309017 } + { 1 1 1 1 } + } + 64 { + -13.1118 0 28.8627 + { + 0.300064 -1 + { 0.309017 0 -0.951057 } + { 0 1 0 } + } + { 0.951057 0 0.309017 } + { 1 1 1 1 } + } + 65 { + -13.1118 50 21.1373 + { + -0.38072 1.04633 + { 3.58256e-006 0 -1 } + { -1 -4.89652e-008 -3.58256e-006 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 66 { + -13.1118 50 21.1373 + { + 0.700064 2 + { -0.309017 0 -0.951057 } + { 0 1 0 } + } + { 0.951057 0 -0.309017 } + { 1 1 1 1 } + } + 67 { + -13.1118 50 28.8627 + { + -0.702613 1.04633 + { -3.58256e-006 0 -1 } + { -1 -4.89652e-008 3.58256e-006 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 68 { + -13.1118 50 28.8627 + { + 0.300064 2 + { 0.309017 0 -0.951057 } + { 0 1 0 } + } + { 0.951057 0 0.309017 } + { 1 1 1 1 } + } + 69 { + -12.5 0 25 + { + 0.500064 -1 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 70 { + -12.5 50 25 + { + -0.541667 1.02083 + { 0 0 -1 } + { -1 -4.89652e-008 0 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + 71 { + -12.5 50 25 + { + 0.500064 2 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 72 { + 5 0 -5 + { + 0.708333 0.291667 + { -1.84039e-007 4.13254e-011 -1 } + { -1 4.13254e-011 1.84039e-007 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 73 { + 5 1.19209e-006 -45 + { + 1.5 -0.5 + { -1 -1.49012e-008 0 } + { -1.49012e-008 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 74 { + 5 10 -45 + { + 1.5 0.166667 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 75 { + 5 10 -45 + { + 1.5 0.166667 + { -1 0 0 } + { 0 0.707107 0.707107 } + } + { 0 0.707107 -0.707107 } + { 1 1 1 1 } + } + 76 { + 25 30 -25 + { + 0.5 1.5 + { -1 0 0 } + { 0 0.707107 0.707107 } + } + { 0 0.707107 -0.707107 } + { 1 1 1 1 } + } + 77 { + 25 30 -25 + { + 0.5 1.5 + { 0 0 -1 } + { -0.707107 0.707107 0 } + } + { 0.707107 0.707107 0 } + { 1 1 1 1 } + } + 78 { + 45 0 -45 + { + -0.5 -0.5 + { -1 -1.49012e-008 0 } + { -1.49012e-008 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 79 { + 45 0 -45 + { + 2.375 -1.375 + { -2.98023e-008 4.13254e-011 -1 } + { -1 4.13254e-011 2.98023e-008 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 80 { + 45 0 -45 + { + 1.5 -0.5 + { 0 1.49012e-008 -1 } + { 0 1 1.49012e-008 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 81 { + 45 1.19209e-006 -5 + { + -0.5 -0.5 + { 0 1.49012e-008 -1 } + { 0 1 1.49012e-008 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 82 { + 45 10 -45 + { + -0.5 0.166667 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 83 { + 45 10 -45 + { + -0.5 0.166667 + { -1 0 0 } + { 0 0.707107 0.707107 } + } + { 0 0.707107 -0.707107 } + { 1 1 1 1 } + } + 84 { + 45 10 -45 + { + 1.5 0.166667 + { 0 0 -1 } + { -0.707107 0.707107 0 } + } + { 0.707107 0.707107 0 } + { 1 1 1 1 } + } + 85 { + 45 10 -45 + { + 1.5 0.166667 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 86 { + 45 10 -5 + { + -0.5 0.166667 + { 0 0 -1 } + { -0.707107 0.707107 0 } + } + { 0.707107 0.707107 0 } + { 1 1 1 1 } + } + 87 { + 45 10 -5 + { + -0.5 0.166667 + { 0 0 -1 } + { 0 1 0 } + } + { 1 0 0 } + { 1 1 1 1 } + } + 88 { + 60 0 -60 + { + 1.5 1.5 + { 0 0 -1 } + { 0 -1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 89 { + 60 0 -60 + { + 1.5 -0.5 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 90 { + 60 0 -60 + { + 3 -2 + { -1.19209e-008 4.13254e-011 -1 } + { -1 4.13254e-011 1.19209e-008 } + } + { 4.13254e-011 1 4.13254e-011 } + { 1 1 1 1 } + } + 91 { + 60 0 60 + { + -0.5 1.5 + { 0 0 -1 } + { 0 -1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 92 { + 60 0 60 + { + -0.5 -0.5 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 93 { + 60 0 60 + { + -2 -2 + { 0 3.91309e-009 -1 } + { -1 3.91309e-009 0 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 94 { + 60 50 -60 + { + 1.5 -0.5 + { 0 0 -1 } + { 0 -1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 95 { + 60 50 -60 + { + 1.5 1.5 + { 1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 96 { + 60 50 60 + { + -0.5 -0.5 + { 0 0 -1 } + { 0 -1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 97 { + 60 50 60 + { + -0.5 1.5 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 -1 } + { 1 1 1 1 } + } + 98 { + -60 50 -60 + { + 3 3 + { 0 0 -1 } + { -1 0 0 } + } + { 0 -1 0 } + { 1 1 1 1 } + } + 99 { + -60 50 60 + { + -2 3 + { -1.18287e-008 0 -1 } + { -1 -2.44826e-008 1.18287e-008 } + } + { 2.44826e-008 -1 0 } + { 1 1 1 1 } + } + 100 { + -37.5 0 25 + { + -0.541667 2.0625 + { 0 -3.83044e-009 -1 } + { -1 -3.83044e-009 0 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 101 { + -36.8882 0 21.1373 + { + -0.38072 2.03701 + { -2.784e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 2.784e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 102 { + -36.8882 0 28.8627 + { + -0.702613 2.03701 + { 2.784e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 -2.784e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 103 { + -35.1127 0 17.6527 + { + -0.235528 1.96303 + { -1.03074e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 1.03074e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 104 { + -35.1127 0 32.3473 + { + -0.847805 1.96303 + { 1.03075e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 -1.03075e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 105 { + -32.3473 0 14.8873 + { + -0.120303 1.84781 + { -8.0634e-006 -3.83047e-009 -1 } + { -1 -3.83041e-009 8.0634e-006 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 106 { + -32.3473 50 35.1127 + { + -0.96303 1.84781 + { 1.52608e-007 0 -1 } + { -1 -2.44826e-008 -1.52608e-007 } + } + { 2.44826e-008 -1 0 } + { 1 1 1 1 } + } + 107 { + -28.8627 0 13.1118 + { + -0.0463246 1.70261 + { 3.81471e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 -3.81471e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 108 { + -28.8627 0 36.8882 + { + -1.03701 1.70261 + { -3.81475e-007 3.91309e-009 -1 } + { -1 3.91309e-009 3.81475e-007 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 109 { + -25 0 12.5 + { + -0.0208334 1.54167 + { 1.16912e-005 -3.83039e-009 -1 } + { -1 -3.83048e-009 -1.16912e-005 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 110 { + -25 0 37.5 + { + -1.0625 1.54167 + { -1.16911e-005 3.91314e-009 -1 } + { -1 3.91304e-009 1.16911e-005 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 111 { + -21.1373 0 13.1118 + { + -0.0463246 1.38072 + { -1.09281e-005 -3.83048e-009 -1 } + { -1 -3.8304e-009 1.09281e-005 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 112 { + -21.1373 0 36.8882 + { + -1.03701 1.38072 + { 1.09282e-005 3.91305e-009 -1 } + { -1 3.91313e-009 -1.09282e-005 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 113 { + -17.6527 0 14.8873 + { + -0.120304 1.23553 + { -7.25597e-007 -3.83044e-009 -1 } + { -1 -3.83044e-009 7.25597e-007 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 114 { + -17.6527 0 35.1127 + { + -0.96303 1.23553 + { 7.25593e-007 3.91309e-009 -1 } + { -1 3.91309e-009 -7.25593e-007 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 115 { + -14.8873 0 32.3473 + { + -0.847805 1.1203 + { -1.79128e-006 3.9131e-009 -1 } + { -1 3.91308e-009 1.79128e-006 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 116 { + -14.8873 50 17.6527 + { + -0.235528 1.1203 + { -4.96743e-007 0 -1 } + { -1 -2.44826e-008 4.96743e-007 } + } + { 2.44826e-008 -1 0 } + { 1 1 1 1 } + } + 117 { + -13.1118 0 21.1373 + { + -0.38072 1.04633 + { 3.58256e-006 3.91308e-009 -1 } + { -1 3.9131e-009 -3.58256e-006 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 118 { + -13.1118 0 28.8627 + { + -0.702613 1.04633 + { -3.58256e-006 3.9131e-009 -1 } + { -1 3.91308e-009 3.58256e-006 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 119 { + -12.5 0 25 + { + -0.541667 1.02083 + { 0 3.91309e-009 -1 } + { -1 3.91309e-009 0 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 120 { + 5 0 -5 + { + -0.5 -0.5 + { 0 -1.49012e-008 -1 } + { 0 1 -1.49012e-008 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 121 { + 5 0 -5 + { + 1.5 -0.5 + { -1 1.49012e-008 0 } + { 1.49012e-008 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 122 { + 5 1.19209e-006 -45 + { + 1.5 -0.5 + { 0 -1.49012e-008 -1 } + { 0 1 -1.49012e-008 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 123 { + 5 1.19209e-006 -45 + { + 2.375 0.291667 + { 0 -3.83044e-009 -1 } + { -1 -3.83044e-009 0 } + } + { -3.83044e-009 1 -3.83044e-009 } + { 1 1 1 1 } + } + 124 { + 5 10 -45 + { + 1.5 0.166667 + { 0 0 -1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 125 { + 5 10 -45 + { + 1.5 0.166667 + { 0 0 -1 } + { 0.707107 0.707107 0 } + } + { -0.707107 0.707107 0 } + { 1 1 1 1 } + } + 126 { + 5 10 -5 + { + -0.5 0.166667 + { 0 0 -1 } + { 0 1 0 } + } + { -1 0 0 } + { 1 1 1 1 } + } + 127 { + 5 10 -5 + { + -0.5 0.166667 + { 0 0 -1 } + { 0.707107 0.707107 0 } + } + { -0.707107 0.707107 0 } + { 1 1 1 1 } + } + 128 { + 5 10 -5 + { + 1.5 0.166667 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 129 { + 5 10 -5 + { + 1.5 0.166667 + { -1 0 0 } + { 0 0.707107 -0.707107 } + } + { 0 0.707107 0.707107 } + { 1 1 1 1 } + } + 130 { + 25 30 -25 + { + 0.5 1.5 + { 0 0 -1 } + { 0.707107 0.707107 0 } + } + { -0.707107 0.707107 0 } + { 1 1 1 1 } + } + 131 { + 25 30 -25 + { + 0.5 1.5 + { -1 0 0 } + { 0 0.707107 -0.707107 } + } + { 0 0.707107 0.707107 } + { 1 1 1 1 } + } + 132 { + 45 1.19209e-006 -5 + { + -0.5 -0.5 + { -1 1.49012e-008 0 } + { 1.49012e-008 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 133 { + 45 1.19209e-006 -5 + { + 0.708333 -1.375 + { 0 3.91309e-009 -1 } + { -1 3.91309e-009 0 } + } + { 3.91309e-009 1 3.91309e-009 } + { 1 1 1 1 } + } + 134 { + 45 10 -5 + { + -0.5 0.166667 + { -1 0 0 } + { 0 1 0 } + } + { 0 0 1 } + { 1 1 1 1 } + } + 135 { + 45 10 -5 + { + -0.5 0.166667 + { -1 0 0 } + { 0 0.707107 -0.707107 } + } + { 0 0.707107 0.707107 } + { 1 1 1 1 } + } + 136 { + 60 50 -60 + { + 3 -2 + { -6.29762e-008 0 -1 } + { -1 -2.44826e-008 6.29762e-008 } + } + { 2.44826e-008 -1 0 } + { 1 1 1 1 } + } + 137 { + 60 50 60 + { + -2 -2 + { 0 0 -1 } + { -1 -4.89652e-008 0 } + } + { 4.89652e-008 -1 0 } + { 1 1 1 1 } + } + } + { + { 0 0.707107 -0.707107 } + { phong2SG } + { phong2SG.tref1 } + { 75 76 83 { polySurfaceShape2.verts } } + } + { + { -0.707107 0.707107 0 } + { phong2SG } + { phong2SG.tref1 } + { 127 130 125 { polySurfaceShape2.verts } } + } + { + { 0 0.707107 0.707107 } + { phong2SG } + { phong2SG.tref1 } + { 135 131 129 { polySurfaceShape2.verts } } + } + { + { 0.707107 0.707107 0 } + { phong2SG } + { phong2SG.tref1 } + { 84 77 86 { polySurfaceShape2.verts } } + } + { + { 0 0 -1 } + { phong2SG } + { phong2SG.tref1 } + { 78 73 74 82 { polySurfaceShape2.verts } } + } + { + { -1 0 0 } + { phong2SG } + { phong2SG.tref1 } + { 122 120 126 124 { polySurfaceShape2.verts } } + } + { + { 0 0 1 } + { phong2SG } + { phong2SG.tref1 } + { 121 132 134 128 { polySurfaceShape2.verts } } + } + { + { 1 0 0 } + { phong2SG } + { phong2SG.tref1 } + { 81 80 85 87 { polySurfaceShape2.verts } } + } + { + { -1 0 0 } + { phong1SG } + { phong1SG.tref2 } + { 91 96 94 88 { polySurfaceShape2.verts } } + } + { + { 4.89652e-008 -1 0 } + { phong1SG } + { phong1SG.tref2 } + { + 49 55 61 67 70 65 116 136 137 99 106 38 44 + { polySurfaceShape2.verts } + } + } + { + { 0 -1 0 } + { phong1SG } + { phong1SG.tref2 } + { + 99 98 136 116 53 47 41 36 31 24 18 14 20 26 106 + { polySurfaceShape2.verts } + } + } + { + { 1 0 0 } + { phong1SG } + { phong1SG.tref2 } + { 9 5 2 7 { polySurfaceShape2.verts } } + } + { + { 3.91309e-009 1 3.91309e-009 } + { phong1SG } + { phong1SG.tref2 } + { + 93 90 79 133 72 57 117 119 118 115 114 112 110 108 29 4 + { polySurfaceShape2.verts } + } + } + { + { -3.83044e-009 1 -3.83044e-009 } + { phong1SG } + { phong1SG.tref2 } + { + 123 79 90 0 4 29 104 102 100 101 103 105 107 109 111 113 57 72 + { polySurfaceShape2.verts } + } + } + { + { 0 0 1 } + { phong1SG } + { phong1SG.tref2 } + { 89 95 6 1 { polySurfaceShape2.verts } } + } + { + { 0 0 -1 } + { phong1SG } + { phong1SG.tref2 } + { 3 8 97 92 { polySurfaceShape2.verts } } + } + { + { -0.987688 0 0.156435 } + { phong3SG } + { phong3SG.tref3 } + { 10 16 19 12 { polySurfaceShape2.verts } } + } + { + { -0.987688 0 -0.156435 } + { phong3SG } + { phong3SG.tref3 } + { 15 11 13 17 { polySurfaceShape2.verts } } + } + { + { -0.891007 0 -0.45399 } + { phong3SG } + { phong3SG.tref3 } + { 21 15 17 23 { polySurfaceShape2.verts } } + } + { + { -0.707107 0 -0.707107 } + { phong3SG } + { phong3SG.tref3 } + { 27 21 23 30 { polySurfaceShape2.verts } } + } + { + { -0.453991 0 -0.891006 } + { phong3SG } + { phong3SG.tref3 } + { 33 27 30 35 { polySurfaceShape2.verts } } + } + { + { -0.156433 0 -0.987689 } + { phong3SG } + { phong3SG.tref3 } + { 39 33 35 42 { polySurfaceShape2.verts } } + } + { + { 0.156434 0 -0.987688 } + { phong3SG } + { phong3SG.tref3 } + { 45 39 42 48 { polySurfaceShape2.verts } } + } + { + { 0.453991 0 -0.891007 } + { phong3SG } + { phong3SG.tref3 } + { 51 45 48 54 { polySurfaceShape2.verts } } + } + { + { 0.707107 0 -0.707107 } + { phong3SG } + { phong3SG.tref3 } + { 58 51 54 60 { polySurfaceShape2.verts } } + } + { + { 0.891007 0 -0.453991 } + { phong3SG } + { phong3SG.tref3 } + { 63 58 60 66 { polySurfaceShape2.verts } } + } + { + { 0.987688 0 -0.156434 } + { phong3SG } + { phong3SG.tref3 } + { 69 63 66 71 { polySurfaceShape2.verts } } + } + { + { 0.987688 0 0.156434 } + { phong3SG } + { phong3SG.tref3 } + { 64 69 71 68 { polySurfaceShape2.verts } } + } + { + { 0.891007 0 0.453991 } + { phong3SG } + { phong3SG.tref3 } + { 59 64 68 62 { polySurfaceShape2.verts } } + } + { + { 0.707107 0 0.707106 } + { phong3SG } + { phong3SG.tref3 } + { 52 59 62 56 { polySurfaceShape2.verts } } + } + { + { 0.45399 0 0.891007 } + { phong3SG } + { phong3SG.tref3 } + { 46 52 56 50 { polySurfaceShape2.verts } } + } + { + { 0.156434 0 0.987688 } + { phong3SG } + { phong3SG.tref3 } + { 40 46 50 43 { polySurfaceShape2.verts } } + } + { + { -0.156434 0 0.987688 } + { phong3SG } + { phong3SG.tref3 } + { 34 40 43 37 { polySurfaceShape2.verts } } + } + { + { -0.45399 0 0.891007 } + { phong3SG } + { phong3SG.tref3 } + { 28 34 37 32 { polySurfaceShape2.verts } } + } + { + { -0.707107 0 0.707107 } + { phong3SG } + { phong3SG.tref3 } + { 22 28 32 25 { polySurfaceShape2.verts } } + } + { + { -0.891007 0 0.45399 } + { phong3SG } + { phong3SG.tref3 } + { 16 22 25 19 { polySurfaceShape2.verts } } + } +} diff --git a/samples/bump-mapping/models/abstractroom.mb b/samples/bump-mapping/models/abstractroom.mb new file mode 100644 index 0000000000..2d64cca3d4 Binary files /dev/null and b/samples/bump-mapping/models/abstractroom.mb differ diff --git a/samples/bump-mapping/models/brick-c.jpg b/samples/bump-mapping/models/brick-c.jpg new file mode 100644 index 0000000000..71c802f937 Binary files /dev/null and b/samples/bump-mapping/models/brick-c.jpg differ diff --git a/samples/bump-mapping/models/brick-n.jpg b/samples/bump-mapping/models/brick-n.jpg new file mode 100644 index 0000000000..e17ec5d941 Binary files /dev/null and b/samples/bump-mapping/models/brick-n.jpg differ diff --git a/samples/bump-mapping/models/fieldstone-c.jpg b/samples/bump-mapping/models/fieldstone-c.jpg new file mode 100644 index 0000000000..a5d8dc9874 Binary files /dev/null and b/samples/bump-mapping/models/fieldstone-c.jpg differ diff --git a/samples/bump-mapping/models/fieldstone-n.jpg b/samples/bump-mapping/models/fieldstone-n.jpg new file mode 100644 index 0000000000..493683ce57 Binary files /dev/null and b/samples/bump-mapping/models/fieldstone-n.jpg differ diff --git a/samples/bump-mapping/models/icosphere.egg b/samples/bump-mapping/models/icosphere.egg new file mode 100644 index 0000000000..474e8740b2 --- /dev/null +++ b/samples/bump-mapping/models/icosphere.egg @@ -0,0 +1,497 @@ + { Z-Up } + + { + "egg-trans -F icosphere.egg -o icosphere.egg" +} + Icosphere { + Icosphere { + 0 { + 0 0 -1 + { 0 0 -1 } + } + 1 { + 0.425323 -0.309011 -0.850654 + { 0.425306 -0.309 -0.850642 } + } + 2 { + -0.162456 -0.499995 -0.850654 + { -0.16245 -0.499985 -0.850642 } + } + 3 { + 0.723607 -0.525725 -0.44722 + { 0.723594 -0.525712 -0.447188 } + } + 4 { + 0.850648 0 -0.525736 + { 0.850642 0 -0.525712 } + } + 5 { + -0.52573 0 -0.850652 + { -0.525712 0 -0.850642 } + } + 6 { + -0.162456 0.499995 -0.850654 + { -0.16245 0.499985 -0.850642 } + } + 7 { + 0.425323 0.309011 -0.850654 + { 0.425306 0.309 -0.850642 } + } + 8 { + 0.951058 -0.309013 0 + { 0.951048 -0.309 0 } + } + 9 { + -0.276388 -0.850649 -0.44722 + { -0.276376 -0.850642 -0.447218 } + } + 10 { + 0.262869 -0.809012 -0.525738 + { 0.262856 -0.808985 -0.525712 } + } + 11 { + 0 -1 0 + { 0 -1 0 } + } + 12 { + -0.894426 0 -0.447216 + { -0.894406 0 -0.447188 } + } + 13 { + -0.688189 -0.499997 -0.525736 + { -0.688162 -0.499985 -0.525712 } + } + 14 { + -0.951058 -0.309013 0 + { -0.951048 -0.309 0 } + } + 15 { + -0.276388 0.850649 -0.44722 + { -0.276376 0.850642 -0.447218 } + } + 16 { + -0.688189 0.499997 -0.525736 + { -0.688162 0.499985 -0.525712 } + } + 17 { + -0.587786 0.809017 0 + { -0.587756 0.809015 0 } + } + 18 { + 0.723607 0.525725 -0.44722 + { 0.723594 0.525712 -0.447188 } + } + 19 { + 0.262869 0.809012 -0.525738 + { 0.262856 0.808985 -0.525712 } + } + 20 { + 0.587786 0.809017 0 + { 0.587756 0.809015 0 } + } + 21 { + 0.587786 -0.809017 0 + { 0.587756 -0.809015 0 } + } + 22 { + -0.587786 -0.809017 0 + { -0.587756 -0.809015 0 } + } + 23 { + -0.951058 0.309013 0 + { -0.951048 0.309 0 } + } + 24 { + 0 1 0 + { 0 1 0 } + } + 25 { + 0.951058 0.309013 0 + { 0.951048 0.309 0 } + } + 26 { + 0.276388 -0.850649 0.44722 + { 0.276376 -0.850642 0.447218 } + } + 27 { + 0.688189 -0.499997 0.525736 + { 0.688162 -0.499985 0.525712 } + } + 28 { + 0.162456 -0.499995 0.850654 + { 0.16245 -0.499985 0.850642 } + } + 29 { + -0.723607 -0.525725 0.44722 + { -0.723594 -0.525712 0.447188 } + } + 30 { + -0.262869 -0.809012 0.525738 + { -0.262856 -0.808985 0.525712 } + } + 31 { + -0.425323 -0.309011 0.850654 + { -0.425306 -0.309 0.850642 } + } + 32 { + -0.723607 0.525725 0.44722 + { -0.723594 0.525712 0.447188 } + } + 33 { + -0.850648 0 0.525736 + { -0.850642 0 0.525712 } + } + 34 { + -0.425323 0.309011 0.850654 + { -0.425306 0.309 0.850642 } + } + 35 { + 0.276388 0.850649 0.44722 + { 0.276376 0.850642 0.447218 } + } + 36 { + -0.262869 0.809012 0.525738 + { -0.262856 0.808985 0.525712 } + } + 37 { + 0.162456 0.499995 0.850654 + { 0.16245 0.499985 0.850642 } + } + 38 { + 0.894426 0 0.447216 + { 0.894406 0 0.447188 } + } + 39 { + 0.688189 0.499997 0.525736 + { 0.688162 0.499985 0.525712 } + } + 40 { + 0.52573 0 0.850652 + { 0.525712 0 0.850642 } + } + 41 { + 0 0 1 + { 0 0 1 } + } + } + { + { 0.102381 -0.31509 -0.943523 } + { 0 1 2 { Icosphere } } + } + { + { 0.700224 -0.268032 -0.661699 } + { 3 1 4 { Icosphere } } + } + { + { -0.268034 -0.194736 -0.943523 } + { 0 2 5 { Icosphere } } + } + { + { -0.268034 0.194737 -0.943523 } + { 0 5 6 { Icosphere } } + } + { + { 0.102381 0.31509 -0.943523 } + { 0 6 7 { Icosphere } } + } + { + { 0.904989 -0.268032 -0.330385 } + { 3 4 8 { Icosphere } } + } + { + { 0.024747 -0.943521 -0.330386 } + { 9 10 11 { Icosphere } } + } + { + { -0.889697 -0.315095 -0.330385 } + { 12 13 14 { Icosphere } } + } + { + { -0.574602 0.748784 -0.330388 } + { 15 16 17 { Icosphere } } + } + { + { 0.534576 0.777865 -0.330387 } + { 18 19 20 { Icosphere } } + } + { + { 0.802609 -0.583126 -0.125627 } + { 3 8 21 { Icosphere } } + } + { + { -0.306569 -0.943522 -0.125629 } + { 9 11 22 { Icosphere } } + } + { + { -0.992077 0 -0.125628 } + { 12 14 23 { Icosphere } } + } + { + { -0.306569 0.943522 -0.125629 } + { 15 17 24 { Icosphere } } + } + { + { 0.802609 0.583126 -0.125627 } + { 18 20 25 { Icosphere } } + } + { + { 0.408946 -0.628425 0.661698 } + { 26 27 28 { Icosphere } } + } + { + { -0.4713 -0.583122 0.661699 } + { 29 30 31 { Icosphere } } + } + { + { -0.700224 0.268032 0.661699 } + { 32 33 34 { Icosphere } } + } + { + { 0.03853 0.748779 0.661699 } + { 35 36 37 { Icosphere } } + } + { + { 0.724042 0.194736 0.661695 } + { 38 39 40 { Icosphere } } + } + { + { 0.268034 0.194737 0.943523 } + { 40 37 41 { Icosphere } } + } + { + { 0.491119 0.356821 0.794657 } + { 40 39 37 { Icosphere } } + } + { + { 0.408946 0.628425 0.661699 } + { 39 35 37 { Icosphere } } + } + { + { -0.102381 0.31509 0.943523 } + { 37 34 41 { Icosphere } } + } + { + { -0.187594 0.577345 0.794658 } + { 37 36 34 { Icosphere } } + } + { + { -0.4713 0.583122 0.661699 } + { 36 32 34 { Icosphere } } + } + { + { -0.331305 0 0.943524 } + { 34 31 41 { Icosphere } } + } + { + { -0.60706 0 0.794656 } + { 34 33 31 { Icosphere } } + } + { + { -0.700224 -0.268032 0.661699 } + { 33 29 31 { Icosphere } } + } + { + { -0.102381 -0.31509 0.943523 } + { 31 28 41 { Icosphere } } + } + { + { -0.187594 -0.577345 0.794658 } + { 31 30 28 { Icosphere } } + } + { + { 0.03853 -0.748779 0.661699 } + { 30 26 28 { Icosphere } } + } + { + { 0.268034 -0.194737 0.943523 } + { 28 40 41 { Icosphere } } + } + { + { 0.491119 -0.356821 0.794657 } + { 28 27 40 { Icosphere } } + } + { + { 0.724042 -0.194736 0.661695 } + { 27 38 40 { Icosphere } } + } + { + { 0.889697 0.315095 0.330385 } + { 25 39 38 { Icosphere } } + } + { + { 0.794656 0.577348 0.187595 } + { 25 20 39 { Icosphere } } + } + { + { 0.574602 0.748784 0.330388 } + { 20 35 39 { Icosphere } } + } + { + { -0.024747 0.943521 0.330386 } + { 24 36 35 { Icosphere } } + } + { + { -0.303531 0.934171 0.187597 } + { 24 17 36 { Icosphere } } + } + { + { -0.534576 0.777865 0.330387 } + { 17 32 36 { Icosphere } } + } + { + { -0.904989 0.268032 0.330385 } + { 23 33 32 { Icosphere } } + } + { + { -0.982246 0 0.187599 } + { 23 14 33 { Icosphere } } + } + { + { -0.904989 -0.268031 0.330385 } + { 14 29 33 { Icosphere } } + } + { + { -0.534576 -0.777865 0.330387 } + { 22 30 29 { Icosphere } } + } + { + { -0.303531 -0.934171 0.187597 } + { 22 11 30 { Icosphere } } + } + { + { -0.024747 -0.943521 0.330386 } + { 11 26 30 { Icosphere } } + } + { + { 0.574602 -0.748784 0.330388 } + { 21 27 26 { Icosphere } } + } + { + { 0.794656 -0.577348 0.187595 } + { 21 8 27 { Icosphere } } + } + { + { 0.889697 -0.315095 0.330385 } + { 8 38 27 { Icosphere } } + } + { + { 0.306569 0.943522 0.125629 } + { 20 24 35 { Icosphere } } + } + { + { 0.303531 0.934171 -0.187597 } + { 20 19 24 { Icosphere } } + } + { + { 0.024747 0.943521 -0.330386 } + { 19 15 24 { Icosphere } } + } + { + { -0.802609 0.583126 0.125627 } + { 17 23 32 { Icosphere } } + } + { + { -0.794656 0.577348 -0.187595 } + { 17 16 23 { Icosphere } } + } + { + { -0.889697 0.315095 -0.330385 } + { 16 12 23 { Icosphere } } + } + { + { -0.802609 -0.583126 0.125627 } + { 14 22 29 { Icosphere } } + } + { + { -0.794656 -0.577348 -0.187595 } + { 14 13 22 { Icosphere } } + } + { + { -0.574602 -0.748784 -0.330388 } + { 13 9 22 { Icosphere } } + } + { + { 0.306569 -0.943522 0.125629 } + { 11 21 26 { Icosphere } } + } + { + { 0.303531 -0.934171 -0.187597 } + { 11 10 21 { Icosphere } } + } + { + { 0.534576 -0.777865 -0.330387 } + { 10 3 21 { Icosphere } } + } + { + { 0.992077 0 0.125628 } + { 8 25 38 { Icosphere } } + } + { + { 0.982246 0 -0.187599 } + { 8 4 25 { Icosphere } } + } + { + { 0.904989 0.268031 -0.330385 } + { 4 18 25 { Icosphere } } + } + { + { 0.4713 0.583122 -0.661699 } + { 7 19 18 { Icosphere } } + } + { + { 0.187594 0.577345 -0.794658 } + { 7 6 19 { Icosphere } } + } + { + { -0.03853 0.748779 -0.661699 } + { 6 15 19 { Icosphere } } + } + { + { -0.408946 0.628425 -0.661698 } + { 6 16 15 { Icosphere } } + } + { + { -0.491119 0.356821 -0.794657 } + { 6 5 16 { Icosphere } } + } + { + { -0.724042 0.194736 -0.661695 } + { 5 12 16 { Icosphere } } + } + { + { -0.724042 -0.194736 -0.661695 } + { 5 13 12 { Icosphere } } + } + { + { -0.491119 -0.356821 -0.794657 } + { 5 2 13 { Icosphere } } + } + { + { -0.408946 -0.628425 -0.661698 } + { 2 9 13 { Icosphere } } + } + { + { 0.700224 0.268032 -0.661699 } + { 4 7 18 { Icosphere } } + } + { + { 0.60706 0 -0.794656 } + { 4 1 7 { Icosphere } } + } + { + { 0.331305 0 -0.943524 } + { 1 0 7 { Icosphere } } + } + { + { -0.03853 -0.748779 -0.661699 } + { 2 10 9 { Icosphere } } + } + { + { 0.187594 -0.577345 -0.794658 } + { 2 1 10 { Icosphere } } + } + { + { 0.4713 -0.583122 -0.661699 } + { 1 3 10 { Icosphere } } + } +} diff --git a/samples/bump-mapping/models/layingrock-c.jpg b/samples/bump-mapping/models/layingrock-c.jpg new file mode 100644 index 0000000000..f89da981ca Binary files /dev/null and b/samples/bump-mapping/models/layingrock-c.jpg differ diff --git a/samples/bump-mapping/models/layingrock-h.jpg b/samples/bump-mapping/models/layingrock-h.jpg new file mode 100644 index 0000000000..af6598d057 Binary files /dev/null and b/samples/bump-mapping/models/layingrock-h.jpg differ diff --git a/samples/bump-mapping/models/layingrock-n.jpg b/samples/bump-mapping/models/layingrock-n.jpg new file mode 100644 index 0000000000..81ec2b090e Binary files /dev/null and b/samples/bump-mapping/models/layingrock-n.jpg differ diff --git a/samples/carousel/main.py b/samples/carousel/main.py new file mode 100755 index 0000000000..a6ae51fea8 --- /dev/null +++ b/samples/carousel/main.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman, and Eddie Canaan +# Last Updated: 2015-03-13 +# +# This tutorial will demonstrate some uses for intervals in Panda +# to move objects in your panda world. +# Intervals are tools that change a value of something, like position, +# rotation or anything else, linearly, over a set period of time. They can be +# also be combined to work in sequence or in Parallel +# +# In this lesson, we will simulate a carousel in motion using intervals. +# The carousel will spin using an hprInterval while 4 pandas will represent +# the horses on a traditional carousel. The 4 pandas will rotate with the +# carousel and also move up and down on their poles using a LerpFunc interval. +# Finally there will also be lights on the outer edge of the carousel that +# will turn on and off by switching their texture with intervals in Sequence +# and Parallel + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import AmbientLight, DirectionalLight, LightAttrib +from panda3d.core import NodePath +from panda3d.core import LVector3 +from direct.interval.IntervalGlobal import * # Needed to use Intervals +from direct.gui.DirectGui import * + +# Importing math constants and functions +from math import pi, sin + + +class CarouselDemo(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This creates the on screen title that is in every tutorial + self.title = OnscreenText(text="Panda3D: Tutorial - Carousel", + parent=base.a2dBottomCenter, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5), + pos=(0, .1), scale=.1) + + base.disableMouse() # Allow manual positioning of the camera + camera.setPosHpr(0, -8, 2.5, 0, -9, 0) # Set the cameras' position + # and orientation + + self.loadModels() # Load and position our models + self.setupLights() # Add some basic lighting + self.startCarousel() # Create the needed intervals and put the + # carousel into motion + + def loadModels(self): + # Load the carousel base + self.carousel = loader.loadModel("models/carousel_base") + self.carousel.reparentTo(render) # Attach it to render + + # Load the modeled lights that are on the outer rim of the carousel + # (not Panda lights) + # There are 2 groups of lights. At any given time, one group will have + # the "on" texture and the other will have the "off" texture. + self.lights1 = loader.loadModel("models/carousel_lights") + self.lights1.reparentTo(self.carousel) + + # Load the 2nd set of lights + self.lights2 = loader.loadModel("models/carousel_lights") + # We need to rotate the 2nd so it doesn't overlap with the 1st set. + self.lights2.setH(36) + self.lights2.reparentTo(self.carousel) + + # Load the textures for the lights. One texture is for the "on" state, + # the other is for the "off" state. + self.lightOffTex = loader.loadTexture("models/carousel_lights_off.jpg") + self.lightOnTex = loader.loadTexture("models/carousel_lights_on.jpg") + + # Create an list (self.pandas) with filled with 4 dummy nodes attached + # to the carousel. + # This uses a python concept called "Array Comprehensions." Check the + # Python manual for more information on how they work + self.pandas = [self.carousel.attachNewNode("panda" + str(i)) + for i in range(4)] + self.models = [loader.loadModel("models/carousel_panda") + for i in range(4)] + self.moves = [0] * 4 + + for i in range(4): + # set the position and orientation of the ith panda node we just created + # The Z value of the position will be the base height of the pandas. + # The headings are multiplied by i to put each panda in its own position + # around the carousel + self.pandas[i].setPosHpr(0, 0, 1.3, i * 90, 0, 0) + + # Load the actual panda model, and parent it to its dummy node + self.models[i].reparentTo(self.pandas[i]) + # Set the distance from the center. This distance is based on the way the + # carousel was modeled in Maya + self.models[i].setY(.85) + + # Load the environment (Sky sphere and ground plane) + self.env = loader.loadModel("models/env") + self.env.reparentTo(render) + self.env.setScale(7) + + # Panda Lighting + def setupLights(self): + # Create some lights and add them to the scene. By setting the lights on + # render they affect the entire scene + # Check out the lighting tutorial for more information on lights + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.4, .4, .35, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 8, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + render.setLight(render.attachNewNode(directionalLight)) + render.setLight(render.attachNewNode(ambientLight)) + + # Explicitly set the environment to not be lit + self.env.setLightOff() + + def startCarousel(self): + # Here's where we actually create the intervals to move the carousel + # The first type of interval we use is one created directly from a NodePath + # This interval tells the NodePath to vary its orientation (hpr) from its + # current value (0,0,0) to (360,0,0) over 20 seconds. Intervals created from + # NodePaths also exist for position, scale, color, and shear + + self.carouselSpin = self.carousel.hprInterval(20, LVector3(360, 0, 0)) + # Once an interval is created, we need to tell it to actually move. + # start() will cause an interval to play once. loop() will tell an interval + # to repeat once it finished. To keep the carousel turning, we use + # loop() + self.carouselSpin.loop() + + # The next type of interval we use is called a LerpFunc interval. It is + # called that becuase it linearly interpolates (aka Lerp) values passed to + # a function over a given amount of time. + + # In this specific case, horses on a carousel don't move contantly up, + # suddenly stop, and then contantly move down again. Instead, they start + # slowly, get fast in the middle, and slow down at the top. This motion is + # close to a sine wave. This LerpFunc calls the function oscillatePanda + # (which we will create below), which changes the height of the panda based + # on the sin of the value passed in. In this way we achieve non-linear + # motion by linearly changing the input to a function + for i in range(4): + self.moves[i] = LerpFunc( + self.oscillatePanda, # function to call + duration=3, # 3 second duration + fromData=0, # starting value (in radians) + toData=2 * pi, # ending value (2pi radians = 360 degrees) + # Additional information to pass to + # self.oscialtePanda + extraArgs=[self.models[i], pi * (i % 2)] + ) + # again, we want these to play continuously so we start them with + # loop() + self.moves[i].loop() + + # Finally, we combine Sequence, Parallel, Func, and Wait intervals, + # to schedule texture swapping on the lights to simulate the lights turning + # on and off. + # Sequence intervals play other intervals in a sequence. In other words, + # it waits for the current interval to finish before playing the next + # one. + # Parallel intervals play a group of intervals at the same time + # Wait intervals simply do nothing for a given amount of time + # Func intervals simply make a single function call. This is helpful because + # it allows us to schedule functions to be called in a larger sequence. They + # take virtually no time so they don't cause a Sequence to wait. + + self.lightBlink = Sequence( + # For the first step in our sequence we will set the on texture on one + # light and set the off texture on the other light at the same time + Parallel( + Func(self.lights1.setTexture, self.lightOnTex, 1), + Func(self.lights2.setTexture, self.lightOffTex, 1)), + Wait(1), # Then we will wait 1 second + # Then we will switch the textures at the same time + Parallel( + Func(self.lights1.setTexture, self.lightOffTex, 1), + Func(self.lights2.setTexture, self.lightOnTex, 1)), + Wait(1) # Then we will wait another second + ) + + self.lightBlink.loop() # Loop this sequence continuously + + def oscillatePanda(self, rad, panda, offset): + # This is the oscillation function mentioned earlier. It takes in a + # degree value, a NodePath to set the height on, and an offset. The + # offset is there so that the different pandas can move opposite to + # each other. The .2 is the amplitude, so the height of the panda will + # vary from -.2 to .2 + panda.setZ(sin(rad + offset) * .2) + +demo = CarouselDemo() +demo.run() diff --git a/samples/carousel/models/carousel_base.egg.pz b/samples/carousel/models/carousel_base.egg.pz new file mode 100644 index 0000000000..4f4de1f11b Binary files /dev/null and b/samples/carousel/models/carousel_base.egg.pz differ diff --git a/samples/carousel/models/carousel_base.jpg b/samples/carousel/models/carousel_base.jpg new file mode 100644 index 0000000000..3e1298a55f Binary files /dev/null and b/samples/carousel/models/carousel_base.jpg differ diff --git a/samples/carousel/models/carousel_lights.egg.pz b/samples/carousel/models/carousel_lights.egg.pz new file mode 100644 index 0000000000..6b712ce708 Binary files /dev/null and b/samples/carousel/models/carousel_lights.egg.pz differ diff --git a/samples/carousel/models/carousel_lights_off.jpg b/samples/carousel/models/carousel_lights_off.jpg new file mode 100644 index 0000000000..4d528ce747 Binary files /dev/null and b/samples/carousel/models/carousel_lights_off.jpg differ diff --git a/samples/carousel/models/carousel_lights_on.jpg b/samples/carousel/models/carousel_lights_on.jpg new file mode 100644 index 0000000000..5688bca94d Binary files /dev/null and b/samples/carousel/models/carousel_lights_on.jpg differ diff --git a/samples/carousel/models/carousel_panda.egg.pz b/samples/carousel/models/carousel_panda.egg.pz new file mode 100644 index 0000000000..a0f74c621f Binary files /dev/null and b/samples/carousel/models/carousel_panda.egg.pz differ diff --git a/samples/carousel/models/carousel_panda.jpg b/samples/carousel/models/carousel_panda.jpg new file mode 100644 index 0000000000..efe0c85981 Binary files /dev/null and b/samples/carousel/models/carousel_panda.jpg differ diff --git a/samples/carousel/models/env.egg.pz b/samples/carousel/models/env.egg.pz new file mode 100644 index 0000000000..abce5df2e9 Binary files /dev/null and b/samples/carousel/models/env.egg.pz differ diff --git a/samples/carousel/models/env_ground.jpg b/samples/carousel/models/env_ground.jpg new file mode 100644 index 0000000000..d563a603c2 Binary files /dev/null and b/samples/carousel/models/env_ground.jpg differ diff --git a/samples/carousel/models/env_sky.jpg b/samples/carousel/models/env_sky.jpg new file mode 100644 index 0000000000..b339776222 Binary files /dev/null and b/samples/carousel/models/env_sky.jpg differ diff --git a/samples/cartoon-shader/advanced.py b/samples/cartoon-shader/advanced.py new file mode 100755 index 0000000000..49f6a2adca --- /dev/null +++ b/samples/cartoon-shader/advanced.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# Author: Kwasi Mensah +# Date: 7/11/2005 +# +# This is a tutorial to show some of the more advanced things +# you can do with Cg. Specifically, with Non Photo Realistic +# effects like Toon Shading. It also shows how to implement +# multiple buffers in Panda. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import PandaNode, LightNode, TextNode +from panda3d.core import Filename +from panda3d.core import NodePath +from panda3d.core import Shader +from panda3d.core import LVecBase4 +from direct.task.Task import Task +from direct.actor.Actor import Actor +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.showbase.BufferViewer import BufferViewer +import sys +import os + + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class ToonMaker(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + self.disableMouse() + camera.setPos(0, -50, 0) + + # Check video card capabilities. + if not self.win.getGsg().getSupportsBasicShaders(): + addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.") + return + + # Show instructions in the corner of the window. + self.title = addTitle( + "Panda3D: Tutorial - Toon Shading with Normals-Based Inking") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness") + self.inst3 = addInstructions(0.18, "Left/Right: Decrease/Increase Line Darkness") + self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") + + # This shader's job is to render the model with discrete lighting + # levels. The lighting calculations built into the shader assume + # a single nonattenuating point light. + + tempnode = NodePath(PandaNode("temp node")) + tempnode.setShader(loader.loadShader("lightingGen.sha")) + self.cam.node().setInitialState(tempnode.getState()) + + # This is the object that represents the single "light", as far + # the shader is concerned. It's not a real Panda3D LightNode, but + # the shader doesn't care about that. + + light = render.attachNewNode("light") + light.setPos(30, -50, 0) + + # this call puts the light's nodepath into the render state. + # this enables the shader to access this light by name. + + render.setShaderInput("light", light) + + # The "normals buffer" will contain a picture of the model colorized + # so that the color of the model is a representation of the model's + # normal at that point. + + normalsBuffer = self.win.makeTextureBuffer("normalsBuffer", 0, 0) + normalsBuffer.setClearColor(LVecBase4(0.5, 0.5, 0.5, 1)) + self.normalsBuffer = normalsBuffer + normalsCamera = self.makeCamera( + normalsBuffer, lens=self.cam.node().getLens()) + normalsCamera.node().setScene(render) + tempnode = NodePath(PandaNode("temp node")) + tempnode.setShader(loader.loadShader("normalGen.sha")) + normalsCamera.node().setInitialState(tempnode.getState()) + + # what we actually do to put edges on screen is apply them as a texture to + # a transparent screen-fitted card + + drawnScene = normalsBuffer.getTextureCard() + drawnScene.setTransparency(1) + drawnScene.setColor(1, 1, 1, 0) + drawnScene.reparentTo(render2d) + self.drawnScene = drawnScene + + # this shader accepts, as input, the picture from the normals buffer. + # it compares each adjacent pixel, looking for discontinuities. + # wherever a discontinuity exists, it emits black ink. + + self.separation = 0.001 + self.cutoff = 0.3 + inkGen = loader.loadShader("inkGen.sha") + drawnScene.setShader(inkGen) + drawnScene.setShaderInput("separation", LVecBase4(self.separation, 0, self.separation, 0)) + drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff)) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + + self.accept("v", self.bufferViewer.toggleEnable) + self.accept("V", self.bufferViewer.toggleEnable) + self.bufferViewer.setPosition("llcorner") + + # Load a dragon model and start its animation. + self.character = Actor() + self.character.loadModel('models/nik-dragon') + self.character.reparentTo(render) + self.character.loop('win') + self.character.hprInterval(15, (360, 0, 0)).loop() + + # These allow you to change cartooning parameters in realtime + self.accept("escape", sys.exit, [0]) + self.accept("arrow_up", self.increaseSeparation) + self.accept("arrow_down", self.decreaseSeparation) + self.accept("arrow_left", self.increaseCutoff) + self.accept("arrow_right", self.decreaseCutoff) + + def increaseSeparation(self): + self.separation = self.separation * 1.11111111 + print("separation: %f" % (self.separation)) + self.drawnScene.setShaderInput( + "separation", LVecBase4(self.separation, 0, self.separation, 0)) + + def decreaseSeparation(self): + self.separation = self.separation * 0.90000000 + print("separation: %f" % (self.separation)) + self.drawnScene.setShaderInput( + "separation", LVecBase4(self.separation, 0, self.separation, 0)) + + def increaseCutoff(self): + self.cutoff = self.cutoff * 1.11111111 + print("cutoff: %f" % (self.cutoff)) + self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff)) + + def decreaseCutoff(self): + self.cutoff = self.cutoff * 0.90000000 + print("cutoff: %f" % (self.cutoff)) + self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff)) + +t = ToonMaker() +t.run() diff --git a/samples/cartoon-shader/basic.py b/samples/cartoon-shader/basic.py new file mode 100755 index 0000000000..2861fc9300 --- /dev/null +++ b/samples/cartoon-shader/basic.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import PandaNode, LightNode, TextNode +from panda3d.core import Filename, NodePath +from panda3d.core import PointLight, AmbientLight +from panda3d.core import LightRampAttrib, AuxBitplaneAttrib +from panda3d.core import CardMaker +from panda3d.core import Shader, Texture +from direct.task.Task import Task +from direct.actor.Actor import Actor +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.showbase.BufferViewer import BufferViewer +from direct.filter.CommonFilters import CommonFilters +import sys +import os + + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class ToonMaker(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + self.disableMouse() + self.cam.node().getLens().setNear(10.0) + self.cam.node().getLens().setFar(200.0) + camera.setPos(0, -50, 0) + + # Check video card capabilities. + if not self.win.getGsg().getSupportsBasicShaders(): + addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.") + return + + # Enable a 'light ramp' - this discretizes the lighting, + # which is half of what makes a model look like a cartoon. + # Light ramps only work if shader generation is enabled, + # so we call 'setShaderAuto'. + + tempnode = NodePath(PandaNode("temp node")) + tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) + tempnode.setShaderAuto() + self.cam.node().setInitialState(tempnode.getState()) + + # Use class 'CommonFilters' to enable a cartoon inking filter. + # This can fail if the video card is not powerful enough, if so, + # display an error and exit. + + self.separation = 1 # Pixels + self.filters = CommonFilters(self.win, self.cam) + filterok = self.filters.setCartoonInk(separation=self.separation) + if (filterok == False): + addTitle( + "Toon Shader: Video card not powerful enough to do image postprocessing") + return + + # Show instructions in the corner of the window. + self.title = addTitle( + "Panda3D: Tutorial - Toon Shading with Normals-Based Inking") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness") + self.inst3 = addInstructions(0.18, "V: View the render-to-texture results") + + # Load a dragon model and animate it. + self.character = Actor() + self.character.loadModel('models/nik-dragon') + self.character.reparentTo(render) + self.character.loadAnims({'win': 'models/nik-dragon'}) + self.character.loop('win') + self.character.hprInterval(15, (360, 0, 0)).loop() + + # Create a non-attenuating point light and an ambient light. + plightnode = PointLight("point light") + plightnode.setAttenuation((1, 0, 0)) + plight = render.attachNewNode(plightnode) + plight.setPos(30, -50, 0) + alightnode = AmbientLight("ambient light") + alightnode.setColor((0.8, 0.8, 0.8, 1)) + alight = render.attachNewNode(alightnode) + render.setLight(alight) + render.setLight(plight) + + # Panda contains a built-in viewer that lets you view the + # results of all render-to-texture operations. This lets you + # see what class CommonFilters is doing behind the scenes. + self.accept("v", self.bufferViewer.toggleEnable) + self.accept("V", self.bufferViewer.toggleEnable) + self.bufferViewer.setPosition("llcorner") + self.accept("s", self.filters.manager.resizeBuffers) + + # These allow you to change cartooning parameters in realtime + self.accept("escape", sys.exit, [0]) + self.accept("arrow_up", self.increaseSeparation) + self.accept("arrow_down", self.decreaseSeparation) + + def increaseSeparation(self): + self.separation = self.separation * 1.11111111 + print("separation: %f" % (self.separation)) + self.filters.setCartoonInk(separation=self.separation) + + def decreaseSeparation(self): + self.separation = self.separation * 0.90000000 + print("separation: %f" % (self.separation)) + self.filters.setCartoonInk(separation=self.separation) + +t = ToonMaker() +t.run() diff --git a/samples/cartoon-shader/inkGen.sha b/samples/cartoon-shader/inkGen.sha new file mode 100644 index 0000000000..930bc00764 --- /dev/null +++ b/samples/cartoon-shader/inkGen.sha @@ -0,0 +1,35 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float4 vtx_texcoord0 : TEXCOORD0, + out float4 l_position : POSITION, + out float4 l_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj) +{ + l_position=mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; +} + +void fshader(float4 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + uniform float4 k_cutoff : C6, + uniform float4 k_separation : C7, + out float4 o_color : COLOR) +{ + float4 texcoord0 = l_texcoord0 + k_separation.xyzw; + float4 color0=tex2D(tex_0, float2(texcoord0.x, texcoord0.y)); + float4 texcoord1 = l_texcoord0 - k_separation.xyzw; + float4 color1=tex2D(tex_0, float2(texcoord1.x, texcoord1.y)); + float4 texcoord2 = l_texcoord0 + k_separation.wzyx; + float4 color2=tex2D(tex_0, float2(texcoord2.x, texcoord2.y)); + float4 texcoord3 = l_texcoord0 - k_separation.wzyx; + float4 color3=tex2D(tex_0, float2(texcoord3.x, texcoord3.y)); + float4 mx = max(color0,max(color1,max(color2,color3))); + float4 mn = min(color0,min(color1,min(color2,color3))); + float4 trigger = saturate(((mx-mn) * 3) - k_cutoff.x); + float thresh = dot(float3(trigger.x, trigger.y, trigger.z),float3(1,1,1)); + float4 output_color = float4 (0, 0, 0, thresh); + o_color = output_color; +} diff --git a/samples/cartoon-shader/lightingGen.sha b/samples/cartoon-shader/lightingGen.sha new file mode 100644 index 0000000000..c7cac59943 --- /dev/null +++ b/samples/cartoon-shader/lightingGen.sha @@ -0,0 +1,29 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float3 vtx_normal : NORMAL, + float4 vtx_color : COLOR, + out float4 l_position : POSITION, + out float4 l_brite : TEXCOORD0, + out float4 l_color : COLOR, + uniform float4 mspos_light, + uniform float4x4 mat_modelproj) +{ + l_position = mul(mat_modelproj, vtx_position); + float3 N = normalize(vtx_normal); + float3 lightVector = normalize(mspos_light - vtx_position); + l_brite = max(dot(N,lightVector), 0); + l_color = vtx_color; +} + + +void fshader(float4 l_brite : TEXCOORD0, + float4 l_color : COLOR, + out float4 o_color : COLOR) +{ + if (l_brite.x<0.5) l_brite=0.8; + else l_brite=1.2; + o_color=l_brite * l_color; +} diff --git a/samples/cartoon-shader/models/nik-dragon.egg.pz b/samples/cartoon-shader/models/nik-dragon.egg.pz new file mode 100644 index 0000000000..7c089ca705 Binary files /dev/null and b/samples/cartoon-shader/models/nik-dragon.egg.pz differ diff --git a/samples/cartoon-shader/normalGen.sha b/samples/cartoon-shader/normalGen.sha new file mode 100644 index 0000000000..a3642b0630 --- /dev/null +++ b/samples/cartoon-shader/normalGen.sha @@ -0,0 +1,25 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float4 vtx_normal : NORMAL, + out float4 l_position : POSITION, + out float3 l_color : TEXCOORD0, + uniform float4x4 mat_modelproj, + uniform float4x4 itp_modelview) +{ + l_position=mul(mat_modelproj, vtx_position); + l_color=(float3)mul(itp_modelview, vtx_normal); +} + +void fshader(float3 l_color: TEXCOORD0, + out float4 o_color: COLOR) +{ + l_color = normalize(l_color); + l_color = l_color/2; + o_color.rgb = l_color + float4(0.5, 0.5, 0.5, 0.5); + o_color.a = 1; +} + + diff --git a/samples/chessboard/main.py b/samples/chessboard/main.py new file mode 100755 index 0000000000..38b4664c41 --- /dev/null +++ b/samples/chessboard/main.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Models: Eddie Canaan +# Last Updated: 2015-03-13 +# +# This tutorial shows how to determine what objects the mouse is pointing to +# We do this using a collision ray that extends from the mouse position +# and points straight into the scene, and see what it collides with. We pick +# the object with the closest collision + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import CollisionTraverser, CollisionNode +from panda3d.core import CollisionHandlerQueue, CollisionRay +from panda3d.core import AmbientLight, DirectionalLight, LightAttrib +from panda3d.core import TextNode +from panda3d.core import LPoint3, LVector3, BitMask32 +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.task.Task import Task +import sys + +# First we define some contants for the colors +BLACK = (0, 0, 0, 1) +WHITE = (1, 1, 1, 1) +HIGHLIGHT = (0, 1, 1, 1) +PIECEBLACK = (.15, .15, .15, 1) + +# Now we define some helper functions that we will need later + +# This function, given a line (vector plus origin point) and a desired z value, +# will give us the point on the line where the desired z value is what we want. +# This is how we know where to position an object in 3D space based on a 2D mouse +# position. It also assumes that we are dragging in the XY plane. +# +# This is derived from the mathmatical of a plane, solved for a given point +def PointAtZ(z, point, vec): + return point + vec * ((z - point.getZ()) / vec.getZ()) + +# A handy little function for getting the proper position for a given square1 +def SquarePos(i): + return LPoint3((i % 8) - 3.5, int(i / 8) - 3.5, 0) + +# Helper function for determining wheter a square should be white or black +# The modulo operations (%) generate the every-other pattern of a chess-board +def SquareColor(i): + if (i + ((i / 8) % 2)) % 2: + return BLACK + else: + return WHITE + + +class ChessboardDemo(ShowBase): + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This code puts the standard title and instruction text on screen + self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking", + style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), + pos=(0.8, -0.95), scale = .07) + self.escapeEvent = OnscreenText( + text="ESC: Quit", parent=base.a2dTopLeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.1), + align=TextNode.ALeft, scale = .05) + self.mouse1Event = OnscreenText( + text="Left-click and drag: Pick up and drag piece", + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.16), scale=.05) + + self.accept('escape', sys.exit) # Escape quits + self.disableMouse() # Disble mouse camera control + camera.setPosHpr(0, -12, 8, 0, -35, 0) # Set the camera + self.setupLights() # Setup default lighting + + # Since we are using collision detection to do picking, we set it up like + # any other collision detection system with a traverser and a handler + self.picker = CollisionTraverser() # Make a traverser + self.pq = CollisionHandlerQueue() # Make a handler + # Make a collision node for our picker ray + self.pickerNode = CollisionNode('mouseRay') + # Attach that node to the camera since the ray will need to be positioned + # relative to it + self.pickerNP = camera.attachNewNode(self.pickerNode) + # Everything to be picked will use bit 1. This way if we were doing other + # collision we could seperate it + self.pickerNode.setFromCollideMask(BitMask32.bit(1)) + self.pickerRay = CollisionRay() # Make our ray + # Add it to the collision node + self.pickerNode.addSolid(self.pickerRay) + # Register the ray as something that can cause collisions + self.picker.addCollider(self.pickerNP, self.pq) + # self.picker.showCollisions(render) + + # Now we create the chess board and its pieces + + # We will attach all of the squares to their own root. This way we can do the + # collision pass just on the sqaures and save the time of checking the rest + # of the scene + self.squareRoot = render.attachNewNode("squareRoot") + + # For each square + self.squares = [None for i in range(64)] + self.pieces = [None for i in range(64)] + for i in range(64): + # Load, parent, color, and position the model (a single square + # polygon) + self.squares[i] = loader.loadModel("models/square") + self.squares[i].reparentTo(self.squareRoot) + self.squares[i].setPos(SquarePos(i)) + self.squares[i].setColor(SquareColor(i)) + # Set the model itself to be collideable with the ray. If this model was + # any more complex than a single polygon, you should set up a collision + # sphere around it instead. But for single polygons this works + # fine. + self.squares[i].find("**/polygon").node().setIntoCollideMask( + BitMask32.bit(1)) + # Set a tag on the square's node so we can look up what square this is + # later during the collision pass + self.squares[i].find("**/polygon").node().setTag('square', str(i)) + + # We will use this variable as a pointer to whatever piece is currently + # in this square + + # The order of pieces on a chessboard from white's perspective. This list + # contains the constructor functions for the piece classes defined + # below + pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook) + + for i in range(8, 16): + # Load the white pawns + self.pieces[i] = Pawn(i, WHITE) + for i in range(48, 56): + # load the black pawns + self.pieces[i] = Pawn(i, PIECEBLACK) + for i in range(8): + # Load the special pieces for the front row and color them white + self.pieces[i] = pieceOrder[i](i, WHITE) + # Load the special pieces for the back row and color them black + self.pieces[i + 56] = pieceOrder[i](i + 56, PIECEBLACK) + + # This will represent the index of the currently highlited square + self.hiSq = False + # This wil represent the index of the square where currently dragged piece + # was grabbed from + self.dragging = False + + # Start the task that handles the picking + self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') + self.accept("mouse1", self.grabPiece) # left-click grabs a piece + self.accept("mouse1-up", self.releasePiece) # releasing places it + + # This function swaps the positions of two pieces + def swapPieces(self, fr, to): + temp = self.pieces[fr] + self.pieces[fr] = self.pieces[to] + self.pieces[to] = temp + if self.pieces[fr]: + self.pieces[fr].square = fr + self.pieces[fr].obj.setPos(SquarePos(fr)) + if self.pieces[to]: + self.pieces[to].square = to + self.pieces[to].obj.setPos(SquarePos(to)) + + def mouseTask(self, task): + # This task deals with the highlighting and dragging based on the mouse + + # First, clear the current highlight + if self.hiSq is not False: + self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) + self.hiSq = False + + # Check to see if we can access the mouse. We need it to do anything + # else + if self.mouseWatcherNode.hasMouse(): + # get the mouse position + mpos = self.mouseWatcherNode.getMouse() + + # Set the position of the ray based on the mouse position + self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY()) + + # If we are dragging something, set the position of the object + # to be at the appropriate point over the plane of the board + if self.dragging is not False: + # Gets the point described by pickerRay.getOrigin(), which is relative to + # camera, relative instead to render + nearPoint = render.getRelativePoint( + camera, self.pickerRay.getOrigin()) + # Same thing with the direction of the ray + nearVec = render.getRelativeVector( + camera, self.pickerRay.getDirection()) + self.pieces[self.dragging].obj.setPos( + PointAtZ(.5, nearPoint, nearVec)) + + # Do the actual collision pass (Do it only on the squares for + # efficiency purposes) + self.picker.traverse(self.squareRoot) + if self.pq.getNumEntries() > 0: + # if we have hit something, sort the hits so that the closest + # is first, and highlight that node + self.pq.sortEntries() + i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) + # Set the highlight on the picked square + self.squares[i].setColor(HIGHLIGHT) + self.hiSq = i + + return Task.cont + + def grabPiece(self): + # If a square is highlighted and it has a piece, set it to dragging + # mode + if self.hiSq is not False and self.pieces[self.hiSq]: + self.dragging = self.hiSq + self.hiSq = False + + def releasePiece(self): + # Letting go of a piece. If we are not on a square, return it to its original + # position. Otherwise, swap it with the piece in the new square + # Make sure we really are dragging something + if self.dragging is not False: + # We have let go of the piece, but we are not on a square + if self.hiSq is False: + self.pieces[self.dragging].obj.setPos( + SquarePos(self.dragging)) + else: + # Otherwise, swap the pieces + self.swapPieces(self.dragging, self.hiSq) + + # We are no longer dragging anything + self.dragging = False + + def setupLights(self): # This function sets up some default lighting + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.8, .8, .8, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 45, -45)) + directionalLight.setColor((0.2, 0.2, 0.2, 1)) + render.setLight(render.attachNewNode(directionalLight)) + render.setLight(render.attachNewNode(ambientLight)) + + +# Class for a piece. This just handels loading the model and setting initial +# position and color +class Piece(object): + def __init__(self, square, color): + self.obj = loader.loadModel(self.model) + self.obj.reparentTo(render) + self.obj.setColor(color) + self.obj.setPos(SquarePos(square)) + + +# Classes for each type of chess piece +# Obviously, we could have done this by just passing a string to Piece's init. +# But if you wanted to make rules for how the pieces move, a good place to start +# would be to make an isValidMove(toSquare) method for each piece type +# and then check if the destination square is acceptible during ReleasePiece +class Pawn(Piece): + model = "models/pawn" + +class King(Piece): + model = "models/king" + +class Queen(Piece): + model = "models/queen" + +class Bishop(Piece): + model = "models/bishop" + +class Knight(Piece): + model = "models/knight" + +class Rook(Piece): + model = "models/rook" + +# Do the main initialization and start 3D rendering +demo = ChessboardDemo() +demo.run() diff --git a/samples/chessboard/models/bishop.egg.pz b/samples/chessboard/models/bishop.egg.pz new file mode 100644 index 0000000000..40260c2bd1 Binary files /dev/null and b/samples/chessboard/models/bishop.egg.pz differ diff --git a/samples/chessboard/models/king.egg.pz b/samples/chessboard/models/king.egg.pz new file mode 100644 index 0000000000..8ba7a57f39 Binary files /dev/null and b/samples/chessboard/models/king.egg.pz differ diff --git a/samples/chessboard/models/knight.egg.pz b/samples/chessboard/models/knight.egg.pz new file mode 100644 index 0000000000..b7cabaa5eb Binary files /dev/null and b/samples/chessboard/models/knight.egg.pz differ diff --git a/samples/chessboard/models/pawn.egg.pz b/samples/chessboard/models/pawn.egg.pz new file mode 100644 index 0000000000..42d7e21113 Binary files /dev/null and b/samples/chessboard/models/pawn.egg.pz differ diff --git a/samples/chessboard/models/queen.egg.pz b/samples/chessboard/models/queen.egg.pz new file mode 100644 index 0000000000..0db73ebbf1 Binary files /dev/null and b/samples/chessboard/models/queen.egg.pz differ diff --git a/samples/chessboard/models/rook.egg.pz b/samples/chessboard/models/rook.egg.pz new file mode 100644 index 0000000000..e7886caf42 Binary files /dev/null and b/samples/chessboard/models/rook.egg.pz differ diff --git a/samples/chessboard/models/square.egg.pz b/samples/chessboard/models/square.egg.pz new file mode 100644 index 0000000000..f2e0a15812 Binary files /dev/null and b/samples/chessboard/models/square.egg.pz differ diff --git a/samples/culling/models/cells.egg b/samples/culling/models/cells.egg new file mode 100644 index 0000000000..50560b6041 --- /dev/null +++ b/samples/culling/models/cells.egg @@ -0,0 +1,586 @@ + { Y-Up } + + cell1 { + cell1-ORG { + 0 { + -4 0 -4 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + 11.5789 0 -11.5789 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -11.5789 0 -11.5789 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -4 0 4 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + 4 0 -4 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + 11.5789 0 11.5789 + { 0 1 0 } + { 1 1 1 1 } + } + 6 { + 4 0 4 + { 0 1 0 } + { 1 1 1 1 } + } + 7 { + -11.5789 0 11.5789 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell1-ORG } } + } + { + { 2 3 0 { cell1-ORG } } + } + { + { 4 5 1 { cell1-ORG } } + } + { + { 6 7 5 { cell1-ORG } } + } + { + { 4 1 0 { cell1-ORG } } + } + { + { 7 3 2 { cell1-ORG } } + } + { + { 6 5 4 { cell1-ORG } } + } + { + { 3 7 6 { cell1-ORG } } + } +} + cell2 { + cell2-ORG { + 0 { + -4 0 -0.838305 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + 4 0 0.527741 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + 4 0 -0.838305 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -4 0 0.527741 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell2-ORG } } + } + { + { 3 1 0 { cell2-ORG } } + } +} + cell3 { + cell3-ORG { + 0 { + -1.02178 0 -0.838305 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + 0.243417 0 -1.36315 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -1.02178 0 -1.36315 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -3.36172 0 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + 2.99397 0 -1.36315 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + 2.99397 0 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 6 { + -3.36172 0 -1.36315 + { 0 1 0 } + { 1 1 1 1 } + } + 7 { + 0.243417 0 -0.838305 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell3-ORG } } + } + { + { 1 3 2 { cell3-ORG } } + } + { + { 4 5 1 { cell3-ORG } } + } + { + { 6 2 3 { cell3-ORG } } + } + { + { 7 1 0 { cell3-ORG } } + } + { + { 5 3 1 { cell3-ORG } } + } +} + cell4 { + cell4-ORG { + 0 { + 2.99397 0 0.527741 + { 0 0.854764 -0.519017 } + { 0.999 0.999 0.999 1 } + } + 1 { + 2.04443 1.03879 2.23852 + { 0.158619 0.946967 -0.279453 } + { 1 1 1 1 } + } + 2 { + 2.99397 1.03879 2.23852 + { 0 0.983872 -0.178875 } + { 1 1 1 1 } + } + 3 { + 2.04443 1.03879 3.18072 + { 0.309844 0.950787 0 } + { 1 1 1 1 } + } + 4 { + 2.99397 1.03879 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + 0.519489 2.15079 2.23852 + { 0.320561 0.934229 0.156384 } + { 1 1 1 1 } + } + 6 { + 0.519489 2.15079 3.18072 + { 0.205355 0.978688 0 } + { 1 1 1 1 } + } + 7 { + -0.423812 2.15079 2.23852 + { 0 0.95348 0.301457 } + { 1 1 1 1 } + } + 8 { + -0.423812 2.15079 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 9 { + 0.519489 3.3704 0.502572 + { 0 0.916633 0.399729 } + { 1 1 1 1 } + } + 10 { + -0.423812 3.3704 0.502572 + { 0 0.979823 0.199865 } + { 1 1 1 1 } + } + 11 { + 0.519489 3.3704 0.115602 + { 0 1 0 } + { 1 1 1 1 } + } + 12 { + -0.423812 3.3704 0.115602 + { 0 1 0 } + { 1 1 1 1 } + } + 13 { + 2.04443 0 0.527741 + { 0 0.854764 -0.519017 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell4-ORG } } + } + { + { 2 3 4 { cell4-ORG } } + } + { + { 3 5 6 { cell4-ORG } } + } + { + { 6 7 8 { cell4-ORG } } + } + { + { 7 9 10 { cell4-ORG } } + } + { + { 10 11 12 { cell4-ORG } } + } + { + { 13 1 0 { cell4-ORG } } + } + { + { 1 3 2 { cell4-ORG } } + } + { + { 1 5 3 { cell4-ORG } } + } + { + { 5 7 6 { cell4-ORG } } + } + { + { 5 9 7 { cell4-ORG } } + } + { + { 9 11 10 { cell4-ORG } } + } +} + cell5 { + cell5-ORG { + 0 { + 2.99397 3.3704 0.115602 + { 0 1 0 } + { 1 1 1 1 } + } + 1 { + -3.36172 3.3704 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -3.36172 3.3704 0.115602 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 3 { + 2.99397 3.3704 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell5-ORG } } + } + { + { 3 1 0 { cell5-ORG } } + } +} + cell6 { + cell6-ORG { + 0 { + 2.99397 3.3704 0.115602 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + 0.997627 3.3704 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + 2.99397 3.3704 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + 0.997627 3.3704 0.115602 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell6-ORG } } + } + { + { 3 1 0 { cell6-ORG } } + } +} + cell7 { + cell7-ORG { + 0 { + -3.36172 3.3704 0.115602 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + -0.900963 3.3704 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -0.900963 3.3704 0.115602 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -2.30748 3.3704 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + -2.30748 3.3704 2.05045 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + -3.36172 3.3704 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell7-ORG } } + } + { + { 0 3 4 { cell7-ORG } } + } + { + { 4 1 0 { cell7-ORG } } + } + { + { 5 3 0 { cell7-ORG } } + } +} + cell8 { + cell8-ORG { + 0 { + -2.30748 3.3704 2.23852 + { -0.430354 0.90266 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + 1.82483 5.34053 3.18072 + { -0.430354 0.90266 0 } + { 1 1 1 1 } + } + 2 { + 1.82483 5.34053 2.23852 + { -0.430354 0.90266 0 } + { 1 1 1 1 } + } + 3 { + -2.30748 3.3704 3.18072 + { -0.430354 0.90266 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell8-ORG } } + } + { + { 3 1 0 { cell8-ORG } } + } +} + cell9 { + cell9-ORG { + 0 { + 1.82483 5.34053 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + 1 { + 2.99397 5.34053 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + 1.82483 5.34053 -2.49425 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -0.485624 5.34053 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + -0.485624 5.34053 -2.49425 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 5 { + 2.99397 5.34053 3.18072 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell9-ORG } } + } + { + { 2 3 4 { cell9-ORG } } + } + { + { 5 1 0 { cell9-ORG } } + } + { + { 1 3 2 { cell9-ORG } } + } +} + cell10 { + cell10-ORG { + 0 { + -3.36172 5.34053 -3.42013 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + -0.485624 5.34053 -2.49425 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -0.485624 5.34053 -3.42013 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + -2.36149 5.34053 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + -2.36149 5.34053 -2.49425 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + -3.36172 5.34053 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell10-ORG } } + } + { + { 0 3 4 { cell10-ORG } } + } + { + { 4 1 0 { cell10-ORG } } + } + { + { 5 3 0 { cell10-ORG } } + } +} + cell11 { + cell11-ORG { + 0 { + -2.36149 5.34053 1.0558 + { 0 1 0 } + { 0.999 0.999 0.999 1 } + } + 1 { + -1.90136 5.34053 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + 2 { + -1.90136 5.34053 1.0558 + { 0 1 0 } + { 1 1 1 1 } + } + 3 { + 1.31869 5.34053 0.350091 + { 0 1 0 } + { 1 1 1 1 } + } + 4 { + 1.31869 5.34053 -2.1928 + { 0 1 0 } + { 1 1 1 1 } + } + 5 { + -1.90136 5.34053 -2.16895 + { 0 1 0 } + { 1 1 1 1 } + } + 6 { + 1.31869 5.34053 -0.718624 + { 0 1 0 } + { 1 1 1 1 } + } + 7 { + 1.82483 5.34053 0.350091 + { 0 1 0 } + { 1 1 1 1 } + } + 8 { + -2.36149 5.34053 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + 9 { + 1.31869 5.34053 2.04979 + { 0 1 0 } + { 1 1 1 1 } + } + 10 { + 1.82483 5.34053 -0.718624 + { 0 1 0 } + { 1 1 1 1 } + } + } + { + { 0 1 2 { cell11-ORG } } + } + { + { 1 3 2 { cell11-ORG } } + } + { + { 4 5 6 { cell11-ORG } } + } + { + { 6 2 3 { cell11-ORG } } + } + { + { 7 6 3 { cell11-ORG } } + } + { + { 8 1 0 { cell11-ORG } } + } + { + { 9 3 1 { cell11-ORG } } + } + { + { 5 2 6 { cell11-ORG } } + } + { + { 10 6 7 { cell11-ORG } } + } +} diff --git a/samples/culling/models/level.egg b/samples/culling/models/level.egg new file mode 100644 index 0000000000..873fae9ff1 --- /dev/null +++ b/samples/culling/models/level.egg @@ -0,0 +1,2435 @@ + { Y-Up } + + levelgeo { + levelgeo-ORG { + 0 { + -4 0 -4 + { 0 0 -1 } + { 0.551941 0.559784 0.544098 1 } + } + 1 { + -4 7.57015 -4 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 2 { + 4 7.57015 -4 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 3 { + 4 0 -4 + { 0 0 -1 } + { 0.552941 0.560784 0.545098 1 } + } + 4 { + -4 0 4 + { 0 0 1 } + { 0.552941 0.560784 0.545098 1 } + } + 5 { + 4 0 4 + { 0 0 1 } + { 0.552941 0.560784 0.545098 1 } + } + 6 { + 4 7.57015 4 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 7 { + -4 7.57015 4 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 8 { + -4 0 -4 + { -1 0 0 } + { 0.551941 0.559784 0.544098 1 } + } + 9 { + -4 0 -0.838305 + { -1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 10 { + -4 1.48611 -0.838305 + { -1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 11 { + -4 7.57015 -4 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 12 { + 4 7.57015 -4 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 13 { + 4 1.48611 -0.838305 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 14 { + 4 0 -0.838305 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 15 { + 4 0 -4 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 16 { + -4 7.57015 -4 + { 0 1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 17 { + -4 7.57015 4 + { 0 1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 18 { + 4 7.57015 4 + { 0 1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 19 { + 4 7.57015 -4 + { 0 1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 20 { + -4 0 -4 + { 0 1 0 } + { 0.375471 0.497039 0.001 1 } + } + 21 { + 4 0 -4 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 22 { + 11.5789 0 -11.5789 + { 0 1 0 } + { 0.462745 0.713726 0.192157 1 } + } + 23 { + -11.5789 0 -11.5789 + { 0 1 0 } + { 0.462745 0.713726 0.192157 1 } + } + 24 { + 4 0 -0.838305 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 25 { + -4 0 4 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 26 { + -4 0 0.527741 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 27 { + -11.5789 0 11.5789 + { 0 1 0 } + { 0.462745 0.713726 0.192157 1 } + } + 28 { + 4 0 4 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 29 { + 11.5789 0 11.5789 + { 0 1 0 } + { 0.462745 0.713726 0.192157 1 } + } + 30 { + -4 0 4 + { -1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 31 { + -4 7.57015 4 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 32 { + -4 1.48611 0.527741 + { -1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 33 { + -4 0 0.527741 + { -1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 34 { + 4 0 4 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 35 { + 4 0 0.527741 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 36 { + 4 1.48611 0.527741 + { 1 0 0 } + { 0.552941 0.560784 0.545098 1 } + } + 37 { + 4 7.57015 4 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 38 { + -4 1.48611 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 39 { + -4 0 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 40 { + -1.02178 0 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 41 { + -1.03253 1.48611 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 42 { + -4 0 -0.838305 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 43 { + -4 0 0.527741 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 44 { + -1.02178 0 -0.838305 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 45 { + -4 0 0.527741 + { 0 1.40115e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 46 { + -4 1.48611 0.527741 + { 0 1.40115e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 47 { + 2.04443 1.48611 0.527741 + { 0 1.40115e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 48 { + 2.04443 0 0.527741 + { 0 1.40115e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 49 { + -4 1.48611 0.527741 + { 0 -1 0 } + { 0.729412 0.396078 0.341176 1 } + } + 50 { + -1.03253 1.48611 -0.838305 + { 0 -1 0 } + { 0.729412 0.396078 0.341176 1 } + } + 51 { + 0.243412 1.48611 -0.838305 + { 0 -1 0 } + { 0.729412 0.396078 0.341176 1 } + } + 52 { + 2.04443 1.48611 0.527741 + { 0 -0.983257 0.182222 } + { 0.729412 0.396078 0.341176 1 } + } + 53 { + 2.99397 0 -0.838305 + { 1.47369e-009 -2.57304e-010 1 } + { 0.729412 0.513726 0.341176 1 } + } + 54 { + 2.99397 1.48611 -0.838305 + { 1.47369e-009 -2.57304e-010 1 } + { 0.729412 0.513726 0.341176 1 } + } + 55 { + 0.243412 1.48611 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 56 { + 0.243417 0 -0.838305 + { 0 -5.3759e-011 1 } + { 0.729412 0.513726 0.341176 1 } + } + 57 { + -1.02178 0 -0.838305 + { 0 1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 58 { + 0.243417 0 -0.838305 + { 0 1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 59 { + 0.243417 0 -1.36315 + { 0 1 0 } + { 0.713726 0.721569 0.788235 1 } + } + 60 { + -1.02178 0 -1.36315 + { 0 1 0 } + { 0.713726 0.721569 0.788235 1 } + } + 61 { + 0.243417 0 -0.838305 + { -1 -3.33861e-006 0 } + { 0.458824 0.666667 0.729412 1 } + } + 62 { + 0.243412 1.48611 -0.838305 + { -1 -3.33861e-006 0 } + { 0.458824 0.666667 0.729412 1 } + } + 63 { + 0.243412 1.48611 -1.3609 + { -1 -3.33861e-006 0 } + { 0.458824 0.666667 0.729412 1 } + } + 64 { + 0.243417 0 -1.36315 + { -1 -3.33861e-006 0 } + { 0.713726 0.721569 0.788235 1 } + } + 65 { + 0.243412 1.48611 -0.838305 + { 0 -1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 66 { + -1.03253 1.48611 -0.838305 + { 0 -1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 67 { + -1.03253 1.48611 -1.3609 + { 0 -1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 68 { + 0.243412 1.48611 -1.3609 + { 0 -1 0 } + { 0.458824 0.666667 0.729412 1 } + } + 69 { + -1.03253 1.48611 -0.838305 + { 0.999974 0.00723416 0 } + { 0.458824 0.666667 0.729412 1 } + } + 70 { + -1.02178 0 -0.838305 + { 0.999974 0.00723416 0 } + { 0.458824 0.666667 0.729412 1 } + } + 71 { + -1.02178 0 -1.36315 + { 0.999974 0.00723416 0 } + { 0.713726 0.721569 0.788235 1 } + } + 72 { + -1.03253 1.48611 -1.3609 + { 0.999974 0.00723416 0 } + { 0.458824 0.666667 0.729412 1 } + } + 73 { + 0.243412 1.48611 -1.3609 + { -0.00013873 0.000502693 -1 } + { 0.458824 0.776471 0.796078 1 } + } + 74 { + 2.99397 3.00428 -1.3609 + { -0.00013873 0.000502693 -1 } + { 0.686275 0.890196 0.796078 1 } + } + 75 { + 2.99397 0 -1.36315 + { -0.00027746 0.00100539 -0.999999 } + { 0.458824 0.776471 0.796078 1 } + } + 76 { + 0.243417 0 -1.36315 + { -0.00027746 0.00100539 -0.999999 } + { 0.713726 0.721569 0.788235 1 } + } + 77 { + -1.02178 0 -1.36315 + { 0.000327154 0.00100617 -0.999999 } + { 0.713726 0.721569 0.788235 1 } + } + 78 { + -3.36172 0 -1.36315 + { 0.000327154 0.00100617 -0.999999 } + { 0.458824 0.776471 0.796078 1 } + } + 79 { + -3.36172 3.00428 -1.3609 + { 0.000163577 0.000503084 -1 } + { 0.686275 0.890196 0.796078 1 } + } + 80 { + -1.03253 1.48611 -1.3609 + { 0.000163577 0.000503084 -1 } + { 0.458824 0.776471 0.796078 1 } + } + 81 { + 2.99397 0 -3.42013 + { 0 -1.77621e-006 1 } + { 0.458824 0.776471 0.796078 1 } + } + 82 { + 2.99397 3.00428 -3.42012 + { 0 -1.77621e-006 1 } + { 0.686275 0.890196 0.796078 1 } + } + 83 { + -3.36172 3.00428 -3.42012 + { 0 -1.77621e-006 1 } + { 0.686275 0.890196 0.796078 1 } + } + 84 { + -3.36172 0 -3.42013 + { 0 -1.77621e-006 1 } + { 0.458824 0.776471 0.796078 1 } + } + 85 { + -3.36172 3.00428 -1.3609 + { 0 -1 0 } + { 0.686275 0.890196 0.796078 1 } + } + 86 { + -3.36172 3.00428 -3.42012 + { 0 -1 0 } + { 0.686275 0.890196 0.796078 1 } + } + 87 { + 2.99397 3.00428 -3.42012 + { 0 -1 0 } + { 0.686275 0.890196 0.796078 1 } + } + 88 { + 2.99397 3.00428 -1.3609 + { 0 -1 0 } + { 0.686275 0.890196 0.796078 1 } + } + 89 { + 2.99397 0 -3.42013 + { 0 1 0 } + { 0.505882 0.721569 0.788235 1 } + } + 90 { + -3.36172 0 -3.42013 + { 0 1 0 } + { 0.505882 0.721569 0.788235 1 } + } + 91 { + 0.519489 4.88145 0.507012 + { 0 -0.953298 -0.302032 } + { 0.733333 0.462745 0.294118 1 } + } + 92 { + -0.423826 4.88145 0.507012 + { 0 -0.953298 -0.302032 } + { 0.733333 0.462745 0.294118 1 } + } + 93 { + -0.423826 4.88145 0.120042 + { 0 -1 0 } + { 0.733333 0.462745 0.294118 1 } + } + 94 { + 0.519489 4.88145 0.120042 + { 0 -1 0 } + { 0.733333 0.462745 0.294118 1 } + } + 95 { + 0.519489 3.3704 0.502572 + { -1 3.06469e-012 1.07738e-012 } + { 0.733333 0.654902 0.294118 1 } + } + 96 { + 0.519489 4.88145 0.507012 + { -1 3.06469e-012 1.07738e-012 } + { 0.733333 0.654902 0.294118 1 } + } + 97 { + 0.519489 4.88145 0.120042 + { -1 3.06627e-012 0 } + { 0.733333 0.654902 0.294118 1 } + } + 98 { + 0.519489 3.3704 0.115602 + { -1 3.06627e-012 0 } + { 0.733333 0.654902 0.294118 1 } + } + 99 { + 2.99397 1.48611 -0.838305 + { 8.64537e-011 -1 7.27066e-011 } + { 0.729412 0.396078 0.341176 1 } + } + 100 { + 2.99397 1.48611 0.527741 + { 5.96697e-011 -0.983257 0.182222 } + { 0.729412 0.396078 0.341176 1 } + } + 101 { + -4 1.48611 -0.838305 + { 0 -1 0 } + { 0.729412 0.396078 0.341176 1 } + } + 102 { + -2.30748 3.3704 3.18072 + { -0.220612 0.975362 0 } + { 1 1 1 1 } + } + 103 { + -2.30748 3.3704 2.23852 + { -0.146659 0.989187 0 } + { 1 1 1 1 } + } + 104 { + -3.36172 3.3704 3.18072 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 105 { + -0.900963 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 106 { + -0.423812 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 107 { + -3.36172 3.3704 -3.42013 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 108 { + 0.997627 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 109 { + 2.99397 3.3704 -3.42013 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 110 { + 0.519489 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 111 { + 2.99397 5.12958 0.115606 + { 3.51998e-009 -1 -1.29401e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 112 { + 0.997627 5.12958 0.115602 + { 3.53529e-009 -1 -1.28696e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 113 { + 2.99397 5.12958 -3.42013 + { 3.54741e-009 -1 -1.28263e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 114 { + 2.99397 0 -3.42013 + { -1 -1.07371e-007 0 } + { 0.458824 0.776471 0.796078 1 } + } + 115 { + 2.99397 0 -1.36315 + { -1 -1.07371e-007 0 } + { 0.458824 0.776471 0.796078 1 } + } + 116 { + 2.99397 3.00428 -1.3609 + { -1 -1.07371e-007 0 } + { 0.686275 0.890196 0.796078 1 } + } + 117 { + 2.99397 3.00428 -3.42012 + { -1 -1.07371e-007 0 } + { 0.686275 0.890196 0.796078 1 } + } + 118 { + -0.423826 4.88145 0.507012 + { 1 9.25347e-006 3.25304e-006 } + { 0.733333 0.654902 0.294118 1 } + } + 119 { + -0.423812 3.3704 0.502572 + { 1 9.25347e-006 3.25304e-006 } + { 0.733333 0.654902 0.294118 1 } + } + 120 { + -0.423812 3.3704 0.115602 + { 1 9.25824e-006 0 } + { 0.733333 0.654902 0.294118 1 } + } + 121 { + -0.423826 4.88145 0.120042 + { 1 9.25824e-006 0 } + { 0.733333 0.654902 0.294118 1 } + } + 122 { + 2.99397 2.54985 3.18072 + { 0 7.83887e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 123 { + 2.99397 1.03879 3.18072 + { 0 7.83887e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 124 { + 2.04443 1.03879 3.18072 + { 2.85806e-008 7.83886e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 125 { + 2.04443 2.54985 3.18072 + { 2.85806e-008 7.83886e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 126 { + 2.99397 1.48611 0.527741 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 127 { + 2.99397 0 0.527741 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 128 { + 2.99397 1.03879 2.23852 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 129 { + 2.99397 2.54985 2.23852 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 130 { + 2.99397 0 0.527741 + { 0 0.983872 -0.178875 } + { 0.901961 0.654902 0.152941 1 } + } + 131 { + 2.04443 0 0.527741 + { 0 0.983872 -0.178875 } + { 0.901961 0.654902 0.152941 1 } + } + 132 { + 2.04443 1.03879 2.23852 + { 0.212236 0.959168 -0.186958 } + { 0.901961 0.654902 0.152941 1 } + } + 133 { + 2.99397 1.03879 2.23852 + { 0 0.963007 -0.269477 } + { 0.901961 0.654902 0.152941 1 } + } + 134 { + 2.04443 0 0.527741 + { 1 -1.48049e-006 9.09752e-007 } + { 0.733333 0.654902 0.294118 1 } + } + 135 { + 2.04443 1.48611 0.527741 + { 1 -1.48049e-006 9.09752e-007 } + { 0.733333 0.654902 0.294118 1 } + } + 136 { + 2.04443 2.54985 2.23852 + { 1 -1.48049e-006 9.09752e-007 } + { 0.733333 0.654902 0.294118 1 } + } + 137 { + 2.04443 1.03879 2.23852 + { 1 -1.48049e-006 9.09752e-007 } + { 0.733333 0.654902 0.294118 1 } + } + 138 { + 2.04443 1.48611 0.527741 + { 0 -0.983257 0.182222 } + { 0.733333 0.462745 0.294118 1 } + } + 139 { + 2.99397 1.48611 0.527741 + { 5.96697e-011 -0.983257 0.182222 } + { 0.733333 0.462745 0.294118 1 } + } + 140 { + 2.99397 2.54985 2.23852 + { 0 -0.961568 0.274569 } + { 0.733333 0.462745 0.294118 1 } + } + 141 { + 2.04443 2.54985 2.23852 + { -0.212513 -0.958418 0.190453 } + { 0.733333 0.462745 0.294118 1 } + } + 142 { + 2.99397 1.03879 3.18072 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 143 { + 2.99397 2.54985 3.18072 + { -1 0 0 } + { 0.733333 0.654902 0.294118 1 } + } + 144 { + 2.04443 1.03879 3.18072 + { 0.309844 0.950787 0 } + { 0.901961 0.654902 0.152941 1 } + } + 145 { + 2.99397 1.03879 3.18072 + { 0 1 0 } + { 0.901961 0.654902 0.152941 1 } + } + 146 { + -0.423812 2.15079 2.23852 + { 1 9.25347e-006 3.25304e-006 } + { 0.733333 0.654902 0.294118 1 } + } + 147 { + -0.423826 3.66184 2.23852 + { 1 9.25347e-006 3.25304e-006 } + { 0.733333 0.654902 0.294118 1 } + } + 148 { + -0.423826 3.66184 3.18072 + { 1 9.25824e-006 0 } + { 0.733333 0.654902 0.294118 1 } + } + 149 { + -0.423812 2.15079 3.18072 + { 1 9.25824e-006 0 } + { 0.733333 0.654902 0.294118 1 } + } + 150 { + 2.99397 2.54985 3.18072 + { 0 -1 0 } + { 0.733333 0.462745 0.294118 1 } + } + 151 { + 2.04443 2.54985 3.18072 + { -0.309844 -0.950787 0 } + { 0.733333 0.462745 0.294118 1 } + } + 152 { + 2.04443 1.03879 2.23852 + { 7.18913e-009 9.85887e-009 1 } + { 0.733333 0.654902 0.294118 1 } + } + 153 { + 2.04443 2.54985 2.23852 + { 7.18913e-009 9.85887e-009 1 } + { 0.733333 0.654902 0.294118 1 } + } + 154 { + 0.519489 3.66184 2.23852 + { 7.18913e-009 9.85887e-009 1 } + { 0.733333 0.654902 0.294118 1 } + } + 155 { + 0.519489 2.15079 2.23852 + { 7.18913e-009 9.85887e-009 1 } + { 0.733333 0.654902 0.294118 1 } + } + 156 { + 0.519489 3.66184 3.18072 + { -0.309844 -0.950787 0 } + { 0.733333 0.462745 0.294118 1 } + } + 157 { + 0.519489 3.66184 2.23852 + { -0.214114 -0.95413 -0.209266 } + { 0.733333 0.462745 0.294118 1 } + } + 158 { + 0.519489 2.15079 3.18072 + { 2.85806e-008 7.83886e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 159 { + 0.519489 3.66184 3.18072 + { 2.85806e-008 7.83886e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 160 { + 0.519489 2.15079 2.23852 + { 0.214078 0.954223 0.208873 } + { 0.901961 0.654902 0.152941 1 } + } + 161 { + 0.519489 2.15079 3.18072 + { 0.309844 0.950787 0 } + { 0.901961 0.654902 0.152941 1 } + } + 162 { + -0.423812 3.3704 0.502572 + { 0 0.95348 0.301457 } + { 0.901961 0.654902 0.152941 1 } + } + 163 { + 0.519489 3.3704 0.502572 + { 0 0.95348 0.301457 } + { 0.901961 0.654902 0.152941 1 } + } + 164 { + 0.519489 3.3704 0.115602 + { 0 1 0 } + { 0.901961 0.654902 0.152941 1 } + } + 165 { + -0.423812 3.3704 0.115602 + { 0 1 0 } + { 0.901961 0.654902 0.152941 1 } + } + 166 { + -0.423826 3.66184 3.18072 + { 0 -1 0 } + { 0.733333 0.462745 0.294118 1 } + } + 167 { + -0.423826 3.66184 2.23852 + { 0 -0.953298 -0.302032 } + { 0.733333 0.462745 0.294118 1 } + } + 168 { + -0.423812 2.15079 3.18072 + { 0 7.83887e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 169 { + -0.423826 3.66184 3.18072 + { 0 7.83887e-008 -1 } + { 0.733333 0.654902 0.294118 1 } + } + 170 { + -0.423812 2.15079 2.23852 + { 0 0.95348 0.301457 } + { 0.901961 0.654902 0.152941 1 } + } + 171 { + -0.423812 2.15079 3.18072 + { 0 1 0 } + { 0.901961 0.654902 0.152941 1 } + } + 172 { + 0.519489 2.15079 2.23852 + { -1 3.06311e-012 2.15477e-012 } + { 0.733333 0.654902 0.294118 1 } + } + 173 { + 0.519489 3.66184 2.23852 + { -1 3.06311e-012 2.15477e-012 } + { 0.733333 0.654902 0.294118 1 } + } + 174 { + -3.36172 3.00428 -3.42012 + { 1 -6.80267e-009 0 } + { 0.686275 0.890196 0.796078 1 } + } + 175 { + -3.36172 3.00428 -1.3609 + { 1 -6.80267e-009 0 } + { 0.686275 0.890196 0.796078 1 } + } + 176 { + -3.36172 0 -1.36315 + { 1 -6.80267e-009 0 } + { 0.458824 0.776471 0.796078 1 } + } + 177 { + -3.36172 0 -3.42013 + { 1 -6.80267e-009 0 } + { 0.458824 0.776471 0.796078 1 } + } + 178 { + -2.36149 7.09573 -2.49425 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 179 { + -3.36172 7.09573 -3.42013 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 180 { + -0.485624 7.09573 -3.42013 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 181 { + -0.485624 7.09573 -2.49425 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 182 { + 2.99397 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 183 { + 0.997627 3.3704 3.18072 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 184 { + 2.99397 3.3704 3.18072 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 185 { + -1.90136 7.09573 -2.16895 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 186 { + 1.31869 7.09573 -0.718624 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 187 { + 1.31869 7.09573 0.350091 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 188 { + -1.90136 7.09573 1.0558 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 189 { + -3.36172 3.3704 0.115602 + { 0 1 0 } + { 0.733333 0.658824 0.760784 1 } + } + 190 { + -2.30748 3.3704 2.05045 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 191 { + -0.900963 3.3704 2.04979 + { 0 1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 192 { + 1.82483 5.34053 3.18072 + { -0.220612 0.975362 0 } + { 0.752941 0.752941 0.752941 1 } + } + 193 { + 1.82483 5.34053 2.23852 + { -0.146659 0.989187 0 } + { 0.752941 0.752941 0.752941 1 } + } + 194 { + 2.99397 5.34053 3.18072 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 195 { + 2.99397 5.34053 2.23852 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 196 { + 1.82483 5.34053 0.350091 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 197 { + 2.99397 7.09573 -3.42013 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 198 { + 1.82483 7.09573 -2.49425 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 199 { + -2.36149 7.09573 1.0558 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 200 { + -3.36172 7.09573 2.04979 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 201 { + 1.82483 5.34053 -0.718624 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 202 { + 1.31869 5.34053 -0.718624 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 203 { + 1.31869 5.34053 0.350091 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 204 { + -2.36149 5.34053 1.0558 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 205 { + -2.36149 5.34053 2.04979 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 206 { + -1.90136 5.34053 2.04979 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 207 { + -1.90136 5.34053 1.0558 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 208 { + 2.99397 3.3704 -3.42013 + { -4.07833e-010 -6.17821e-011 1 } + { 0.733333 0.658824 0.760784 1 } + } + 209 { + 2.99397 5.12958 -3.42013 + { -4.07833e-010 -6.17821e-011 1 } + { 0.376471 0.498039 0.760784 1 } + } + 210 { + -3.36172 5.12958 -3.42013 + { -4.07833e-010 -6.17821e-011 1 } + { 0.376471 0.498039 0.760784 1 } + } + 211 { + -3.36172 3.3704 -3.42013 + { -4.07833e-010 -6.17821e-011 1 } + { 0.733333 0.658824 0.760784 1 } + } + 212 { + 2.99397 5.12958 1.67403 + { 3.51106e-009 -1 -1.29564e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 213 { + 0.997673 5.12958 1.67402 + { 3.51106e-009 -1 -1.29564e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 214 { + 2.99397 3.3704 -3.42013 + { -1 -8.366e-011 9.98999e-012 } + { 0.733333 0.658824 0.760784 1 } + } + 215 { + 2.99397 3.3704 0.115602 + { -1 6.51735e-011 6.38546e-011 } + { 0.733333 0.658824 0.760784 1 } + } + 216 { + 2.99397 5.12958 0.115606 + { -1 9.77602e-011 9.57819e-011 } + { 0.376471 0.498039 0.760784 1 } + } + 217 { + 2.99397 5.12958 -3.42013 + { -1 -8.366e-011 9.98999e-012 } + { 0.376471 0.498039 0.760784 1 } + } + 218 { + -3.36172 3.3704 0.115602 + { 1 2.60413e-011 1.94624e-012 } + { 0.733333 0.658824 0.760784 1 } + } + 219 { + -3.36172 3.3704 -3.42013 + { 1 2.23911e-011 1.9372e-012 } + { 0.733333 0.658824 0.760784 1 } + } + 220 { + -3.36172 5.12958 -3.42013 + { 1 2.23911e-011 1.9372e-012 } + { 0.376471 0.498039 0.760784 1 } + } + 221 { + -3.36172 5.12958 0.115593 + { 1 2.60413e-011 1.94624e-012 } + { 0.376471 0.498039 0.760784 1 } + } + 222 { + 2.99397 4.51665 1.67402 + { -1 1.3959e-010 9.07869e-011 } + { 0.733333 0.462745 0.760784 1 } + } + 223 { + 2.99397 5.12958 1.67403 + { -1 2.7918e-010 1.81574e-010 } + { 0.376471 0.498039 0.760784 1 } + } + 224 { + -3.36172 5.12958 0.115593 + { 3.60372e-009 -1 -1.29603e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 225 { + -2.30746 5.12958 2.05044 + { 3.60957e-009 -1 -1.29785e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 226 { + -2.30746 5.12958 2.23852 + { 0.146407 -0.989224 -1.50609e-007 } + { 0.376471 0.498039 0.760784 1 } + } + 227 { + -3.36172 5.12958 3.18072 + { 3.62962e-009 -1 -1.30501e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 228 { + 2.99397 3.3704 3.18072 + { 0 0 -1 } + { 0.733333 0.658824 0.760784 1 } + } + 229 { + 0.997627 3.3704 3.18072 + { 0 0 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 230 { + 0.997627 4.51665 3.18072 + { 0 0 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 231 { + 2.99397 4.51665 3.18072 + { 0 0 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 232 { + -3.36172 5.12958 3.18072 + { 1 2.96915e-011 1.95529e-012 } + { 0.376471 0.498039 0.760784 1 } + } + 233 { + -3.36172 3.3704 3.18072 + { 1 2.96915e-011 1.95529e-012 } + { 0.733333 0.658824 0.760784 1 } + } + 234 { + 0.997627 3.3704 3.18072 + { 1 0 0 } + { 0.733333 0.462745 0.760784 1 } + } + 235 { + 0.997627 3.3704 0.115602 + { 1 -9.56319e-006 -3.85741e-006 } + { 0.733333 0.462745 0.760784 1 } + } + 236 { + 0.997627 4.51665 1.67402 + { 1 -9.56319e-006 -3.85741e-006 } + { 0.733333 0.462745 0.760784 1 } + } + 237 { + 0.997627 4.51665 3.18072 + { 1 0 0 } + { 0.733333 0.462745 0.760784 1 } + } + 238 { + -2.30748 3.3704 3.18072 + { 4.42741e-011 -1.09614e-010 -1 } + { 1 1 1 1 } + } + 239 { + -3.36172 3.3704 3.18072 + { 2.75919e-011 -1.51613e-010 -1 } + { 0.733333 0.658824 0.760784 1 } + } + 240 { + -3.36172 5.12958 3.18072 + { 2.75919e-011 -1.51613e-010 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 241 { + -2.30746 5.12958 3.18072 + { 4.42741e-011 -1.09614e-010 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 242 { + 1.82483 5.34053 3.18072 + { 3.04782e-011 -3.38078e-011 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 243 { + 1.82483 7.09573 3.18072 + { 3.04782e-011 -3.38078e-011 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 244 { + 2.99397 5.34053 3.18072 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 245 { + 2.99397 7.09573 3.18072 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 246 { + 2.99397 5.34053 2.23852 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 247 { + 2.99397 5.34053 3.18072 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 248 { + 2.99397 7.09573 3.18072 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 249 { + 2.99397 7.09573 2.23852 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 250 { + 2.99397 5.34053 -3.42013 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 251 { + 2.99397 7.09573 -3.42013 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 252 { + 2.99397 5.34053 -3.42013 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 253 { + 2.99397 7.09573 -3.42013 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 254 { + -0.485624 7.09573 -3.42013 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 255 { + -0.485624 5.34053 -3.42013 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 256 { + -3.36172 5.34053 -3.42013 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 257 { + -2.36149 5.34053 -2.49425 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 258 { + -0.485624 5.34053 -2.49425 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 259 { + -0.485624 5.34053 -3.42013 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 260 { + -3.36172 5.34053 -3.42013 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 261 { + -3.36172 7.09573 -3.42013 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 262 { + -3.36172 7.09573 2.04979 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 263 { + -3.36172 5.34053 2.04979 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 264 { + -3.36172 5.34053 2.04979 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 265 { + -2.36149 5.34053 2.04979 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 266 { + -3.36172 5.34053 2.04979 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 267 { + -3.36172 7.09573 2.04979 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 268 { + -2.36149 7.09573 2.04979 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 269 { + -1.90136 5.34053 2.04979 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 270 { + -1.90136 7.09573 2.04979 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 271 { + -0.900963 3.3704 0.115602 + { -1 7.02332e-006 1.01878e-006 } + { 0.733333 0.462745 0.760784 1 } + } + 272 { + -0.900963 3.3704 2.04979 + { -1 7.02332e-006 1.01878e-006 } + { 0.733333 0.462745 0.760784 1 } + } + 273 { + -0.900949 5.12958 2.04979 + { -1 7.02332e-006 1.01878e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 274 { + -0.900953 5.12958 0.115599 + { -1 7.02332e-006 1.01878e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 275 { + -0.900963 3.3704 2.04979 + { -0.000468015 -2.28319e-006 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 276 { + -2.30748 3.3704 2.05045 + { -0.000468015 -2.28319e-006 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 277 { + -2.30746 5.12958 2.05044 + { -0.000468015 -2.28319e-006 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 278 { + -0.900949 5.12958 2.04979 + { -0.000468015 -2.28319e-006 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 279 { + -2.30748 3.3704 2.05045 + { -1 1.2768e-005 1.17008e-006 } + { 0.733333 0.462745 0.760784 1 } + } + 280 { + -2.30748 3.3704 2.23852 + { -1 1.2768e-005 1.17008e-006 } + { 1 1 1 1 } + } + 281 { + -2.30746 5.12958 2.23852 + { -1 1.2768e-005 1.17008e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 282 { + -2.30746 5.12958 2.05044 + { -1 1.2768e-005 1.17008e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 283 { + -2.30748 3.3704 2.23852 + { 7.06683e-012 -7.83886e-012 1 } + { 1 1 1 1 } + } + 284 { + 1.82483 5.34053 2.23852 + { 7.06683e-012 -7.83886e-012 1 } + { 0.752941 0.752941 0.752941 1 } + } + 285 { + 1.82483 7.09573 2.23852 + { 7.06683e-012 -7.83886e-012 1 } + { 0.482353 0.560784 0.611765 1 } + } + 286 { + -2.30746 5.12958 2.23852 + { 7.06683e-012 -7.83886e-012 1 } + { 0.376471 0.498039 0.760784 1 } + } + 287 { + 1.82483 5.34053 2.23852 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 288 { + 1.82483 5.34053 0.350091 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 289 { + 1.82483 7.09573 0.350091 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 290 { + 1.82483 7.09573 2.23852 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 291 { + -0.423826 4.88145 0.120042 + { 0.00250514 -0.00827025 -0.999963 } + { 0.733333 0.462745 0.760784 1 } + } + 292 { + -0.900953 5.12958 0.115599 + { 0.00250514 -0.00827025 -0.999963 } + { 0.376471 0.498039 0.760784 1 } + } + 293 { + 0.997627 5.12958 0.115602 + { -0.00249668 -0.00826966 -0.999963 } + { 0.376471 0.498039 0.760784 1 } + } + 294 { + 0.519489 4.88145 0.120042 + { -0.00249668 -0.00826966 -0.999963 } + { 0.733333 0.462745 0.760784 1 } + } + 295 { + 0.997627 3.3704 0.115602 + { -0.00499447 0.00135754 -0.999987 } + { 0.733333 0.462745 0.760784 1 } + } + 296 { + 0.519489 3.3704 0.115602 + { -0.00499447 0.00135754 -0.999987 } + { 0.733333 0.462745 0.760784 1 } + } + 297 { + -0.423812 3.3704 0.115602 + { 0.00500867 0.00135636 -0.999987 } + { 0.733333 0.462745 0.760784 1 } + } + 298 { + -0.900963 3.3704 0.115602 + { 0.00500867 0.00135636 -0.999987 } + { 0.733333 0.462745 0.760784 1 } + } + 299 { + 2.99397 3.3704 3.18072 + { -1 0 0 } + { 0.733333 0.658824 0.760784 1 } + } + 300 { + 2.99397 4.51665 3.18072 + { -1 0 0 } + { 0.733333 0.462745 0.760784 1 } + } + 301 { + 0.997627 4.51665 3.18072 + { 0 -1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 302 { + 0.997627 4.51665 1.67402 + { 0 -1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 303 { + 2.99397 4.51665 1.67402 + { 0 -1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 304 { + 2.99397 4.51665 3.18072 + { 0 -1 0 } + { 0.733333 0.462745 0.760784 1 } + } + 305 { + -0.900953 5.12958 0.115599 + { 3.58039e-009 -1 -1.28204e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 306 { + -3.36172 5.12958 -3.42013 + { 3.57897e-009 -1 -1.28263e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 307 { + -2.36149 5.34053 -2.49425 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 308 { + -2.36149 7.09573 -2.49425 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 309 { + -0.485624 7.09573 -2.49425 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 310 { + -0.485624 5.34053 -2.49425 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 311 { + 0.997627 5.12958 0.115602 + { 1 -1.91264e-005 -7.71482e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 312 { + 0.997673 5.12958 1.67402 + { 1 -1.91264e-005 -7.71482e-006 } + { 0.376471 0.498039 0.760784 1 } + } + 313 { + 2.99397 4.51665 1.67402 + { 3.17248e-006 2.8011e-006 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 314 { + 0.997627 4.51665 1.67402 + { 3.17248e-006 2.8011e-006 -1 } + { 0.733333 0.462745 0.760784 1 } + } + 315 { + 0.997673 5.12958 1.67402 + { 3.17248e-006 2.8011e-006 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 316 { + 2.99397 5.12958 1.67403 + { 3.17248e-006 2.8011e-006 -1 } + { 0.376471 0.498039 0.760784 1 } + } + 317 { + -0.900949 5.12958 2.04979 + { 3.58324e-009 -1 -1.28086e-009 } + { 0.376471 0.498039 0.760784 1 } + } + 318 { + -3.36172 0 -1.36315 + { 0 1 0 } + { 0.505882 0.721569 0.788235 1 } + } + 319 { + 2.99397 0 -1.36315 + { 0 1 0 } + { 0.505882 0.721569 0.788235 1 } + } + 320 { + 1.82483 7.09573 2.23852 + { 0.146407 -0.989224 -1.4972e-007 } + { 0.482353 0.560784 0.611765 1 } + } + 321 { + 1.82483 7.09573 3.18072 + { 0.220231 -0.975448 -2.25215e-007 } + { 0.482353 0.560784 0.611765 1 } + } + 322 { + -2.30746 5.12958 3.18072 + { 0.220231 -0.975448 -2.25879e-007 } + { 0.376471 0.498039 0.760784 1 } + } + 323 { + 2.99397 7.09573 2.23852 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 324 { + 2.99397 7.09573 3.18072 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 325 { + 1.82483 7.09573 0.350091 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 326 { + 1.82483 7.09573 -0.718624 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 327 { + -1.90136 5.34053 -2.16895 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 328 { + 1.82483 5.34053 -0.718624 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 329 { + 1.82483 5.34053 -2.49425 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 330 { + 1.82483 7.09573 -2.49425 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 331 { + 1.82483 7.09573 -0.718624 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 332 { + 1.82483 5.34053 -2.49425 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 333 { + 1.82483 7.09573 -2.49425 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 334 { + -2.36149 5.34053 -2.49425 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 335 { + -2.36149 5.34053 1.0558 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 336 { + -2.36149 7.09573 1.0558 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 337 { + -2.36149 7.09573 -2.49425 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 338 { + 1.31869 5.34053 -2.1928 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 339 { + 1.31869 5.34053 2.04979 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 340 { + 1.31869 5.34053 0.350091 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 341 { + 1.31869 5.34053 2.04979 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 342 { + 1.31869 7.09573 2.04979 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 343 { + 1.31869 7.09573 0.350091 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 344 { + 1.31869 5.34053 0.350091 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 345 { + 1.31869 7.09573 0.350091 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 346 { + 1.82483 7.09573 0.350091 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 347 { + 1.82483 5.34053 0.350091 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 348 { + 1.31869 7.09573 2.04979 + { 0 0 -1 } + { 0.482353 0.560784 0.611765 1 } + } + 349 { + 1.31869 5.34053 2.04979 + { 0 0 -1 } + { 0.752941 0.752941 0.752941 1 } + } + 350 { + 1.31869 5.34053 -2.1928 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 351 { + 1.31869 5.34053 -0.718624 + { -1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 352 { + 1.31869 7.09573 -0.718624 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 353 { + 1.31869 7.09573 -2.1928 + { -1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 354 { + -1.90136 5.34053 -2.16895 + { 0.00740377 0 0.999973 } + { 0.752941 0.752941 0.752941 1 } + } + 355 { + 1.31869 5.34053 -2.1928 + { 0.00740377 0 0.999973 } + { 0.752941 0.752941 0.752941 1 } + } + 356 { + 1.31869 7.09573 -2.1928 + { 0.00740377 0 0.999973 } + { 0.482353 0.560784 0.611765 1 } + } + 357 { + -1.90136 7.09573 -2.16895 + { 0.00740377 0 0.999973 } + { 0.482353 0.560784 0.611765 1 } + } + 358 { + -1.90136 5.34053 1.0558 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 359 { + -1.90136 5.34053 -2.16895 + { 1 0 0 } + { 0.752941 0.752941 0.752941 1 } + } + 360 { + -1.90136 7.09573 -2.16895 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 361 { + -1.90136 7.09573 1.0558 + { 1 0 0 } + { 0.482353 0.560784 0.611765 1 } + } + 362 { + -1.90136 5.34053 1.0558 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 363 { + -1.90136 7.09573 1.0558 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 364 { + -2.36149 7.09573 1.0558 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 365 { + -2.36149 5.34053 1.0558 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 366 { + 1.31869 7.09573 -0.718624 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 367 { + 1.31869 5.34053 -0.718624 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 368 { + 1.82483 5.34053 -0.718624 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 369 { + 1.82483 7.09573 -0.718624 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 370 { + -3.36172 7.09573 -3.42013 + { 0 0 1 } + { 0.482353 0.560784 0.611765 1 } + } + 371 { + -3.36172 5.34053 -3.42013 + { 0 0 1 } + { 0.752941 0.752941 0.752941 1 } + } + 372 { + 1.31869 7.09573 -2.1928 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 373 { + -1.90136 7.09573 2.04979 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 374 { + 1.31869 7.09573 2.04979 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 375 { + -2.36149 7.09573 2.04979 + { 0 -1 0 } + { 0.482353 0.560784 0.611765 1 } + } + 376 { + 2.99397 5.34053 -3.42013 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 377 { + 1.82483 5.34053 -2.49425 + { 0 1 0 } + { 0.752941 0.752941 0.752941 1 } + } + 378 { + 4 0 -0.838305 + { 2.94739e-009 -4.60849e-010 1 } + { 0.729412 0.513726 0.341176 1 } + } + 379 { + 4 1.48611 -0.838305 + { 2.94739e-009 -4.60849e-010 1 } + { 0.729412 0.513726 0.341176 1 } + } + 380 { + 2.99397 0 -0.838305 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 381 { + 2.99397 0 0.527741 + { 0 0.983872 -0.178875 } + { 0.729412 0.192157 0.247059 1 } + } + 382 { + 4 0 0.527741 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 383 { + 4 0 -0.838305 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 384 { + 4 1.48611 0.527741 + { -1.20596e-007 -9.54098e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 385 { + 4 0 0.527741 + { -1.20596e-007 -9.54098e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 386 { + 2.99397 0 0.527741 + { -1.20596e-007 -9.54098e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 387 { + 2.99397 1.48611 0.527741 + { -1.20596e-007 -9.54098e-009 -1 } + { 0.729412 0.513726 0.341176 1 } + } + 388 { + 4 1.48611 -0.838305 + { 1.72907e-010 -1 1.45424e-010 } + { 0.729412 0.396078 0.341176 1 } + } + 389 { + 4 1.48611 0.527741 + { 1.72907e-010 -1 1.45424e-010 } + { 0.729412 0.396078 0.341176 1 } + } + 390 { + -4 0 -0.838305 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 391 { + 4 0 0.527741 + { 0 1 0 } + { 0.376471 0.498039 0 1 } + } + 392 { + 0.243417 0 -0.838305 + { 0 1 0 } + { 0.729412 0.192157 0.247059 1 } + } + 393 { + 2.04443 0 0.527741 + { 0 0.983872 -0.178875 } + { 0.729412 0.192157 0.247059 1 } + } + } + { + { 0 1 2 3 { levelgeo-ORG } } + } + { + { 4 5 6 7 { levelgeo-ORG } } + } + { + { 8 9 10 11 { levelgeo-ORG } } + } + { + { 12 13 14 15 { levelgeo-ORG } } + } + { + { 16 17 18 19 { levelgeo-ORG } } + } + { + { 20 21 22 23 { levelgeo-ORG } } + } + { + { 21 24 22 { levelgeo-ORG } } + } + { + { 25 26 27 { levelgeo-ORG } } + } + { + { 28 25 27 29 { levelgeo-ORG } } + } + { + { 30 31 32 33 { levelgeo-ORG } } + } + { + { 11 10 32 31 { levelgeo-ORG } } + } + { + { 34 35 36 37 { levelgeo-ORG } } + } + { + { 38 39 40 41 { levelgeo-ORG } } + } + { + { 42 43 44 { levelgeo-ORG } } + } + { + { 45 46 47 48 { levelgeo-ORG } } + } + { + { 49 50 51 52 { levelgeo-ORG } } + } + { + { 53 54 55 56 { levelgeo-ORG } } + } + { + { 57 58 59 60 { levelgeo-ORG } } + } + { + { 61 62 63 64 { levelgeo-ORG } } + } + { + { 65 66 67 68 { levelgeo-ORG } } + } + { + { 69 70 71 72 { levelgeo-ORG } } + } + { + { 73 74 75 76 { levelgeo-ORG } } + } + { + { 77 78 79 80 { levelgeo-ORG } } + } + { + { 81 82 83 84 { levelgeo-ORG } } + } + { + { 85 86 87 88 { levelgeo-ORG } } + } + { + { 80 79 74 73 { levelgeo-ORG } } + } + { + { 59 89 90 60 { levelgeo-ORG } } + } + { + { 91 92 93 94 { levelgeo-ORG } } + } + { + { 95 96 97 98 { levelgeo-ORG } } + } + { + { 99 100 52 51 { levelgeo-ORG } } + } + { + { 101 50 49 { levelgeo-ORG } } + } + { + { 102 103 104 { levelgeo-ORG } } + } + { + { 105 106 107 { levelgeo-ORG } } + } + { + { 108 109 110 { levelgeo-ORG } } + } + { + { 111 112 113 { levelgeo-ORG } } + } + { + { 114 115 116 117 { levelgeo-ORG } } + } + { + { 118 119 120 121 { levelgeo-ORG } } + } + { + { 122 123 124 125 { levelgeo-ORG } } + } + { + { 126 127 128 129 { levelgeo-ORG } } + } + { + { 130 131 132 133 { levelgeo-ORG } } + } + { + { 134 135 136 137 { levelgeo-ORG } } + } + { + { 138 139 140 141 { levelgeo-ORG } } + } + { + { 129 128 142 143 { levelgeo-ORG } } + } + { + { 133 132 144 145 { levelgeo-ORG } } + } + { + { 146 147 148 149 { levelgeo-ORG } } + } + { + { 141 140 150 151 { levelgeo-ORG } } + } + { + { 152 153 154 155 { levelgeo-ORG } } + } + { + { 141 151 156 157 { levelgeo-ORG } } + } + { + { 125 124 158 159 { levelgeo-ORG } } + } + { + { 144 132 160 161 { levelgeo-ORG } } + } + { + { 162 163 164 165 { levelgeo-ORG } } + } + { + { 157 156 166 167 { levelgeo-ORG } } + } + { + { 159 158 168 169 { levelgeo-ORG } } + } + { + { 161 160 170 171 { levelgeo-ORG } } + } + { + { 172 173 96 95 { levelgeo-ORG } } + } + { + { 157 167 92 91 { levelgeo-ORG } } + } + { + { 147 146 119 118 { levelgeo-ORG } } + } + { + { 170 160 163 162 { levelgeo-ORG } } + } + { + { 174 175 176 177 { levelgeo-ORG } } + } + { + { 110 109 107 106 { levelgeo-ORG } } + } + { + { 178 179 180 181 { levelgeo-ORG } } + } + { + { 182 108 183 184 { levelgeo-ORG } } + } + { + { 185 186 187 188 { levelgeo-ORG } } + } + { + { 189 190 191 105 { levelgeo-ORG } } + } + { + { 103 190 189 104 { levelgeo-ORG } } + } + { + { 103 102 192 193 { levelgeo-ORG } } + } + { + { 193 192 194 195 { levelgeo-ORG } } + } + { + { 193 195 196 { levelgeo-ORG } } + } + { + { 197 198 181 180 { levelgeo-ORG } } + } + { + { 179 178 199 200 { levelgeo-ORG } } + } + { + { 196 201 202 203 { levelgeo-ORG } } + } + { + { 204 205 206 207 { levelgeo-ORG } } + } + { + { 208 209 210 211 { levelgeo-ORG } } + } + { + { 182 109 108 { levelgeo-ORG } } + } + { + { 112 111 212 213 { levelgeo-ORG } } + } + { + { 214 215 216 217 { levelgeo-ORG } } + } + { + { 218 219 220 221 { levelgeo-ORG } } + } + { + { 215 222 223 216 { levelgeo-ORG } } + } + { + { 224 225 226 227 { levelgeo-ORG } } + } + { + { 228 229 230 231 { levelgeo-ORG } } + } + { + { 218 221 232 233 { levelgeo-ORG } } + } + { + { 234 235 236 237 { levelgeo-ORG } } + } + { + { 238 239 240 241 { levelgeo-ORG } } + } + { + { 242 238 241 243 { levelgeo-ORG } } + } + { + { 244 242 243 245 { levelgeo-ORG } } + } + { + { 246 247 248 249 { levelgeo-ORG } } + } + { + { 250 246 249 251 { levelgeo-ORG } } + } + { + { 252 253 254 255 { levelgeo-ORG } } + } + { + { 256 257 258 259 { levelgeo-ORG } } + } + { + { 260 261 262 263 { levelgeo-ORG } } + } + { + { 264 204 257 256 { levelgeo-ORG } } + } + { + { 265 266 267 268 { levelgeo-ORG } } + } + { + { 269 265 268 270 { levelgeo-ORG } } + } + { + { 271 272 273 274 { levelgeo-ORG } } + } + { + { 275 276 277 278 { levelgeo-ORG } } + } + { + { 279 280 281 282 { levelgeo-ORG } } + } + { + { 283 284 285 286 { levelgeo-ORG } } + } + { + { 287 288 289 290 { levelgeo-ORG } } + } + { + { 291 292 293 294 { levelgeo-ORG } } + } + { + { 293 295 296 294 { levelgeo-ORG } } + } + { + { 291 297 298 292 { levelgeo-ORG } } + } + { + { 205 204 264 { levelgeo-ORG } } + } + { + { 299 300 222 215 { levelgeo-ORG } } + } + { + { 301 302 303 304 { levelgeo-ORG } } + } + { + { 113 112 305 306 { levelgeo-ORG } } + } + { + { 307 308 309 310 { levelgeo-ORG } } + } + { + { 311 312 236 235 { levelgeo-ORG } } + } + { + { 313 314 315 316 { levelgeo-ORG } } + } + { + { 306 305 224 { levelgeo-ORG } } + } + { + { 224 305 317 225 { levelgeo-ORG } } + } + { + { 318 60 90 { levelgeo-ORG } } + } + { + { 319 89 59 { levelgeo-ORG } } + } + { + { 226 320 321 322 { levelgeo-ORG } } + } + { + { 323 324 321 320 { levelgeo-ORG } } + } + { + { 323 325 326 197 { levelgeo-ORG } } + } + { + { 202 327 207 203 { levelgeo-ORG } } + } + { + { 328 329 330 331 { levelgeo-ORG } } + } + { + { 332 310 309 333 { levelgeo-ORG } } + } + { + { 334 335 336 337 { levelgeo-ORG } } + } + { + { 338 327 202 { levelgeo-ORG } } + } + { + { 206 339 203 207 { levelgeo-ORG } } + } + { + { 340 341 342 343 { levelgeo-ORG } } + } + { + { 344 345 346 347 { levelgeo-ORG } } + } + { + { 348 349 269 270 { levelgeo-ORG } } + } + { + { 350 351 352 353 { levelgeo-ORG } } + } + { + { 354 355 356 357 { levelgeo-ORG } } + } + { + { 358 359 360 361 { levelgeo-ORG } } + } + { + { 362 363 364 365 { levelgeo-ORG } } + } + { + { 366 367 368 369 { levelgeo-ORG } } + } + { + { 325 187 186 326 { levelgeo-ORG } } + } + { + { 189 105 107 { levelgeo-ORG } } + } + { + { 370 371 255 254 { levelgeo-ORG } } + } + { + { 197 326 198 { levelgeo-ORG } } + } + { + { 372 186 185 { levelgeo-ORG } } + } + { + { 373 188 187 374 { levelgeo-ORG } } + } + { + { 320 325 323 { levelgeo-ORG } } + } + { + { 199 188 373 375 { levelgeo-ORG } } + } + { + { 200 199 375 { levelgeo-ORG } } + } + { + { 376 201 196 195 { levelgeo-ORG } } + } + { + { 377 201 376 { levelgeo-ORG } } + } + { + { 37 36 13 12 { levelgeo-ORG } } + } + { + { 378 379 54 53 { levelgeo-ORG } } + } + { + { 380 381 382 383 { levelgeo-ORG } } + } + { + { 384 385 386 387 { levelgeo-ORG } } + } + { + { 100 99 388 389 { levelgeo-ORG } } + } + { + { 390 23 27 26 { levelgeo-ORG } } + } + { + { 20 23 390 { levelgeo-ORG } } + } + { + { 391 29 22 24 { levelgeo-ORG } } + } + { + { 28 29 391 { levelgeo-ORG } } + } + { + { 381 380 392 393 { levelgeo-ORG } } + } + { + { 393 392 44 43 { levelgeo-ORG } } + } + { + { 377 376 259 258 { levelgeo-ORG } } + } + { + { 322 227 226 { levelgeo-ORG } } + } +} diff --git a/samples/culling/models/occluders.egg b/samples/culling/models/occluders.egg new file mode 100644 index 0000000000..e50e82550c --- /dev/null +++ b/samples/culling/models/occluders.egg @@ -0,0 +1,562 @@ + { Y-Up } + + occluder1 { + occluder { 1 } + occluder1-ORG { + 0 { + 4 7.57015 4 + } + 1 { + 4 7.57015 -4 + } + 2 { + -4 7.57015 -4 + } + 3 { + -4 7.57015 4 + } + } + { + { 0 1 2 3 { occluder1-ORG } } + } +} + occluder4 { + occluder { 1 } + occluder4-ORG { + 0 { + 4 2.96946e-016 -0.84965 + } + 1 { + 4 7.57015 -0.84965 + } + 2 { + 4 7.57015 -4 + } + 3 { + 4 0 -4 + } + } + { + { 0 1 2 3 { occluder4-ORG } } + } +} + occluder2 { + occluder { 1 } + occluder2-ORG { + 0 { + 4 0 -4 + } + 1 { + 4 7.57015 -4 + } + 2 { + -4 7.57015 -4 + } + 3 { + -4 0 -4 + } + } + { + { 0 1 2 3 { occluder2-ORG } } + } +} + occluder3 { + occluder { 1 } + occluder3-ORG { + 0 { + 4 0 4 + } + 1 { + 4 7.57015 4 + } + 2 { + -4 7.57015 4 + } + 3 { + -4 0 4 + } + } + { + { 0 1 2 3 { occluder3-ORG } } + } +} + occluder5 { + occluder { 1 } + occluder5-ORG { + 0 { + 4 0 4 + } + 1 { + 4 7.57015 4 + } + 2 { + 4 7.57015 0.551412 + } + 3 { + 4 -2.78684e-016 0.551412 + } + } + { + { 0 1 2 3 { occluder5-ORG } } + } +} + occluder6 { + occluder { 1 } + occluder6-ORG { + 0 { + 4 1.49418 4 + } + 1 { + 4 7.57015 4 + } + 2 { + 4 7.57015 -4 + } + 3 { + 4 1.49418 -4 + } + } + { + { 0 1 2 3 { occluder6-ORG } } + } +} + occluder7 { + occluder { 1 } + occluder7-ORG { + 0 { + -3.99576 2.96946e-016 -0.84965 + } + 1 { + -3.99576 7.57015 -0.84965 + } + 2 { + -3.99576 7.57015 -4 + } + 3 { + -3.99576 0 -4 + } + } + { + { 0 1 2 3 { occluder7-ORG } } + } +} + occluder8 { + occluder { 1 } + occluder8-ORG { + 0 { + -3.99576 0 4 + } + 1 { + -3.99576 7.57015 4 + } + 2 { + -3.99576 7.57015 0.551412 + } + 3 { + -3.99576 -2.78684e-016 0.551412 + } + } + { + { 0 1 2 3 { occluder8-ORG } } + } +} + occluder9 { + occluder { 1 } + occluder9-ORG { + 0 { + -3.99576 1.49418 4 + } + 1 { + -3.99576 7.57015 4 + } + 2 { + -3.99576 7.57015 -4 + } + 3 { + -3.99576 1.49418 -4 + } + } + { + { 0 1 2 3 { occluder9-ORG } } + } +} + occluder10 { + occluder { 1 } + occluder10-ORG { + 0 { + -4 1.49418 -1.13843 + } + 1 { + -4 3.24289 -1.13843 + } + 2 { + 4 3.24289 -1.13843 + } + 3 { + 4 1.49418 -1.13843 + } + } + { + { 0 1 2 3 { occluder10-ORG } } + } +} + occluder11 { + occluder { 1 } + occluder11-ORG { + 0 { + -4 0 -1.13843 + } + 1 { + -4 3.24289 -1.13843 + } + 2 { + -1.01962 3.24289 -1.13843 + } + 3 { + -1.01962 -2.78684e-016 -1.13843 + } + } + { + { 0 1 2 3 { occluder11-ORG } } + } +} + occluder12 { + occluder { 1 } + occluder12-ORG { + 0 { + 0.242712 2.96946e-016 -1.13843 + } + 1 { + 0.242712 3.24289 -1.13843 + } + 2 { + 4 3.24289 -1.13843 + } + 3 { + 4 0 -1.13843 + } + } + { + { 0 1 2 3 { occluder12-ORG } } + } +} + occluder13 { + occluder { 1 } + occluder13-ORG { + 0 { + -4 3.19567 0.643347 + } + 1 { + -4 3.19567 -3.99828 + } + 2 { + 4 3.19567 -3.99828 + } + 3 { + 4 3.19567 0.643347 + } + } + { + { 0 1 2 3 { occluder13-ORG } } + } +} + occluder14 { + occluder { 1 } + occluder14-ORG { + 0 { + -4 3.19567 4.00073 + } + 1 { + -4 3.19567 -3.99828 + } + 2 { + -0.508583 3.19567 -3.99828 + } + 3 { + -0.508583 3.19567 4.00073 + } + } + { + { 0 1 2 3 { occluder14-ORG } } + } +} + occluder15 { + occluder { 1 } + occluder15-ORG { + 0 { + 1.25869 3.19567 3.98826 + } + 1 { + 1.25869 3.19567 -3.99828 + } + 2 { + 4 3.19567 -3.99828 + } + 3 { + 4 3.19567 3.98826 + } + } + { + { 0 1 2 3 { occluder15-ORG } } + } +} + occluder16 { + occluder { 1 } + occluder16-ORG { + 0 { + -4 5.26173 2.21946 + } + 1 { + -4 5.26173 -3.99828 + } + 2 { + 4 5.26173 -3.99828 + } + 3 { + 4 5.26173 2.21946 + } + } + { + { 0 1 2 3 { occluder16-ORG } } + } +} + occluder17 { + occluder { 1 } + occluder17-ORG { + 0 { + 1.78343 5.26173 3.98826 + } + 1 { + 1.78343 5.26173 -3.99828 + } + 2 { + 4 5.26173 -3.99828 + } + 3 { + 4 5.26173 3.98826 + } + } + { + { 0 1 2 3 { occluder17-ORG } } + } +} + occluder18 { + occluder { 1 } + occluder18-ORG { + 0 { + -3.99383 5.23279 2.0951 + } + 1 { + 1.8032 5.23279 2.0951 + } + 2 { + 1.8032 7.56072 2.0951 + } + 3 { + -3.99383 7.56072 2.0951 + } + } + { + { 0 1 2 3 { occluder18-ORG } } + } +} + occluder19 { + occluder { 1 } + occluder19-ORG { + 0 { + -3.93576 0.0379318 0.6175 + } + 1 { + 1.94837 0.0379318 0.6175 + } + 2 { + 1.94837 3.21608 0.6175 + } + 3 { + -3.93576 3.21608 0.6175 + } + } + { + { 0 1 2 3 { occluder19-ORG } } + } +} + occluder20 { + occluder { 1 } + occluder20-ORG { + 0 { + 11.5843 -0.215275 11.5843 + } + 1 { + 11.5843 -0.215275 -11.5843 + } + 2 { + -11.5843 -0.215275 -11.5843 + } + 3 { + -11.5843 -0.215275 11.5843 + } + } + { + { 0 1 2 3 { occluder20-ORG } } + } +} + occluder21 { + occluder { 1 } + occluder21-ORG { + 0 { + -3.93576 1.56418 0.5914 + } + 1 { + 3.94341 1.56418 0.5914 + } + 2 { + 3.94341 3.26834 0.5914 + } + 3 { + -3.93576 3.26834 0.5914 + } + } + { + { 0 1 2 3 { occluder21-ORG } } + } +} + occluder22 { + occluder { 1 } + occluder22-ORG { + 0 { + -0.694875 1.49418 2.11153 + } + 1 { + -0.694875 5.26968 2.11153 + } + 2 { + -0.694875 5.26968 0.21275 + } + 3 { + -0.694875 1.49418 0.21275 + } + } + { + { 0 1 2 3 { occluder22-ORG } } + } +} + occluder23 { + occluder { 1 } + occluder23-ORG { + 0 { + 0.756062 1.49418 2.11153 + } + 1 { + 0.756062 5.26968 2.11153 + } + 2 { + 0.756062 5.26968 0.21275 + } + 3 { + 0.756062 1.49418 0.21275 + } + } + { + { 0 1 2 3 { occluder23-ORG } } + } +} + occluder24 { + occluder { 1 } + occluder24-ORG { + 0 { + 1.60244 5.2309 2.14179 + } + 1 { + 1.60244 7.52263 2.14179 + } + 2 { + 1.60244 7.52263 0.394334 + } + 3 { + 1.60244 5.2309 0.394334 + } + } + { + { 0 1 2 3 { occluder24-ORG } } + } +} + occluder25 { + occluder { 1 } + occluder25-ORG { + 0 { + 1.60244 5.2309 -0.763555 + } + 1 { + 1.60244 7.52263 -0.763555 + } + 2 { + 1.60244 7.52263 -2.42694 + } + 3 { + 1.60244 5.2309 -2.42694 + } + } + { + { 0 1 2 3 { occluder25-ORG } } + } +} + occluder26 { + occluder { 1 } + occluder26-ORG { + 0 { + 1.78944 5.2309 -2.34074 + } + 1 { + 1.78944 7.52263 -2.34074 + } + 2 { + -2.28798 7.52263 -2.34074 + } + 3 { + -2.28798 5.2309 -2.34074 + } + } + { + { 0 1 2 3 { occluder26-ORG } } + } +} + occluder27 { + occluder { 1 } + occluder27-ORG { + 0 { + -2.15855 5.2309 1.03841 + } + 1 { + -2.15855 7.52263 1.03841 + } + 2 { + -2.15855 7.52263 -2.44292 + } + 3 { + -2.15855 5.2309 -2.44292 + } + } + { + { 0 1 2 3 { occluder27-ORG } } + } +} + occluder28 { + occluder { 1 } + occluder28-ORG { + 0 { + -3.98717 1.54945 3.90197 + } + 1 { + -3.98717 1.54945 -1.30625 + } + 2 { + 1.28595 1.54945 -1.30625 + } + 3 { + 1.28595 1.54945 3.90197 + } + } + { + { 0 1 2 3 { occluder28-ORG } } + } +} diff --git a/samples/culling/models/portals.egg b/samples/culling/models/portals.egg new file mode 100644 index 0000000000..c7536d79dc --- /dev/null +++ b/samples/culling/models/portals.egg @@ -0,0 +1,674 @@ + { Y-Up } + + portal_10to11_1 { + portal { 1 } + portal_10to11_1-ORG { + 0 { + -2.36149 5.34053 2.04979 + { -1 0 0 } + { 0 0 1 1 } + } + 1 { + -2.36149 7.09573 2.04979 + { -1 0 0 } + { 0.001 0.001 0.999 1 } + } + 2 { + -2.36149 7.09573 1.0558 + { -1 0 0 } + { 0 0 1 1 } + } + 3 { + -2.36149 5.34053 1.0558 + { -1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_10to11_1-ORG } } + } +} + portal_1to2_1 { + portal { 1 } + portal_1to2_1-ORG { + 0 { + 4 4.44089e-016 -0.838305 + { 1 0 0 } + { 0 0 1 1 } + } + 1 { + 4 1.48611 -0.838305 + { 1 0 0 } + { 0 0 1 1 } + } + 2 { + 4 1.48611 0.527741 + { 1 0 0 } + { 0.001 0.001 0.999 1 } + } + 3 { + 4 4.44089e-016 0.527741 + { 1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_1to2_1-ORG } } + } +} + portal_1to2_2 { + portal { 1 } + portal_1to2_2-ORG { + 0 { + -4 0 0.527741 + { -1 0 0 } + { 0 0 1 1 } + } + 1 { + -4 1.48611 0.527741 + { -1 0 0 } + { 0.001 0.001 0.999 1 } + } + 2 { + -4 1.48611 -0.838305 + { -1 0 0 } + { 0 0 1 1 } + } + 3 { + -4 0 -0.838305 + { -1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_1to2_2-ORG } } + } +} + portal_2to3_1 { + portal { 1 } + portal_2to3_1-ORG { + 0 { + 0.243417 0 -0.838305 + { 0 -5.37589e-011 1 } + { 0 0 1 1 } + } + 1 { + 0.243412 1.48611 -0.838305 + { 0 -5.37589e-011 1 } + { 0 0 1 1 } + } + 2 { + -1.03253 1.48611 -0.838305 + { 0 -5.37589e-011 1 } + { 0.001 0.001 0.999 1 } + } + 3 { + -1.02178 0 -0.838305 + { 0 -5.37589e-011 1 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_2to3_1-ORG } } + } +} + portal_2to4_1 { + portal { 1 } + portal_2to4_1-ORG { + 0 { + 2.04443 0 0.527741 + { 0 1.40115e-009 -1 } + { 0 0 1 1 } + } + 1 { + 2.04443 1.48611 0.527741 + { 0 1.40115e-009 -1 } + { 0.001 0.001 0.999 1 } + } + 2 { + 2.99397 1.48611 0.527741 + { 0 1.40115e-009 -1 } + { 0 0 1 1 } + } + 3 { + 2.99397 0 0.527741 + { 0 1.40115e-009 -1 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_2to4_1-ORG } } + } +} + portal_4to5_1 { + portal { 1 } + portal_4to5_1-ORG { + 0 { + 0.519489 3.3704 0.115602 + { 0 -0.00293795 0.999996 } + { 0 0 1 1 } + } + 1 { + 0.519489 4.88145 0.120042 + { 0 -0.00293795 0.999996 } + { 0 0 1 1 } + } + 2 { + -0.423826 4.88145 0.120042 + { 0 -0.00293795 0.999996 } + { 0.001 0.001 0.999 1 } + } + 3 { + -0.423812 3.3704 0.115602 + { 0 -0.00293795 0.999996 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_4to5_1-ORG } } + } +} + portal_5to6_1 { + portal { 1 } + portal_5to6_1-ORG { + 0 { + 0.997627 3.3704 0.115602 + { 1.00717e-006 1.20465e-006 -1 } + { 0 0 1 1 } + } + 1 { + 0.997627 5.12958 0.115602 + { 1.00717e-006 1.20465e-006 -1 } + { 0.001 0.001 0.999 1 } + } + 2 { + 2.99397 5.12958 0.115606 + { 1.00717e-006 1.20465e-006 -1 } + { 0 0 1 1 } + } + 3 { + 2.99397 3.3704 0.115602 + { 1.00717e-006 1.20465e-006 -1 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_5to6_1-ORG } } + } +} + portal_5to7_1 { + portal { 1 } + portal_5to7_1-ORG { + 0 { + -3.36172 3.3704 0.115602 + { 1.02518e-006 -3.5707e-006 -1 } + { 0 0 1 1 } + } + 1 { + -3.36172 5.12958 0.115593 + { 1.02518e-006 -3.5707e-006 -1 } + { 0.001 0.001 0.999 1 } + } + 2 { + -0.900953 5.12958 0.115599 + { 1.02518e-006 -3.5707e-006 -1 } + { 0 0 1 1 } + } + 3 { + -0.900963 3.3704 0.115602 + { 1.02518e-006 -3.5707e-006 -1 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_5to7_1-ORG } } + } +} + portal_7to8_1 { + portal { 1 } + portal_7to8_1-ORG { + 0 { + -2.30748 3.3704 3.18072 + { -1 1.34401e-005 1.02127e-006 } + { 0 0 1 1 } + } + 1 { + -2.30746 5.12958 3.18072 + { -1 1.34401e-005 1.02127e-006 } + { 0.001 0.001 0.999 1 } + } + 2 { + -2.30746 5.12958 2.23852 + { -1 1.34401e-005 1.02127e-006 } + { 0 0 1 1 } + } + 3 { + -2.30748 3.3704 2.23852 + { -1 1.34401e-005 1.02127e-006 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_7to8_1-ORG } } + } +} + portal_8to9_1 { + portal { 1 } + portal_8to9_1-ORG { + 0 { + 1.82483 5.34053 3.18072 + { -1 0 0 } + { 0 0 1 1 } + } + 1 { + 1.82483 7.09573 3.18072 + { -1 0 0 } + { 0.001 0.001 0.999 1 } + } + 2 { + 1.82483 7.09573 2.23852 + { -1 0 0 } + { 0 0 1 1 } + } + 3 { + 1.82483 5.34053 2.23852 + { -1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_8to9_1-ORG } } + } +} + portal_9to10_1 { + portal { 1 } + portal_9to10_1-ORG { + 0 { + -0.485624 5.34053 -3.42013 + { 1 0 0 } + { 0 0 1 1 } + } + 1 { + -0.485624 7.09573 -3.42013 + { 1 0 0 } + { 0 0 1 1 } + } + 2 { + -0.485624 7.09573 -2.49425 + { 1 0 0 } + { 0.001 0.001 0.999 1 } + } + 3 { + -0.485624 5.34053 -2.49425 + { 1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_9to10_1-ORG } } + } +} + portal_9to11_1 { + portal { 1 } + portal_9to11_1-ORG { + 0 { + 1.82483 5.34053 -0.718624 + { 1 0 0 } + { 0 0 1 1 } + } + 1 { + 1.82483 7.09573 -0.718624 + { 1 0 0 } + { 0 0 1 1 } + } + 2 { + 1.82483 7.09573 0.350091 + { 1 0 0 } + { 0.001 0.001 0.999 1 } + } + 3 { + 1.82483 5.34053 0.350091 + { 1 0 0 } + { 0 0 1 1 } + } + } + { + { 0 1 2 3 { portal_9to11_1-ORG } } + } +} + portal_11to9_1 { + portal { 1 } + portal_11to9_1-ORG { + 0 { + 1.82483 5.34053 0.350091 + { -1 0 0 } + { 1 0 0 1 } + } + 1 { + 1.82483 7.09573 0.350091 + { -1 0 0 } + { 0.999 0.001 0.001 1 } + } + 2 { + 1.82483 7.09573 -0.718624 + { -1 0 0 } + { 1 0 0 1 } + } + 3 { + 1.82483 5.34053 -0.718624 + { -1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_11to9_1-ORG } } + } +} + portal_10to9_1 { + portal { 1 } + portal_10to9_1-ORG { + 0 { + -0.485624 5.34053 -2.49425 + { -1 0 0 } + { 1 0 0 1 } + } + 1 { + -0.485624 7.09573 -2.49425 + { -1 0 0 } + { 0.999 0.001 0.001 1 } + } + 2 { + -0.485624 7.09573 -3.42013 + { -1 0 0 } + { 1 0 0 1 } + } + 3 { + -0.485624 5.34053 -3.42013 + { -1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_10to9_1-ORG } } + } +} + portal_9to8_1 { + portal { 1 } + portal_9to8_1-ORG { + 0 { + 1.82483 5.34053 2.23852 + { 1 0 0 } + { 1 0 0 1 } + } + 1 { + 1.82483 7.09573 2.23852 + { 1 0 0 } + { 1 0 0 1 } + } + 2 { + 1.82483 7.09573 3.18072 + { 1 0 0 } + { 0.999 0.001 0.001 1 } + } + 3 { + 1.82483 5.34053 3.18072 + { 1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_9to8_1-ORG } } + } +} + portal_8to7_1 { + portal { 1 } + portal_8to7_1-ORG { + 0 { + -2.30748 3.3704 2.23852 + { 1 -1.34401e-005 -1.02127e-006 } + { 1 0 0 1 } + } + 1 { + -2.30746 5.12958 2.23852 + { 1 -1.34401e-005 -1.02127e-006 } + { 1 0 0 1 } + } + 2 { + -2.30746 5.12958 3.18072 + { 1 -1.34401e-005 -1.02127e-006 } + { 0.999 0.001 0.001 1 } + } + 3 { + -2.30748 3.3704 3.18072 + { 1 -1.34401e-005 -1.02127e-006 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_8to7_1-ORG } } + } +} + portal_7to5_1 { + portal { 1 } + portal_7to5_1-ORG { + 0 { + -0.900963 3.3704 0.115602 + { -1.02518e-006 3.5707e-006 1 } + { 1 0 0 1 } + } + 1 { + -0.900953 5.12958 0.115599 + { -1.02518e-006 3.5707e-006 1 } + { 1 0 0 1 } + } + 2 { + -3.36172 5.12958 0.115593 + { -1.02518e-006 3.5707e-006 1 } + { 0.999 0.001 0.001 1 } + } + 3 { + -3.36172 3.3704 0.115602 + { -1.02518e-006 3.5707e-006 1 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_7to5_1-ORG } } + } +} + portal_6to5_1 { + portal { 1 } + portal_6to5_1-ORG { + 0 { + 2.99397 3.3704 0.115602 + { -1.00717e-006 -1.20465e-006 1 } + { 1 0 0 1 } + } + 1 { + 2.99397 5.12958 0.115606 + { -1.00717e-006 -1.20465e-006 1 } + { 1 0 0 1 } + } + 2 { + 0.997627 5.12958 0.115602 + { -1.00717e-006 -1.20465e-006 1 } + { 0.999 0.001 0.001 1 } + } + 3 { + 0.997627 3.3704 0.115602 + { -1.00717e-006 -1.20465e-006 1 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_6to5_1-ORG } } + } +} + portal_5to4_1 { + portal { 1 } + portal_5to4_1-ORG { + 0 { + -0.423812 3.3704 0.115602 + { 0 0.00293795 -0.999996 } + { 1 0 0 1 } + } + 1 { + -0.423826 4.88145 0.120042 + { 0 0.00293795 -0.999996 } + { 0.999 0.001 0.001 1 } + } + 2 { + 0.519489 4.88145 0.120042 + { 0 0.00293795 -0.999996 } + { 1 0 0 1 } + } + 3 { + 0.519489 3.3704 0.115602 + { 0 0.00293795 -0.999996 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_5to4_1-ORG } } + } +} + portal_4to2_1 { + portal { 1 } + portal_4to2_1-ORG { + 0 { + 2.99397 0 0.527741 + { 0 -1.40115e-009 1 } + { 1 0 0 1 } + } + 1 { + 2.99397 1.48611 0.527741 + { 0 -1.40115e-009 1 } + { 1 0 0 1 } + } + 2 { + 2.04443 1.48611 0.527741 + { 0 -1.40115e-009 1 } + { 0.999 0.001 0.001 1 } + } + 3 { + 2.04443 0 0.527741 + { 0 -1.40115e-009 1 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_4to2_1-ORG } } + } +} + portal_3to2_1 { + portal { 1 } + portal_3to2_1-ORG { + 0 { + -1.02178 0 -0.838305 + { 0 5.37589e-011 -1 } + { 1 0 0 1 } + } + 1 { + -1.03253 1.48611 -0.838305 + { 0 5.37589e-011 -1 } + { 0.999 0.001 0.001 1 } + } + 2 { + 0.243412 1.48611 -0.838305 + { 0 5.37589e-011 -1 } + { 1 0 0 1 } + } + 3 { + 0.243417 0 -0.838305 + { 0 5.37589e-011 -1 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_3to2_1-ORG } } + } +} + portal_2to1_2 { + portal { 1 } + portal_2to1_2-ORG { + 0 { + -4 0 -0.838305 + { 1 0 0 } + { 1 0 0 1 } + } + 1 { + -4 1.48611 -0.838305 + { 1 0 0 } + { 1 0 0 1 } + } + 2 { + -4 1.48611 0.527741 + { 1 0 0 } + { 0.999 0.001 0.001 1 } + } + 3 { + -4 0 0.527741 + { 1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_2to1_2-ORG } } + } +} + portal_2to1_1 { + portal { 1 } + portal_2to1_1-ORG { + 0 { + 4 4.44089e-016 0.527741 + { -1 0 0 } + { 1 0 0 1 } + } + 1 { + 4 1.48611 0.527741 + { -1 0 0 } + { 0.999 0.001 0.001 1 } + } + 2 { + 4 1.48611 -0.838305 + { -1 0 0 } + { 1 0 0 1 } + } + 3 { + 4 4.44089e-016 -0.838305 + { -1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_2to1_1-ORG } } + } +} + portal_11to10_1 { + portal { 1 } + portal_11to10_1-ORG { + 0 { + -2.36149 5.34053 1.0558 + { 1 0 0 } + { 1 0 0 1 } + } + 1 { + -2.36149 7.09573 1.0558 + { 1 0 0 } + { 1 0 0 1 } + } + 2 { + -2.36149 7.09573 2.04979 + { 1 0 0 } + { 0.999 0.001 0.001 1 } + } + 3 { + -2.36149 5.34053 2.04979 + { 1 0 0 } + { 1 0 0 1 } + } + } + { + { 0 1 2 3 { portal_11to10_1-ORG } } + } +} diff --git a/samples/culling/models/tex_0.png b/samples/culling/models/tex_0.png new file mode 100644 index 0000000000..48239f4784 Binary files /dev/null and b/samples/culling/models/tex_0.png differ diff --git a/samples/culling/models/tex_1.png b/samples/culling/models/tex_1.png new file mode 100644 index 0000000000..4f8ca64d38 Binary files /dev/null and b/samples/culling/models/tex_1.png differ diff --git a/samples/culling/models/tex_2.png b/samples/culling/models/tex_2.png new file mode 100644 index 0000000000..a628c0c563 Binary files /dev/null and b/samples/culling/models/tex_2.png differ diff --git a/samples/culling/models/tex_3.png b/samples/culling/models/tex_3.png new file mode 100644 index 0000000000..58b46129b6 Binary files /dev/null and b/samples/culling/models/tex_3.png differ diff --git a/samples/culling/models/tex_4.png b/samples/culling/models/tex_4.png new file mode 100644 index 0000000000..58b46129b6 Binary files /dev/null and b/samples/culling/models/tex_4.png differ diff --git a/samples/culling/models/tex_5.png b/samples/culling/models/tex_5.png new file mode 100644 index 0000000000..a628c0c563 Binary files /dev/null and b/samples/culling/models/tex_5.png differ diff --git a/samples/culling/models/tex_6.png b/samples/culling/models/tex_6.png new file mode 100644 index 0000000000..4f8ca64d38 Binary files /dev/null and b/samples/culling/models/tex_6.png differ diff --git a/samples/culling/models/tex_7.png b/samples/culling/models/tex_7.png new file mode 100644 index 0000000000..48239f4784 Binary files /dev/null and b/samples/culling/models/tex_7.png differ diff --git a/samples/culling/occluder_culling.py b/samples/culling/occluder_culling.py new file mode 100755 index 0000000000..9743c159c0 --- /dev/null +++ b/samples/culling/occluder_culling.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python + +""" +Author: Josh Enes +Last Updated: 2015-03-13 + +This is a demo of Panda's occluder-culling system. It demonstrates loading +occluder from an EGG file and adding them to a CullTraverser. +""" + +# Load PRC data +from panda3d.core import loadPrcFileData +loadPrcFileData('', 'window-title Occluder Demo') +loadPrcFileData('', 'sync-video false') +loadPrcFileData('', 'show-frame-rate-meter true') +loadPrcFileData('', 'texture-minfilter linear-mipmap-linear') +#loadPrcFileData('', 'fake-view-frustum-cull true') # show culled nodes in red + +# Import needed modules +import random +from direct.showbase.ShowBase import ShowBase +from direct.gui.OnscreenText import OnscreenText +from panda3d.core import PerspectiveLens, TextNode, \ +TexGenAttrib, TextureStage, TransparencyAttrib, LPoint3, Texture + + +def add_instructions(pos, msg): + """Function to put instructions on the screen.""" + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +def add_title(text): + """Function to put title on the screen.""" + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class Game(ShowBase): + """Sets up the game, camera, controls, and loads models.""" + def __init__(self): + ShowBase.__init__(self) + self.xray_mode = False + self.show_model_bounds = False + + # Display instructions + add_title("Panda3D Tutorial: Occluder Culling") + add_instructions(0.06, "[Esc]: Quit") + add_instructions(0.12, "[W]: Move Forward") + add_instructions(0.18, "[A]: Move Left") + add_instructions(0.24, "[S]: Move Right") + add_instructions(0.30, "[D]: Move Back") + add_instructions(0.36, "Arrow Keys: Look Around") + add_instructions(0.42, "[F]: Toggle Wireframe") + add_instructions(0.48, "[X]: Toggle X-Ray Mode") + add_instructions(0.54, "[B]: Toggle Bounding Volumes") + + # Setup controls + self.keys = {} + for key in ['arrow_left', 'arrow_right', 'arrow_up', 'arrow_down', + 'a', 'd', 'w', 's']: + self.keys[key] = 0 + self.accept(key, self.push_key, [key, 1]) + self.accept('shift-%s' % key, self.push_key, [key, 1]) + self.accept('%s-up' % key, self.push_key, [key, 0]) + self.accept('f', self.toggleWireframe) + self.accept('x', self.toggle_xray_mode) + self.accept('b', self.toggle_model_bounds) + self.accept('escape', __import__('sys').exit, [0]) + self.disableMouse() + + # Setup camera + self.lens = PerspectiveLens() + self.lens.setFov(60) + self.lens.setNear(0.01) + self.lens.setFar(1000.0) + self.cam.node().setLens(self.lens) + self.camera.setPos(-9, -0.5, 1) + self.heading = -95.0 + self.pitch = 0.0 + + # Load level geometry + self.level_model = self.loader.loadModel('models/level') + self.level_model.reparentTo(self.render) + self.level_model.setTexGen(TextureStage.getDefault(), + TexGenAttrib.MWorldPosition) + self.level_model.setTexProjector(TextureStage.getDefault(), + self.render, self.level_model) + self.level_model.setTexScale(TextureStage.getDefault(), 4) + tex = self.loader.load3DTexture('models/tex_#.png') + self.level_model.setTexture(tex) + + # Load occluders + occluder_model = self.loader.loadModel('models/occluders') + occluder_nodepaths = occluder_model.findAllMatches('**/+OccluderNode') + for occluder_nodepath in occluder_nodepaths: + self.render.setOccluder(occluder_nodepath) + occluder_nodepath.node().setDoubleSided(True) + + # Randomly spawn some models to test the occluders + self.models = [] + box_model = self.loader.loadModel('box') + + for dummy in xrange(0, 500): + pos = LPoint3((random.random() - 0.5) * 9, + (random.random() - 0.5) * 9, + random.random() * 8) + box = box_model.copy_to(self.render) + box.setScale(random.random() * 0.2 + 0.1) + box.setPos(pos) + box.setHpr(random.random() * 360, + random.random() * 360, + random.random() * 360) + box.reparentTo(self.render) + self.models.append(box) + + self.taskMgr.add(self.update, 'main loop') + + def push_key(self, key, value): + """Stores a value associated with a key.""" + self.keys[key] = value + + def update(self, task): + """Updates the camera based on the keyboard input.""" + delta = globalClock.getDt() + move_x = delta * 3 * -self.keys['a'] + delta * 3 * self.keys['d'] + move_z = delta * 3 * self.keys['s'] + delta * 3 * -self.keys['w'] + self.camera.setPos(self.camera, move_x, -move_z, 0) + self.heading += (delta * 90 * self.keys['arrow_left'] + + delta * 90 * -self.keys['arrow_right']) + self.pitch += (delta * 90 * self.keys['arrow_up'] + + delta * 90 * -self.keys['arrow_down']) + self.camera.setHpr(self.heading, self.pitch, 0) + return task.cont + + def toggle_xray_mode(self): + """Toggle X-ray mode on and off. This is useful for seeing the + effectiveness of the occluder culling.""" + self.xray_mode = not self.xray_mode + if self.xray_mode: + self.level_model.setColorScale((1, 1, 1, 0.5)) + self.level_model.setTransparency(TransparencyAttrib.MDual) + else: + self.level_model.setColorScaleOff() + self.level_model.setTransparency(TransparencyAttrib.MNone) + + def toggle_model_bounds(self): + """Toggle bounding volumes on and off on the models.""" + self.show_model_bounds = not self.show_model_bounds + if self.show_model_bounds: + for model in self.models: + model.showBounds() + else: + for model in self.models: + model.hideBounds() + +game = Game() +game.run() diff --git a/samples/culling/portal_culling.py b/samples/culling/portal_culling.py new file mode 100755 index 0000000000..26ae074dd8 --- /dev/null +++ b/samples/culling/portal_culling.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python + +""" +Author: Josh Enes +Last Updated: 2015-03-13 + +This is a demo of Panda's portal-culling system. It demonstrates loading +portals from an EGG file, and shows an example method of selecting the +current cell using geoms and a collision ray. +""" + +# Some config options which can be changed. +ENABLE_PORTALS = True # Set False to disable portal culling and see FPS drop! +DEBUG_PORTALS = False # Set True to see visually which portals are used + +# Load PRC data +from panda3d.core import loadPrcFileData +if ENABLE_PORTALS: + loadPrcFileData('', 'allow-portal-cull true') + if DEBUG_PORTALS: + loadPrcFileData('', 'debug-portal-cull true') +loadPrcFileData('', 'window-title Portal Demo') +loadPrcFileData('', 'sync-video false') +loadPrcFileData('', 'show-frame-rate-meter true') +loadPrcFileData('', 'texture-minfilter linear-mipmap-linear') + +# Import needed modules +import random +from direct.showbase.ShowBase import ShowBase +from direct.gui.OnscreenText import OnscreenText +from panda3d.core import PerspectiveLens, NodePath, LVector3, LPoint3, \ + TexGenAttrib, TextureStage, TransparencyAttrib, CollisionTraverser, \ + CollisionHandlerQueue, TextNode, CollisionRay, CollisionNode + + +def add_instructions(pos, msg): + """Function to put instructions on the screen.""" + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +def add_title(text): + """Function to put title on the screen.""" + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class Game(ShowBase): + """Sets up the game, camera, controls, and loads models.""" + def __init__(self): + ShowBase.__init__(self) + self.cellmanager = CellManager(self) + self.xray_mode = False + self.show_model_bounds = False + + # Display instructions + add_title("Panda3D Tutorial: Portal Culling") + add_instructions(0.06, "[Esc]: Quit") + add_instructions(0.12, "[W]: Move Forward") + add_instructions(0.18, "[A]: Move Left") + add_instructions(0.24, "[S]: Move Right") + add_instructions(0.30, "[D]: Move Back") + add_instructions(0.36, "Arrow Keys: Look Around") + add_instructions(0.42, "[F]: Toggle Wireframe") + add_instructions(0.48, "[X]: Toggle X-Ray Mode") + add_instructions(0.54, "[B]: Toggle Bounding Volumes") + + # Setup controls + self.keys = {} + for key in ['arrow_left', 'arrow_right', 'arrow_up', 'arrow_down', + 'a', 'd', 'w', 's']: + self.keys[key] = 0 + self.accept(key, self.push_key, [key, 1]) + self.accept('shift-%s' % key, self.push_key, [key, 1]) + self.accept('%s-up' % key, self.push_key, [key, 0]) + self.accept('f', self.toggleWireframe) + self.accept('x', self.toggle_xray_mode) + self.accept('b', self.toggle_model_bounds) + self.accept('escape', __import__('sys').exit, [0]) + self.disableMouse() + + # Setup camera + lens = PerspectiveLens() + lens.setFov(60) + lens.setNear(0.01) + lens.setFar(1000.0) + self.cam.node().setLens(lens) + self.camera.setPos(-9, -0.5, 1) + self.heading = -95.0 + self.pitch = 0.0 + + # Load level geometry + self.level_model = self.loader.loadModel('models/level') + self.level_model.reparentTo(self.render) + self.level_model.setTexGen(TextureStage.getDefault(), + TexGenAttrib.MWorldPosition) + self.level_model.setTexProjector(TextureStage.getDefault(), + self.render, self.level_model) + self.level_model.setTexScale(TextureStage.getDefault(), 4) + tex = self.loader.load3DTexture('models/tex_#.png') + self.level_model.setTexture(tex) + + # Load cells + self.cellmanager.load_cells_from_model('models/cells') + # Load portals + self.cellmanager.load_portals_from_model('models/portals') + + # Randomly spawn some models to test the portals + self.models = [] + for dummy in xrange(0, 500): + pos = LPoint3((random.random() - 0.5) * 6, + (random.random() - 0.5) * 6, + random.random() * 7) + cell = self.cellmanager.get_cell(pos) + if cell is None: # skip if the random position is not over a cell + continue + dist = self.cellmanager.get_dist_to_cell(pos) + if dist > 1.5: # skip if the random position is too far from ground + continue + box = self.loader.loadModel('box') + box.setScale(random.random() * 0.2 + 0.1) + box.setPos(pos) + box.setHpr(random.random() * 360, + random.random() * 360, + random.random() * 360) + box.reparentTo(cell.nodepath) + self.models.append(box) + self.taskMgr.add(self.update, 'main loop') + + def push_key(self, key, value): + """Stores a value associated with a key.""" + self.keys[key] = value + + def update(self, task): + """Updates the camera based on the keyboard input. Once this is + done, then the CellManager's update function is called.""" + delta = globalClock.getDt() + move_x = delta * 3 * -self.keys['a'] + delta * 3 * self.keys['d'] + move_z = delta * 3 * self.keys['s'] + delta * 3 * -self.keys['w'] + self.camera.setPos(self.camera, move_x, -move_z, 0) + self.heading += (delta * 90 * self.keys['arrow_left'] + + delta * 90 * -self.keys['arrow_right']) + self.pitch += (delta * 90 * self.keys['arrow_up'] + + delta * 90 * -self.keys['arrow_down']) + self.camera.setHpr(self.heading, self.pitch, 0) + if ENABLE_PORTALS: + self.cellmanager.update() + return task.cont + + def toggle_xray_mode(self): + """Toggle X-ray mode on and off. This is useful for seeing the + effectiveness of the portal culling.""" + self.xray_mode = not self.xray_mode + if self.xray_mode: + self.level_model.setColorScale((1, 1, 1, 0.5)) + self.level_model.setTransparency(TransparencyAttrib.MDual) + else: + self.level_model.setColorScaleOff() + self.level_model.setTransparency(TransparencyAttrib.MNone) + + def toggle_model_bounds(self): + """Toggle bounding volumes on and off on the models.""" + self.show_model_bounds = not self.show_model_bounds + if self.show_model_bounds: + for model in self.models: + model.showBounds() + else: + for model in self.models: + model.hideBounds() + + +class CellManager(object): + """Creates a collision ray and collision traverser to use for + selecting the current cell.""" + def __init__(self, game): + self.game = game + self.cells = {} + self.cells_by_collider = {} + self.cell_picker_world = NodePath('cell_picker_world') + self.ray = CollisionRay() + self.ray.setDirection(LVector3.down()) + cnode = CollisionNode('cell_raycast_cnode') + self.ray_nodepath = self.cell_picker_world.attachNewNode(cnode) + self.ray_nodepath.node().addSolid(self.ray) + self.ray_nodepath.node().setIntoCollideMask(0) # not for colliding into + self.ray_nodepath.node().setFromCollideMask(1) + self.traverser = CollisionTraverser('traverser') + self.last_known_cell = None + + def add_cell(self, collider, name): + """Add a new cell.""" + cell = Cell(self, name, collider) + self.cells[name] = cell + self.cells_by_collider[collider.node()] = cell + + def get_cell(self, pos): + """Given a position, return the nearest cell below that position. + If no cell is found, returns None.""" + self.ray.setOrigin(pos) + queue = CollisionHandlerQueue() + self.traverser.addCollider(self.ray_nodepath, queue) + self.traverser.traverse(self.cell_picker_world) + self.traverser.removeCollider(self.ray_nodepath) + queue.sortEntries() + if not queue.getNumEntries(): + return None + entry = queue.getEntry(0) + cnode = entry.getIntoNode() + try: + return self.cells_by_collider[cnode] + except KeyError: + raise Warning('collision ray collided with something ' + 'other than a cell: %s' % cnode) + + def get_dist_to_cell(self, pos): + """Given a position, return the distance to the nearest cell + below that position. If no cell is found, returns None.""" + self.ray.setOrigin(pos) + queue = CollisionHandlerQueue() + self.traverser.addCollider(self.ray_nodepath, queue) + self.traverser.traverse(self.cell_picker_world) + self.traverser.removeCollider(self.ray_nodepath) + queue.sortEntries() + if not queue.getNumEntries(): + return None + entry = queue.getEntry(0) + return (entry.getSurfacePoint(self.cell_picker_world) - pos).length() + + def load_cells_from_model(self, modelpath): + """Loads cells from an EGG file. Cells must be named in the + format "cell#" to be loaded by this function.""" + cell_model = self.game.loader.loadModel(modelpath) + for collider in cell_model.findAllMatches('**/+GeomNode'): + name = collider.getName() + if name.startswith('cell'): + self.add_cell(collider, name[4:]) + cell_model.removeNode() + + def load_portals_from_model(self, modelpath): + """Loads portals from an EGG file. Portals must be named in the + format "portal_#to#_*" to be loaded by this function, whereby the + first # is the from cell, the second # is the into cell, and * can + be anything.""" + portal_model = loader.loadModel(modelpath) + portal_nodepaths = portal_model.findAllMatches('**/+PortalNode') + for portal_nodepath in portal_nodepaths: + name = portal_nodepath.getName() + if name.startswith('portal_'): + from_cell_id, into_cell_id = name.split('_')[1].split('to') + try: + from_cell = self.cells[from_cell_id] + except KeyError: + print ('could not load portal "%s" because cell "%s"' + 'does not exist' % (name, from_cell_id)) + continue + try: + into_cell = self.cells[into_cell_id] + except KeyError: + print ('could not load portal "%s" because cell "%s"' + 'does not exist' % (name, into_cell_id)) + continue + from_cell.add_portal(portal_nodepath, into_cell) + portal_model.removeNode() + + def update(self): + """Show the cell the camera is currently in and hides the rest. + If the camera is not in a cell, use the last known cell that the + camera was in. If the camera has not yet been in a cell, then all + cells will be hidden.""" + camera_pos = self.game.camera.getPos(self.game.render) + for cell in self.cells: + self.cells[cell].nodepath.hide() + current_cell = self.get_cell(camera_pos) + if current_cell is None: + if self.last_known_cell is None: + return + self.last_known_cell.nodepath.show() + else: + self.last_known_cell = current_cell + current_cell.nodepath.show() + + +class Cell(object): + """The Cell class is a handy way to keep an association between + all the related nodes and information of a cell.""" + def __init__(self, cellmanager, name, collider): + self.cellmanager = cellmanager + self.name = name + self.collider = collider + self.collider.reparentTo(self.cellmanager.cell_picker_world) + self.collider.setCollideMask(1) + self.collider.hide() + self.nodepath = NodePath('cell_%s_root' % name) + self.nodepath.reparentTo(self.cellmanager.game.render) + self.portals = [] + + def add_portal(self, portal, cell_out): + """Add a portal from this cell going into another one.""" + portal.reparentTo(self.nodepath) + portal.node().setCellIn(self.nodepath) + portal.node().setCellOut(cell_out.nodepath) + self.portals.append(portal) + +game = Game() +game.run() diff --git a/samples/disco-lights/main.py b/samples/disco-lights/main.py new file mode 100755 index 0000000000..8e3bad3cf6 --- /dev/null +++ b/samples/disco-lights/main.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python + +# Author: Jason Pratt (pratt@andrew.cmu.edu) +# Last Updated: 2015-03-13 +# +# This project demonstrates how to use various types of +# lighting +# +from direct.showbase.ShowBase import ShowBase +from panda3d.core import PerspectiveLens +from panda3d.core import NodePath +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import PointLight, Spotlight +from panda3d.core import TextNode +from panda3d.core import Material +from panda3d.core import LVector3 +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +import math +import sys +import colorsys + +# Simple function to keep a value in a given range (by default 0 to 1) +def clamp(i, mn=0, mx=1): + return min(max(i, mn), mx) + + +class DiscoLightsDemo(ShowBase): + + # Macro-like function to reduce the amount of code needed to create the + # onscreen instructions + def makeStatusLabel(self, i): + return OnscreenText( + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 0, 1), shadow=(0, 0, 0, .4), + pos=(0.06, -0.1 -(.06 * i)), scale=.05, mayChange=True) + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # The main initialization of our class + # This creates the on screen title that is in every tutorial + self.title = OnscreenText(text="Panda3D: Tutorial - Lighting", + style=1, fg=(1, 1, 0, 1), shadow=(0, 0, 0, 0.5), + pos=(0.87, -0.95), scale = .07) + + # Creates labels used for onscreen instructions + self.ambientText = self.makeStatusLabel(0) + self.directionalText = self.makeStatusLabel(1) + self.spotlightText = self.makeStatusLabel(2) + self.pointLightText = self.makeStatusLabel(3) + self.spinningText = self.makeStatusLabel(4) + self.ambientBrightnessText = self.makeStatusLabel(5) + self.directionalBrightnessText = self.makeStatusLabel(6) + self.spotlightBrightnessText = self.makeStatusLabel(7) + self.spotlightExponentText = self.makeStatusLabel(8) + self.lightingPerPixelText = self.makeStatusLabel(9) + self.lightingShadowsText = self.makeStatusLabel(10) + + self.disco = loader.loadModel("models/disco_hall") + self.disco.reparentTo(render) + self.disco.setPosHpr(0, 50, -4, 90, 0, 0) + + # First we create an ambient light. All objects are affected by ambient + # light equally + # Create and name the ambient light + self.ambientLight = render.attachNewNode(AmbientLight("ambientLight")) + # Set the color of the ambient light + self.ambientLight.node().setColor((.1, .1, .1, 1)) + # add the newly created light to the lightAttrib + + # Now we create a directional light. Directional lights add shading from a + # given angle. This is good for far away sources like the sun + self.directionalLight = render.attachNewNode( + DirectionalLight("directionalLight")) + self.directionalLight.node().setColor((.35, .35, .35, 1)) + # The direction of a directional light is set as a 3D vector + self.directionalLight.node().setDirection(LVector3(1, 1, -2)) + # These settings are necessary for shadows to work correctly + self.directionalLight.setZ(6) + dlens = self.directionalLight.node().getLens() + dlens.setFilmSize(41, 21) + dlens.setNearFar(50, 75) + # self.directionalLight.node().showFrustum() + + # Now we create a spotlight. Spotlights light objects in a given cone + # They are good for simulating things like flashlights + self.spotlight = camera.attachNewNode(Spotlight("spotlight")) + self.spotlight.node().setColor((.45, .45, .45, 1)) + self.spotlight.node().setSpecularColor((0, 0, 0, 1)) + # The cone of a spotlight is controlled by it's lens. This creates the + # lens + self.spotlight.node().setLens(PerspectiveLens()) + # This sets the Field of View (fov) of the lens, in degrees for width + # and height. The lower the numbers, the tighter the spotlight. + self.spotlight.node().getLens().setFov(16, 16) + # Attenuation controls how the light fades with distance. The three + # values represent the three attenuation constants (constant, linear, + # and quadratic) in the internal lighting equation. The higher the + # numbers the shorter the light goes. + self.spotlight.node().setAttenuation(LVector3(1, 0.0, 0.0)) + # This exponent value sets how soft the edge of the spotlight is. + # 0 means a hard edge. 128 means a very soft edge. + self.spotlight.node().setExponent(60.0) + + # Now we create three colored Point lights. Point lights are lights that + # radiate from a single point, like a light bulb. Like spotlights, they + # are given position by attaching them to NodePaths in the world + self.redHelper = loader.loadModel('models/sphere') + self.redHelper.setColor((1, 0, 0, 1)) + self.redHelper.setPos(-6.5, -3.75, 0) + self.redHelper.setScale(.25) + self.redPointLight = self.redHelper.attachNewNode( + PointLight("redPointLight")) + self.redPointLight.node().setColor((.35, 0, 0, 1)) + self.redPointLight.node().setAttenuation(LVector3(.1, 0.04, 0.0)) + + # The green point light and helper + self.greenHelper = loader.loadModel('models/sphere') + self.greenHelper.setColor((0, 1, 0, 1)) + self.greenHelper.setPos(0, 7.5, 0) + self.greenHelper.setScale(.25) + self.greenPointLight = self.greenHelper.attachNewNode( + PointLight("greenPointLight")) + self.greenPointLight.node().setAttenuation(LVector3(.1, .04, .0)) + self.greenPointLight.node().setColor((0, .35, 0, 1)) + + # The blue point light and helper + self.blueHelper = loader.loadModel('models/sphere') + self.blueHelper.setColor((0, 0, 1, 1)) + self.blueHelper.setPos(6.5, -3.75, 0) + self.blueHelper.setScale(.25) + self.bluePointLight = self.blueHelper.attachNewNode( + PointLight("bluePointLight")) + self.bluePointLight.node().setAttenuation(LVector3(.1, 0.04, 0.0)) + self.bluePointLight.node().setColor((0, 0, .35, 1)) + self.bluePointLight.node().setSpecularColor((1, 1, 1, 1)) + + # Create a dummy node so the lights can be spun with one command + self.pointLightHelper = render.attachNewNode("pointLightHelper") + self.pointLightHelper.setPos(0, 50, 11) + self.redHelper.reparentTo(self.pointLightHelper) + self.greenHelper.reparentTo(self.pointLightHelper) + self.blueHelper.reparentTo(self.pointLightHelper) + + # Finally we store the lights on the root of the scene graph. + # This will cause them to affect everything in the scene. + render.setLight(self.ambientLight) + render.setLight(self.directionalLight) + render.setLight(self.spotlight) + render.setLight(self.redPointLight) + render.setLight(self.greenPointLight) + render.setLight(self.bluePointLight) + + # Create and start interval to spin the lights, and a variable to + # manage them. + self.pointLightsSpin = self.pointLightHelper.hprInterval( + 6, LVector3(360, 0, 0)) + self.pointLightsSpin.loop() + self.arePointLightsSpinning = True + + # Per-pixel lighting and shadows are initially off + self.perPixelEnabled = False + self.shadowsEnabled = False + + # listen to keys for controlling the lights + self.accept("escape", sys.exit) + self.accept("a", self.toggleLights, [[self.ambientLight]]) + self.accept("d", self.toggleLights, [[self.directionalLight]]) + self.accept("s", self.toggleLights, [[self.spotlight]]) + self.accept("p", self.toggleLights, [[self.redPointLight, + self.greenPointLight, + self.bluePointLight]]) + self.accept("r", self.toggleSpinningPointLights) + self.accept("l", self.togglePerPixelLighting) + self.accept("e", self.toggleShadows) + self.accept("z", self.addBrightness, [self.ambientLight, -.05]) + self.accept("x", self.addBrightness, [self.ambientLight, .05]) + self.accept("c", self.addBrightness, [self.directionalLight, -.05]) + self.accept("v", self.addBrightness, [self.directionalLight, .05]) + self.accept("b", self.addBrightness, [self.spotlight, -.05]) + self.accept("n", self.addBrightness, [self.spotlight, .05]) + self.accept("q", self.adjustSpotlightExponent, [self.spotlight, -1]) + self.accept("w", self.adjustSpotlightExponent, [self.spotlight, 1]) + + # Finally call the function that builds the instruction texts + self.updateStatusLabel() + + # This function takes a list of lights and toggles their state. It takes in a + # list so that more than one light can be toggled in a single command + def toggleLights(self, lights): + for light in lights: + # If the given light is in our lightAttrib, remove it. + # This has the effect of turning off the light + if render.hasLight(light): + render.clearLight(light) + # Otherwise, add it back. This has the effect of turning the light + # on + else: + render.setLight(light) + self.updateStatusLabel() + + # This function toggles the spinning of the point intervals by pausing and + # resuming the interval + def toggleSpinningPointLights(self): + if self.arePointLightsSpinning: + self.pointLightsSpin.pause() + else: + self.pointLightsSpin.resume() + self.arePointLightsSpinning = not self.arePointLightsSpinning + self.updateStatusLabel() + + # This function turns per-pixel lighting on or off. + def togglePerPixelLighting(self): + if self.perPixelEnabled: + self.perPixelEnabled = False + render.clearShader() + else: + self.perPixelEnabled = True + render.setShaderAuto() + self.updateStatusLabel() + + # This function turns shadows on or off. + def toggleShadows(self): + if self.shadowsEnabled: + self.shadowsEnabled = False + self.directionalLight.node().setShadowCaster(False) + else: + if not self.perPixelEnabled: + self.togglePerPixelLighting() + self.shadowsEnabled = True + self.directionalLight.node().setShadowCaster(True, 512, 512) + self.updateStatusLabel() + + # This function changes the spotlight's exponent. It is kept to the range + # 0 to 128. Going outside of this range causes an error + def adjustSpotlightExponent(self, spotlight, amount): + e = clamp(spotlight.node().getExponent() + amount, 0, 128) + spotlight.node().setExponent(e) + self.updateStatusLabel() + + # This function reads the color of the light, uses a built-in python function + #(from the library colorsys) to convert from RGB (red, green, blue) color + # representation to HSB (hue, saturation, brightness), so that we can get the + # brighteness of a light, change it, and then convert it back to rgb to chagne + # the light's color + def addBrightness(self, light, amount): + color = light.node().getColor() + h, s, b = colorsys.rgb_to_hsv(color[0], color[1], color[2]) + brightness = clamp(b + amount) + r, g, b = colorsys.hsv_to_rgb(h, s, brightness) + light.node().setColor((r, g, b, 1)) + + self.updateStatusLabel() + + # Builds the onscreen instruction labels + def updateStatusLabel(self): + self.updateLabel(self.ambientText, "(a) ambient is", + render.hasLight(self.ambientLight)) + self.updateLabel(self.directionalText, "(d) directional is", + render.hasLight(self.directionalLight)) + self.updateLabel(self.spotlightText, "(s) spotlight is", + render.hasLight(self.spotlight)) + self.updateLabel(self.pointLightText, "(p) point lights are", + render.hasLight(self.redPointLight)) + self.updateLabel(self.spinningText, "(r) point light spinning is", + self.arePointLightsSpinning) + self.ambientBrightnessText.setText( + "(z,x) Ambient Brightness: " + + self.getBrightnessString(self.ambientLight)) + self.directionalBrightnessText.setText( + "(c,v) Directional Brightness: " + + self.getBrightnessString(self.directionalLight)) + self.spotlightBrightnessText.setText( + "(b,n) Spotlight Brightness: " + + self.getBrightnessString(self.spotlight)) + self.spotlightExponentText.setText( + "(q,w) Spotlight Exponent: " + + str(int(self.spotlight.node().getExponent()))) + self.updateLabel(self.lightingPerPixelText, "(l) Per-pixel lighting is", + self.perPixelEnabled) + self.updateLabel(self.lightingShadowsText, "(e) Shadows are", + self.shadowsEnabled) + + # Appends eitehr (on) or (off) to the base string based on the bassed value + def updateLabel(self, obj, base, var): + if var: + s = " (on)" + else: + s = " (off)" + obj.setText(base + s) + + # Returns the brightness of a light as a string to put it in the instruction + # labels + def getBrightnessString(self, light): + color = light.node().getColor() + h, s, b = colorsys.rgb_to_hsv(color[0], color[1], color[2]) + return "%.2f" % b + + +# Make an instance of our class and run the demo +demo = DiscoLightsDemo() +demo.run() diff --git a/samples/disco-lights/models/disco_hall.egg.pz b/samples/disco-lights/models/disco_hall.egg.pz new file mode 100644 index 0000000000..a02ef2fd46 Binary files /dev/null and b/samples/disco-lights/models/disco_hall.egg.pz differ diff --git a/samples/disco-lights/models/sphere.egg.pz b/samples/disco-lights/models/sphere.egg.pz new file mode 100644 index 0000000000..2257d21365 Binary files /dev/null and b/samples/disco-lights/models/sphere.egg.pz differ diff --git a/samples/distortion/distortion.sha b/samples/distortion/distortion.sha new file mode 100644 index 0000000000..a4c5b081f6 --- /dev/null +++ b/samples/distortion/distortion.sha @@ -0,0 +1,53 @@ +//Cg +// +// time +// +// You need to pass the frame time here. +// +// desat.x +// +// Desaturation level. If zero, the bloom's color is equal to +// the color of the input pixel. If one, the bloom's color is +// white. +// +// trigger.x +// +// Must be equal to mintrigger. +// +// mintrigger is the minimum brightness to trigger a bloom, +// and maxtrigger is the brightness at which the bloom +// reaches maximum intensity. +// +// trigger.y +// +// Must be equal to (1.0/(maxtrigger-mintrigger)) where +// +// mintrigger is the minimum brightness to trigger a bloom, +// and maxtrigger is the brightness at which the bloom +// reaches maximum intensity. +// + +void vshader(float4 vtx_position : POSITION, + uniform float4x4 mat_modelproj, + uniform float4x4 trans_model_to_clip, + out float4 l_position : POSITION, + out float4 l_texcoord0 : TEXCOORD0) +{ + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = mul(trans_model_to_clip, vtx_position); + l_texcoord0.z = l_texcoord0.w; +} + +void fshader(float4 l_texcoord0 : TEXCOORD0, + uniform sampler2D k_screen : TEXUNIT1, + uniform sampler2D k_waves : TEXUNIT0, + uniform float4 texpad_screen, + in uniform float sys_time, + out float4 o_color : COLOR) +{ + float3 screen = l_texcoord0.xyz / l_texcoord0.w; + float2 texcoords = float2(screen.xy) * texpad_screen.xy + texpad_screen.xy; + float4 disturbance = tex2D(k_waves, texcoords); + //o_color = tex2D(k_screen, texcoords + disturbance.xy * 0.05 * disturbance.z * sys_time.x * 1); + o_color = tex2D(k_screen, texcoords + disturbance.xy * 0.05 * disturbance.z * sin(sys_time.x) * 1); +} diff --git a/samples/distortion/main.py b/samples/distortion/main.py new file mode 100755 index 0000000000..387156aa25 --- /dev/null +++ b/samples/distortion/main.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +# Author: Tree Form starplant@gmail.com + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import FrameBufferProperties, TextNode, BitMask32, LPoint3 +from panda3d.core import WindowProperties, GraphicsOutput, Texture, GraphicsPipe +from direct.showbase.DirectObject import DirectObject +from direct.gui.OnscreenText import OnscreenText +from sys import exit + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), + pos=(-1.25, pos), align=TextNode.ALeft, scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), + pos=(1.25, -0.95), align=TextNode.ARight, scale=.07) + + +class DistortionDemo(ShowBase): + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + if not base.win.getGsg().getSupportsBasicShaders(): + t = addTitle("Distortion Demo: Video driver says Cg shaders not supported.") + return + + self.disableMouse() + self.setBackgroundColor(0, 0, 0) + + # Show the instructions + self.title = addTitle("Panda3D: Tutorial - Distortion Effect") + self.inst1 = addInstructions(0.92, "ESC: Quit") + self.inst2 = addInstructions(0.86, "Space: Toggle distortion filter On/Off") + self.inst4 = addInstructions(0.80, "V: View the render-to-texture results") + + # Load background + self.seascape = loader.loadModel("models/plane") + self.seascape.reparentTo(render) + self.seascape.setPosHpr(0, 145, 0, 0, 0, 0) + self.seascape.setScale(100) + self.seascape.setTexture(loader.loadTexture("models/ocean.jpg")) + + # Create the distortion buffer. This buffer renders like a normal + # scene, + self.distortionBuffer = self.makeFBO("model buffer") + self.distortionBuffer.setSort(-3) + self.distortionBuffer.setClearColor((0, 0, 0, 0)) + + # We have to attach a camera to the distortion buffer. The distortion camera + # must have the same frustum as the main camera. As long as the aspect + # ratios match, the rest will take care of itself. + distortionCamera = self.makeCamera(self.distortionBuffer, scene=render, + lens=self.cam.node().getLens(), mask=BitMask32.bit(4)) + + # load the object with the distortion + self.distortionObject = loader.loadModel("models/boat") + self.distortionObject.setScale(1) + self.distortionObject.setPos(0, 20, -3) + self.distortionObject.hprInterval(10, LPoint3(360, 0, 0)).loop() + self.distortionObject.reparentTo(render) + + # Create the shader that will determime what parts of the scene will + # distortion + distortionShader = loader.loadShader("distortion.sha") + self.distortionObject.setShader(distortionShader) + self.distortionObject.hide(BitMask32.bit(4)) + + # Textures + tex1 = loader.loadTexture("models/water.png") + self.distortionObject.setShaderInput("waves", tex1) + + self.texDistortion = Texture() + self.distortionBuffer.addRenderTexture( + self.texDistortion, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) + self.distortionObject.setShaderInput("screen", self.texDistortion) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + self.accept("v", self.bufferViewer.toggleEnable) + self.accept("V", self.bufferViewer.toggleEnable) + self.bufferViewer.setPosition("llcorner") + self.bufferViewer.setLayout("hline") + self.bufferViewer.setCardSize(0.652, 0) + + # event handling + self.accept("space", self.toggleDistortion) + self.accept("escape", exit, [0]) + self.distortionOn = True + + def makeFBO(self, name): + # This routine creates an offscreen buffer. All the complicated + # parameters are basically demanding capabilities from the offscreen + # buffer - we demand that it be able to render to texture on every + # bitplane, that it can support aux bitplanes, that it track + # the size of the host window, that it can render to texture + # cumulatively, and so forth. + winprops = WindowProperties() + props = FrameBufferProperties() + props.setRgbColor(1) + return self.graphicsEngine.makeOutput( + self.pipe, "model buffer", -2, props, winprops, + GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFRefuseWindow, + self.win.getGsg(), self.win) + + def toggleDistortion(self): + # Toggles the distortion on/off. + if self.distortionOn: + self.distortionObject.hide() + else: + self.distortionObject.show() + self.distortionOn = not(self.distortionOn) + +demo = DistortionDemo() +demo.run() diff --git a/samples/distortion/models/boat.egg.pz b/samples/distortion/models/boat.egg.pz new file mode 100644 index 0000000000..a5604dc525 Binary files /dev/null and b/samples/distortion/models/boat.egg.pz differ diff --git a/samples/distortion/models/ocean.jpg b/samples/distortion/models/ocean.jpg new file mode 100644 index 0000000000..e84a1a91cd Binary files /dev/null and b/samples/distortion/models/ocean.jpg differ diff --git a/samples/distortion/models/plane.egg.pz b/samples/distortion/models/plane.egg.pz new file mode 100644 index 0000000000..0a88c05d92 --- /dev/null +++ b/samples/distortion/models/plane.egg.pz @@ -0,0 +1,2 @@ +xÚ’Ákƒ0ÆïƒþÞc»[¶zÒÒÂN#£©˜¼³Q)þï}±Ñ‰l‡…h4ß÷|¿|D¾A´§O-œÜ·“ª€+¼&Ýbñ@ß RR;Òi°T¢¹¬*0µÐ2Uïá…¤%UøÏøÖâ—) ¢‡>•Þ~sVèæŒVõ}¦U¦/`¡??Jëä¥D¬kÿ!Œdé7M¨ë +`£déã}ÊF¿TÔ>WF#a}²àŽƒ>wÛç'/³0«›£ó9:‚̢ȫ 9.3‹Ì¼ž‘#Àÿˆ|Ÿy‰u[¡.~NÿONØèNž½¹¢ýæÔ•‡õ/?T7AùûxÒ³I \ No newline at end of file diff --git a/samples/distortion/models/water.png b/samples/distortion/models/water.png new file mode 100644 index 0000000000..1ba0c0f65e Binary files /dev/null and b/samples/distortion/models/water.png differ diff --git a/samples/fireflies/light.sha b/samples/fireflies/light.sha new file mode 100644 index 0000000000..e074b15abd --- /dev/null +++ b/samples/fireflies/light.sha @@ -0,0 +1,45 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + out float4 l_position : POSITION, + out float4 l_pos : TEXCOORD0, + uniform float4x4 mat_modelproj, + uniform float4x4 trans_model_to_clip) +{ + l_position=mul(mat_modelproj, vtx_position); + l_pos=mul(trans_model_to_clip, vtx_position); + l_pos.z = l_pos.w; +} + +void fshader(float4 l_pos: TEXCOORD0, + float4 l_scale: TEXCOORD1, + uniform sampler2D k_texnormal : TEXUNIT0, + uniform sampler2D k_texalbedo : TEXUNIT1, + uniform sampler2D k_texdepth : TEXUNIT2, + uniform float4 texpad_texnormal, + uniform float4 k_proj, + uniform float4 vspos_model, + uniform float4 k_lightcolor, + uniform float4 row0_model_to_view, + out float4 o_color: COLOR) +{ + float3 screen = l_pos.xyz / l_pos.w; + float2 texcoords = float2(screen.xy) * texpad_texnormal.xy + texpad_texnormal.xy; + + float4 albedo = tex2D(k_texalbedo, texcoords); + float4 normal = tex2D(k_texnormal, texcoords); + float depth = tex2D(k_texdepth, texcoords); + + float3 view = (screen.xzy * k_proj.xyz) / (depth + k_proj.w); + + float3 lightvec = float3(vspos_model) - view; + float lightdist = length(lightvec); + float3 lightdir = lightvec / lightdist; + float scaledist = (lightdist / row0_model_to_view.x); + float falloff = saturate(1.0 - scaledist); + float brite = falloff * falloff * dot(lightdir, float3(normal)); + o_color = albedo * k_lightcolor * brite; + o_color.a = 1; +} diff --git a/samples/fireflies/main.py b/samples/fireflies/main.py new file mode 100755 index 0000000000..994a7d09ec --- /dev/null +++ b/samples/fireflies/main.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python + +# Author: Josh Yelon +# Date: 7/11/2005 +# +# See the associated manual page for an explanation. +# +from direct.showbase.ShowBase import ShowBase +from panda3d.core import FrameBufferProperties, WindowProperties +from panda3d.core import GraphicsPipe, GraphicsOutput +from panda3d.core import Filename, Texture, Shader +from panda3d.core import RenderState, CardMaker +from panda3d.core import PandaNode, TextNode, NodePath +from panda3d.core import RenderAttrib, AlphaTestAttrib, ColorBlendAttrib +from panda3d.core import CullFaceAttrib, DepthTestAttrib, DepthWriteAttrib +from panda3d.core import LPoint3, LVector3, BitMask32 +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.interval.MetaInterval import Sequence +from direct.task.Task import Task +from direct.actor.Actor import Actor +import sys +import os +import random + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class FireflyDemo(ShowBase): + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + self.setBackgroundColor((0, 0, 0, 0)) + + # Preliminary capabilities check. + + if not self.win.getGsg().getSupportsBasicShaders(): + self.t = addTitle("Firefly Demo: Video driver reports that Cg " + "shaders are not supported.") + return + if not self.win.getGsg().getSupportsDepthTexture(): + self.t = addTitle("Firefly Demo: Video driver reports that depth " + "textures are not supported.") + return + + # This algorithm uses two offscreen buffers, one of which has + # an auxiliary bitplane, and the offscreen buffers share a single + # depth buffer. This is a heck of a complicated buffer setup. + + self.modelbuffer = self.makeFBO("model buffer", 1) + self.lightbuffer = self.makeFBO("light buffer", 0) + + # Creation of a high-powered buffer can fail, if the graphics card + # doesn't support the necessary OpenGL extensions. + + if self.modelbuffer is None or self.lightbuffer is None: + self.t = addTitle("Toon Shader: Video driver does not support " + "multiple render targets") + return + + # Create four render textures: depth, normal, albedo, and final. + # attach them to the various bitplanes of the offscreen buffers. + + self.texDepth = Texture() + self.texDepth.setFormat(Texture.FDepthStencil) + self.texAlbedo = Texture() + self.texNormal = Texture() + self.texFinal = Texture() + + self.modelbuffer.addRenderTexture(self.texDepth, + GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil) + self.modelbuffer.addRenderTexture(self.texAlbedo, + GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) + self.modelbuffer.addRenderTexture(self.texNormal, + GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0) + + self.lightbuffer.addRenderTexture(self.texFinal, + GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) + + # Set the near and far clipping planes. + + self.cam.node().getLens().setNear(50.0) + self.cam.node().getLens().setFar(500.0) + lens = self.cam.node().getLens() + + # This algorithm uses three cameras: one to render the models into the + # model buffer, one to render the lights into the light buffer, and + # one to render "plain" stuff (non-deferred shaded) stuff into the + # light buffer. Each camera has a bitmask to identify it. + + self.modelMask = 1 + self.lightMask = 2 + self.plainMask = 4 + + self.modelcam = self.makeCamera(self.modelbuffer, + lens=lens, scene=render, mask=self.modelMask) + self.lightcam = self.makeCamera(self.lightbuffer, + lens=lens, scene=render, mask=self.lightMask) + self.plaincam = self.makeCamera(self.lightbuffer, + lens=lens, scene=render, mask=self.plainMask) + + # Panda's main camera is not used. + + self.cam.node().setActive(0) + + # Take explicit control over the order in which the three + # buffers are rendered. + + self.modelbuffer.setSort(1) + self.lightbuffer.setSort(2) + self.win.setSort(3) + + # Within the light buffer, control the order of the two cams. + + self.lightcam.node().getDisplayRegion(0).setSort(1) + self.plaincam.node().getDisplayRegion(0).setSort(2) + + # By default, panda usually clears the screen before every + # camera and before every window. Tell it not to do that. + # Then, tell it specifically when to clear and what to clear. + + self.modelcam.node().getDisplayRegion(0).disableClears() + self.lightcam.node().getDisplayRegion(0).disableClears() + self.plaincam.node().getDisplayRegion(0).disableClears() + self.cam.node().getDisplayRegion(0).disableClears() + self.cam2d.node().getDisplayRegion(0).disableClears() + self.modelbuffer.disableClears() + self.win.disableClears() + + self.modelbuffer.setClearColorActive(1) + self.modelbuffer.setClearDepthActive(1) + self.lightbuffer.setClearColorActive(1) + self.lightbuffer.setClearColor((0, 0, 0, 1)) + + # Miscellaneous stuff. + + self.disableMouse() + self.camera.setPos(-9.112, -211.077, 46.951) + self.camera.setHpr(0, -7.5, 2.4) + random.seed() + + # Calculate the projection parameters for the final shader. + # The math here is too complex to explain in an inline comment, + # I've put in a full explanation into the HTML intro. + + proj = self.cam.node().getLens().getProjectionMat() + proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0) + proj_y = 0.5 * proj.getCell(3, 2) + proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1) + proj_w = -0.5 - 0.5 * proj.getCell(1, 2) + + # Configure the render state of the model camera. + + tempnode = NodePath(PandaNode("temp node")) + tempnode.setAttrib( + AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5)) + tempnode.setShader(loader.loadShader("model.sha")) + tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual)) + self.modelcam.node().setInitialState(tempnode.getState()) + + # Configure the render state of the light camera. + + tempnode = NodePath(PandaNode("temp node")) + tempnode.setShader(loader.loadShader("light.sha")) + tempnode.setShaderInput("texnormal", self.texNormal) + tempnode.setShaderInput("texalbedo", self.texAlbedo) + tempnode.setShaderInput("texdepth", self.texDepth) + tempnode.setShaderInput("proj", (proj_x, proj_y, proj_z, proj_w)) + tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, + ColorBlendAttrib.OOne, ColorBlendAttrib.OOne)) + tempnode.setAttrib( + CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) + # The next line causes problems on Linux. + # tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual)) + tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff)) + self.lightcam.node().setInitialState(tempnode.getState()) + + # Configure the render state of the plain camera. + + rs = RenderState.makeEmpty() + self.plaincam.node().setInitialState(rs) + + # Clear any render attribs on the root node. This is necessary + # because by default, panda assigns some attribs to the root + # node. These default attribs will override the + # carefully-configured render attribs that we just attached + # to the cameras. The simplest solution is to just clear + # them all out. + + render.setState(RenderState.makeEmpty()) + + # My artist created a model in which some of the polygons + # don't have textures. This confuses the shader I wrote. + # This little hack guarantees that everything has a texture. + + white = loader.loadTexture("models/white.jpg") + render.setTexture(white, 0) + + # Create two subroots, to help speed cull traversal. + + self.lightroot = NodePath(PandaNode("lightroot")) + self.lightroot.reparentTo(render) + self.modelroot = NodePath(PandaNode("modelroot")) + self.modelroot.reparentTo(render) + self.lightroot.hide(BitMask32(self.modelMask)) + self.modelroot.hide(BitMask32(self.lightMask)) + self.modelroot.hide(BitMask32(self.plainMask)) + + # Load the model of a forest. Make it visible to the model camera. + # This is a big model, so we load it asynchronously while showing a + # load text. We do this by passing in a callback function. + self.loading = addTitle("Loading models...") + + self.forest = NodePath(PandaNode("Forest Root")) + self.forest.reparentTo(render) + self.forest.hide(BitMask32(self.lightMask | self.plainMask)) + loader.loadModel([ + "models/background", + "models/foliage01", + "models/foliage02", + "models/foliage03", + "models/foliage04", + "models/foliage05", + "models/foliage06", + "models/foliage07", + "models/foliage08", + "models/foliage09"], + callback=self.finishLoading) + + # Cause the final results to be rendered into the main window on a + # card. + + self.card = self.lightbuffer.getTextureCard() + self.card.setTexture(self.texFinal) + self.card.reparentTo(render2d) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + + self.bufferViewer.setPosition("llcorner") + self.bufferViewer.setCardSize(0, 0.40) + self.bufferViewer.setLayout("vline") + self.toggleCards() + self.toggleCards() + + # Firefly parameters + + self.fireflies = [] + self.sequences = [] + self.scaleseqs = [] + self.glowspheres = [] + self.fireflysize = 1.0 + self.spheremodel = loader.loadModel("misc/sphere") + + # Create the firefly model, a fuzzy dot + dotSize = 1.0 + cm = CardMaker("firefly") + cm.setFrame(-dotSize, dotSize, -dotSize, dotSize) + self.firefly = NodePath(cm.generate()) + self.firefly.setTexture(loader.loadTexture("models/firefly.png")) + self.firefly.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add, + ColorBlendAttrib.O_incoming_alpha, ColorBlendAttrib.O_one)) + + # these allow you to change parameters in realtime + + self.accept("escape", sys.exit, [0]) + self.accept("arrow_up", self.incFireflyCount, [1.1111111]) + self.accept("arrow_down", self.decFireflyCount, [0.9000000]) + self.accept("arrow_right", self.setFireflySize, [1.1111111]) + self.accept("arrow_left", self.setFireflySize, [0.9000000]) + self.accept("v", self.toggleCards) + self.accept("V", self.toggleCards) + + def finishLoading(self, models): + # This function is used as callback to loader.loadModel, and called + # when all of the models have finished loading. + + # Attach the models to the scene graph. + for model in models: + model.reparentTo(self.forest) + + # Show the instructions. + self.loading.destroy() + self.title = addTitle("Panda3D: Tutorial - Fireflies using Deferred Shading") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Up/Down: More / Fewer Fireflies (Count: unknown)") + self.inst3 = addInstructions(0.18, "Right/Left: Bigger / Smaller Fireflies (Radius: unknown)") + self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") + + self.setFireflySize(25.0) + while len(self.fireflies) < 5: + self.addFirefly() + self.updateReadout() + + self.nextadd = 0 + taskMgr.add(self.spawnTask, "spawner") + + def makeFBO(self, name, auxrgba): + # This routine creates an offscreen buffer. All the complicated + # parameters are basically demanding capabilities from the offscreen + # buffer - we demand that it be able to render to texture on every + # bitplane, that it can support aux bitplanes, that it track + # the size of the host window, that it can render to texture + # cumulatively, and so forth. + winprops = WindowProperties() + props = FrameBufferProperties() + props.setRgbColor(True) + props.setRgbaBits(8, 8, 8, 8) + props.setDepthBits(1) + props.setAuxRgba(auxrgba) + return self.graphicsEngine.makeOutput( + self.pipe, "model buffer", -2, + props, winprops, + GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFCanBindEvery | + GraphicsPipe.BFRttCumulative | GraphicsPipe.BFRefuseWindow, + self.win.getGsg(), self.win) + + def addFirefly(self): + pos1 = LPoint3(random.uniform(-50, 50), random.uniform(-100, 150), random.uniform(-10, 80)) + dir = LVector3(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)) + dir.normalize() + pos2 = pos1 + (dir * 20) + fly = self.lightroot.attachNewNode(PandaNode("fly")) + glow = fly.attachNewNode(PandaNode("glow")) + dot = fly.attachNewNode(PandaNode("dot")) + color_r = 1.0 + color_g = random.uniform(0.8, 1.0) + color_b = min(color_g, random.uniform(0.5, 1.0)) + fly.setColor(color_r, color_g, color_b, 1.0) + fly.setShaderInput("lightcolor", color_r, color_g, color_b, 1.0) + int1 = fly.posInterval(random.uniform(7, 12), pos1, pos2) + int2 = fly.posInterval(random.uniform(7, 12), pos2, pos1) + si1 = fly.scaleInterval(random.uniform(0.8, 1.5), + LPoint3(0.2, 0.2, 0.2), LPoint3(0.2, 0.2, 0.2)) + si2 = fly.scaleInterval(random.uniform(1.5, 0.8), + LPoint3(1.0, 1.0, 1.0), LPoint3(0.2, 0.2, 0.2)) + si3 = fly.scaleInterval(random.uniform(1.0, 2.0), + LPoint3(0.2, 0.2, 0.2), LPoint3(1.0, 1.0, 1.0)) + siseq = Sequence(si1, si2, si3) + siseq.loop() + siseq.setT(random.uniform(0, 1000)) + seq = Sequence(int1, int2) + seq.loop() + self.spheremodel.instanceTo(glow) + self.firefly.instanceTo(dot) + glow.setScale(self.fireflysize * 1.1) + glow.hide(BitMask32(self.modelMask | self.plainMask)) + dot.hide(BitMask32(self.modelMask | self.lightMask)) + dot.setColor(color_r, color_g, color_b, 1.0) + self.fireflies.append(fly) + self.sequences.append(seq) + self.glowspheres.append(glow) + self.scaleseqs.append(siseq) + + def updateReadout(self): + self.inst2.destroy() + self.inst2 = addInstructions(0.12, + "Up/Down: More / Fewer Fireflies (Currently: %d)" % len(self.fireflies)) + self.inst3.destroy() + self.inst3 = addInstructions(0.18, + "Right/Left: Bigger / Smaller Fireflies (Radius: %d ft)" % self.fireflysize) + + def toggleCards(self): + self.bufferViewer.toggleEnable() + # When the cards are not visible, I also disable the color clear. + # This color-clear is actually not necessary, the depth-clear is + # sufficient for the purposes of the algorithm. + if (self.bufferViewer.isEnabled()): + self.modelbuffer.setClearColorActive(True) + else: + self.modelbuffer.setClearColorActive(False) + + def incFireflyCount(self, scale): + n = int((len(self.fireflies) * scale) + 1) + while (n > len(self.fireflies)): + self.addFirefly() + self.updateReadout() + + def decFireflyCount(self, scale): + n = int(len(self.fireflies) * scale) + if (n < 1): + n = 1 + while (len(self.fireflies) > n): + self.glowspheres.pop() + self.sequences.pop().finish() + self.scaleseqs.pop().finish() + self.fireflies.pop().removeNode() + self.updateReadout() + + def setFireflySize(self, n): + n = n * self.fireflysize + self.fireflysize = n + for x in self.glowspheres: + x.setScale(self.fireflysize * 1.1) + self.updateReadout() + + def spawnTask(self, task): + if task.time > self.nextadd: + self.nextadd = task.time + 1.0 + if (len(self.fireflies) < 300): + self.incFireflyCount(1.03) + return Task.cont + +demo = FireflyDemo() +demo.run() diff --git a/samples/fireflies/model.sha b/samples/fireflies/model.sha new file mode 100644 index 0000000000..f1867ee5da --- /dev/null +++ b/samples/fireflies/model.sha @@ -0,0 +1,35 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + float4 vtx_normal : NORMAL, + float4 vtx_color : COLOR, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0, + out float4 l_color : COLOR, + out float3 l_normal : TEXCOORD1, + uniform float4x4 mat_modelproj, + uniform float4x4 itp_modelview) +{ + l_position=mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; + l_color = vtx_color; + l_normal = (float3)mul(itp_modelview, vtx_normal); +} + +void fshader(float2 l_texcoord0: TEXCOORD0, + float4 l_color: COLOR, + float3 l_normal: TEXCOORD1, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color: COLOR0, + out float4 o_normal: COLOR1) +{ + l_normal = normalize(l_normal); + o_color = l_color * tex2D(tex_0, l_texcoord0); + o_normal.rgb = (l_normal * 0.5) + float3(0.5, 0.5, 0.5); + o_normal.a = o_color.a; +} + + diff --git a/samples/fireflies/models/Bark.jpg b/samples/fireflies/models/Bark.jpg new file mode 100644 index 0000000000..f20500e6d2 Binary files /dev/null and b/samples/fireflies/models/Bark.jpg differ diff --git a/samples/fireflies/models/Bark03.jpg b/samples/fireflies/models/Bark03.jpg new file mode 100644 index 0000000000..f5a3d982f6 Binary files /dev/null and b/samples/fireflies/models/Bark03.jpg differ diff --git a/samples/fireflies/models/background.egg.pz b/samples/fireflies/models/background.egg.pz new file mode 100644 index 0000000000..b3981f5df6 Binary files /dev/null and b/samples/fireflies/models/background.egg.pz differ diff --git a/samples/fireflies/models/bigleaf3.png b/samples/fireflies/models/bigleaf3.png new file mode 100644 index 0000000000..39ed4c8640 Binary files /dev/null and b/samples/fireflies/models/bigleaf3.png differ diff --git a/samples/fireflies/models/elmleaf.png b/samples/fireflies/models/elmleaf.png new file mode 100644 index 0000000000..c3bc9a742f Binary files /dev/null and b/samples/fireflies/models/elmleaf.png differ diff --git a/samples/fireflies/models/firefly.png b/samples/fireflies/models/firefly.png new file mode 100644 index 0000000000..0e95d39cde Binary files /dev/null and b/samples/fireflies/models/firefly.png differ diff --git a/samples/fireflies/models/foliage01.egg.pz b/samples/fireflies/models/foliage01.egg.pz new file mode 100644 index 0000000000..50249f973e Binary files /dev/null and b/samples/fireflies/models/foliage01.egg.pz differ diff --git a/samples/fireflies/models/foliage02.egg.pz b/samples/fireflies/models/foliage02.egg.pz new file mode 100644 index 0000000000..f8cc0d2957 Binary files /dev/null and b/samples/fireflies/models/foliage02.egg.pz differ diff --git a/samples/fireflies/models/foliage03.egg.pz b/samples/fireflies/models/foliage03.egg.pz new file mode 100644 index 0000000000..764cda7ebc Binary files /dev/null and b/samples/fireflies/models/foliage03.egg.pz differ diff --git a/samples/fireflies/models/foliage04.egg.pz b/samples/fireflies/models/foliage04.egg.pz new file mode 100644 index 0000000000..b0531ee51f Binary files /dev/null and b/samples/fireflies/models/foliage04.egg.pz differ diff --git a/samples/fireflies/models/foliage05.egg.pz b/samples/fireflies/models/foliage05.egg.pz new file mode 100644 index 0000000000..5c8d0a75e6 Binary files /dev/null and b/samples/fireflies/models/foliage05.egg.pz differ diff --git a/samples/fireflies/models/foliage06.egg.pz b/samples/fireflies/models/foliage06.egg.pz new file mode 100644 index 0000000000..ceed26624d Binary files /dev/null and b/samples/fireflies/models/foliage06.egg.pz differ diff --git a/samples/fireflies/models/foliage07.egg.pz b/samples/fireflies/models/foliage07.egg.pz new file mode 100644 index 0000000000..dd7c6909e0 Binary files /dev/null and b/samples/fireflies/models/foliage07.egg.pz differ diff --git a/samples/fireflies/models/foliage08.egg.pz b/samples/fireflies/models/foliage08.egg.pz new file mode 100644 index 0000000000..968a496444 Binary files /dev/null and b/samples/fireflies/models/foliage08.egg.pz differ diff --git a/samples/fireflies/models/foliage09.egg.pz b/samples/fireflies/models/foliage09.egg.pz new file mode 100644 index 0000000000..dfd7583f08 Binary files /dev/null and b/samples/fireflies/models/foliage09.egg.pz differ diff --git a/samples/fireflies/models/indt04S.jpg b/samples/fireflies/models/indt04S.jpg new file mode 100644 index 0000000000..ba52ae94fc Binary files /dev/null and b/samples/fireflies/models/indt04S.jpg differ diff --git a/samples/fireflies/models/noise.jpg b/samples/fireflies/models/noise.jpg new file mode 100644 index 0000000000..12231717db Binary files /dev/null and b/samples/fireflies/models/noise.jpg differ diff --git a/samples/fireflies/models/oakleaf.png b/samples/fireflies/models/oakleaf.png new file mode 100644 index 0000000000..5aa44c1e11 Binary files /dev/null and b/samples/fireflies/models/oakleaf.png differ diff --git a/samples/fireflies/models/shrb03S.jpg b/samples/fireflies/models/shrb03S.jpg new file mode 100644 index 0000000000..3fcffb2896 Binary files /dev/null and b/samples/fireflies/models/shrb03S.jpg differ diff --git a/samples/fireflies/models/white.jpg b/samples/fireflies/models/white.jpg new file mode 100644 index 0000000000..6707c09aa6 Binary files /dev/null and b/samples/fireflies/models/white.jpg differ diff --git a/samples/fireflies/models/wood.jpg b/samples/fireflies/models/wood.jpg new file mode 100644 index 0000000000..3b74af8699 Binary files /dev/null and b/samples/fireflies/models/wood.jpg differ diff --git a/samples/fireflies/models/wood02S.jpg b/samples/fireflies/models/wood02S.jpg new file mode 100644 index 0000000000..5df8ee6a1d Binary files /dev/null and b/samples/fireflies/models/wood02S.jpg differ diff --git a/samples/fractal-plants/barkTexture.jpg b/samples/fractal-plants/barkTexture.jpg new file mode 100644 index 0000000000..85651f1304 Binary files /dev/null and b/samples/fractal-plants/barkTexture.jpg differ diff --git a/samples/fractal-plants/main.py b/samples/fractal-plants/main.py new file mode 100755 index 0000000000..fb16a5b1bc --- /dev/null +++ b/samples/fractal-plants/main.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python + +# Author: Kwasi Mensah (kmensah@andrew.cmu.edu) +# Date: 8/05/2005 +# +# This demo shows how to make quasi-fractal trees in Panda. +# Its primarily meant to be a more complex example on how to use +# Panda's Geom interface. +# +from direct.showbase.ShowBase import ShowBase +from panda3d.core import Filename, InternalName +from panda3d.core import GeomVertexArrayFormat, GeomVertexFormat +from panda3d.core import Geom, GeomNode, GeomTrifans, GeomTristrips +from panda3d.core import GeomVertexReader, GeomVertexWriter +from panda3d.core import GeomVertexRewriter, GeomVertexData +from panda3d.core import PerspectiveLens, TextNode +from panda3d.core import TransformState, CullFaceAttrib +from panda3d.core import Light, AmbientLight, Spotlight +from panda3d.core import NodePath +from panda3d.core import LVector3, LMatrix4 +from direct.task.Task import Task +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +import math +import random +import time +import sys +import os + +random.seed() +base = ShowBase() +base.disableMouse() +base.camera.setPos(0, -180, 30) +numPrimitives = 0 + +title = OnscreenText(text="Panda3D: Tutorial - Procedurally Making a Tree", + style=1, fg=(1, 1, 1, 1), parent=base.a2dBottomCenter, + pos=(0, 0.1), scale=.08) +qEvent = OnscreenText( + text="Q: Start Scene Over", + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.10), + scale=.05) +wEvent = OnscreenText( + text="W: Add Another Tree", + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.16), + scale=.05) + + +# this computes the new Axis which we'll make a branch grow alowng when we +# split +def randomAxis(vecList): + fwd = vecList[0] + perp1 = vecList[1] + perp2 = vecList[2] + + nfwd = fwd + perp1 * (2 * random.random() - 1) + \ + perp2 * (2 * random.random() - 1) + nfwd.normalize() + + nperp2 = nfwd.cross(perp1) + nperp2.normalize() + + nperp1 = nfwd.cross(nperp2) + nperp1.normalize() + + return [nfwd, nperp1, nperp2] + + +# this makes smalle variations in direction when we are growing a branch +# but not splitting +def smallRandomAxis(vecList): + fwd = vecList[0] + perp1 = vecList[1] + perp2 = vecList[2] + + nfwd = fwd + perp1 * (1 * random.random() - 0.5) + \ + perp2 * (1 * random.random() - 0.5) + nfwd.normalize() + + nperp2 = nfwd.cross(perp1) + nperp2.normalize() + + nperp1 = nfwd.cross(nperp2) + nperp1.normalize() + + return [nfwd, nperp1, nperp2] + + +# this draws the body of the tree. This draws a ring of vertices and connects the rings with +# triangles to form the body. +# this keepDrawing paramter tells the function wheter or not we're at an end +# if the vertices before you were an end, dont draw branches to it +def drawBody(nodePath, vdata, pos, vecList, radius=1, keepDrawing=True, numVertices=8): + + circleGeom = Geom(vdata) + + vertWriter = GeomVertexWriter(vdata, "vertex") + colorWriter = GeomVertexWriter(vdata, "color") + normalWriter = GeomVertexWriter(vdata, "normal") + drawReWriter = GeomVertexRewriter(vdata, "drawFlag") + texReWriter = GeomVertexRewriter(vdata, "texcoord") + + startRow = vdata.getNumRows() + vertWriter.setRow(startRow) + colorWriter.setRow(startRow) + normalWriter.setRow(startRow) + + sCoord = 0 + + if (startRow != 0): + texReWriter.setRow(startRow - numVertices) + sCoord = texReWriter.getData2f().getX() + 1 + + drawReWriter.setRow(startRow - numVertices) + if(drawReWriter.getData1f() == False): + sCoord -= 1 + + drawReWriter.setRow(startRow) + texReWriter.setRow(startRow) + + angleSlice = 2 * math.pi / numVertices + currAngle = 0 + + #axisAdj=LMatrix4.rotateMat(45, axis)*LMatrix4.scaleMat(radius)*LMatrix4.translateMat(pos) + + perp1 = vecList[1] + perp2 = vecList[2] + + # vertex information is written here + for i in range(numVertices): + adjCircle = pos + \ + (perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle)) * \ + radius + normal = perp1 * math.cos(currAngle) + perp2 * math.sin(currAngle) + normalWriter.addData3f(normal) + vertWriter.addData3f(adjCircle) + texReWriter.addData2f(sCoord, (i + 0.001) / (numVertices - 1)) + colorWriter.addData4f(0.5, 0.5, 0.5, 1) + drawReWriter.addData1f(keepDrawing) + currAngle += angleSlice + + drawReader = GeomVertexReader(vdata, "drawFlag") + drawReader.setRow(startRow - numVertices) + + # we cant draw quads directly so we use Tristrips + if (startRow != 0) & (drawReader.getData1f() != False): + lines = GeomTristrips(Geom.UHStatic) + half = int(numVertices * 0.5) + for i in range(numVertices): + lines.addVertex(i + startRow) + if i < half: + lines.addVertex(i + startRow - half) + else: + lines.addVertex(i + startRow - half - numVertices) + + lines.addVertex(startRow) + lines.addVertex(startRow - half) + lines.closePrimitive() + lines.decompose() + circleGeom.addPrimitive(lines) + + circleGeomNode = GeomNode("Debug") + circleGeomNode.addGeom(circleGeom) + + # I accidentally made the front-face face inwards. Make reverse makes the tree render properly and + # should cause any surprises to any poor programmer that tries to use + # this code + circleGeomNode.setAttrib(CullFaceAttrib.makeReverse(), 1) + global numPrimitives + numPrimitives += numVertices * 2 + + nodePath.attachNewNode(circleGeomNode) + + +# this draws leafs when we reach an end +def drawLeaf(nodePath, vdata, pos=LVector3(0, 0, 0), vecList=[LVector3(0, 0, 1), LVector3(1, 0, 0), LVector3(0, -1, 0)], scale=0.125): + + # use the vectors that describe the direction the branch grows to make the right + # rotation matrix + newCs = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + newCs.setRow(0, vecList[2]) # right + newCs.setRow(1, vecList[1]) # up + newCs.setRow(2, vecList[0]) # forward + newCs.setRow(3, (0, 0, 0)) + newCs.setCol(3, (0, 0, 0, 1)) + + axisAdj = LMatrix4.scaleMat(scale) * newCs * LMatrix4.translateMat(pos) + + # orginlly made the leaf out of geometry but that didnt look good + # I also think there should be a better way to handle the leaf texture other than + # hardcoding the filename + leafModel = loader.loadModel('models/shrubbery') + leafTexture = loader.loadTexture('models/material-10-cl.png') + + leafModel.reparentTo(nodePath) + leafModel.setTexture(leafTexture, 1) + leafModel.setTransform(TransformState.makeMat(axisAdj)) + + +# recursive algorthim to make the tree +def makeFractalTree(bodydata, nodePath, length, pos=LVector3(0, 0, 0), numIterations=11, numCopies=4, vecList=[LVector3(0, 0, 1), LVector3(1, 0, 0), LVector3(0, -1, 0)]): + if numIterations > 0: + + drawBody(nodePath, bodydata, pos, vecList, length.getX()) + + # move foward along the right axis + newPos = pos + vecList[0] * length.length() + + # only branch every third level (sorta) + if numIterations % 3 == 0: + # decrease dimensions when we branch + length = LVector3( + length.getX() / 2, length.getY() / 2, length.getZ() / 1.1) + for i in range(numCopies): + makeFractalTree(bodydata, nodePath, length, newPos, + numIterations - 1, numCopies, randomAxis(vecList)) + else: + # just make another branch connected to this one with a small + # variation in direction + makeFractalTree(bodydata, nodePath, length, newPos, + numIterations - 1, numCopies, smallRandomAxis(vecList)) + + else: + drawBody(nodePath, bodydata, pos, vecList, length.getX(), False) + drawLeaf(nodePath, bodydata, pos, vecList) + + +alight = AmbientLight('alight') +alight.setColor((0.5, 0.5, 0.5, 1)) +alnp = render.attachNewNode(alight) +render.setLight(alnp) + +slight = Spotlight('slight') +slight.setColor((1, 1, 1, 1)) +lens = PerspectiveLens() +slight.setLens(lens) +slnp = render.attachNewNode(slight) +render.setLight(slnp) + +slnp.setPos(0, 0, 40) + +# rotating light to show that normals are calculated correctly +def updateLight(task): + global slnp + currPos = slnp.getPos() + currPos.setX(100 * math.cos(task.time) / 2) + currPos.setY(100 * math.sin(task.time) / 2) + slnp.setPos(currPos) + + slnp.lookAt(render) + return Task.cont + +taskMgr.add(updateLight, "rotating Light") + + +# add some interactivity to the program +class MyTapper(DirectObject): + + def __init__(self): + formatArray = GeomVertexArrayFormat() + formatArray.addColumn( + InternalName.make("drawFlag"), 1, Geom.NTUint8, Geom.COther) + + format = GeomVertexFormat(GeomVertexFormat.getV3n3cpt2()) + format.addArray(formatArray) + self.format = GeomVertexFormat.registerFormat(format) + + bodydata = GeomVertexData("body vertices", format, Geom.UHStatic) + + self.barkTexture = loader.loadTexture("barkTexture.jpg") + treeNodePath = NodePath("Tree Holder") + makeFractalTree(bodydata, treeNodePath, LVector3(4, 4, 7)) + + treeNodePath.setTexture(self.barkTexture, 1) + treeNodePath.reparentTo(render) + + self.accept("q", self.regenTree) + self.accept("w", self.addTree) + self.accept("arrow_up", self.upIterations) + self.accept("arrow_down", self.downIterations) + self.accept("arrow_right", self.upCopies) + self.accept("arrow_left", self.downCopies) + + self.numIterations = 11 + self.numCopies = 4 + + self.upDownEvent = OnscreenText( + text="Up/Down: Increase/Decrease the number of iterations (" + str( + self.numIterations) + ")", + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.22), + scale=.05, mayChange=True) + + self.leftRightEvent = OnscreenText( + text="Left/Right: Increase/Decrease branching (" + str( + self.numCopies) + ")", + parent=base.a2dTopLeft, align=TextNode.ALeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.28), + scale=.05, mayChange=True) + + def upIterations(self): + self.numIterations += 1 + self.upDownEvent.setText( + "Up/Down: Increase/Decrease the number of iterations (" + str(self.numIterations) + ")") + + def downIterations(self): + self.numIterations -= 1 + self.upDownEvent.setText( + "Up/Down: Increase/Decrease the number of iterations (" + str(self.numIterations) + ")") + + def upCopies(self): + self.numCopies += 1 + self.leftRightEvent.setText( + "Left/Right: Increase/Decrease branching (" + str(self.numCopies) + ")") + + def downCopies(self): + self.numCopies -= 1 + self.leftRightEvent.setText( + "Left/Right: Increase/Decrease branching (" + str(self.numCopies) + ")") + + def regenTree(self): + forest = render.findAllMatches("Tree Holder") + forest.detach() + + bodydata = GeomVertexData("body vertices", self.format, Geom.UHStatic) + + treeNodePath = NodePath("Tree Holder") + makeFractalTree(bodydata, treeNodePath, LVector3(4, 4, 7), LVector3( + 0, 0, 0), self.numIterations, self.numCopies) + + treeNodePath.setTexture(self.barkTexture, 1) + treeNodePath.reparentTo(render) + + def addTree(self): + bodydata = GeomVertexData("body vertices", self.format, Geom.UHStatic) + + randomPlace = LVector3( + 200 * random.random() - 100, 200 * random.random() - 100, 0) + # randomPlace.normalize() + + treeNodePath = NodePath("Tree Holder") + makeFractalTree(bodydata, treeNodePath, LVector3( + 4, 4, 7), randomPlace, self.numIterations, self.numCopies) + + treeNodePath.setTexture(self.barkTexture, 1) + treeNodePath.reparentTo(render) + +t = MyTapper() +print(numPrimitives) + +base.run() diff --git a/samples/fractal-plants/models/material-10-cl.png b/samples/fractal-plants/models/material-10-cl.png new file mode 100644 index 0000000000..e4d6ad1d08 Binary files /dev/null and b/samples/fractal-plants/models/material-10-cl.png differ diff --git a/samples/fractal-plants/models/shrubbery.egg.pz b/samples/fractal-plants/models/shrubbery.egg.pz new file mode 100644 index 0000000000..7bfc21da6e Binary files /dev/null and b/samples/fractal-plants/models/shrubbery.egg.pz differ diff --git a/samples/glow-filter/advanced.py b/samples/glow-filter/advanced.py new file mode 100755 index 0000000000..7d74780906 --- /dev/null +++ b/samples/glow-filter/advanced.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +# Author: Kwasi Mensah (kmensah@andrew.cmu.edu) +# Date: 7/25/2005 + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import Filename, Shader +from panda3d.core import PandaNode, NodePath +from panda3d.core import ColorBlendAttrib +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import TextNode, LPoint3, LVector4 +from direct.showbase.DirectObject import DirectObject +from direct.gui.OnscreenText import OnscreenText +from direct.actor.Actor import Actor +import sys +import os + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), scale=.08) + + +# This function is responsible for setting up the two blur filters. +# It just makes a temp Buffer, puts a screen aligned card, and then sets +# the appropiate shader to do all the work. Gaussian blurs are decomposable +# into a two-pass algorithm which is faster than the equivalent one-pass +# algorithm, so we do it in two passes: one pass that blurs in the horizontal +# direction, and one in the vertical direction. +def makeFilterBuffer(srcbuffer, name, sort, prog): + blurBuffer = base.win.makeTextureBuffer(name, 512, 512) + blurBuffer.setSort(sort) + blurBuffer.setClearColor(LVector4(1, 0, 0, 1)) + blurCamera = base.makeCamera2d(blurBuffer) + blurScene = NodePath("new Scene") + blurCamera.node().setScene(blurScene) + shader = loader.loadShader(prog) + card = srcbuffer.getTextureCard() + card.reparentTo(blurScene) + card.setShader(shader) + return blurBuffer + + +class GlowDemo(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + base.disableMouse() + base.setBackgroundColor(0, 0, 0) + camera.setPos(0, -50, 0) + + # Check video card capabilities. + if not base.win.getGsg().getSupportsBasicShaders(): + addTitle( + "Glow Filter: Video driver reports that Cg shaders are not supported.") + return + + # Post the instructions + self.title = addTitle("Panda3D: Tutorial - Glow Filter") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Space: Toggle Glow Filter On/Off") + self.inst3 = addInstructions(0.18, "Enter: Toggle Running/Spinning") + self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") + + # Create the shader that will determime what parts of the scene will + # glow + glowShader = loader.loadShader("shaders/glowShader.sha") + + # load our model + self.tron = Actor() + self.tron.loadModel("models/tron") + self.tron.loadAnims({"running": "models/tron_anim"}) + self.tron.reparentTo(render) + self.interval = self.tron.hprInterval(60, LPoint3(360, 0, 0)) + self.interval.loop() + self.isRunning = False + + # put some lighting on the tron model + dlight = DirectionalLight('dlight') + alight = AmbientLight('alight') + dlnp = render.attachNewNode(dlight) + alnp = render.attachNewNode(alight) + dlight.setColor(LVector4(1.0, 0.7, 0.2, 1)) + alight.setColor(LVector4(0.2, 0.2, 0.2, 1)) + dlnp.setHpr(0, -60, 0) + render.setLight(dlnp) + render.setLight(alnp) + + # create the glow buffer. This buffer renders like a normal scene, + # except that only the glowing materials should show up nonblack. + glowBuffer = base.win.makeTextureBuffer("Glow scene", 512, 512) + glowBuffer.setSort(-3) + glowBuffer.setClearColor(LVector4(0, 0, 0, 1)) + + # We have to attach a camera to the glow buffer. The glow camera + # must have the same frustum as the main camera. As long as the aspect + # ratios match, the rest will take care of itself. + glowCamera = base.makeCamera( + glowBuffer, lens=base.cam.node().getLens()) + + # Tell the glow camera to use the glow shader + tempnode = NodePath(PandaNode("temp node")) + tempnode.setShader(glowShader) + glowCamera.node().setInitialState(tempnode.getState()) + + # set up the pipeline: from glow scene to blur x to blur y to main + # window. + blurXBuffer = makeFilterBuffer( + glowBuffer, "Blur X", -2, "shaders/XBlurShader.sha") + blurYBuffer = makeFilterBuffer( + blurXBuffer, "Blur Y", -1, "shaders/YBlurShader.sha") + self.finalcard = blurYBuffer.getTextureCard() + self.finalcard.reparentTo(render2d) + + # This attribute is used to add the results of the post-processing + # effects to the existing framebuffer image, rather than replace it. + # This is mainly useful for glow effects like ours. + self.finalcard.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + self.accept("v", base.bufferViewer.toggleEnable) + self.accept("V", base.bufferViewer.toggleEnable) + base.bufferViewer.setPosition("llcorner") + base.bufferViewer.setLayout("hline") + base.bufferViewer.setCardSize(0.652, 0) + + # event handling + self.accept("space", self.toggleGlow) + self.accept("enter", self.toggleDisplay) + self.accept("escape", sys.exit, [0]) + + self.glowOn = True + + def toggleGlow(self): + if self.glowOn: + self.finalcard.reparentTo(hidden) + else: + self.finalcard.reparentTo(render2d) + self.glowOn = not(self.glowOn) + + def toggleDisplay(self): + self.isRunning = not(self.isRunning) + if not(self.isRunning): + camera.setPos(0, -50, 0) + self.tron.stop("running") + self.tron.pose("running", 0) + self.interval.loop() + else: + camera.setPos(0, -170, 3) + self.interval.finish() + self.tron.setHpr(0, 0, 0) + self.tron.loop("running") + +demo = GlowDemo() +demo.run() diff --git a/samples/glow-filter/basic.py b/samples/glow-filter/basic.py new file mode 100755 index 0000000000..adba975ce3 --- /dev/null +++ b/samples/glow-filter/basic.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +# Author: Kwasi Mensah (kmensah@andrew.cmu.edu) +# Date: 7/25/2005 + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import Filename, Shader +from panda3d.core import PandaNode, NodePath +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import TextNode, LPoint3 +from direct.showbase.DirectObject import DirectObject +from direct.filter.CommonFilters import CommonFilters +from direct.gui.OnscreenText import OnscreenText +from direct.actor.Actor import Actor +import sys +import os + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.05) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), scale=.08) + + +class GlowDemo(ShowBase): + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + base.disableMouse() + base.setBackgroundColor(0, 0, 0) + camera.setPos(0, -50, 0) + + # Check video card capabilities. + if not base.win.getGsg().getSupportsBasicShaders(): + addTitle( + "Glow Filter: Video driver reports that Cg shaders are not supported.") + return + + # Use class 'CommonFilters' to enable a bloom filter. + # The brightness of a pixel is measured using a weighted average + # of R,G,B,A. We put all the weight on Alpha, meaning that for + # us, the framebuffer's alpha channel alpha controls bloom. + + self.filters = CommonFilters(base.win, base.cam) + filterok = self.filters.setBloom( + blend=(0, 0, 0, 1), desat=-0.5, intensity=3.0, size="small") + if (filterok == False): + addTitle( + "Toon Shader: Video card not powerful enough to do image postprocessing") + return + self.glowSize = 1 + + # Post the instructions + self.title = addTitle("Panda3D: Tutorial - Glow Filter") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Space: Toggle Glow Filter Small/Med/Large/Off") + self.inst3 = addInstructions(0.18, "Enter: Toggle Running/Spinning") + self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") + + # load our model + + self.tron = Actor() + self.tron.loadModel("models/tron") + self.tron.loadAnims({"running": "models/tron_anim"}) + self.tron.reparentTo(render) + self.interval = self.tron.hprInterval(60, LPoint3(360, 0, 0)) + self.interval.loop() + self.isRunning = False + + # put some lighting on the model + + dlight = DirectionalLight('dlight') + alight = AmbientLight('alight') + dlnp = render.attachNewNode(dlight) + alnp = render.attachNewNode(alight) + dlight.setColor((1.0, 0.7, 0.2, 1)) + alight.setColor((0.2, 0.2, 0.2, 1)) + dlnp.setHpr(0, -60, 0) + render.setLight(dlnp) + render.setLight(alnp) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + self.accept("v", base.bufferViewer.toggleEnable) + self.accept("V", base.bufferViewer.toggleEnable) + base.bufferViewer.setPosition("llcorner") + base.bufferViewer.setLayout("hline") + # base.camLens.setFov(100) + # event handling + self.accept("space", self.toggleGlow) + self.accept("enter", self.toggleDisplay) + self.accept("escape", sys.exit, [0]) + + def toggleGlow(self): + self.glowSize = self.glowSize + 1 + if self.glowSize == 4: + self.glowSize = 0 + self.filters.setBloom(blend=(0, 0, 0, 1), desat=-0.5, intensity=3.0, + size=self.glowSize) + + def toggleDisplay(self): + self.isRunning = not self.isRunning + if not self.isRunning: + camera.setPos(0, -50, 0) + self.tron.stop("running") + self.tron.pose("running", 0) + self.interval.loop() + else: + camera.setPos(0, -170, 3) + self.interval.finish() + self.tron.setHpr(0, 0, 0) + self.tron.loop("running") + +demo = GlowDemo() +demo.run() diff --git a/samples/glow-filter/models/tron-color.png b/samples/glow-filter/models/tron-color.png new file mode 100644 index 0000000000..df4bb8fd7e Binary files /dev/null and b/samples/glow-filter/models/tron-color.png differ diff --git a/samples/glow-filter/models/tron-glow.png b/samples/glow-filter/models/tron-glow.png new file mode 100644 index 0000000000..7f8f0cb1cd Binary files /dev/null and b/samples/glow-filter/models/tron-glow.png differ diff --git a/samples/glow-filter/models/tron.egg.pz b/samples/glow-filter/models/tron.egg.pz new file mode 100644 index 0000000000..59b640940d Binary files /dev/null and b/samples/glow-filter/models/tron.egg.pz differ diff --git a/samples/glow-filter/models/tron_anim.egg.pz b/samples/glow-filter/models/tron_anim.egg.pz new file mode 100644 index 0000000000..e31a780bac Binary files /dev/null and b/samples/glow-filter/models/tron_anim.egg.pz differ diff --git a/samples/glow-filter/shaders/XBlurShader.sha b/samples/glow-filter/shaders/XBlurShader.sha new file mode 100644 index 0000000000..08049d83ab --- /dev/null +++ b/samples/glow-filter/shaders/XBlurShader.sha @@ -0,0 +1,28 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj) +{ + l_position=mul(mat_modelproj, vtx_position); + l_texcoord0=vtx_texcoord0; +} + + +void fshader(float2 l_texcoord0 : TEXCOORD0, + out float4 o_color : COLOR, + uniform sampler2D tex_0 : TEXUNIT0) +{ + float3 offset = float3(1.0/1024.0, 5.0/1024.0, 9.0/1024.0); + o_color = tex2D(tex_0, float2(l_texcoord0.x - offset.z, l_texcoord0.y)) * 5.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x - offset.y, l_texcoord0.y)) * 8.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x - offset.x, l_texcoord0.y)) * 10.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x + offset.x, l_texcoord0.y)) * 10.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x + offset.y, l_texcoord0.y)) * 8.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x + offset.z, l_texcoord0.y)) * 5.0; + o_color = o_color * 0.030; +} diff --git a/samples/glow-filter/shaders/YBlurShader.sha b/samples/glow-filter/shaders/YBlurShader.sha new file mode 100644 index 0000000000..5a84701035 --- /dev/null +++ b/samples/glow-filter/shaders/YBlurShader.sha @@ -0,0 +1,30 @@ +//Cg +// +//Cg profile arbvp1 arbfp1 + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj) +{ + l_position=mul(mat_modelproj, vtx_position); + l_texcoord0=vtx_texcoord0; +} + + +void fshader(float2 l_texcoord0 : TEXCOORD0, + out float4 o_color : COLOR, + uniform sampler2D tex_0 : TEXUNIT0) +{ + float3 offset = float3(1.0/1024.0, 5.0/1024.0, 9.0/1024.0); + o_color = tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y - offset.z)) * 5.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y - offset.y)) * 8.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y - offset.x)) * 10.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y + offset.x)) * 10.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y + offset.y)) * 8.0; + o_color += tex2D(tex_0, float2(l_texcoord0.x, l_texcoord0.y + offset.z)) * 5.0; + o_color = o_color * 0.030; +} + + diff --git a/samples/glow-filter/shaders/glowShader.sha b/samples/glow-filter/shaders/glowShader.sha new file mode 100644 index 0000000000..f94c22cc98 --- /dev/null +++ b/samples/glow-filter/shaders/glowShader.sha @@ -0,0 +1,21 @@ +//Cg +// + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0) +{ + l_position=mul(mat_modelproj, vtx_position); + l_texcoord0=vtx_texcoord0; +} + +void fshader(float2 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color : COLOR) +{ + float4 texColor=tex2D(tex_0, l_texcoord0); + o_color=texColor*2*(texColor.w - 0.5); +} + diff --git a/samples/infinite-tunnel/main.py b/samples/infinite-tunnel/main.py new file mode 100755 index 0000000000..bc32fde065 --- /dev/null +++ b/samples/infinite-tunnel/main.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial will cover fog and how it can be used to make a finite length +# tunnel seem endless by hiding its endpoint in darkness. Fog in panda works by +# coloring objects based on their distance from the camera. Fog is not a 3D +# volume object like real world fog. +# With the right settings, Fog in panda can mimic the appearence of real world # fog. +# +# The tunnel and texture was originally created by Vamsi Bandaru and Victoria +# Webb for the Entertainment Technology Center class Building Virtual Worlds + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import Fog +from panda3d.core import TextNode +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.interval.MetaInterval import Sequence +from direct.interval.LerpInterval import LerpFunc +from direct.interval.FunctionInterval import Func +import sys + +# Global variables for the tunnel dimensions and speed of travel +TUNNEL_SEGMENT_LENGTH = 50 +TUNNEL_TIME = 2 # Amount of time for one segment to travel the +# distance of TUNNEL_SEGMENT_LENGTH + + +class FogDemo(ShowBase): + + # Macro-like function used to reduce the amount to code needed to create the + # on screen instructions + def genLabelText(self, i, text): + return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.05, + pos=(0.06, -.065 * i), fg=(1, 1, 1, 1), + align=TextNode.ALeft) + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # Standard initialization stuff + # Standard title that's on screen in every tutorial + self.title = OnscreenText(text="Panda3D: Tutorial - Fog", style=1, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5), parent=base.a2dBottomRight, + align=TextNode.ARight, pos=(-0.1, 0.1), scale=.08) + + # Code to generate the on screen instructions + self.escapeEventText = self.genLabelText(1, "ESC: Quit") + self.pkeyEventText = self.genLabelText(2, "[P]: Pause") + self.tkeyEventText = self.genLabelText(3, "[T]: Toggle Fog") + self.dkeyEventText = self.genLabelText(4, "[D]: Make fog color black") + self.sdkeyEventText = self.genLabelText(5, "[SHIFT+D]: Make background color black") + self.rkeyEventText = self.genLabelText(6, "[R]: Make fog color red") + self.srkeyEventText = self.genLabelText(7, "[SHIFT+R]: Make background color red") + self.bkeyEventText = self.genLabelText(8, "[B]: Make fog color blue") + self.sbkeyEventText = self.genLabelText(9, "[SHIFT+B]: Make background color blue") + self.gkeyEventText = self.genLabelText(10, "[G]: Make fog color green") + self.sgkeyEventText = self.genLabelText(11, "[SHIFT+G]: Make background color green") + self.lkeyEventText = self.genLabelText(12, "[L]: Make fog color light grey") + self.slkeyEventText = self.genLabelText(13, "[SHIFT+L]: Make background color light grey") + self.pluskeyEventText = self.genLabelText(14, "[+]: Increase fog density") + self.minuskeyEventText = self.genLabelText(15, "[-]: Decrease fog density") + + # disable mouse control so that we can place the camera + base.disableMouse() + camera.setPosHpr(0, 0, 10, 0, -90, 0) + base.setBackgroundColor(0, 0, 0) # set the background color to black + + # World specific-code + + # Create an instance of fog called 'distanceFog'. + #'distanceFog' is just a name for our fog, not a specific type of fog. + self.fog = Fog('distanceFog') + # Set the initial color of our fog to black. + self.fog.setColor(0, 0, 0) + # Set the density/falloff of the fog. The range is 0-1. + # The higher the numer, the "bigger" the fog effect. + self.fog.setExpDensity(.08) + # We will set fog on render which means that everything in our scene will + # be affected by fog. Alternatively, you could only set fog on a specific + # object/node and only it and the nodes below it would be affected by + # the fog. + render.setFog(self.fog) + + # Define the keyboard input + # Escape closes the demo + self.accept('escape', sys.exit) + # Handle pausing the tunnel + self.accept('p', self.handlePause) + # Handle turning the fog on and off + self.accept('t', toggleFog, [render, self.fog]) + # Sets keys to set the fog to various colors + self.accept('r', self.fog.setColor, [1, 0, 0]) + self.accept('g', self.fog.setColor, [0, 1, 0]) + self.accept('b', self.fog.setColor, [0, 0, 1]) + self.accept('l', self.fog.setColor, [.7, .7, .7]) + self.accept('d', self.fog.setColor, [0, 0, 0]) + # Sets keys to change the background colors + self.accept('shift-r', base.setBackgroundColor, [1, 0, 0]) + self.accept('shift-g', base.setBackgroundColor, [0, 1, 0]) + self.accept('shift-b', base.setBackgroundColor, [0, 0, 1]) + self.accept('shift-l', base.setBackgroundColor, [.7, .7, .7]) + self.accept('shift-d', base.setBackgroundColor, [0, 0, 0]) + # Increases the fog density when "+" key is pressed + self.accept('+', self.addFogDensity, [.01]) + # This is to handle the other "+" key (it's over = on the keyboard) + self.accept('=', self.addFogDensity, [.01]) + self.accept('shift-=', self.addFogDensity, [.01]) + # Decreases the fog density when the "-" key is pressed + self.accept('-', self.addFogDensity, [-.01]) + + # Load the tunel and start the tunnel + self.initTunnel() + self.contTunnel() + + # This function will change the fog density by the amount passed into it + # This function is needed so that it can look up the current value and + # change it when the key is pressed. If you wanted to bind a key to set it + # at a given value you could call self.fog.setExpDensity directly + def addFogDensity(self, change): + # The min() statement makes sure the density is never over 1 + # The max() statement makes sure the density is never below 0 + self.fog.setExpDensity( + min(1, max(0, self.fog.getExpDensity() + change))) + + # Code to initialize the tunnel + def initTunnel(self): + self.tunnel = [None] * 4 + + for x in range(4): + # Load a copy of the tunnel + self.tunnel[x] = loader.loadModel('models/tunnel') + # The front segment needs to be attached to render + if x == 0: + self.tunnel[x].reparentTo(render) + # The rest of the segments parent to the previous one, so that by moving + # the front segement, the entire tunnel is moved + else: + self.tunnel[x].reparentTo(self.tunnel[x - 1]) + # We have to offset each segment by its length so that they stack onto + # each other. Otherwise, they would all occupy the same space. + self.tunnel[x].setPos(0, 0, -TUNNEL_SEGMENT_LENGTH) + # Now we have a tunnel consisting of 4 repeating segments with a + # hierarchy like this: + # render<-tunnel[0]<-tunnel[1]<-tunnel[2]<-tunnel[3] + + # This function is called to snap the front of the tunnel to the back + # to simulate traveling through it + def contTunnel(self): + # This line uses slices to take the front of the list and put it on the + # back. For more information on slices check the Python manual + self.tunnel = self.tunnel[1:] + self.tunnel[0:1] + # Set the front segment (which was at TUNNEL_SEGMENT_LENGTH) to 0, which + # is where the previous segment started + self.tunnel[0].setZ(0) + # Reparent the front to render to preserve the hierarchy outlined above + self.tunnel[0].reparentTo(render) + # Set the scale to be apropriate (since attributes like scale are + # inherited, the rest of the segments have a scale of 1) + self.tunnel[0].setScale(.155, .155, .305) + # Set the new back to the values that the rest of teh segments have + self.tunnel[3].reparentTo(self.tunnel[2]) + self.tunnel[3].setZ(-TUNNEL_SEGMENT_LENGTH) + self.tunnel[3].setScale(1) + + # Set up the tunnel to move one segment and then call contTunnel again + # to make the tunnel move infinitely + self.tunnelMove = Sequence( + LerpFunc(self.tunnel[0].setZ, + duration=TUNNEL_TIME, + fromData=0, + toData=TUNNEL_SEGMENT_LENGTH * .305), + Func(self.contTunnel) + ) + self.tunnelMove.start() + + # This function calls toggle interval to pause or unpause the tunnel. + # Like addFogDensity, toggleInterval could not be passed directly in the + # accept command since the value of self.tunnelMove changes whenever + #self.contTunnel is called + def handlePause(self): + toggleInterval(self.tunnelMove) +# End Class World + + +# This function will toggle any interval passed to it between playing and +# paused +def toggleInterval(interval): + if interval.isPlaying(): + interval.pause() + else: + interval.resume() + + +# This function will toggle fog on a node +def toggleFog(node, fog): + # If the fog attached to the node is equal to the one we passed in, then + # fog is on and we should clear it + if node.getFog() == fog: + node.clearFog() + # Otherwise fog is not set so we should set it + else: + node.setFog(fog) + + +demo = FogDemo() +demo.run() diff --git a/samples/infinite-tunnel/models/tunnel.egg.pz b/samples/infinite-tunnel/models/tunnel.egg.pz new file mode 100644 index 0000000000..bfb460b2a8 Binary files /dev/null and b/samples/infinite-tunnel/models/tunnel.egg.pz differ diff --git a/samples/infinite-tunnel/models/tunnel.jpg b/samples/infinite-tunnel/models/tunnel.jpg new file mode 100644 index 0000000000..870403aa0b Binary files /dev/null and b/samples/infinite-tunnel/models/tunnel.jpg differ diff --git a/samples/looking-and-gripping/main.py b/samples/looking-and-gripping/main.py new file mode 100755 index 0000000000..186073d097 --- /dev/null +++ b/samples/looking-and-gripping/main.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Models and Textures by: Shaun Budhram, Will Houng, and David Tucker +# Last Updated: 2015-03-13 +# +# This tutorial will cover exposing joints and manipulating them. Specifically, +# we will take control of the neck joint of a humanoid character and rotate that +# joint to always face the mouse cursor. This will in turn make the head of the +# character "look" at the mouse cursor. We will also expose the hand joint and +# use it as a point to "attach" objects that the character can hold. By +# parenting an object to a hand joint, the object will stay in the character's +# hand even if the hand is moving through an animation. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import TextNode, NodePath, LightAttrib +from panda3d.core import LVector3 +from direct.actor.Actor import Actor +from direct.task.Task import Task +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +import sys + +# A simple function to make sure a value is in a given range, -1 to 1 by +# default +def clamp(i, mn=-1, mx=1): + return min(max(i, mn), mx) + +# Macro-like function used to reduce the amount to code needed to create the +# on screen instructions +def genLabelText(text, i): + return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.06, + pos=(0.06, -.08 * i), fg=(1, 1, 1, 1), + shadow=(0, 0, 0, .5), align=TextNode.ALeft) + + +class LookingGrippingDemo(ShowBase): + + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + # This code puts the standard title and instruction text on screen + self.title = OnscreenText(text="Panda3D: Tutorial - Joint Manipulation", + fg=(1, 1, 1, 1), parent=base.a2dBottomRight, + align=TextNode.ARight, pos=(-0.1, 0.1), + shadow=(0, 0, 0, .5), scale=.08) + + self.onekeyText = genLabelText("ESC: Quit", 1) + self.onekeyText = genLabelText("[1]: Teapot", 2) + self.twokeyText = genLabelText("[2]: Candy cane", 3) + self.threekeyText = genLabelText("[3]: Banana", 4) + self.fourkeyText = genLabelText("[4]: Sword", 5) + + # Set up key input + self.accept('escape', sys.exit) + self.accept('1', self.switchObject, [0]) + self.accept('2', self.switchObject, [1]) + self.accept('3', self.switchObject, [2]) + self.accept('4', self.switchObject, [3]) + + base.disableMouse() # Disable mouse-based camera-control + camera.setPos(0, -15, 2) # Position the camera + + self.eve = Actor("models/eve", # Load our animated charachter + {'walk': "models/eve_walk"}) + self.eve.reparentTo(render) # Put it in the scene + + # Now we use controlJoint to get a NodePath that's in control of her neck + # This must be done before any animations are played + self.eveNeck = self.eve.controlJoint(None, 'modelRoot', 'Neck') + + # We now play an animation. An animation must be played, or at least posed + # for the nodepath we just got from controlJoint to actually effect the + # model + self.eve.actorInterval("walk", playRate=2).loop() + + # Now we add a task that will take care of turning the head + taskMgr.add(self.turnHead, "turnHead") + + # Now we will expose the joint the hand joint. ExposeJoint allows us to + # get the position of a joint while it is animating. This is different than + # controlJonit which stops that joint from animating but lets us move it. + # This is particularly usefull for putting an object (like a weapon) in an + # actor's hand + self.rightHand = self.eve.exposeJoint(None, 'modelRoot', 'RightHand') + + # This is a table with models, positions, rotations, and scales of objects to + # be attached to our exposed joint. These are stock models and so they needed + # to be repositioned to look right. + positions = [("teapot", (0, -.66, -.95), (90, 0, 90), .4), + ("models/candycane", (.15, -.99, -.22), (90, 0, 90), 1), + ("models/banana", (.08, -.1, .09), (0, -90, 0), 1.75), + ("models/sword", (.11, .19, .06), (0, 0, 90), 1)] + + self.models = [] # A list that will store our models objects + for row in positions: + np = loader.loadModel(row[0]) # Load the model + np.setPos(row[1][0], row[1][1], row[1][2]) # Position it + np.setHpr(row[2][0], row[2][1], row[2][2]) # Rotate it + np.setScale(row[3]) # Scale it + # Reparent the model to the exposed joint. That way when the joint moves, + # the model we just loaded will move with it. + np.reparentTo(self.rightHand) + self.models.append(np) # Add it to our models list + + self.switchObject(0) # Make object 0 the first shown + self.setupLights() # Put in some default lighting + + # This is what we use to change which object it being held. It just hides all of + # the objects and then unhides the one that was selected + def switchObject(self, i): + for np in self.models: + np.hide() + self.models[i].show() + + # This task gets the position of mouse each frame, and rotates the neck based + # on it. + def turnHead(self, task): + # Check to make sure the mouse is readable + if base.mouseWatcherNode.hasMouse(): + # get the mouse position as a LVector2. The values for each axis are from -1 to + # 1. The top-left is (-1,-1), the bottom right is (1,1) + mpos = base.mouseWatcherNode.getMouse() + # Here we multiply the values to get the amount of degrees to turn + # Restrain is used to make sure the values returned by getMouse are in the + # valid range. If this particular model were to turn more than this, + # significant tearing would be visable + self.eveNeck.setP(clamp(mpos.getX()) * 50) + self.eveNeck.setH(clamp(mpos.getY()) * 20) + + return Task.cont # Task continues infinitely + + def setupLights(self): # Sets up some default lighting + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.4, .4, .35, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 8, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + render.setLight(render.attachNewNode(directionalLight)) + render.setLight(render.attachNewNode(ambientLight)) + +demo = LookingGrippingDemo() # Create an instance of our class +demo.run() # Run the simulation diff --git a/samples/looking-and-gripping/models/banana.egg.pz b/samples/looking-and-gripping/models/banana.egg.pz new file mode 100644 index 0000000000..c8081ae403 Binary files /dev/null and b/samples/looking-and-gripping/models/banana.egg.pz differ diff --git a/samples/looking-and-gripping/models/candycane.egg.pz b/samples/looking-and-gripping/models/candycane.egg.pz new file mode 100644 index 0000000000..00fd4d9ee6 Binary files /dev/null and b/samples/looking-and-gripping/models/candycane.egg.pz differ diff --git a/samples/looking-and-gripping/models/eve.egg.pz b/samples/looking-and-gripping/models/eve.egg.pz new file mode 100644 index 0000000000..83ab673c46 Binary files /dev/null and b/samples/looking-and-gripping/models/eve.egg.pz differ diff --git a/samples/looking-and-gripping/models/eve_run.egg.pz b/samples/looking-and-gripping/models/eve_run.egg.pz new file mode 100644 index 0000000000..3ea3950afd Binary files /dev/null and b/samples/looking-and-gripping/models/eve_run.egg.pz differ diff --git a/samples/looking-and-gripping/models/eve_walk.egg.pz b/samples/looking-and-gripping/models/eve_walk.egg.pz new file mode 100644 index 0000000000..ae565590bf Binary files /dev/null and b/samples/looking-and-gripping/models/eve_walk.egg.pz differ diff --git a/samples/looking-and-gripping/models/sword.egg.pz b/samples/looking-and-gripping/models/sword.egg.pz new file mode 100644 index 0000000000..22e204bd34 Binary files /dev/null and b/samples/looking-and-gripping/models/sword.egg.pz differ diff --git a/samples/looking-and-gripping/models/textures/banana.jpg b/samples/looking-and-gripping/models/textures/banana.jpg new file mode 100644 index 0000000000..3473625f85 Binary files /dev/null and b/samples/looking-and-gripping/models/textures/banana.jpg differ diff --git a/samples/looking-and-gripping/models/textures/candycane.jpg b/samples/looking-and-gripping/models/textures/candycane.jpg new file mode 100644 index 0000000000..9d63aec222 Binary files /dev/null and b/samples/looking-and-gripping/models/textures/candycane.jpg differ diff --git a/samples/looking-and-gripping/models/textures/eve.jpg b/samples/looking-and-gripping/models/textures/eve.jpg new file mode 100644 index 0000000000..3c2aad6517 Binary files /dev/null and b/samples/looking-and-gripping/models/textures/eve.jpg differ diff --git a/samples/looking-and-gripping/models/textures/eveface.jpg b/samples/looking-and-gripping/models/textures/eveface.jpg new file mode 100644 index 0000000000..7ae72b80e1 Binary files /dev/null and b/samples/looking-and-gripping/models/textures/eveface.jpg differ diff --git a/samples/looking-and-gripping/models/textures/sword.jpg b/samples/looking-and-gripping/models/textures/sword.jpg new file mode 100644 index 0000000000..eab2267c0d Binary files /dev/null and b/samples/looking-and-gripping/models/textures/sword.jpg differ diff --git a/samples/media-player/PandaSneezes.ogv b/samples/media-player/PandaSneezes.ogv new file mode 100644 index 0000000000..b58e308687 Binary files /dev/null and b/samples/media-player/PandaSneezes.ogv differ diff --git a/samples/media-player/main.py b/samples/media-player/main.py new file mode 100755 index 0000000000..838cb54961 --- /dev/null +++ b/samples/media-player/main.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from panda3d.core import * +# Tell Panda3D to use OpenAL, not FMOD +loadPrcFileData("", "audio-library-name p3openal_audio") + +from direct.showbase.DirectObject import DirectObject +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.ShowBase import ShowBase + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(0, 0, 0, 1), shadow=(1, 1, 1, 1), + parent=base.a2dTopLeft, align=TextNode.ALeft, + pos=(0.08, -pos - 0.04), scale=.06) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + + +class MediaPlayer(ShowBase): + + def __init__(self, media_file): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + self.title = addTitle("Panda3D: Tutorial - Media Player") + self.inst1 = addInstructions(0.06, "P: Play/Pause") + self.inst2 = addInstructions(0.12, "S: Stop and Rewind") + self.inst3 = addInstructions(0.18, + "M: Slow Motion / Normal Motion toggle") + + # Load the texture. We could use loader.loadTexture for this, + # but we want to make sure we get a MovieTexture, since it + # implements synchronizeTo. + self.tex = MovieTexture("name") + assert self.tex.read(media_file), "Failed to load video!" + + # Set up a fullscreen card to set the video texture on. + cm = CardMaker("My Fullscreen Card") + cm.setFrameFullscreenQuad() + + # Tell the CardMaker to create texture coordinates that take into + # account the padding region of the texture. + cm.setUvRange(self.tex) + + # Now place the card in the scene graph and apply the texture to it. + card = NodePath(cm.generate()) + card.reparentTo(self.render2d) + card.setTexture(self.tex) + + self.sound = loader.loadSfx(media_file) + # Synchronize the video to the sound. + self.tex.synchronizeTo(self.sound) + + self.accept('p', self.playpause) + self.accept('P', self.playpause) + self.accept('s', self.stopsound) + self.accept('S', self.stopsound) + self.accept('m', self.fastforward) + self.accept('M', self.fastforward) + + def stopsound(self): + self.sound.stop() + self.sound.setPlayRate(1.0) + + def fastforward(self): + if self.sound.status() == AudioSound.PLAYING: + t = self.sound.getTime() + self.sound.stop() + if self.sound.getPlayRate() == 1.0: + self.sound.setPlayRate(0.5) + else: + self.sound.setPlayRate(1.0) + self.sound.setTime(t) + self.sound.play() + + def playpause(self): + if self.sound.status() == AudioSound.PLAYING: + t = self.sound.getTime() + self.sound.stop() + self.sound.setTime(t) + else: + self.sound.play() + +player = MediaPlayer("PandaSneezes.ogv") +player.run() diff --git a/samples/motion-trails/main.py b/samples/motion-trails/main.py new file mode 100755 index 0000000000..b0764e5cf9 --- /dev/null +++ b/samples/motion-trails/main.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python + +# Author: Josh Yelon +# +# This is a tutorial to show one of the simplest applications +# of copy-to-texture: motion trails. +# + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import GraphicsOutput +from panda3d.core import Filename, Texture +from panda3d.core import CardMaker +from panda3d.core import NodePath, TextNode +from panda3d.core import AmbientLight, DirectionalLight +from direct.showbase.DirectObject import DirectObject +from direct.gui.OnscreenText import OnscreenText +from direct.task.Task import Task +from direct.actor.Actor import Actor +from random import uniform +import sys +import os + +def addInstructions(pos, msg): + return OnscreenText(text=msg, parent=base.a2dTopLeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -pos - 0.03), + align=TextNode.ALeft, scale=.05) + + +class MotionTrails(ShowBase): + def __init__(self): + # Initialize the ShowBase class from which we inherit, which will + # create a window and set up everything we need for rendering into it. + ShowBase.__init__(self) + + self.disableMouse() + self.camera.setPos(0, -26, 4) + self.setBackgroundColor(0, 0, 0) + + # Create a texture into which we can copy the main window. + # We set it to RTMTriggeredCopyTexture mode, which tells it that we + # want it to copy the window contents into a texture every time we + # call self.win.triggerCopy(). + self.tex = Texture() + self.tex.setMinfilter(Texture.FTLinear) + self.win.addRenderTexture(self.tex, + GraphicsOutput.RTMTriggeredCopyTexture) + + # Set the initial color to clear the texture to, before rendering it. + # This is necessary because we don't clear the texture while rendering, + # and otherwise the user might see garbled random data from GPU memory. + self.tex.setClearColor((0, 0, 0, 1)) + self.tex.clearImage() + + # Create another 2D camera. Tell it to render before the main camera. + self.backcam = self.makeCamera2d(self.win, sort=-10) + self.background = NodePath("background") + self.backcam.reparentTo(self.background) + self.background.setDepthTest(0) + self.background.setDepthWrite(0) + self.backcam.node().getDisplayRegion(0).setClearDepthActive(0) + + # Obtain two texture cards. One renders before the dragon, the other + # after. + self.bcard = self.win.getTextureCard() + self.bcard.reparentTo(self.background) + self.bcard.setTransparency(1) + self.fcard = self.win.getTextureCard() + self.fcard.reparentTo(self.render2d) + self.fcard.setTransparency(1) + + # Initialize one of the nice effects. + self.chooseEffectGhost() + + # Add the task that initiates the screenshots. + taskMgr.add(self.takeSnapShot, "takeSnapShot") + + # Create some black squares on top of which we will + # place the instructions. + blackmaker = CardMaker("blackmaker") + blackmaker.setColor(0, 0, 0, 1) + blackmaker.setFrame(-1.00, -0.50, 0.65, 1.00) + instcard = NodePath(blackmaker.generate()) + instcard.reparentTo(self.render2d) + blackmaker.setFrame(-0.5, 0.5, -1.00, -0.85) + titlecard = NodePath(blackmaker.generate()) + titlecard.reparentTo(self.render2d) + + # Panda does its best to hide the differences between DirectX and + # OpenGL. But there are a few differences that it cannot hide. + # One such difference is that when OpenGL copies from a + # visible window to a texture, it gets it right-side-up. When + # DirectX does it, it gets it upside-down. There is nothing panda + # can do to compensate except to expose a flag and let the + # application programmer deal with it. You should only do this + # in the rare event that you're copying from a visible window + # to a texture. + if self.win.getGsg().getCopyTextureInverted(): + print("Copy texture is inverted.") + self.bcard.setScale(1, 1, -1) + self.fcard.setScale(1, 1, -1) + + # Put up the instructions + title = OnscreenText(text="Panda3D: Tutorial - Motion Trails", + fg=(1, 1, 1, 1), parent=base.a2dBottomCenter, + pos=(0, 0.1), scale=.08) + + instr0 = addInstructions(0.06, "Press ESC to exit") + instr1 = addInstructions(0.12, "Press 1: Ghost effect") + instr2 = addInstructions(0.18, "Press 2: PaintBrush effect") + instr3 = addInstructions(0.24, "Press 3: Double Vision effect") + instr4 = addInstructions(0.30, "Press 4: Wings of Blue effect") + instr5 = addInstructions(0.36, "Press 5: Whirlpool effect") + + # Enable the key events + self.accept("escape", sys.exit, [0]) + self.accept("1", self.chooseEffectGhost) + self.accept("2", self.chooseEffectPaintBrush) + self.accept("3", self.chooseEffectDoubleVision) + self.accept("4", self.chooseEffectWingsOfBlue) + self.accept("5", self.chooseEffectWhirlpool) + + def takeSnapShot(self, task): + if task.time > self.nextclick: + self.nextclick += 1.0 / self.clickrate + if self.nextclick < task.time: + self.nextclick = task.time + self.win.triggerCopy() + return Task.cont + + def chooseEffectGhost(self): + self.setBackgroundColor(0, 0, 0, 1) + self.bcard.hide() + self.fcard.show() + self.fcard.setColor(1.0, 1.0, 1.0, 0.99) + self.fcard.setScale(1.00) + self.fcard.setPos(0, 0, 0) + self.fcard.setR(0) + self.clickrate = 30 + self.nextclick = 0 + + def chooseEffectPaintBrush(self): + self.setBackgroundColor(0, 0, 0, 1) + self.bcard.show() + self.fcard.hide() + self.bcard.setColor(1, 1, 1, 1) + self.bcard.setScale(1.0) + self.bcard.setPos(0, 0, 0) + self.bcard.setR(0) + self.clickrate = 10000 + self.nextclick = 0 + + def chooseEffectDoubleVision(self): + self.setBackgroundColor(0, 0, 0, 1) + self.bcard.show() + self.bcard.setColor(1, 1, 1, 1) + self.bcard.setScale(1.0) + self.bcard.setPos(-0.05, 0, 0) + self.bcard.setR(0) + self.fcard.show() + self.fcard.setColor(1, 1, 1, 0.60) + self.fcard.setScale(1.0) + self.fcard.setPos(0.05, 0, 0) + self.fcard.setR(0) + self.clickrate = 10000 + self.nextclick = 0 + + def chooseEffectWingsOfBlue(self): + self.setBackgroundColor(0, 0, 0, 1) + self.fcard.hide() + self.bcard.show() + self.bcard.setColor(1.0, 0.90, 1.0, 254.0 / 255.0) + self.bcard.setScale(1.1, 1, 0.95) + self.bcard.setPos(0, 0, 0.05) + self.bcard.setR(0) + self.clickrate = 30 + self.nextclick = 0 + + def chooseEffectWhirlpool(self): + self.setBackgroundColor(0, 0, 0, 1) + self.bcard.show() + self.fcard.hide() + self.bcard.setColor(1, 1, 1, 1) + self.bcard.setScale(0.999) + self.bcard.setPos(0, 0, 0) + self.bcard.setR(1) + self.clickrate = 10000 + self.nextclick = 0 + + +t = MotionTrails() + +character = Actor() +character.loadModel('models/dancer') +character.reparentTo(t.render) +character.loadAnims({'win': 'models/dancer'}) +character.loop('win') +# character.hprInterval(15, LPoint3(360, 0,0)).loop() + +# put some lighting on the model +dlight = DirectionalLight('dlight') +alight = AmbientLight('alight') +dlnp = t.render.attachNewNode(dlight) +alnp = t.render.attachNewNode(alight) +dlight.setColor((1.0, 0.9, 0.8, 1)) +alight.setColor((0.2, 0.3, 0.4, 1)) +dlnp.setHpr(0, -60, 0) +t.render.setLight(dlnp) +t.render.setLight(alnp) + +t.run() diff --git a/samples/motion-trails/models/dancer.egg.pz b/samples/motion-trails/models/dancer.egg.pz new file mode 100644 index 0000000000..bc1260e71f Binary files /dev/null and b/samples/motion-trails/models/dancer.egg.pz differ diff --git a/samples/motion-trails/models/tex_skratixOpt.jpg b/samples/motion-trails/models/tex_skratixOpt.jpg new file mode 100644 index 0000000000..74112ec797 Binary files /dev/null and b/samples/motion-trails/models/tex_skratixOpt.jpg differ diff --git a/samples/music-box/main.py b/samples/music-box/main.py new file mode 100755 index 0000000000..774168f782 --- /dev/null +++ b/samples/music-box/main.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman, and Elan Ruskin +# Last Updated: 2015-03-13 +# +# This tutorial shows how to load, play, and manipulate sounds +# and sound intervals in a panda project. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import NodePath, TextNode +from panda3d.core import PointLight, AmbientLight +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.interval.SoundInterval import SoundInterval +from direct.gui.DirectSlider import DirectSlider +from direct.gui.DirectButton import DirectButton +from direct.interval.MetaInterval import Parallel +from direct.interval.LerpInterval import LerpHprInterval +import sys + +# Create an instance of ShowBase, which will open a window and set up a +# scene graph and camera. +base = ShowBase() + +class MusicBox(DirectObject): + def __init__(self): + # Our standard title and instructions text + self.title = OnscreenText(text="Panda3D: Tutorial - Music Box", + parent=base.a2dBottomCenter, + pos=(0, 0.08), scale=0.08, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5)) + self.escapeText = OnscreenText(text="ESC: Quit", parent=base.a2dTopLeft, + fg=(1, 1, 1, 1), pos=(0.06, -0.1), + align=TextNode.ALeft, scale=.05) + + # Set up the key input + self.accept('escape', sys.exit) + + # Fix the camera position + base.disableMouse() + + # Loading sounds is done in a similar way to loading other things + # Loading the main music box song + self.musicBoxSound = base.loadMusic('music/musicbox.ogg') + self.musicBoxSound.setVolume(.5) # Volume is a percentage from 0 to 1 + # 0 means loop forever, 1 (default) means + # play once. 2 or higher means play that many times + self.musicBoxSound.setLoopCount(0) + + # Set up a simple light. + self.plight = PointLight("light") + self.plight.setColor((0.7, 0.7, 0.5, 1)) + light_path = base.render.attachNewNode(self.plight) + light_path.setPos(0, 0, 20) + base.render.setLight(light_path) + + alight = AmbientLight("ambient") + alight.setColor((0.3, 0.3, 0.4, 1)) + base.render.setLight(base.render.attachNewNode(alight)) + + # Enable per-pixel lighting + base.render.setShaderAuto() + + # Sound objects do not have a pause function, just play and stop. So we will + # Use this variable to keep track of where the sound is at when it was stoped + # to impliment pausing + self.musicTime = 0 + + # Loading the open/close effect + # loadSFX and loadMusic are identical. They are often used for organization + #(loadMusic is used for background music, loadSfx is used for other effects) + self.lidSfx = base.loadSfx('music/openclose.ogg') + # The open/close file has both effects in it. Fortunatly we can use intervals + # to easily define parts of a sound file to play + self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0) + self.lidCloseSfx = SoundInterval(self.lidSfx, startTime=5) + + # For this tutorial, it seemed appropriate to have on screen controls. + # The following code creates them. + # This is a label for a slider + self.sliderText = OnscreenText("Volume", pos=(-0.1, 0.87), scale=.07, + fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1)) + # The slider itself. It calls self.setMusicBoxVolume when changed + self.slider = DirectSlider(pos=(-0.1, 0, .75), scale=0.8, value=.50, + command=self.setMusicBoxVolume) + # A button that calls self.toggleMusicBox when pressed + self.button = DirectButton(pos=(.9, 0, .75), text="Open", + scale=.1, pad=(.2, .2), + rolloverSound=None, clickSound=None, + command=self.toggleMusicBox) + + # A variable to represent the state of the simulation. It starts closed + self.boxOpen = False + + # Here we load and set up the music box. It was modeled in a complex way, so + # setting it up will be complicated + self.musicBox = loader.loadModel('models/MusicBox') + self.musicBox.setPos(0, 60, -9) + self.musicBox.reparentTo(render) + # Just like the scene graph contains hierarchies of nodes, so can + # models. You can get the NodePath for the node using the find + # function, and then you can animate the model by moving its parts + # To see the hierarchy of a model, use, the ls function + # self.musicBox.ls() prints out the entire hierarchy of the model + + # Finding pieces of the model + self.Lid = self.musicBox.find('**/lid') + self.Panda = self.musicBox.find('**/turningthing') + + # This model was made with the hinge in the wrong place + # this is here so we have something to turn + self.HingeNode = self.musicBox.find( + '**/box').attachNewNode('nHingeNode') + self.HingeNode.setPos(.8659, 6.5, 5.4) + # WRT - ie with respect to. Reparents the object without changing + # its position, size, or orientation + self.Lid.wrtReparentTo(self.HingeNode) + self.HingeNode.setHpr(0, 90, 0) + + # This sets up an interval to play the close sound and actually close the box + # at the same time. + self.lidClose = Parallel( + self.lidCloseSfx, + LerpHprInterval(self.HingeNode, 2.0, (0, 90, 0), blendType='easeInOut')) + + # Same thing for opening the box + self.lidOpen = Parallel( + self.lidOpenSfx, + LerpHprInterval(self.HingeNode, 2.0, (0, 0, 0), blendType='easeInOut')) + + # The interval for turning the panda + self.PandaTurn = self.Panda.hprInterval(7, (360, 0, 0)) + # Do a quick loop and pause to set it as a looping interval so it can be + # started with resume and loop properly + self.PandaTurn.loop() + self.PandaTurn.pause() + + def setMusicBoxVolume(self): + # Simply reads the current value from the slider and sets it in the + # sound + newVol = self.slider.guiItem.getValue() + self.musicBoxSound.setVolume(newVol) + + def toggleMusicBox(self): + #if self.lidOpen.isPlaying() or self.lidClose.isPlaying(): + # # It's currently already opening or closing + # return + + if self.boxOpen: + self.lidOpen.pause() + + self.lidClose.start() # Start the close box interval + self.PandaTurn.pause() # Pause the figurine turning + # Save the current time of the music + self.musicTime = self.musicBoxSound.getTime() + self.musicBoxSound.stop() # Stop the music + self.button['text'] = "Open" # Prepare to change button label + else: + self.lidClose.pause() + + self.lidOpen.start() # Start the open box interval + self.PandaTurn.resume() # Resume the figuring turning + # Reset the time of the music so it starts where it left off + self.musicBoxSound.setTime(self.musicTime) + self.musicBoxSound.play() # Play the music + self.button['text'] = "Close" # Prepare to change button label + + self.button.setText() # Actually change the button label + # Set our state to opposite what it was + self.boxOpen = not self.boxOpen + #(closed to open or open to closed) + +# and we can run! +mb = MusicBox() +base.run() diff --git a/samples/music-box/models/MusicBox.egg.pz b/samples/music-box/models/MusicBox.egg.pz new file mode 100644 index 0000000000..9eac077186 Binary files /dev/null and b/samples/music-box/models/MusicBox.egg.pz differ diff --git a/samples/music-box/models/box.jpg b/samples/music-box/models/box.jpg new file mode 100644 index 0000000000..4e713dd1bc Binary files /dev/null and b/samples/music-box/models/box.jpg differ diff --git a/samples/music-box/models/panda.jpg b/samples/music-box/models/panda.jpg new file mode 100644 index 0000000000..df6459280a Binary files /dev/null and b/samples/music-box/models/panda.jpg differ diff --git a/samples/music-box/music/musicbox.ogg b/samples/music-box/music/musicbox.ogg new file mode 100755 index 0000000000..cc6b062490 Binary files /dev/null and b/samples/music-box/music/musicbox.ogg differ diff --git a/samples/music-box/music/openclose.ogg b/samples/music-box/music/openclose.ogg new file mode 100644 index 0000000000..46c2bb69ac Binary files /dev/null and b/samples/music-box/music/openclose.ogg differ diff --git a/samples/particles/dust.png b/samples/particles/dust.png new file mode 100644 index 0000000000..2379566221 Binary files /dev/null and b/samples/particles/dust.png differ diff --git a/samples/particles/dust.ptf b/samples/particles/dust.ptf new file mode 100644 index 0000000000..908b56ea08 --- /dev/null +++ b/samples/particles/dust.ptf @@ -0,0 +1,54 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(1.000, 1.000, 1.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("SpriteParticleRenderer") +p0.setEmitter("SphereVolumeEmitter") +p0.setPoolSize(10000) +p0.setBirthRate(0.0500) +p0.setLitterSize(10) +p0.setLitterSpread(0) +p0.setSystemLifespan(0.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(2.0000) +p0.factory.setLifespanSpread(0.2500) +p0.factory.setMassBase(2.0000) +p0.factory.setMassSpread(0.0100) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.07) +# Sprite parameters +p0.renderer.setTexture(loader.loadTexture('dust.png')) +p0.renderer.setColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setXScaleFlag(0) +p0.renderer.setYScaleFlag(0) +p0.renderer.setAnimAngleFlag(0) +p0.renderer.setInitialXScale(0.0100) +p0.renderer.setFinalXScale(0.0200) +p0.renderer.setInitialYScale(0.0100) +p0.renderer.setFinalYScale(0.0200) +p0.renderer.setNonanimatedTheta(0.0000) +p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) +p0.renderer.setAlphaDisable(0) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.0000) +p0.emitter.setOffsetForce(LVector3(0.0000, 0.0000, 0.0000)) +p0.emitter.setExplicitLaunchVector(LVector3(1.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Sphere Volume parameters +p0.emitter.setRadius(0.1000) +self.addParticles(p0) +f0 = ForceGroup.ForceGroup('gravity') +# Force parameters +self.addForceGroup(f0) diff --git a/samples/particles/fireish.ptf b/samples/particles/fireish.ptf new file mode 100644 index 0000000000..3e0ba4a631 --- /dev/null +++ b/samples/particles/fireish.ptf @@ -0,0 +1,51 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(1.000, 1.000, 1.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("SpriteParticleRenderer") +p0.setEmitter("DiscEmitter") +p0.setPoolSize(1024) +p0.setBirthRate(0.0200) +p0.setLitterSize(10) +p0.setLitterSpread(0) +p0.setSystemLifespan(1200.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(0.5000) +p0.factory.setLifespanSpread(0.0000) +p0.factory.setMassBase(1.0000) +p0.factory.setMassSpread(0.0000) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.22) +# Sprite parameters +p0.renderer.setTexture(loader.loadTexture('sparkle.png')) +p0.renderer.setColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setXScaleFlag(1) +p0.renderer.setYScaleFlag(1) +p0.renderer.setAnimAngleFlag(0) +p0.renderer.setInitialXScale(0.0050) +p0.renderer.setFinalXScale(0.0200) +p0.renderer.setInitialYScale(0.0100) +p0.renderer.setFinalYScale(0.0200) +p0.renderer.setNonanimatedTheta(0.0000) +p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPNOBLEND) +p0.renderer.setAlphaDisable(0) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.0000) +p0.emitter.setOffsetForce(LVector3(0.0000, 0.0000, 3.0000)) +p0.emitter.setExplicitLaunchVector(LVector3(1.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Disc parameters +p0.emitter.setRadius(0.5000) +self.addParticles(p0) diff --git a/samples/particles/fountain.ptf b/samples/particles/fountain.ptf new file mode 100644 index 0000000000..19efa0bdc0 --- /dev/null +++ b/samples/particles/fountain.ptf @@ -0,0 +1,58 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(2.000, 2.000, 2.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("PointParticleRenderer") +p0.setEmitter("DiscEmitter") +p0.setPoolSize(10000) +p0.setBirthRate(0.0200) +p0.setLitterSize(300) +p0.setLitterSpread(100) +p0.setSystemLifespan(0.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(0.5000) +p0.factory.setLifespanSpread(0.2500) +p0.factory.setMassBase(2.0000) +p0.factory.setMassSpread(0.0100) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.45) +# Point parameters +p0.renderer.setPointSize(3.00) +p0.renderer.setStartColor(LVector4(0.25, 0.90, 1.00, 1.00)) +p0.renderer.setEndColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setBlendType(PointParticleRenderer.PPONECOLOR) +p0.renderer.setBlendMethod(BaseParticleRenderer.PPNOBLEND) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETCUSTOM) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.0000) +p0.emitter.setOffsetForce(LVector3(0.0000, 0.0000, 4.0000)) +p0.emitter.setExplicitLaunchVector(LVector3(1.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Disc parameters +p0.emitter.setRadius(0.0500) +p0.emitter.setOuterAngle(356.1859) +p0.emitter.setInnerAngle(0.0000) +p0.emitter.setOuterMagnitude(2.0000) +p0.emitter.setInnerMagnitude(1.0000) +p0.emitter.setCubicLerping(0) +self.addParticles(p0) +f0 = ForceGroup.ForceGroup('gravity') +# Force parameters +force0 = LinearVectorForce(LVector3(0.0000, 0.0000, -1.0000), 25.0000, 1) +force0.setActive(1) +f0.addForce(force0) +force1 = LinearJitterForce(3.0000, 1) +force1.setActive(1) +f0.addForce(force1) +self.addForceGroup(f0) diff --git a/samples/particles/particle_panel.py b/samples/particles/particle_panel.py new file mode 100755 index 0000000000..d74e42155c --- /dev/null +++ b/samples/particles/particle_panel.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Author: Shao Zhang, Phil Saltzman +# Last Updated: 2015-03-13 +# +# This file contians the minimum code needed to load the particle panel tool +# See readme.txt for more information + +import sys +try: + import _tkinter +except: + sys.exit("Please install python module 'Tkinter'") + +try: + import Pmw +except: + sys.exit("Please install Python megawidgets") + +# Open the Panda window +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +# Makes sure that Panda is configured to play nice with Tkinter +base.startTk() + +from direct.tkpanels.ParticlePanel import ParticlePanel + +pp = ParticlePanel() # Create the panel +base.disableMouse() # Disable camera control to place it +base.camera.setY(-10) # Place the camera +base.setBackgroundColor(0, 0, 0) # Most particle systems show up better on black backgrounds +base.run() diff --git a/samples/particles/smoke.png b/samples/particles/smoke.png new file mode 100644 index 0000000000..2d4cbfbc1e Binary files /dev/null and b/samples/particles/smoke.png differ diff --git a/samples/particles/smoke.ptf b/samples/particles/smoke.ptf new file mode 100644 index 0000000000..3972c60499 --- /dev/null +++ b/samples/particles/smoke.ptf @@ -0,0 +1,54 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(1.000, 1.000, 1.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("SpriteParticleRenderer") +p0.setEmitter("SphereSurfaceEmitter") +p0.setPoolSize(10000) +p0.setBirthRate(0.0500) +p0.setLitterSize(10) +p0.setLitterSpread(0) +p0.setSystemLifespan(0.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(2.0000) +p0.factory.setLifespanSpread(0.2500) +p0.factory.setMassBase(2.0000) +p0.factory.setMassSpread(0.0100) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.13) +# Sprite parameters +p0.renderer.setTexture(loader.loadTexture('smoke.png')) +p0.renderer.setColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setXScaleFlag(0) +p0.renderer.setYScaleFlag(0) +p0.renderer.setAnimAngleFlag(0) +p0.renderer.setInitialXScale(0.0100) +p0.renderer.setFinalXScale(0.0200) +p0.renderer.setInitialYScale(0.0100) +p0.renderer.setFinalYScale(0.0200) +p0.renderer.setNonanimatedTheta(0.0000) +p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) +p0.renderer.setAlphaDisable(0) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.0000) +p0.emitter.setOffsetForce(LVector3(0.0000, 0.0000, 0.0000)) +p0.emitter.setExplicitLaunchVector(LVector3(1.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Sphere Surface parameters +p0.emitter.setRadius(0.0100) +self.addParticles(p0) +f0 = ForceGroup.ForceGroup('gravity') +# Force parameters +self.addForceGroup(f0) diff --git a/samples/particles/smokering.ptf b/samples/particles/smokering.ptf new file mode 100644 index 0000000000..d679afa929 --- /dev/null +++ b/samples/particles/smokering.ptf @@ -0,0 +1,60 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(1.000, 1.000, 1.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("SpriteParticleRenderer") +p0.setEmitter("DiscEmitter") +p0.setPoolSize(1024) +p0.setBirthRate(0.0200) +p0.setLitterSize(10) +p0.setLitterSpread(0) +p0.setSystemLifespan(1200.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(10.0000) +p0.factory.setLifespanSpread(0.0000) +p0.factory.setMassBase(1.0000) +p0.factory.setMassSpread(0.0000) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.10) +# Sprite parameters +p0.renderer.setTexture(loader.loadTexture('smoke.png')) +p0.renderer.setColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setXScaleFlag(1) +p0.renderer.setYScaleFlag(1) +p0.renderer.setAnimAngleFlag(0) +p0.renderer.setInitialXScale(0.0050) +p0.renderer.setFinalXScale(0.0400) +p0.renderer.setInitialYScale(0.0100) +p0.renderer.setFinalYScale(0.0400) +p0.renderer.setNonanimatedTheta(0.0000) +p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPNOBLEND) +p0.renderer.setAlphaDisable(0) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.0000) +p0.emitter.setOffsetForce(LVector3(0.1000, 0.0000, 0.1000)) +p0.emitter.setExplicitLaunchVector(LVector3(1.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Disc parameters +p0.emitter.setRadius(0.5000) +self.addParticles(p0) +f0 = ForceGroup.ForceGroup('vertex') +# Force parameters +force0 = LinearCylinderVortexForce(4.0000, 1.0000, 1.0000, 1.0000, 0) +force0.setActive(1) +f0.addForce(force0) +force1 = LinearVectorForce(LVector3(0.0000, 0.0000, 1.0000), 0.0500, 0) +force1.setActive(1) +f0.addForce(force1) +self.addForceGroup(f0) diff --git a/samples/particles/sparkle.png b/samples/particles/sparkle.png new file mode 100644 index 0000000000..5f6cde1802 Binary files /dev/null and b/samples/particles/sparkle.png differ diff --git a/samples/particles/steam.png b/samples/particles/steam.png new file mode 100644 index 0000000000..bef6c043b1 Binary files /dev/null and b/samples/particles/steam.png differ diff --git a/samples/particles/steam.ptf b/samples/particles/steam.ptf new file mode 100644 index 0000000000..2fe6b63d0f --- /dev/null +++ b/samples/particles/steam.ptf @@ -0,0 +1,57 @@ + +self.reset() +self.setPos(0.000, 0.000, 0.000) +self.setHpr(0.000, 0.000, 0.000) +self.setScale(1.000, 1.000, 1.000) +p0 = Particles.Particles('particles-1') +# Particles parameters +p0.setFactory("PointParticleFactory") +p0.setRenderer("SpriteParticleRenderer") +p0.setEmitter("DiscEmitter") +p0.setPoolSize(10000) +p0.setBirthRate(0.2500) +p0.setLitterSize(20) +p0.setLitterSpread(0) +p0.setSystemLifespan(0.0000) +p0.setLocalVelocityFlag(1) +p0.setSystemGrowsOlderFlag(0) +# Factory parameters +p0.factory.setLifespanBase(8.0000) +p0.factory.setLifespanSpread(0.0000) +p0.factory.setMassBase(1.0000) +p0.factory.setMassSpread(0.0000) +p0.factory.setTerminalVelocityBase(400.0000) +p0.factory.setTerminalVelocitySpread(0.0000) +# Point factory parameters +# Renderer parameters +p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) +p0.renderer.setUserAlpha(0.10) +# Sprite parameters +p0.renderer.setTexture(loader.loadTexture('steam.png')) +p0.renderer.setColor(LVector4(1.00, 1.00, 1.00, 1.00)) +p0.renderer.setXScaleFlag(1) +p0.renderer.setYScaleFlag(1) +p0.renderer.setAnimAngleFlag(0) +p0.renderer.setInitialXScale(0.0025) +p0.renderer.setFinalXScale(0.0400) +p0.renderer.setInitialYScale(0.0025) +p0.renderer.setFinalYScale(0.0400) +p0.renderer.setNonanimatedTheta(0.0000) +p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPNOBLEND) +p0.renderer.setAlphaDisable(0) +# Emitter parameters +p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) +p0.emitter.setAmplitude(1.0000) +p0.emitter.setAmplitudeSpread(0.7500) +p0.emitter.setOffsetForce(LVector3(0.0000, 0.0000, 1.0000)) +p0.emitter.setExplicitLaunchVector(LVector3(0.0000, 0.0000, 0.0000)) +p0.emitter.setRadiateOrigin(LPoint3(0.0000, 0.0000, 0.0000)) +# Disc parameters +p0.emitter.setRadius(0.5000) +self.addParticles(p0) +f0 = ForceGroup.ForceGroup('vertex') +# Force parameters +force0 = LinearNoiseForce(0.1500, 0) +force0.setActive(1) +f0.addForce(force0) +self.addForceGroup(f0) diff --git a/samples/particles/steam_example.py b/samples/particles/steam_example.py new file mode 100755 index 0000000000..4a003f5ab0 --- /dev/null +++ b/samples/particles/steam_example.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial shows how to take an existing particle effect taken from a +# .ptf file and run it in a general Panda project. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import TextNode +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import LPoint3, LVector3 +from panda3d.core import Filename +from panda3d.physics import BaseParticleEmitter, BaseParticleRenderer +from panda3d.physics import PointParticleFactory, SpriteParticleRenderer +from panda3d.physics import LinearNoiseForce, DiscEmitter +from direct.particles.Particles import Particles +from direct.particles.ParticleEffect import ParticleEffect +from direct.particles.ForceGroup import ForceGroup +from direct.gui.OnscreenText import OnscreenText +import sys + +HELP_TEXT = """ +1: Load Steam +2: Load Dust +3: Load Fountain +4: Load Smoke +5: Load Smokering +6: Load Fireish +ESC: Quit +""" + +class ParticleDemo(ShowBase): + + def __init__(self): + ShowBase.__init__(self) + + # Standard title and instruction text + self.title = OnscreenText( + text="Panda3D: Tutorial - Particles", + parent=base.a2dBottomCenter, + style=1, fg=(1, 1, 1, 1), pos=(0, 0.1), scale=.08) + self.escapeEvent = OnscreenText( + text=HELP_TEXT, parent=base.a2dTopLeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.06), + align=TextNode.ALeft, scale=.05) + + # More standard initialization + self.accept('escape', sys.exit) + self.accept('1', self.loadParticleConfig, ['steam.ptf']) + self.accept('2', self.loadParticleConfig, ['dust.ptf']) + self.accept('3', self.loadParticleConfig, ['fountain.ptf']) + self.accept('4', self.loadParticleConfig, ['smoke.ptf']) + self.accept('5', self.loadParticleConfig, ['smokering.ptf']) + self.accept('6', self.loadParticleConfig, ['fireish.ptf']) + + self.accept('escape', sys.exit) + base.disableMouse() + base.camera.setPos(0, -20, 2) + base.camLens.setFov(25) + base.setBackgroundColor(0, 0, 0) + + # This command is required for Panda to render particles + base.enableParticles() + self.t = loader.loadModel("teapot") + self.t.setPos(0, 10, 0) + self.t.reparentTo(render) + self.setupLights() + self.p = ParticleEffect() + self.loadParticleConfig('steam.ptf') + + def loadParticleConfig(self, filename): + # Start of the code from steam.ptf + self.p.cleanup() + self.p = ParticleEffect() + self.p.loadConfig(Filename(filename)) + # Sets particles to birth relative to the teapot, but to render at + # toplevel + self.p.start(self.t) + self.p.setPos(3.000, 0.000, 2.250) + + # Setup lighting + def setupLights(self): + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.4, .4, .35, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 8, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + # Set lighting on teapot so steam doesn't get affected + self.t.setLight(self.t.attachNewNode(directionalLight)) + self.t.setLight(self.t.attachNewNode(ambientLight)) + +demo = ParticleDemo() +demo.run() diff --git a/samples/procedural-cube/main.py b/samples/procedural-cube/main.py new file mode 100755 index 0000000000..2f21cd9a0f --- /dev/null +++ b/samples/procedural-cube/main.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python + +# Author: Kwasi Mensah (kmensah@andrew.cmu.edu) +# Date: 8/02/2005 +# +# This is meant to be a simple example of how to draw a cube +# using Panda's new Geom Interface. Quads arent directly supported +# since they get broken down to trianlges anyway. +# + +from direct.showbase.ShowBase import ShowBase +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from panda3d.core import lookAt +from panda3d.core import GeomVertexFormat, GeomVertexData +from panda3d.core import Geom, GeomTriangles, GeomVertexWriter +from panda3d.core import Texture, GeomNode +from panda3d.core import PerspectiveLens +from panda3d.core import CardMaker +from panda3d.core import Light, Spotlight +from panda3d.core import TextNode +from panda3d.core import LVector3 +import sys +import os + +base = ShowBase() +base.disableMouse() +base.camera.setPos(0, -10, 0) + +title = OnscreenText(text="Panda3D: Tutorial - Making a Cube Procedurally", + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07, + parent=base.a2dBottomRight, align=TextNode.ARight) +escapeEvent = OnscreenText(text="1: Set a Texture onto the Cube", + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.08), + align=TextNode.ALeft, scale=.05, + parent=base.a2dTopLeft) +spaceEvent = OnscreenText(text="2: Toggle Light from the front On/Off", + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.14), + align=TextNode.ALeft, scale=.05, + parent=base.a2dTopLeft) +upDownEvent = OnscreenText(text="3: Toggle Light from on top On/Off", + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.20), + align=TextNode.ALeft, scale=.05, + parent=base.a2dTopLeft) + + +# You can't normalize inline so this is a helper function +def normalized(*args): + myVec = LVector3(*args) + myVec.normalize() + return myVec + +# helper function to make a square given the Lower-Left-Hand and +# Upper-Right-Hand corners + +def makeSquare(x1, y1, z1, x2, y2, z2): + format = GeomVertexFormat.getV3n3cpt2() + vdata = GeomVertexData('square', format, Geom.UHDynamic) + + vertex = GeomVertexWriter(vdata, 'vertex') + normal = GeomVertexWriter(vdata, 'normal') + color = GeomVertexWriter(vdata, 'color') + texcoord = GeomVertexWriter(vdata, 'texcoord') + + # make sure we draw the sqaure in the right plane + if x1 != x2: + vertex.addData3(x1, y1, z1) + vertex.addData3(x2, y1, z1) + vertex.addData3(x2, y2, z2) + vertex.addData3(x1, y2, z2) + + normal.addData3(normalized(2 * x1 - 1, 2 * y1 - 1, 2 * z1 - 1)) + normal.addData3(normalized(2 * x2 - 1, 2 * y1 - 1, 2 * z1 - 1)) + normal.addData3(normalized(2 * x2 - 1, 2 * y2 - 1, 2 * z2 - 1)) + normal.addData3(normalized(2 * x1 - 1, 2 * y2 - 1, 2 * z2 - 1)) + + else: + vertex.addData3(x1, y1, z1) + vertex.addData3(x2, y2, z1) + vertex.addData3(x2, y2, z2) + vertex.addData3(x1, y1, z2) + + normal.addData3(normalized(2 * x1 - 1, 2 * y1 - 1, 2 * z1 - 1)) + normal.addData3(normalized(2 * x2 - 1, 2 * y2 - 1, 2 * z1 - 1)) + normal.addData3(normalized(2 * x2 - 1, 2 * y2 - 1, 2 * z2 - 1)) + normal.addData3(normalized(2 * x1 - 1, 2 * y1 - 1, 2 * z2 - 1)) + + # adding different colors to the vertex for visibility + color.addData4f(1.0, 0.0, 0.0, 1.0) + color.addData4f(0.0, 1.0, 0.0, 1.0) + color.addData4f(0.0, 0.0, 1.0, 1.0) + color.addData4f(1.0, 0.0, 1.0, 1.0) + + texcoord.addData2f(0.0, 1.0) + texcoord.addData2f(0.0, 0.0) + texcoord.addData2f(1.0, 0.0) + texcoord.addData2f(1.0, 1.0) + + # Quads aren't directly supported by the Geom interface + # you might be interested in the CardMaker class if you are + # interested in rectangle though + tris = GeomTriangles(Geom.UHDynamic) + tris.addVertices(0, 1, 3) + tris.addVertices(1, 2, 3) + + square = Geom(vdata) + square.addPrimitive(tris) + return square + +# Note: it isn't particularly efficient to make every face as a separate Geom. +# instead, it would be better to create one Geom holding all of the faces. +square0 = makeSquare(-1, -1, -1, 1, -1, 1) +square1 = makeSquare(-1, 1, -1, 1, 1, 1) +square2 = makeSquare(-1, 1, 1, 1, -1, 1) +square3 = makeSquare(-1, 1, -1, 1, -1, -1) +square4 = makeSquare(-1, -1, -1, -1, 1, 1) +square5 = makeSquare(1, -1, -1, 1, 1, 1) +snode = GeomNode('square') +snode.addGeom(square0) +snode.addGeom(square1) +snode.addGeom(square2) +snode.addGeom(square3) +snode.addGeom(square4) +snode.addGeom(square5) + +cube = render.attachNewNode(snode) +cube.hprInterval(1.5, (360, 360, 360)).loop() + +# OpenGl by default only draws "front faces" (polygons whose vertices are +# specified CCW). +cube.setTwoSided(True) + + +class MyTapper(DirectObject): + + def __init__(self): + self.testTexture = loader.loadTexture("maps/envir-reeds.png") + self.accept("1", self.toggleTex) + self.accept("2", self.toggleLightsSide) + self.accept("3", self.toggleLightsUp) + + self.LightsOn = False + self.LightsOn1 = False + slight = Spotlight('slight') + slight.setColor((1, 1, 1, 1)) + lens = PerspectiveLens() + slight.setLens(lens) + self.slnp = render.attachNewNode(slight) + self.slnp1 = render.attachNewNode(slight) + + def toggleTex(self): + global cube + if cube.hasTexture(): + cube.setTextureOff(1) + else: + cube.setTexture(self.testTexture) + + def toggleLightsSide(self): + global cube + self.LightsOn = not self.LightsOn + + if self.LightsOn: + render.setLight(self.slnp) + self.slnp.setPos(cube, 10, -400, 0) + self.slnp.lookAt(10, 0, 0) + else: + render.setLightOff(self.slnp) + + def toggleLightsUp(self): + global cube + self.LightsOn1 = not self.LightsOn1 + + if self.LightsOn1: + render.setLight(self.slnp1) + self.slnp1.setPos(cube, 10, 0, 400) + self.slnp1.lookAt(10, 0, 0) + else: + render.setLightOff(self.slnp1) + +t = MyTapper() +base.run() diff --git a/samples/render-to-texture/main.py b/samples/render-to-texture/main.py new file mode 100755 index 0000000000..ec6836bb4d --- /dev/null +++ b/samples/render-to-texture/main.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import Filename, Texture +from panda3d.core import AmbientLight, DirectionalLight, PointLight +from panda3d.core import NodePath, TextNode +from direct.task.Task import Task +from direct.actor.Actor import Actor +from direct.gui.OnscreenText import OnscreenText +import sys +import os +import random + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, + shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, + pos=(0.08, -pos - 0.04), align=TextNode.ALeft) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08, + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), shadow=(0, 0, 0, 1)) + +random.seed() + +class TeapotOnTVDemo(ShowBase): + + def __init__(self): + ShowBase.__init__(self) + self.disableMouse() + self.setBackgroundColor((0, 0, 0, 1)) + + # Post the instructions. + self.title = addTitle("Panda3D: Tutorial - Using Render-to-Texture") + self.inst1 = addInstructions(0.06, "ESC: Quit") + self.inst2 = addInstructions(0.12, "Up/Down: Zoom in/out on the Teapot") + self.inst3 = addInstructions(0.18, "Left/Right: Move teapot left/right") + self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") + + # we now get buffer thats going to hold the texture of our new scene + altBuffer = self.win.makeTextureBuffer("hello", 256, 256) + + # now we have to setup a new scene graph to make this scene + altRender = NodePath("new render") + + # this takes care of setting up ther camera properly + self.altCam = self.makeCamera(altBuffer) + self.altCam.reparentTo(altRender) + self.altCam.setPos(0, -10, 0) + + # get the teapot and rotates it for a simple animation + self.teapot = loader.loadModel('teapot') + self.teapot.reparentTo(altRender) + self.teapot.setPos(0, 0, -1) + self.teapot.hprInterval(1.5, (360, 360, 360)).loop() + + # put some lighting on the teapot + dlight = DirectionalLight('dlight') + alight = AmbientLight('alight') + dlnp = altRender.attachNewNode(dlight) + alnp = altRender.attachNewNode(alight) + dlight.setColor((0.8, 0.8, 0.5, 1)) + alight.setColor((0.2, 0.2, 0.2, 1)) + dlnp.setHpr(0, -60, 0) + altRender.setLight(dlnp) + altRender.setLight(alnp) + + # Put lighting on the main scene + plight = PointLight('plight') + plnp = render.attachNewNode(plight) + plnp.setPos(0, 0, 10) + render.setLight(plnp) + render.setLight(alnp) + + # Panda contains a built-in viewer that lets you view the results of + # your render-to-texture operations. This code configures the viewer. + + self.accept("v", self.bufferViewer.toggleEnable) + self.accept("V", self.bufferViewer.toggleEnable) + self.bufferViewer.setPosition("llcorner") + self.bufferViewer.setCardSize(1.0, 0.0) + + # Create the tv-men. Each TV-man will display the + # offscreen-texture on his TV screen. + self.tvMen = [] + self.makeTvMan(-5, 30, 1, altBuffer.getTexture(), 0.9) + self.makeTvMan(5, 30, 1, altBuffer.getTexture(), 1.4) + self.makeTvMan(0, 23, -3, altBuffer.getTexture(), 2.0) + self.makeTvMan(-5, 20, -6, altBuffer.getTexture(), 1.1) + self.makeTvMan(5, 18, -5, altBuffer.getTexture(), 1.7) + + self.accept("escape", sys.exit, [0]) + self.accept("arrow_up", self.zoomIn) + self.accept("arrow_down", self.zoomOut) + self.accept("arrow_left", self.moveLeft) + self.accept("arrow_right", self.moveRight) + + def makeTvMan(self, x, y, z, tex, playrate): + man = Actor() + man.loadModel('models/mechman_idle') + man.setPos(x, y, z) + man.reparentTo(render) + faceplate = man.find("**/faceplate") + faceplate.setTexture(tex, 1) + man.setPlayRate(playrate, "mechman_anim") + man.loop("mechman_anim") + self.tvMen.append(man) + + def zoomIn(self): + self.altCam.setY(self.altCam.getY() * 0.9) + + def zoomOut(self): + self.altCam.setY(self.altCam.getY() * 1.2) + + def moveLeft(self): + self.altCam.setX(self.altCam.getX() + 1) + + def moveRight(self): + self.altCam.setX(self.altCam.getX() - 1) + +demo = TeapotOnTVDemo() +demo.run() diff --git a/samples/render-to-texture/models/mechman_idle.egg.pz b/samples/render-to-texture/models/mechman_idle.egg.pz new file mode 100644 index 0000000000..5fa90b3d8e Binary files /dev/null and b/samples/render-to-texture/models/mechman_idle.egg.pz differ diff --git a/samples/render-to-texture/models/mechmanskin.jpg b/samples/render-to-texture/models/mechmanskin.jpg new file mode 100644 index 0000000000..d9160b1065 Binary files /dev/null and b/samples/render-to-texture/models/mechmanskin.jpg differ diff --git a/samples/render-to-texture/models/static.jpg b/samples/render-to-texture/models/static.jpg new file mode 100644 index 0000000000..93739eba8a Binary files /dev/null and b/samples/render-to-texture/models/static.jpg differ diff --git a/samples/roaming-ralph/main.py b/samples/roaming-ralph/main.py new file mode 100755 index 0000000000..0290c3e74b --- /dev/null +++ b/samples/roaming-ralph/main.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python + +# Author: Ryan Myers +# Models: Jeff Styers, Reagan Heller +# +# Last Updated: 2015-03-13 +# +# This tutorial provides an example of creating a character +# and having it walk around on uneven terrain, as well +# as implementing a fully rotatable camera. + +from direct.showbase.ShowBase import ShowBase +from panda3d.core import CollisionTraverser, CollisionNode +from panda3d.core import CollisionHandlerQueue, CollisionRay +from panda3d.core import Filename, AmbientLight, DirectionalLight +from panda3d.core import PandaNode, NodePath, Camera, TextNode +from panda3d.core import CollideMask +from direct.gui.OnscreenText import OnscreenText +from direct.actor.Actor import Actor +import random +import sys +import os +import math + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, + shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, + pos=(0.08, -pos - 0.04), align=TextNode.ALeft) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.07, + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), shadow=(0, 0, 0, 1)) + + +class RoamingRalphDemo(ShowBase): + def __init__(self): + # Set up the window, camera, etc. + ShowBase.__init__(self) + + # Set the background color to black + self.win.setClearColor((0, 0, 0, 1)) + + # This is used to store which keys are currently pressed. + self.keyMap = { + "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} + + # Post the instructions + self.title = addTitle( + "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") + self.inst1 = addInstructions(0.06, "[ESC]: Quit") + self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") + self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") + self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") + self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") + self.inst7 = addInstructions(0.36, "[S]: Rotate Camera Right") + + # Set up the environment + # + # This environment model contains collision meshes. If you look + # in the egg file, you will see the following: + # + # { Polyset keep descend } + # + # This tag causes the following mesh to be converted to a collision + # mesh -- a mesh which is optimized for collision, not rendering. + # It also keeps the original mesh, so there are now two copies --- + # one optimized for rendering, one for collisions. + + self.environ = loader.loadModel("models/world") + self.environ.reparentTo(render) + + # Create the main character, Ralph + + ralphStartPos = self.environ.find("**/start_point").getPos() + self.ralph = Actor("models/ralph", + {"run": "models/ralph-run", + "walk": "models/ralph-walk"}) + self.ralph.reparentTo(render) + self.ralph.setScale(.2) + self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) + + # Create a floater object, which floats 2 units above ralph. We + # use this as a target for the camera to look at. + + self.floater = NodePath(PandaNode("floater")) + self.floater.reparentTo(self.ralph) + self.floater.setZ(2.0) + + # Accept the control keys for movement and rotation + + self.accept("escape", sys.exit) + self.accept("arrow_left", self.setKey, ["left", True]) + self.accept("arrow_right", self.setKey, ["right", True]) + self.accept("arrow_up", self.setKey, ["forward", True]) + self.accept("a", self.setKey, ["cam-left", True]) + self.accept("s", self.setKey, ["cam-right", True]) + self.accept("arrow_left-up", self.setKey, ["left", False]) + self.accept("arrow_right-up", self.setKey, ["right", False]) + self.accept("arrow_up-up", self.setKey, ["forward", False]) + self.accept("a-up", self.setKey, ["cam-left", False]) + self.accept("s-up", self.setKey, ["cam-right", False]) + + taskMgr.add(self.move, "moveTask") + + # Game state variables + self.isMoving = False + + # Set up the camera + self.disableMouse() + self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) + + # We will detect the height of the terrain by creating a collision + # ray and casting it downward toward the terrain. One ray will + # start above ralph's head, and the other will start above the camera. + # A ray may hit the terrain, or it may hit a rock or a tree. If it + # hits the terrain, we can detect the height. If it hits anything + # else, we rule that the move is illegal. + self.cTrav = CollisionTraverser() + + self.ralphGroundRay = CollisionRay() + self.ralphGroundRay.setOrigin(0, 0, 9) + self.ralphGroundRay.setDirection(0, 0, -1) + self.ralphGroundCol = CollisionNode('ralphRay') + self.ralphGroundCol.addSolid(self.ralphGroundRay) + self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) + self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff()) + self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) + self.ralphGroundHandler = CollisionHandlerQueue() + self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) + + self.camGroundRay = CollisionRay() + self.camGroundRay.setOrigin(0, 0, 9) + self.camGroundRay.setDirection(0, 0, -1) + self.camGroundCol = CollisionNode('camRay') + self.camGroundCol.addSolid(self.camGroundRay) + self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) + self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) + self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) + self.camGroundHandler = CollisionHandlerQueue() + self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) + + # Uncomment this line to see the collision rays + #self.ralphGroundColNp.show() + #self.camGroundColNp.show() + + # Uncomment this line to show a visual representation of the + # collisions occuring + #self.cTrav.showCollisions(render) + + # Create some lighting + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.3, .3, .3, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection((-5, -5, -5)) + directionalLight.setColor((1, 1, 1, 1)) + directionalLight.setSpecularColor((1, 1, 1, 1)) + render.setLight(render.attachNewNode(ambientLight)) + render.setLight(render.attachNewNode(directionalLight)) + + # Records the state of the arrow keys + def setKey(self, key, value): + self.keyMap[key] = value + + # Accepts arrow keys to move either the player or the menu cursor, + # Also deals with grid checking and collision detection + def move(self, task): + + # Get the time that elapsed since last frame. We multiply this with + # the desired speed in order to find out with which distance to move + # in order to achieve that desired speed. + dt = globalClock.getDt() + + # If the camera-left key is pressed, move camera left. + # If the camera-right key is pressed, move camera right. + + if self.keyMap["cam-left"]: + self.camera.setX(self.camera, -20 * dt) + if self.keyMap["cam-right"]: + self.camera.setX(self.camera, +20 * dt) + + # save ralph's initial position so that we can restore it, + # in case he falls off the map or runs into something. + + startpos = self.ralph.getPos() + + # If a move-key is pressed, move ralph in the specified direction. + + if self.keyMap["left"]: + self.ralph.setH(self.ralph.getH() + 300 * dt) + if self.keyMap["right"]: + self.ralph.setH(self.ralph.getH() - 300 * dt) + if self.keyMap["forward"]: + self.ralph.setY(self.ralph, -25 * dt) + + # If ralph is moving, loop the run animation. + # If he is standing still, stop the animation. + + if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"]: + if self.isMoving is False: + self.ralph.loop("run") + self.isMoving = True + else: + if self.isMoving: + self.ralph.stop() + self.ralph.pose("walk", 5) + self.isMoving = False + + # If the camera is too far from ralph, move it closer. + # If the camera is too close to ralph, move it farther. + + camvec = self.ralph.getPos() - self.camera.getPos() + camvec.setZ(0) + camdist = camvec.length() + camvec.normalize() + if camdist > 10.0: + self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) + camdist = 10.0 + if camdist < 5.0: + self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) + camdist = 5.0 + + # Normally, we would have to call traverse() to check for collisions. + # However, the class ShowBase that we inherit from has a task to do + # this for us, if we assign a CollisionTraverser to self.cTrav. + #self.cTrav.traverse(render) + + # Adjust ralph's Z coordinate. If ralph's ray hit terrain, + # update his Z. If it hit anything else, or didn't hit anything, put + # him back where he was last frame. + + entries = list(self.ralphGroundHandler.getEntries()) + entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) + + if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": + self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) + else: + self.ralph.setPos(startpos) + + # Keep the camera at one foot above the terrain, + # or two feet above ralph, whichever is greater. + + entries = list(self.camGroundHandler.getEntries()) + entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) + + if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": + self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) + if self.camera.getZ() < self.ralph.getZ() + 2.0: + self.camera.setZ(self.ralph.getZ() + 2.0) + + # The camera should look in ralph's direction, + # but it should also try to stay horizontal, so look at + # a floater which hovers above ralph's head. + self.camera.lookAt(self.floater) + + return task.cont + + +demo = RoamingRalphDemo() +demo.run() diff --git a/samples/roaming-ralph/models/ground.jpg b/samples/roaming-ralph/models/ground.jpg new file mode 100644 index 0000000000..d36aae42fd Binary files /dev/null and b/samples/roaming-ralph/models/ground.jpg differ diff --git a/samples/roaming-ralph/models/hedge.jpg b/samples/roaming-ralph/models/hedge.jpg new file mode 100644 index 0000000000..233dcbdc83 Binary files /dev/null and b/samples/roaming-ralph/models/hedge.jpg differ diff --git a/samples/roaming-ralph/models/ralph-run.egg.pz b/samples/roaming-ralph/models/ralph-run.egg.pz new file mode 100644 index 0000000000..e50ff41d24 Binary files /dev/null and b/samples/roaming-ralph/models/ralph-run.egg.pz differ diff --git a/samples/roaming-ralph/models/ralph-walk.egg.pz b/samples/roaming-ralph/models/ralph-walk.egg.pz new file mode 100644 index 0000000000..955352549c Binary files /dev/null and b/samples/roaming-ralph/models/ralph-walk.egg.pz differ diff --git a/samples/roaming-ralph/models/ralph.egg.pz b/samples/roaming-ralph/models/ralph.egg.pz new file mode 100644 index 0000000000..5749c11650 Binary files /dev/null and b/samples/roaming-ralph/models/ralph.egg.pz differ diff --git a/samples/roaming-ralph/models/ralph.jpg b/samples/roaming-ralph/models/ralph.jpg new file mode 100644 index 0000000000..198b4f5424 Binary files /dev/null and b/samples/roaming-ralph/models/ralph.jpg differ diff --git a/samples/roaming-ralph/models/rock03.jpg b/samples/roaming-ralph/models/rock03.jpg new file mode 100644 index 0000000000..8598e6de36 Binary files /dev/null and b/samples/roaming-ralph/models/rock03.jpg differ diff --git a/samples/roaming-ralph/models/tree.jpg b/samples/roaming-ralph/models/tree.jpg new file mode 100644 index 0000000000..fcdf4c73ca Binary files /dev/null and b/samples/roaming-ralph/models/tree.jpg differ diff --git a/samples/roaming-ralph/models/world.egg.pz b/samples/roaming-ralph/models/world.egg.pz new file mode 100644 index 0000000000..9ab9439f74 Binary files /dev/null and b/samples/roaming-ralph/models/world.egg.pz differ diff --git a/samples/shadows/advanced.py b/samples/shadows/advanced.py new file mode 100755 index 0000000000..6e8fbf5eb2 --- /dev/null +++ b/samples/shadows/advanced.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python + +from panda3d.core import * +import sys +import os + +import direct.directbase.DirectStart +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.actor import Actor +from random import * + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, + shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, + pos=(0.08, -pos - 0.04), align=TextNode.ALeft) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.07, + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), shadow=(0, 0, 0, 1)) + + +class World(DirectObject): + + def __init__(self): + # Preliminary capabilities check. + + if not base.win.getGsg().getSupportsBasicShaders(): + self.t = addTitle( + "Shadow Demo: Video driver reports that shaders are not supported.") + return + if not base.win.getGsg().getSupportsDepthTexture(): + self.t = addTitle( + "Shadow Demo: Video driver reports that depth textures are not supported.") + return + + # creating the offscreen buffer. + + winprops = WindowProperties.size(512, 512) + props = FrameBufferProperties() + props.setRgbColor(1) + props.setAlphaBits(1) + props.setDepthBits(1) + LBuffer = base.graphicsEngine.makeOutput( + base.pipe, "offscreen buffer", -2, + props, winprops, + GraphicsPipe.BFRefuseWindow, + base.win.getGsg(), base.win) + + if not LBuffer: + self.t = addTitle( + "Shadow Demo: Video driver cannot create an offscreen buffer.") + return + + Ldepthmap = Texture() + LBuffer.addRenderTexture(Ldepthmap, GraphicsOutput.RTMBindOrCopy, + GraphicsOutput.RTPDepthStencil) + if base.win.getGsg().getSupportsShadowFilter(): + Ldepthmap.setMinfilter(Texture.FTShadow) + Ldepthmap.setMagfilter(Texture.FTShadow) + + # Adding a color texture is totally unnecessary, but it helps with + # debugging. + Lcolormap = Texture() + LBuffer.addRenderTexture(Lcolormap, GraphicsOutput.RTMBindOrCopy, + GraphicsOutput.RTPColor) + + self.inst_p = addInstructions(0.06, 'P : stop/start the Panda Rotation') + self.inst_w = addInstructions(0.12, 'W : stop/start the Walk Cycle') + self.inst_t = addInstructions(0.18, 'T : stop/start the Teapot') + self.inst_l = addInstructions(0.24, 'L : move light source far or close') + self.inst_v = addInstructions(0.30, 'V: View the Depth-Texture results') + self.inst_x = addInstructions(0.36, 'Left/Right Arrow : switch camera angles') + self.inst_a = addInstructions(0.42, 'Something about A/Z and push bias') + + base.setBackgroundColor(0, 0, 0.2, 1) + + base.camLens.setNearFar(1.0, 10000) + base.camLens.setFov(75) + base.disableMouse() + + # Load the scene. + + floorTex = loader.loadTexture('maps/envir-ground.jpg') + cm = CardMaker('') + cm.setFrame(-2, 2, -2, 2) + floor = render.attachNewNode(PandaNode("floor")) + for y in range(12): + for x in range(12): + nn = floor.attachNewNode(cm.generate()) + nn.setP(-90) + nn.setPos((x - 6) * 4, (y - 6) * 4, 0) + floor.setTexture(floorTex) + floor.flattenStrong() + + self.pandaAxis = render.attachNewNode('panda axis') + self.pandaModel = Actor.Actor('panda-model', {'walk': 'panda-walk4'}) + self.pandaModel.reparentTo(self.pandaAxis) + self.pandaModel.setPos(9, 0, 0) + self.pandaModel.setShaderInput("scale", 0.01, 0.01, 0.01, 1.0) + self.pandaWalk = self.pandaModel.actorInterval('walk', playRate=1.8) + self.pandaWalk.loop() + self.pandaMovement = self.pandaAxis.hprInterval( + 20.0, LPoint3(-360, 0, 0), startHpr=LPoint3(0, 0, 0)) + self.pandaMovement.loop() + + self.teapot = loader.loadModel('teapot') + self.teapot.reparentTo(render) + self.teapot.setPos(0, -20, 10) + self.teapot.setShaderInput("texDisable", 1, 1, 1, 1) + self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360)) + self.teapotMovement.loop() + + self.accept('escape', sys.exit) + + self.accept("arrow_left", self.incrementCameraPosition, [-1]) + self.accept("arrow_right", self.incrementCameraPosition, [1]) + self.accept("p", self.toggleInterval, [self.pandaMovement]) + self.accept("P", self.toggleInterval, [self.pandaMovement]) + self.accept("t", self.toggleInterval, [self.teapotMovement]) + self.accept("T", self.toggleInterval, [self.teapotMovement]) + self.accept("w", self.toggleInterval, [self.pandaWalk]) + self.accept("W", self.toggleInterval, [self.pandaWalk]) + self.accept("v", base.bufferViewer.toggleEnable) + self.accept("V", base.bufferViewer.toggleEnable) + self.accept("l", self.incrementLightPosition, [1]) + self.accept("L", self.incrementLightPosition, [1]) + self.accept("o", base.oobe) + self.accept('a', self.adjustPushBias, [1.1]) + self.accept('A', self.adjustPushBias, [1.1]) + self.accept('z', self.adjustPushBias, [0.9]) + self.accept('Z', self.adjustPushBias, [0.9]) + + self.LCam = base.makeCamera(LBuffer) + self.LCam.node().setScene(render) + self.LCam.node().getLens().setFov(40) + self.LCam.node().getLens().setNearFar(10, 100) + + # default values + self.pushBias = 0.04 + self.ambient = 0.2 + self.cameraSelection = 0 + self.lightSelection = 0 + + # setting up shader + render.setShaderInput('light', self.LCam) + render.setShaderInput('Ldepthmap', Ldepthmap) + render.setShaderInput('ambient', self.ambient, 0, 0, 1.0) + render.setShaderInput('texDisable', 0, 0, 0, 0) + render.setShaderInput('scale', 1, 1, 1, 1) + + # Put a shader on the Light camera. + lci = NodePath(PandaNode("Light Camera Initializer")) + lci.setShader(loader.loadShader('caster.sha')) + self.LCam.node().setInitialState(lci.getState()) + + # Put a shader on the Main camera. + # Some video cards have special hardware for shadow maps. + # If the card has that, use it. If not, use a different + # shader that does not require hardware support. + + mci = NodePath(PandaNode("Main Camera Initializer")) + if base.win.getGsg().getSupportsShadowFilter(): + mci.setShader(loader.loadShader('shadow.sha')) + else: + mci.setShader(loader.loadShader('shadow-nosupport.sha')) + base.cam.node().setInitialState(mci.getState()) + + self.incrementCameraPosition(0) + self.incrementLightPosition(0) + self.adjustPushBias(1.0) + + def toggleInterval(self, ival): + if ival.isPlaying(): + ival.pause() + else: + ival.resume() + + def incrementCameraPosition(self, n): + self.cameraSelection = (self.cameraSelection + n) % 6 + if (self.cameraSelection == 0): + base.cam.reparentTo(render) + base.cam.setPos(30, -45, 26) + base.cam.lookAt(0, 0, 0) + self.LCam.node().hideFrustum() + if (self.cameraSelection == 1): + base.cam.reparentTo(self.pandaModel) + base.cam.setPos(7, -3, 9) + base.cam.lookAt(0, 0, 0) + self.LCam.node().hideFrustum() + if (self.cameraSelection == 2): + base.cam.reparentTo(self.pandaModel) + base.cam.setPos(-7, -3, 9) + base.cam.lookAt(0, 0, 0) + self.LCam.node().hideFrustum() + if (self.cameraSelection == 3): + base.cam.reparentTo(render) + base.cam.setPos(7, -23, 12) + base.cam.lookAt(self.teapot) + self.LCam.node().hideFrustum() + if (self.cameraSelection == 4): + base.cam.reparentTo(render) + base.cam.setPos(-7, -23, 12) + base.cam.lookAt(self.teapot) + self.LCam.node().hideFrustum() + if (self.cameraSelection == 5): + base.cam.reparentTo(render) + base.cam.setPos(1000, 0, 195) + base.cam.lookAt(0, 0, 0) + self.LCam.node().showFrustum() + + def incrementLightPosition(self, n): + self.lightSelection = (self.lightSelection + n) % 2 + if (self.lightSelection == 0): + self.LCam.setPos(0, -40, 25) + self.LCam.lookAt(0, -10, 0) + self.LCam.node().getLens().setNearFar(10, 100) + if (self.lightSelection == 1): + self.LCam.setPos(0, -600, 200) + self.LCam.lookAt(0, -10, 0) + self.LCam.node().getLens().setNearFar(10, 1000) + + def shaderSupported(self): + return base.win.getGsg().getSupportsBasicShaders() and \ + base.win.getGsg().getSupportsDepthTexture() and \ + base.win.getGsg().getSupportsShadowFilter() + + def adjustPushBias(self, inc): + self.pushBias *= inc + self.inst_a.setText( + 'A/Z: Increase/Decrease the Push-Bias [%F]' % self.pushBias) + render.setShaderInput('push', self.pushBias) + +w = World() +base.run() diff --git a/samples/shadows/basic.py b/samples/shadows/basic.py new file mode 100755 index 0000000000..d6ec8671f5 --- /dev/null +++ b/samples/shadows/basic.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +from panda3d.core import * +import sys +import os + +import direct.directbase.DirectStart +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.actor import Actor +from random import * + +# Function to put instructions on the screen. +def addInstructions(pos, msg): + return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, + shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, + pos=(0.08, -pos - 0.04), align=TextNode.ALeft) + +# Function to put title on the screen. +def addTitle(text): + return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.07, + parent=base.a2dBottomRight, align=TextNode.ARight, + pos=(-0.1, 0.09), shadow=(0, 0, 0, 1)) + + +class World(DirectObject): + + def __init__(self): + # Preliminary capabilities check. + + if not base.win.getGsg().getSupportsBasicShaders(): + self.t = addTitle( + "Shadow Demo: Video driver reports that shaders are not supported.") + return + if not base.win.getGsg().getSupportsDepthTexture(): + self.t = addTitle( + "Shadow Demo: Video driver reports that depth textures are not supported.") + return + + self.inst_p = addInstructions(0.06, 'P : stop/start the Panda Rotation') + self.inst_w = addInstructions(0.12, 'W : stop/start the Walk Cycle') + self.inst_t = addInstructions(0.18, 'T : stop/start the Teapot') + self.inst_l = addInstructions(0.24, 'L : move light source far or close') + self.inst_v = addInstructions(0.30, 'V: View the Depth-Texture results') + self.inst_x = addInstructions(0.36, 'Left/Right Arrow : switch camera angles') + + base.setBackgroundColor(0, 0, 0.2, 1) + + base.camLens.setNearFar(1.0, 10000) + base.camLens.setFov(75) + base.disableMouse() + + # Load the scene. + floorTex = loader.loadTexture('maps/envir-ground.jpg') + + cm = CardMaker('') + cm.setFrame(-2, 2, -2, 2) + floor = render.attachNewNode(PandaNode("floor")) + for y in range(12): + for x in range(12): + nn = floor.attachNewNode(cm.generate()) + nn.setP(-90) + nn.setPos((x - 6) * 4, (y - 6) * 4, 0) + floor.setTexture(floorTex) + floor.flattenStrong() + + self.pandaAxis = render.attachNewNode('panda axis') + self.pandaModel = Actor.Actor('panda-model', {'walk': 'panda-walk4'}) + self.pandaModel.reparentTo(self.pandaAxis) + self.pandaModel.setPos(9, 0, 0) + self.pandaModel.setScale(0.01) + self.pandaWalk = self.pandaModel.actorInterval('walk', playRate=1.8) + self.pandaWalk.loop() + self.pandaMovement = self.pandaAxis.hprInterval( + 20.0, LPoint3(-360, 0, 0), startHpr=LPoint3(0, 0, 0)) + self.pandaMovement.loop() + + self.teapot = loader.loadModel('teapot') + self.teapot.reparentTo(render) + self.teapot.setPos(0, -20, 10) + self.teapot.setShaderInput("texDisable", 1, 1, 1, 1) + self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360)) + self.teapotMovement.loop() + + self.accept('escape', sys.exit) + + self.accept("arrow_left", self.incrementCameraPosition, [-1]) + self.accept("arrow_right", self.incrementCameraPosition, [1]) + self.accept("p", self.toggleInterval, [self.pandaMovement]) + self.accept("P", self.toggleInterval, [self.pandaMovement]) + self.accept("t", self.toggleInterval, [self.teapotMovement]) + self.accept("T", self.toggleInterval, [self.teapotMovement]) + self.accept("w", self.toggleInterval, [self.pandaWalk]) + self.accept("W", self.toggleInterval, [self.pandaWalk]) + self.accept("v", base.bufferViewer.toggleEnable) + self.accept("V", base.bufferViewer.toggleEnable) + self.accept("l", self.incrementLightPosition, [1]) + self.accept("L", self.incrementLightPosition, [1]) + self.accept("o", base.oobe) + + self.light = render.attachNewNode(Spotlight("Spot")) + self.light.node().setScene(render) + self.light.node().setShadowCaster(True) + self.light.node().showFrustum() + self.light.node().getLens().setFov(40) + self.light.node().getLens().setNearFar(10, 100) + render.setLight(self.light) + + self.alight = render.attachNewNode(AmbientLight("Ambient")) + self.alight.node().setColor(LVector4(0.2, 0.2, 0.2, 1)) + render.setLight(self.alight) + + # Important! Enable the shader generator. + render.setShaderAuto() + + # default values + self.cameraSelection = 0 + self.lightSelection = 0 + + self.incrementCameraPosition(0) + self.incrementLightPosition(0) + + def toggleInterval(self, ival): + if ival.isPlaying(): + ival.pause() + else: + ival.resume() + + def incrementCameraPosition(self, n): + self.cameraSelection = (self.cameraSelection + n) % 6 + if (self.cameraSelection == 0): + base.cam.reparentTo(render) + base.cam.setPos(30, -45, 26) + base.cam.lookAt(0, 0, 0) + self.light.node().hideFrustum() + if (self.cameraSelection == 1): + base.cam.reparentTo(self.pandaModel) + base.cam.setPos(7, -3, 9) + base.cam.lookAt(0, 0, 0) + self.light.node().hideFrustum() + if (self.cameraSelection == 2): + base.cam.reparentTo(self.pandaModel) + base.cam.setPos(-7, -3, 9) + base.cam.lookAt(0, 0, 0) + self.light.node().hideFrustum() + if (self.cameraSelection == 3): + base.cam.reparentTo(render) + base.cam.setPos(7, -23, 12) + base.cam.lookAt(self.teapot) + self.light.node().hideFrustum() + if (self.cameraSelection == 4): + base.cam.reparentTo(render) + base.cam.setPos(-7, -23, 12) + base.cam.lookAt(self.teapot) + self.light.node().hideFrustum() + if (self.cameraSelection == 5): + base.cam.reparentTo(render) + base.cam.setPos(1000, 0, 195) + base.cam.lookAt(0, 0, 0) + self.light.node().showFrustum() + + def incrementLightPosition(self, n): + self.lightSelection = (self.lightSelection + n) % 2 + if (self.lightSelection == 0): + self.light.setPos(0, -40, 25) + self.light.lookAt(0, -10, 0) + self.light.node().getLens().setNearFar(10, 100) + if (self.lightSelection == 1): + self.light.setPos(0, -600, 200) + self.light.lookAt(0, -10, 0) + self.light.node().getLens().setNearFar(10, 1000) + + def shaderSupported(self): + return base.win.getGsg().getSupportsBasicShaders() and \ + base.win.getGsg().getSupportsDepthTexture() and \ + base.win.getGsg().getSupportsShadowFilter() + +w = World() +base.run() diff --git a/samples/shadows/caster.sha b/samples/shadows/caster.sha new file mode 100644 index 0000000000..ea463d3bd2 --- /dev/null +++ b/samples/shadows/caster.sha @@ -0,0 +1,20 @@ +//Cg + +void vshader(float4 vtx_position : POSITION, + uniform float4x4 mat_modelproj, + uniform float4 k_scale, + out float4 l_position : POSITION, + out float4 l_pos : TEXCOORD0 + ) +{ + float4 position = vtx_position * k_scale; + l_pos = mul(mat_modelproj, position); + l_position = l_pos; +} + +void fshader(in float4 l_pos : TEXCOORD0, + out float4 o_color : COLOR) +{ + float z = (l_pos.z / l_pos.w) * 0.5 + 0.5; + o_color = float4(z, z, z, 1); +} diff --git a/samples/shadows/shadow-nosupport.sha b/samples/shadows/shadow-nosupport.sha new file mode 100644 index 0000000000..a486c91891 --- /dev/null +++ b/samples/shadows/shadow-nosupport.sha @@ -0,0 +1,59 @@ +//Cg + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + float3 vtx_normal : NORMAL, + + uniform float4x4 trans_model_to_clip_of_light, + uniform float4x4 mat_modelproj, + uniform float4 mspos_light, + uniform float4 k_ambient, + uniform float4 k_scale, + uniform float k_push, + + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0, + out float4 l_shadowcoord : TEXCOORD1, + out float l_smooth : TEXCOORD2, + out float4 l_lightclip : TEXCOORD3) +{ + float4 position = vtx_position * k_scale; + + // vertex position + l_position = mul(mat_modelproj, position); + + // Pass through texture coordinate for main texture. + l_texcoord0 = vtx_texcoord0; + + // Calculate the surface lighting factor. + l_smooth = saturate(dot(vtx_normal, normalize(mspos_light.xyz - position.xyz))); + + // Calculate light-space clip position. + float4 pushed = position + float4(vtx_normal * k_push, 0); + l_lightclip = mul(trans_model_to_clip_of_light, pushed); + + // Calculate shadow-map texture coordinates. + l_shadowcoord = l_lightclip * float4(0.5, 0.5, 0.5, 1.0) + + l_lightclip.w * float4(0.5, 0.5, 0.5, 0.0); +} + + +void fshader(in float2 l_texcoord0 : TEXCOORD0, + in float4 l_shadowcoord : TEXCOORD1, + in float l_smooth : TEXCOORD2, + in float4 l_lightclip : TEXCOORD3, + uniform sampler2D tex_0 : TEXUNIT0, + uniform sampler2D k_Ldepthmap : TEXUNIT1, + uniform float4 k_ambient, + uniform float4 k_texDisable, + out float4 o_color : COLOR) +{ + float3 circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0); + float falloff = saturate(1.0 - dot(circleoffs, circleoffs)); + float4 baseColor = saturate(tex2D(tex_0, l_texcoord0) + k_texDisable); + float4 proj = l_shadowcoord / l_shadowcoord.w; + float mapval = f1tex2D(k_Ldepthmap, proj.xy); + float shade = (mapval > proj.z); + o_color = baseColor * (falloff * shade * l_smooth + k_ambient.x); +} + diff --git a/samples/shadows/shadow.sha b/samples/shadows/shadow.sha new file mode 100644 index 0000000000..b9ada780f5 --- /dev/null +++ b/samples/shadows/shadow.sha @@ -0,0 +1,56 @@ +//Cg + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + float3 vtx_normal : NORMAL, + + uniform float4x4 trans_model_to_clip_of_light, + uniform float4x4 mat_modelproj, + uniform float4 mspos_light, + uniform float4 k_ambient, + uniform float4 k_scale, + uniform float k_push, + + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0, + out float4 l_shadowcoord : TEXCOORD1, + out float l_smooth : TEXCOORD2, + out float4 l_lightclip : TEXCOORD3) +{ + float4 position = vtx_position * k_scale; + + // vertex position + l_position = mul(mat_modelproj, position); + + // Pass through texture coordinate for main texture. + l_texcoord0 = vtx_texcoord0; + + // Calculate the surface lighting factor. + l_smooth = saturate(dot(vtx_normal, normalize(mspos_light.xyz - position.xyz))); + + // Calculate light-space clip position. + float4 pushed = position + float4(vtx_normal * k_push, 0); + l_lightclip = mul(trans_model_to_clip_of_light, pushed); + + // Calculate shadow-map texture coordinates. + l_shadowcoord = l_lightclip * float4(0.5, 0.5, 0.5, 1.0) + + l_lightclip.w * float4(0.5, 0.5, 0.5, 0.0); +} + + +void fshader(in float2 l_texcoord0 : TEXCOORD0, + in float4 l_shadowcoord : TEXCOORD1, + in float l_smooth : TEXCOORD2, + in float4 l_lightclip : TEXCOORD3, + uniform sampler2D tex_0 : TEXUNIT0, + uniform sampler2DShadow k_Ldepthmap : TEXUNIT1, + uniform float4 k_ambient, + uniform float4 k_texDisable, + out float4 o_color : COLOR) +{ + float3 circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0); + float falloff = saturate(1.0 - dot(circleoffs, circleoffs)); + float4 baseColor = saturate(tex2D(tex_0, l_texcoord0) + k_texDisable); + float shade = shadow2DProj(k_Ldepthmap, l_shadowcoord).r; + o_color = baseColor * (falloff * shade * l_smooth + k_ambient.x); +} diff --git a/samples/solar-system/models/deimos_1k_tex.jpg b/samples/solar-system/models/deimos_1k_tex.jpg new file mode 100644 index 0000000000..9ab0fbee80 Binary files /dev/null and b/samples/solar-system/models/deimos_1k_tex.jpg differ diff --git a/samples/solar-system/models/earth_1k_tex.jpg b/samples/solar-system/models/earth_1k_tex.jpg new file mode 100644 index 0000000000..97bc0662f8 Binary files /dev/null and b/samples/solar-system/models/earth_1k_tex.jpg differ diff --git a/samples/solar-system/models/mars_1k_tex.jpg b/samples/solar-system/models/mars_1k_tex.jpg new file mode 100644 index 0000000000..d78e1e7a05 Binary files /dev/null and b/samples/solar-system/models/mars_1k_tex.jpg differ diff --git a/samples/solar-system/models/mercury_1k_tex.jpg b/samples/solar-system/models/mercury_1k_tex.jpg new file mode 100644 index 0000000000..b971efd382 Binary files /dev/null and b/samples/solar-system/models/mercury_1k_tex.jpg differ diff --git a/samples/solar-system/models/moon_1k_tex.jpg b/samples/solar-system/models/moon_1k_tex.jpg new file mode 100644 index 0000000000..2a0b0d0234 Binary files /dev/null and b/samples/solar-system/models/moon_1k_tex.jpg differ diff --git a/samples/solar-system/models/phobos_1k_tex.jpg b/samples/solar-system/models/phobos_1k_tex.jpg new file mode 100644 index 0000000000..516cbec6d8 Binary files /dev/null and b/samples/solar-system/models/phobos_1k_tex.jpg differ diff --git a/samples/solar-system/models/planet_sphere.egg.pz b/samples/solar-system/models/planet_sphere.egg.pz new file mode 100644 index 0000000000..bc83ac21c1 Binary files /dev/null and b/samples/solar-system/models/planet_sphere.egg.pz differ diff --git a/samples/solar-system/models/solar_sky_sphere.egg.pz b/samples/solar-system/models/solar_sky_sphere.egg.pz new file mode 100644 index 0000000000..3ce49155e8 Binary files /dev/null and b/samples/solar-system/models/solar_sky_sphere.egg.pz differ diff --git a/samples/solar-system/models/stars_1k_tex.jpg b/samples/solar-system/models/stars_1k_tex.jpg new file mode 100644 index 0000000000..07735f1fb1 Binary files /dev/null and b/samples/solar-system/models/stars_1k_tex.jpg differ diff --git a/samples/solar-system/models/sun_1k_tex.jpg b/samples/solar-system/models/sun_1k_tex.jpg new file mode 100644 index 0000000000..42ffcd1339 Binary files /dev/null and b/samples/solar-system/models/sun_1k_tex.jpg differ diff --git a/samples/solar-system/models/venus_1k_tex.jpg b/samples/solar-system/models/venus_1k_tex.jpg new file mode 100644 index 0000000000..865af1a785 Binary files /dev/null and b/samples/solar-system/models/venus_1k_tex.jpg differ diff --git a/samples/solar-system/step1_blank_window.py b/samples/solar-system/step1_blank_window.py new file mode 100755 index 0000000000..a702a382af --- /dev/null +++ b/samples/solar-system/step1_blank_window.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial is intended as a initial panda scripting lesson going over +# display initialization, loading models, placing objects, and the scene graph. +# +# Step 1: ShowBase contains the main Panda3D modules. Importing it +# initializes Panda and creates the window. The run() command causes the +# real-time simulation to begin + +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +base.run() diff --git a/samples/solar-system/step2_basic_setup.py b/samples/solar-system/step2_basic_setup.py new file mode 100755 index 0000000000..0588491b6d --- /dev/null +++ b/samples/solar-system/step2_basic_setup.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial is intended as a initial panda scripting lesson going over +# display initialization, loading models, placing objects, and the scene graph. +# +# Step 2: After initializing panda, we define a class called World. We put +# all of our code in a class to provide a convenient way to keep track of +# all of the variables our project will use, and in later tutorials to handle +# keyboard input. +# The code contained in the __init__ method is executed when we instantiate +# the class (at the end of this file). Inside __init__ we will first change +# the background color of the window. We then disable the mouse-based camera +# control and set the camera position. + +# Initialize Panda and create a window +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +from panda3d.core import * # Contains most of Panda's modules +from direct.gui.DirectGui import * # Imports Gui objects we use for putting +# text on the screen +import sys + + +class World(object): # Our main class + def __init__(self): # The initialization method caused when a + # world object is created + + # Create some text overlayed on our screen. + # We will use similar commands in all of our tutorials to create titles and + # instruction guides. + self.title = OnscreenText( + text="Panda3D: Tutorial 1 - Solar System", + parent=base.a2dBottomRight, align=TextNode.A_right, + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07) + + # Make the background color black (R=0, G=0, B=0) + # instead of the default grey + base.setBackgroundColor(0, 0, 0) + + # By default, the mouse controls the camera. Often, we disable that so that + # the camera can be placed manually (if we don't do this, our placement + # commands will be overridden by the mouse control) + base.disableMouse() + + # Set the camera position (x, y, z) + camera.setPos(0, 0, 45) + + # Set the camera orientation (heading, pitch, roll) in degrees + camera.setHpr(0, -90, 0) +# end class world + +# Now that our class is defined, we create an instance of it. +# Doing so calls the __init__ method set up above +w = World() +# As usual - run() must be called before anything can be shown on screen +base.run() diff --git a/samples/solar-system/step3_load_model.py b/samples/solar-system/step3_load_model.py new file mode 100755 index 0000000000..d60be02c98 --- /dev/null +++ b/samples/solar-system/step3_load_model.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial is intended as a initial panda scripting lesson going over +# display initialization, loading models, placing objects, and the scene graph. +# +# Step 3: In this step, we create a function called loadPlanets, which will +# eventually be used to load all of the planets in our simulation. For now +# we will load just the sun and and the sky-sphere we use to create the +# star-field. + +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +from panda3d.core import NodePath, TextNode +from direct.gui.DirectGui import * +import sys + + +class World(object): + def __init__(self): + # This is the initialization we had before + self.title = OnscreenText( # Create the title + text="Panda3D: Tutorial 1 - Solar System", + parent=base.a2dBottomRight, align=TextNode.A_right, + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07) + + base.setBackgroundColor(0, 0, 0) # Set the background to black + base.disableMouse() # disable mouse control of the camera + camera.setPos(0, 0, 45) # Set the camera position (X, Y, Z) + camera.setHpr(0, -90, 0) # Set the camera orientation + #(heading, pitch, roll) in degrees + + # We will now define a variable to help keep a consistent scale in + # our model. As we progress, we will continue to add variables here as we + # need them + + # The value of this variable scales the size of the planets. True scale size + # would be 1 + self.sizescale = 0.6 + + # Now that we have finished basic initialization, we call loadPlanets which + # will handle actually getting our objects in the world + self.loadPlanets() + + def loadPlanets(self): + # Here, inside our class, is where we are creating the loadPlanets function + # For now we are just loading the star-field and sun. In the next step we + # will load all of the planets + + # Loading objects in Panda is done via the command loader.loadModel, which + # takes one argument, the path to the model file. Models in Panda come in + # two types, .egg (which is readable in a text editor), and .bam (which is + # not readable but makes smaller files). When you load a file you leave the + # extension off so that it can choose the right version + + # Load model returns a NodePath, which you can think of as an object + # containing your model + + # Here we load the sky model. For all the planets we will use the same + # sphere model and simply change textures. However, even though the sky is + # a sphere, it is different from the planet model because its polygons + #(which are always one-sided in Panda) face inside the sphere instead of + # outside (this is known as a model with reversed normals). Because of + # that it has to be a separate model. + self.sky = loader.loadModel("models/solar_sky_sphere") + + # After the object is loaded, it must be placed in the scene. We do this by + # changing the parent of self.sky to render, which is a special NodePath. + # Each frame, Panda starts with render and renders everything attached to + # it. + self.sky.reparentTo(render) + + # You can set the position, orientation, and scale on a NodePath the same + # way that you set those properties on the camera. In fact, the camera is + # just another special NodePath + self.sky.setScale(40) + + # Very often, the egg file will know what textures are needed and load them + # automatically. But sometimes we want to set our textures manually, (for + # instance we want to put different textures on the same planet model) + # Loading textures works the same way as loading models, but instead of + # calling loader.loadModel, we call loader.loadTexture + self.sky_tex = loader.loadTexture("models/stars_1k_tex.jpg") + + # Finally, the following line sets our new sky texture on our sky model. + # The second argument must be one or the command will be ignored. + self.sky.setTexture(self.sky_tex, 1) + + # Now we load the sun. + self.sun = loader.loadModel("models/planet_sphere") + # Now we repeat our other steps + self.sun.reparentTo(render) + self.sun_tex = loader.loadTexture("models/sun_1k_tex.jpg") + self.sun.setTexture(self.sun_tex, 1) + # The sun is really much bigger than + self.sun.setScale(2 * self.sizescale) + # this, but to be able to see the + # planets we're making it smaller + + # end loadPlanets() + +# end class world + +# instantiate the class +w = World() +base.run() diff --git a/samples/solar-system/step4_load_system.py b/samples/solar-system/step4_load_system.py new file mode 100755 index 0000000000..e2e530a868 --- /dev/null +++ b/samples/solar-system/step4_load_system.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial is intended as a initial panda scripting lesson going over +# display initialization, loading models, placing objects, and the scene graph. +# +# Step 4: In this step, we will load the rest of the planets up to Mars. +# In addition to loading them, we will organize how the planets are grouped +# hierarchically in the scene. This will help us rotate them in the next step +# to give a rough simulation of the solar system. You can see them move by +# running step_5_complete_solar_system.py. + +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +from panda3d.core import NodePath, TextNode +from direct.gui.DirectGui import * +import sys + + +class World(object): + + def __init__(self): + # This is the initialization we had before + self.title = OnscreenText( # Create the title + text="Panda3D: Tutorial 1 - Solar System", + parent=base.a2dBottomRight, align=TextNode.A_right, + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07) + + base.setBackgroundColor(0, 0, 0) # Set the background to black + base.disableMouse() # disable mouse control of the camera + camera.setPos(0, 0, 45) # Set the camera position (X, Y, Z) + camera.setHpr(0, -90, 0) # Set the camera orientation + #(heading, pitch, roll) in degrees + + # This section has our variables. This time we are adding a variable to + # control the relative size of the orbits. + self.sizescale = 0.6 # relative size of planets + self.orbitscale = 10 # relative size of orbits + + self.loadPlanets() # Load our models and make them render + + def loadPlanets(self): + # Here is where we load all of the planets, and place them. + # The first thing we do is create a dummy node for each planet. A dummy + # node is simply a node path that does not have any geometry attached to it. + # This is done by .attachNewNode('name_of_new_node') + + # We do this because positioning the planets around a circular orbit could + # be done with a lot of messy sine and cosine operations. Instead, we define + # our planets to be a given distance from a dummy node, and when we turn the + # dummy, the planets will move along with it, kind of like turning the + # center of a disc and having an object at its edge move. Most attributes, + # like position, orientation, scale, texture, color, etc., are inherited + # this way. Panda deals with the fact that the objects are not attached + # directly to render (they are attached through other NodePaths to render), + # and makes sure the attributes inherit. + + # This system of attaching NodePaths to each other is called the Scene + # Graph + self.orbit_root_mercury = render.attachNewNode('orbit_root_mercury') + self.orbit_root_venus = render.attachNewNode('orbit_root_venus') + self.orbit_root_mars = render.attachNewNode('orbit_root_mars') + self.orbit_root_earth = render.attachNewNode('orbit_root_earth') + + # orbit_root_moon is like all the other orbit_root dummy nodes except that + # it will be parented to orbit_root_earth so that the moon will orbit the + # earth instead of the sun. So, the moon will first inherit + # orbit_root_moon's position and then orbit_root_earth's. There is no hard + # limit on how many objects can inherit from each other. + self.orbit_root_moon = ( + self.orbit_root_earth.attachNewNode('orbit_root_moon')) + + ############################################################### + + # These are the same steps used to load the sky model that we used in the + # last step + # Load the model for the sky + self.sky = loader.loadModel("models/solar_sky_sphere") + # Load the texture for the sky. + self.sky_tex = loader.loadTexture("models/stars_1k_tex.jpg") + # Set the sky texture to the sky model + self.sky.setTexture(self.sky_tex, 1) + # Parent the sky model to the render node so that the sky is rendered + self.sky.reparentTo(render) + # Scale the size of the sky. + self.sky.setScale(40) + + # These are the same steps we used to load the sun in the last step. + # Again, we use loader.loadModel since we're using planet_sphere more + # than once. + self.sun = loader.loadModel("models/planet_sphere") + self.sun_tex = loader.loadTexture("models/sun_1k_tex.jpg") + self.sun.setTexture(self.sun_tex, 1) + self.sun.reparentTo(render) + self.sun.setScale(2 * self.sizescale) + + # Now we load the planets, which we load using the same steps we used to + # load the sun. The only difference is that the models are not parented + # directly to render for the reasons described above. + # The values used for scale are the ratio of the planet's radius to Earth's + # radius, multiplied by our global scale variable. In the same way, the + # values used for orbit are the ratio of the planet's orbit to Earth's + # orbit, multiplied by our global orbit scale variable + + # Load mercury + self.mercury = loader.loadModel("models/planet_sphere") + self.mercury_tex = loader.loadTexture("models/mercury_1k_tex.jpg") + self.mercury.setTexture(self.mercury_tex, 1) + self.mercury.reparentTo(self.orbit_root_mercury) + # Set the position of mercury. By default, all nodes are pre assigned the + # position (0, 0, 0) when they are first loaded. We didn't reposition the + # sun and sky because they are centered in the solar system. Mercury, + # however, needs to be offset so we use .setPos to offset the + # position of mercury in the X direction with respect to its orbit radius. + # We will do this for the rest of the planets. + self.mercury.setPos(0.38 * self.orbitscale, 0, 0) + self.mercury.setScale(0.385 * self.sizescale) + + # Load Venus + self.venus = loader.loadModel("models/planet_sphere") + self.venus_tex = loader.loadTexture("models/venus_1k_tex.jpg") + self.venus.setTexture(self.venus_tex, 1) + self.venus.reparentTo(self.orbit_root_venus) + self.venus.setPos(0.72 * self.orbitscale, 0, 0) + self.venus.setScale(0.923 * self.sizescale) + + # Load Mars + self.mars = loader.loadModel("models/planet_sphere") + self.mars_tex = loader.loadTexture("models/mars_1k_tex.jpg") + self.mars.setTexture(self.mars_tex, 1) + self.mars.reparentTo(self.orbit_root_mars) + self.mars.setPos(1.52 * self.orbitscale, 0, 0) + self.mars.setScale(0.515 * self.sizescale) + + # Load Earth + self.earth = loader.loadModel("models/planet_sphere") + self.earth_tex = loader.loadTexture("models/earth_1k_tex.jpg") + self.earth.setTexture(self.earth_tex, 1) + self.earth.reparentTo(self.orbit_root_earth) + self.earth.setScale(self.sizescale) + self.earth.setPos(self.orbitscale, 0, 0) + + # The center of the moon's orbit is exactly the same distance away from + # The sun as the Earth's distance from the sun + self.orbit_root_moon.setPos(self.orbitscale, 0, 0) + + # Load the moon + self.moon = loader.loadModel("models/planet_sphere") + self.moon_tex = loader.loadTexture("models/moon_1k_tex.jpg") + self.moon.setTexture(self.moon_tex, 1) + self.moon.reparentTo(self.orbit_root_moon) + self.moon.setScale(0.1 * self.sizescale) + self.moon.setPos(0.1 * self.orbitscale, 0, 0) + # end loadPlanets() + +# end class world + +w = World() +base.run() diff --git a/samples/solar-system/step5_complete_solar_system.py b/samples/solar-system/step5_complete_solar_system.py new file mode 100755 index 0000000000..3e2a0e2bd1 --- /dev/null +++ b/samples/solar-system/step5_complete_solar_system.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial is intended as a initial panda scripting lesson going over +# display initialization, loading models, placing objects, and the scene graph. +# +# Step 5: Here we put the finishing touches on our solar system model by +# making the planets move. The actual code for doing the movement is covered +# in the next tutorial, but watching it move really shows what inheritance on +# the scene graph is all about. + +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +from direct.gui.DirectGui import * +from panda3d.core import TextNode +import sys + + +class World(object): + + def __init__(self): + # This is the initialization we had before + self.title = OnscreenText( # Create the title + text="Panda3D: Tutorial 1 - Solar System", + parent=base.a2dBottomRight, align=TextNode.A_right, + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07) + + base.setBackgroundColor(0, 0, 0) # Set the background to black + base.disableMouse() # disable mouse control of the camera + camera.setPos(0, 0, 45) # Set the camera position (X, Y, Z) + camera.setHpr(0, -90, 0) # Set the camera orientation + #(heading, pitch, roll) in degrees + + # Here again is where we put our global variables. Added this time are + # variables to control the relative speeds of spinning and orbits in the + # simulation + # Number of seconds a full rotation of Earth around the sun should take + self.yearscale = 60 + # Number of seconds a day rotation of Earth should take. + # It is scaled from its correct value for easier visability + self.dayscale = self.yearscale / 365.0 * 5 + self.orbitscale = 10 # Orbit scale + self.sizescale = 0.6 # Planet size scale + + self.loadPlanets() # Load and position the models + + # Finally, we call the rotatePlanets function which puts the planets, + # sun, and moon into motion. + self.rotatePlanets() + + def loadPlanets(self): + # This is the same function that we completed in the previous step + # It is unchanged in this version + + # Create the dummy nodes + self.orbit_root_mercury = render.attachNewNode('orbit_root_mercury') + self.orbit_root_venus = render.attachNewNode('orbit_root_venus') + self.orbit_root_mars = render.attachNewNode('orbit_root_mars') + self.orbit_root_earth = render.attachNewNode('orbit_root_earth') + + # The moon orbits Earth, not the sun + self.orbit_root_moon = ( + self.orbit_root_earth.attachNewNode('orbit_root_moon')) + + ############################################################### + + # Load the sky + self.sky = loader.loadModel("models/solar_sky_sphere") + self.sky_tex = loader.loadTexture("models/stars_1k_tex.jpg") + self.sky.setTexture(self.sky_tex, 1) + self.sky.reparentTo(render) + self.sky.setScale(40) + + # Load the Sun + self.sun = loader.loadModel("models/planet_sphere") + self.sun_tex = loader.loadTexture("models/sun_1k_tex.jpg") + self.sun.setTexture(self.sun_tex, 1) + self.sun.reparentTo(render) + self.sun.setScale(2 * self.sizescale) + + # Load mercury + self.mercury = loader.loadModel("models/planet_sphere") + self.mercury_tex = loader.loadTexture("models/mercury_1k_tex.jpg") + self.mercury.setTexture(self.mercury_tex, 1) + self.mercury.reparentTo(self.orbit_root_mercury) + self.mercury.setPos(0.38 * self.orbitscale, 0, 0) + self.mercury.setScale(0.385 * self.sizescale) + + # Load Venus + self.venus = loader.loadModel("models/planet_sphere") + self.venus_tex = loader.loadTexture("models/venus_1k_tex.jpg") + self.venus.setTexture(self.venus_tex, 1) + self.venus.reparentTo(self.orbit_root_venus) + self.venus.setPos(0.72 * self.orbitscale, 0, 0) + self.venus.setScale(0.923 * self.sizescale) + + # Load Mars + self.mars = loader.loadModel("models/planet_sphere") + self.mars_tex = loader.loadTexture("models/mars_1k_tex.jpg") + self.mars.setTexture(self.mars_tex, 1) + self.mars.reparentTo(self.orbit_root_mars) + self.mars.setPos(1.52 * self.orbitscale, 0, 0) + self.mars.setScale(0.515 * self.sizescale) + + # Load Earth + self.earth = loader.loadModel("models/planet_sphere") + self.earth_tex = loader.loadTexture("models/earth_1k_tex.jpg") + self.earth.setTexture(self.earth_tex, 1) + self.earth.reparentTo(self.orbit_root_earth) + self.earth.setScale(self.sizescale) + self.earth.setPos(self.orbitscale, 0, 0) + + # Offest the moon dummy node so that it is positioned properly + self.orbit_root_moon.setPos(self.orbitscale, 0, 0) + + # Load the moon + self.moon = loader.loadModel("models/planet_sphere") + self.moon_tex = loader.loadTexture("models/moon_1k_tex.jpg") + self.moon.setTexture(self.moon_tex, 1) + self.moon.reparentTo(self.orbit_root_moon) + self.moon.setScale(0.1 * self.sizescale) + self.moon.setPos(0.1 * self.orbitscale, 0, 0) + # end loadPlanets() + + def rotatePlanets(self): + # rotatePlanets creates intervals to actually use the hierarchy we created + # to turn the sun, planets, and moon to give a rough representation of the + # solar system. The next lesson will go into more depth on intervals. + self.day_period_sun = self.sun.hprInterval(20, (360, 0, 0)) + + self.orbit_period_mercury = self.orbit_root_mercury.hprInterval( + (0.241 * self.yearscale), (360, 0, 0)) + self.day_period_mercury = self.mercury.hprInterval( + (59 * self.dayscale), (360, 0, 0)) + + self.orbit_period_venus = self.orbit_root_venus.hprInterval( + (0.615 * self.yearscale), (360, 0, 0)) + self.day_period_venus = self.venus.hprInterval( + (243 * self.dayscale), (360, 0, 0)) + + self.orbit_period_earth = self.orbit_root_earth.hprInterval( + self.yearscale, (360, 0, 0)) + self.day_period_earth = self.earth.hprInterval( + self.dayscale, (360, 0, 0)) + + self.orbit_period_moon = self.orbit_root_moon.hprInterval( + (.0749 * self.yearscale), (360, 0, 0)) + self.day_period_moon = self.moon.hprInterval( + (.0749 * self.yearscale), (360, 0, 0)) + + self.orbit_period_mars = self.orbit_root_mars.hprInterval( + (1.881 * self.yearscale), (360, 0, 0)) + self.day_period_mars = self.mars.hprInterval( + (1.03 * self.dayscale), (360, 0, 0)) + + self.day_period_sun.loop() + self.orbit_period_mercury.loop() + self.day_period_mercury.loop() + self.orbit_period_venus.loop() + self.day_period_venus.loop() + self.orbit_period_earth.loop() + self.day_period_earth.loop() + self.orbit_period_moon.loop() + self.day_period_moon.loop() + self.orbit_period_mars.loop() + self.day_period_mars.loop() + # end RotatePlanets() +# end class world + +w = World() +base.run() diff --git a/samples/solar-system/step6_controllable_system.py b/samples/solar-system/step6_controllable_system.py new file mode 100755 index 0000000000..8758fb0509 --- /dev/null +++ b/samples/solar-system/step6_controllable_system.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python + +# Author: Shao Zhang and Phil Saltzman +# Last Updated: 2015-03-13 +# +# This tutorial will cover events and how they can be used in Panda +# Specifically, this lesson will use events to capture keyboard presses and +# mouse clicks to trigger actions in the world. It will also use events +# to count the number of orbits the Earth makes around the sun. This +# tutorial uses the same base code from the solar system tutorial. + +from direct.showbase.ShowBase import ShowBase +base = ShowBase() + +from panda3d.core import TextNode +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from direct.showbase.DirectObject import DirectObject +import sys + +# We start this tutorial with the standard class. However, the class is a +# subclass of an object called DirectObject. This gives the class the ability +# to listen for and respond to events. From now on the main class in every +# tutorial will be a subclass of DirectObject + + +class World(DirectObject): + # Macro-like function used to reduce the amount to code needed to create the + # on screen instructions + + def genLabelText(self, text, i): + return OnscreenText(text=text, pos=(0.06, -.06 * (i + 0.5)), fg=(1, 1, 1, 1), + parent=base.a2dTopLeft,align=TextNode.ALeft, scale=.05) + + def __init__(self): + + # The standard camera position and background initialization + base.setBackgroundColor(0, 0, 0) + base.disableMouse() + camera.setPos(0, 0, 45) + camera.setHpr(0, -90, 0) + + # The global variables we used to control the speed and size of objects + self.yearscale = 60 + self.dayscale = self.yearscale / 365.0 * 5 + self.orbitscale = 10 + self.sizescale = 0.6 + + self.loadPlanets() # Load, texture, and position the planets + self.rotatePlanets() # Set up the motion to start them moving + + # The standard title text that's in every tutorial + # Things to note: + #-fg represents the forground color of the text in (r,g,b,a) format + #-pos represents the position of the text on the screen. + # The coordinate system is a x-y based wih 0,0 as the center of the + # screen + #-align sets the alingment of the text relative to the pos argument. + # Default is center align. + #-scale set the scale of the text + #-mayChange argument lets us change the text later in the program. + # By default mayChange is set to 0. Trying to change text when + # mayChange is set to 0 will cause the program to crash. + self.title = OnscreenText( + text="Panda3D: Tutorial 3 - Events", + parent=base.a2dBottomRight, align=TextNode.A_right, + style=1, fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.07) + + self.mouse1EventText = self.genLabelText( + "Mouse Button 1: Toggle entire Solar System [RUNNING]", 1) + self.skeyEventText = self.genLabelText("[S]: Toggle Sun [RUNNING]", 2) + self.ykeyEventText = self.genLabelText("[Y]: Toggle Mercury [RUNNING]", 3) + self.vkeyEventText = self.genLabelText("[V]: Toggle Venus [RUNNING]", 4) + self.ekeyEventText = self.genLabelText("[E]: Toggle Earth [RUNNING]", 5) + self.mkeyEventText = self.genLabelText("[M]: Toggle Mars [RUNNING]", 6) + self.yearCounterText = self.genLabelText("0 Earth years completed", 7) + + self.yearCounter = 0 # year counter for earth years + self.simRunning = True # boolean to keep track of the + # state of the global simulation + + # Events + # Each self.accept statement creates an event handler object that will call + # the specified function when that event occurs. + # Certain events like "mouse1", "a", "b", "c" ... "z", "1", "2", "3"..."0" + # are references to keyboard keys and mouse buttons. You can also define + # your own events to be used within your program. In this tutorial, the + # event "newYear" is not tied to a physical input device, but rather + # is sent by the function that rotates the Earth whenever a revolution + # completes to tell the counter to update + # Exit the program when escape is pressed + self.accept("escape", sys.exit) + self.accept("mouse1", self.handleMouseClick) + self.accept("e", self.handleEarth) + self.accept("s", # message name + self.togglePlanet, # function to call + ["Sun", # arguments to be passed to togglePlanet + # See togglePlanet's definition below for + # an explanation of what they are + self.day_period_sun, + None, + self.skeyEventText]) + # Repeat the structure above for the other planets + self.accept("y", self.togglePlanet, + ["Mercury", self.day_period_mercury, + self.orbit_period_mercury, self.ykeyEventText]) + self.accept("v", self.togglePlanet, + ["Venus", self.day_period_venus, + self.orbit_period_venus, self.vkeyEventText]) + self.accept("m", self.togglePlanet, + ["Mars", self.day_period_mars, + self.orbit_period_mars, self.mkeyEventText]) + self.accept("newYear", self.incYear) + # end __init__ + + def handleMouseClick(self): + # When the mouse is clicked, if the simulation is running pause all the + # planets and sun, otherwise resume it + if self.simRunning: + print "Pausing Simulation" + # changing the text to reflect the change from "RUNNING" to + # "PAUSED" + self.mouse1EventText.setText( + "Mouse Button 1: Toggle entire Solar System [PAUSED]") + # For each planet, check if it is moving and if so, pause it + # Sun + if self.day_period_sun.isPlaying(): + self.togglePlanet("Sun", self.day_period_sun, None, + self.skeyEventText) + if self.day_period_mercury.isPlaying(): + self.togglePlanet("Mercury", self.day_period_mercury, + self.orbit_period_mercury, self.ykeyEventText) + # Venus + if self.day_period_venus.isPlaying(): + self.togglePlanet("Venus", self.day_period_venus, + self.orbit_period_venus, self.vkeyEventText) + #Earth and moon + if self.day_period_earth.isPlaying(): + self.togglePlanet("Earth", self.day_period_earth, + self.orbit_period_earth, self.ekeyEventText) + self.togglePlanet("Moon", self.day_period_moon, + self.orbit_period_moon) + # Mars + if self.day_period_mars.isPlaying(): + self.togglePlanet("Mars", self.day_period_mars, + self.orbit_period_mars, self.mkeyEventText) + else: + #"The simulation is paused, so resume it + print "Resuming Simulation" + self.mouse1EventText.setText( + "Mouse Button 1: Toggle entire Solar System [RUNNING]") + # the not operator does the reverse of the previous code + if not self.day_period_sun.isPlaying(): + self.togglePlanet("Sun", self.day_period_sun, None, + self.skeyEventText) + if not self.day_period_mercury.isPlaying(): + self.togglePlanet("Mercury", self.day_period_mercury, + self.orbit_period_mercury, self.ykeyEventText) + if not self.day_period_venus.isPlaying(): + self.togglePlanet("Venus", self.day_period_venus, + self.orbit_period_venus, self.vkeyEventText) + if not self.day_period_earth.isPlaying(): + self.togglePlanet("Earth", self.day_period_earth, + self.orbit_period_earth, self.ekeyEventText) + self.togglePlanet("Moon", self.day_period_moon, + self.orbit_period_moon) + if not self.day_period_mars.isPlaying(): + self.togglePlanet("Mars", self.day_period_mars, + self.orbit_period_mars, self.mkeyEventText) + # toggle self.simRunning + self.simRunning = not self.simRunning + # end handleMouseClick + + # The togglePlanet function will toggle the intervals that are given to it + # between paused and playing. + # Planet is the name to print + # Day is the interval that spins the planet + # Orbit is the interval that moves around the orbit + # Text is the OnscreenText object that needs to be updated + def togglePlanet(self, planet, day, orbit=None, text=None): + if day.isPlaying(): + print "Pausing " + planet + state = " [PAUSED]" + else: + print "Resuming " + planet + state = " [RUNNING]" + + # Update the onscreen text if it is given as an argument + if text: + old = text.getText() + # strip out the last segment of text after the last white space + # and append the string stored in 'state' + text.setText(old[0:old.rfind(' ')] + state) + + # toggle the day interval + self.toggleInterval(day) + # if there is an orbit interval, toggle it + if orbit: + self.toggleInterval(orbit) + # end togglePlanet + + # toggleInterval does exactly as its name implies + # It takes an interval as an argument. Then it checks to see if it is playing. + # If it is, it pauses it, otherwise it resumes it. + def toggleInterval(self, interval): + if interval.isPlaying(): + interval.pause() + else: + interval.resume() + # end toggleInterval + + # Earth needs a special buffer function because the moon is tied to it + # When the "e" key is pressed, togglePlanet is called on both the earth and + # the moon. + def handleEarth(self): + self.togglePlanet("Earth", self.day_period_earth, + self.orbit_period_earth, self.ekeyEventText) + self.togglePlanet("Moon", self.day_period_moon, + self.orbit_period_moon) + # end handleEarth + + # the function incYear increments the variable yearCounter and then updates + # the OnscreenText 'yearCounterText' every time the message "newYear" is + # sent + def incYear(self): + self.yearCounter += 1 + self.yearCounterText.setText( + str(self.yearCounter) + " Earth years completed") + # end incYear + + +######################################################################### +# Except for the one commented line below, this is all as it was before # +# Scroll down to the next comment to see an example of sending messages # +######################################################################### + + def loadPlanets(self): + self.orbit_root_mercury = render.attachNewNode('orbit_root_mercury') + self.orbit_root_venus = render.attachNewNode('orbit_root_venus') + self.orbit_root_mars = render.attachNewNode('orbit_root_mars') + self.orbit_root_earth = render.attachNewNode('orbit_root_earth') + + self.orbit_root_moon = ( + self.orbit_root_earth.attachNewNode('orbit_root_moon')) + + self.sky = loader.loadModel("models/solar_sky_sphere") + + self.sky_tex = loader.loadTexture("models/stars_1k_tex.jpg") + self.sky.setTexture(self.sky_tex, 1) + self.sky.reparentTo(render) + self.sky.setScale(40) + + self.sun = loader.loadModel("models/planet_sphere") + self.sun_tex = loader.loadTexture("models/sun_1k_tex.jpg") + self.sun.setTexture(self.sun_tex, 1) + self.sun.reparentTo(render) + self.sun.setScale(2 * self.sizescale) + + self.mercury = loader.loadModel("models/planet_sphere") + self.mercury_tex = loader.loadTexture("models/mercury_1k_tex.jpg") + self.mercury.setTexture(self.mercury_tex, 1) + self.mercury.reparentTo(self.orbit_root_mercury) + self.mercury.setPos(0.38 * self.orbitscale, 0, 0) + self.mercury.setScale(0.385 * self.sizescale) + + self.venus = loader.loadModel("models/planet_sphere") + self.venus_tex = loader.loadTexture("models/venus_1k_tex.jpg") + self.venus.setTexture(self.venus_tex, 1) + self.venus.reparentTo(self.orbit_root_venus) + self.venus.setPos(0.72 * self.orbitscale, 0, 0) + self.venus.setScale(0.923 * self.sizescale) + + self.mars = loader.loadModel("models/planet_sphere") + self.mars_tex = loader.loadTexture("models/mars_1k_tex.jpg") + self.mars.setTexture(self.mars_tex, 1) + self.mars.reparentTo(self.orbit_root_mars) + self.mars.setPos(1.52 * self.orbitscale, 0, 0) + self.mars.setScale(0.515 * self.sizescale) + + self.earth = loader.loadModel("models/planet_sphere") + self.earth_tex = loader.loadTexture("models/earth_1k_tex.jpg") + self.earth.setTexture(self.earth_tex, 1) + self.earth.reparentTo(self.orbit_root_earth) + self.earth.setScale(self.sizescale) + self.earth.setPos(self.orbitscale, 0, 0) + + self.orbit_root_moon.setPos(self.orbitscale, 0, 0) + + self.moon = loader.loadModel("models/planet_sphere") + self.moon_tex = loader.loadTexture("models/moon_1k_tex.jpg") + self.moon.setTexture(self.moon_tex, 1) + self.moon.reparentTo(self.orbit_root_moon) + self.moon.setScale(0.1 * self.sizescale) + self.moon.setPos(0.1 * self.orbitscale, 0, 0) + + def rotatePlanets(self): + self.day_period_sun = self.sun.hprInterval(20, (360, 0, 0)) + + self.orbit_period_mercury = self.orbit_root_mercury.hprInterval( + (0.241 * self.yearscale), (360, 0, 0)) + self.day_period_mercury = self.mercury.hprInterval( + (59 * self.dayscale), (360, 0, 0)) + + self.orbit_period_venus = self.orbit_root_venus.hprInterval( + (0.615 * self.yearscale), (360, 0, 0)) + self.day_period_venus = self.venus.hprInterval( + (243 * self.dayscale), (360, 0, 0)) + + # Here the earth interval has been changed to rotate like the rest of the + # planets and send a message before it starts turning again. To send a + # message, the call is simply messenger.send("message"). The "newYear" + # message is picked up by the accept("newYear"...) statement earlier, and + # calls the incYear function as a result + self.orbit_period_earth = Sequence( + self.orbit_root_earth.hprInterval( + self.yearscale, (360, 0, 0)), + Func(messenger.send, "newYear")) + self.day_period_earth = self.earth.hprInterval( + self.dayscale, (360, 0, 0)) + + self.orbit_period_moon = self.orbit_root_moon.hprInterval( + (.0749 * self.yearscale), (360, 0, 0)) + self.day_period_moon = self.moon.hprInterval( + (.0749 * self.yearscale), (360, 0, 0)) + + self.orbit_period_mars = self.orbit_root_mars.hprInterval( + (1.881 * self.yearscale), (360, 0, 0)) + self.day_period_mars = self.mars.hprInterval( + (1.03 * self.dayscale), (360, 0, 0)) + + self.day_period_sun.loop() + self.orbit_period_mercury.loop() + self.day_period_mercury.loop() + self.orbit_period_venus.loop() + self.day_period_venus.loop() + self.orbit_period_earth.loop() + self.day_period_earth.loop() + self.orbit_period_moon.loop() + self.day_period_moon.loop() + self.orbit_period_mars.loop() + self.day_period_mars.loop() + # end RotatePlanets() + +# end class world + +w = World() +base.run()