From 183f0f0b905715f0d89b38174fdfd44641c1a79c Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Wed, 12 Aug 2009 12:03:18 +0000 Subject: src/model filenames cleanup, parse .map texture coordinates, early loading of material textures --- src/model/Makefile.am | 6 +- src/model/asefile.cc | 26 +- src/model/classes.cc | 87 --- src/model/classes.h | 259 --------- src/model/face.cc | 43 ++ src/model/face.h | 113 ++++ src/model/map.cc | 1212 ------------------------------------------ src/model/map.h | 189 ------- src/model/mapfile.cc | 1353 +++++++++++++++++++++++++++++++++++++++++++++++ src/model/mapfile.h | 189 +++++++ src/model/material.cc | 34 +- src/model/material.h | 34 +- src/model/model.cc | 6 +- src/model/model.h | 2 +- src/model/parts.cc | 87 +++ src/model/parts.h | 259 +++++++++ src/model/plane.cc | 42 -- src/model/plane.h | 88 --- src/model/primitives.cc | 6 +- src/model/primitives.h | 4 +- src/render/particles.h | 2 +- src/render/textures.cc | 25 +- src/render/textures.h | 5 + 23 files changed, 2164 insertions(+), 1907 deletions(-) delete mode 100644 src/model/classes.cc delete mode 100644 src/model/classes.h create mode 100644 src/model/face.cc create mode 100644 src/model/face.h delete mode 100644 src/model/map.cc delete mode 100644 src/model/map.h create mode 100644 src/model/mapfile.cc create mode 100644 src/model/mapfile.h create mode 100644 src/model/parts.cc create mode 100644 src/model/parts.h delete mode 100644 src/model/plane.cc delete mode 100644 src/model/plane.h (limited to 'src') diff --git a/src/model/Makefile.am b/src/model/Makefile.am index 94b3c96..fe398a2 100644 --- a/src/model/Makefile.am +++ b/src/model/Makefile.am @@ -1,11 +1,11 @@ METASOURCES = AUTO -libmodel_la_SOURCES = asefile.cc classes.cc fragment.cc map.cc material.cc \ - model.cc plane.cc primitives.cc quad.cc triangle.cc vertexarray.cc +libmodel_la_SOURCES = asefile.cc parts.cc fragment.cc mapfile.cc material.cc \ + model.cc face.cc primitives.cc quad.cc triangle.cc vertexarray.cc libmodel_la_LDFLAGS = -avoid-version -no-undefined -lm noinst_LTLIBRARIES = libmodel.la -noinst_HEADERS = asefile.h classes.h fragment.h map.h material.h model.h plane.h \ +noinst_HEADERS = asefile.h parts.h fragment.h mapfile.h material.h model.h face.h \ primitives.h quad.h triangle.h vertexarray.h INCLUDES = -I$(top_srcdir)/src diff --git a/src/model/asefile.cc b/src/model/asefile.cc index 28c84cf..67be531 100644 --- a/src/model/asefile.cc +++ b/src/model/asefile.cc @@ -89,7 +89,7 @@ bool ASEFile::read_mesh_vertex_list(std::istream &is) line >> firstword; if (firstword.compare("}") == 0) { - con_debug << " " << count << " mesh vertices" << std::endl; + //con_debug << " " << count << " mesh vertices" << std::endl; return true; @@ -130,7 +130,7 @@ bool ASEFile::read_mesh_face_list(std::istream &is) line >> word; if (word.compare("}") == 0) { - con_debug << " " << count << " mesh faces" << std::endl; + //con_debug << " " << count << " mesh faces" << std::endl; return true; } else if ( word.compare("*MESH_FACE") == 0) { @@ -178,7 +178,7 @@ bool ASEFile::read_mesh_normals(std::istream &is) line >> firstword; if (firstword.compare("}") == 0) { - con_debug << " " << count << " face normals" << std::endl; + //con_debug << " " << count << " face normals" << std::endl; return true; } else if ( firstword.compare("*MESH_FACENORMAL") == 0) { @@ -188,7 +188,7 @@ bool ASEFile::read_mesh_normals(std::istream &is) (*it).second->normal().assign(x, y, z); count++; } else { - con_debug << " could not find face " << index << std::endl; + con_warn << " could not find face " << index << std::endl; } vertindex = 0; } else { @@ -227,7 +227,7 @@ bool ASEFile::read_mesh_tvertex_list(std::istream &is) line >> firstword; if (firstword.compare("}") == 0) { - con_debug << " " << count << " texture vertices" << std::endl; + //con_debug << " " << count << " texture vertices" << std::endl; return true; } else if ( firstword.compare("*MESH_TVERT") == 0) { @@ -258,7 +258,7 @@ size_t count = 0; line >> firstword; if (firstword.compare("}") == 0) { - con_debug << " " << count << " face texture coordinates" << std::endl; + //con_debug << " " << count << " face texture coordinates" << std::endl; return true; } else if ( firstword.compare("*MESH_TFACE") == 0) { @@ -300,31 +300,31 @@ bool ASEFile::read_mesh(std::istream &is) if ((level == 1 ) && (word.compare("*MESH_VERTEX_LIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " *MESH_VERTEX_LIST" << std::endl; + //con_debug << " " << name() << " *MESH_VERTEX_LIST" << std::endl; read_mesh_vertex_list(is); } } else if ((level == 1 ) && (word.compare("*MESH_FACE_LIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " *MESH_FACE_LIST" << std::endl; + //con_debug << " " << name() << " *MESH_FACE_LIST" << std::endl; read_mesh_face_list(is); } } else if ((level == 1 ) && (word.compare("*MESH_NORMALS") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " *MESH_NORMALS" << std::endl; + //con_debug << " " << name() << " *MESH_NORMALS" << std::endl; read_mesh_normals(is); } } else if ((level == 1 ) && (word.compare("*MESH_TVERTLIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " *MESH_TVERTLIST" << std::endl; + //con_debug << " " << name() << " *MESH_TVERTLIST" << std::endl; read_mesh_tvertex_list(is); } } else if ((level == 1 ) && (word.compare("*MESH_TFACELIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " *MESH_TFACELIST" << std::endl; + //con_debug << " " << name() << " *MESH_TFACELIST" << std::endl; read_mesh_tface_list(is); } @@ -360,7 +360,7 @@ bool ASEFile::read_geom(std::istream &is) if ((level == 1 ) && (word.compare("*MESH") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " " << "*MESH" << std::endl; + //con_debug << " " << name() << " " << "*MESH" << std::endl; read_mesh(is); } @@ -400,7 +400,7 @@ bool ASEFile::read() if (word.compare("*GEOMOBJECT") == 0) { if ((line >> word) && (word.compare("{") == 0)) { - con_debug << " " << name() << " " << "*GEOMOBJECT" << std::endl; + //con_debug << " " << name() << " " << "*GEOMOBJECT" << std::endl; read_geom(asefile_ifs); } } diff --git a/src/model/classes.cc b/src/model/classes.cc deleted file mode 100644 index 2a16bae..0000000 --- a/src/model/classes.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* - model/classes.cc - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#include "model/classes.h" - -namespace model { - -/* ---- class Light ------------------------------------------------ */ - -Light::Light() : - light_location(), - light_color(1.0f, 1.0f, 1.0f) -{ - light_entity = false; - light_strobe = false; - light_radius = 1.0f; - light_frequency = 1.0f; - light_offset = 0.0f; - light_time = 0.5f; - light_flare = 0; - render_texture = 0; -} - -Light::~Light() -{} - -/* ---- class Flare ------------------------------------------------ */ - -Flare::Flare() : Light() -{ - flare_engine = false; - flare_cull = CullBack; -} - -Flare::~Flare() -{} - -/* ---- class Particles -------------------------------------------- */ - -Particles::Particles() : - particles_location() -{ - particles_entity = false; - particles_engine = false; - particles_radius = 0.0f; - particles_cull = CullNone; -} - -Particles::Particles(math::Vector3f const & location) : - particles_location(location) -{ -} - -Particles::~Particles() -{} - -void Particles::set_radius(const float radius) -{ - particles_radius = radius; -} - -/* ---- class Dock ------------------------------------------------- */ - -Dock::Dock() -{ - dock_radius = 0.01f; -} - -Dock::~Dock() -{ -} - -/* ---- class SubModel---------------------------------------------- */ - -SubModel::SubModel() -{ - submodel_scale = 1.0f; -} - -SubModel::~SubModel() -{ -} - -} diff --git a/src/model/classes.h b/src/model/classes.h deleted file mode 100644 index 111d7d0..0000000 --- a/src/model/classes.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - model/classes.h - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#ifndef __INCLUDED_MODEL_CLASSES_H__ -#define __INCLUDED_MODEL_CLASSES_H__ - -#include "math/axis.h" -#include "math/color.h" -#include "math/vector3f.h" - -namespace model -{ - - -/** - * @brief - * culling parameter values - * Culling is a paremeter used by flares and particles to indicate - * with side of the polygons should be culled during rendering - */ -enum Cull { CullNone=0, CullBack=1, CullFront=2 }; - -/* ---- class Light ------------------------------------------------ */ - -/// an exterior light -class Light -{ -public: - Light(); - - Light(const math::Vector3f & location, const math::Color & color, bool strobe=false); - - ~Light(); - - inline const math::Vector3f & location() const - { - return light_location; - } - - inline const math::Color & color() const - { - return light_color; - }; - - /// true if this is a strobe light - inline bool strobe() const - { - return light_strobe; - } - - /// true if this light has entity color - inline bool entity() const - { - return light_entity; - } - - /// size of the light, default is 1.0f - inline float radius() const - { - return light_radius; - } - - /// strobe time offset, in seconds - inline float offset() const - { - return light_offset; - } - - /// frequency in strobes per second - inline float frequency() const - { - return light_frequency; - } - - /// fraction a strobe light will be on, default is 0.5f - inline float time() const - { - return light_time; - } - - /// flare texture number - inline unsigned int flare() const - { - return light_flare; - } - - /// render texture number - inline size_t texture() const - { - return render_texture; - } - - math::Vector3f light_location; - math::Color light_color; - bool light_strobe; - bool light_entity; - float light_radius; - float light_frequency; - float light_offset; - float light_time; - - unsigned int light_flare; - - size_t render_texture; -}; - -/* ---- class Flare ------------------------------------------------ */ - -/// a flare is a directional light -class Flare : public Light -{ -public: - Flare(); - ~Flare(); - - inline const math::Axis axis() const - { - return flare_axis; - } - - inline const bool engine() const - { - return flare_engine; - } - - inline const Cull cull() const - { - return flare_cull; - } - - math::Axis flare_axis; - bool flare_engine; - Cull flare_cull; -}; - -/* ---- class Particles -------------------------------------------- */ - -/// a particle system -class Particles -{ -public: - Particles(); - - Particles(const math::Vector3f & location); - ~Particles(); - - void set_radius(const float radius); - - inline const math::Vector3f & location() const - { - return particles_location; - } - - inline const math::Axis &axis() const - { - return particles_axis; - } - - inline const std::string & script() const - { - return particles_script; - } - - inline const bool entity() const - { - return particles_entity; - } - - inline const bool engine() const - { - return particles_engine; - } - - inline const float radius() const - { - return particles_radius; - } - - inline const Cull cull() const - { - return particles_cull; - } - - std::string particles_script; - math::Vector3f particles_location; - math::Axis particles_axis; - - bool particles_entity; - bool particles_engine; - - float particles_radius; - Cull particles_cull; -}; - -/* ---- class Dock ------------------------------------------------- */ - -/// a docking location -class Dock -{ -public: - Dock(); - ~Dock(); - - /// location of the dock - inline const math::Vector3f & location() const - { - return dock_location; - } - - /// trigger distance default is 0.01f - inline float radius() const - { - return dock_radius; - } - - - math::Vector3f dock_location; - float dock_radius; -}; - -/* ---- class SubModel --------------------------------------------- */ - -/// a submodel -class SubModel -{ -public: - SubModel(); - ~SubModel(); - - inline const std::string &name() const { return submodel_name; } - - inline const float scale() const { return submodel_scale; } - - inline const math::Vector3f &location() const { return submodel_location; } - - inline math::Axis &axis() { return submodel_axis; } - - inline void set_scale(const float scale) { submodel_scale = scale; } - - inline void set_name(const std::string &name) { submodel_name.assign(name); } - - inline void set_location(const math::Vector3f &location) { submodel_location.assign(location); } - - inline void set_axis(const math::Axis &axis) { submodel_axis.assign(axis); } - -private: - std::string submodel_name; - float submodel_scale; - math::Vector3f submodel_location; - math::Axis submodel_axis; -}; - -} - -#endif // __INCLUDED_MODEL_CLASSES_H__ - diff --git a/src/model/face.cc b/src/model/face.cc new file mode 100644 index 0000000..c6c3269 --- /dev/null +++ b/src/model/face.cc @@ -0,0 +1,43 @@ +/* + model/face.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include + +#include "model/face.h" + +namespace model +{ + +using math::Vector3f; +/* + * all points p(x, y, z) on the plane satisfy the general equation + * x*a() + y*b() + z*c() + d() = 0 + */ + +Face::Face(Vector3f const & point0, Vector3f const &point1, Vector3f const &point2) +{ + face_detail = false; + face_surface_flags = 0; + face_material = 0; + + face_point[0] = point0; + face_point[1] = point1; + face_point[2] = point2; + + face_normal = crossproduct((face_point[1] - face_point[0]) , (face_point[2] - face_point[0])); + pd = -1 * (face_normal.x * face_point[0].x + face_normal.y * face_point[0].y + face_normal.z * face_point[0].z); +} + +Face::Face(Face const & other) +{ + for (size_t i=0; i < 3; i++) + this->face_point[i] = other.face_point[i]; + + face_normal = crossproduct((face_point[1] - face_point[0]) , (face_point[2] - face_point[0])); + pd = -1 * (face_normal.x * face_point[0].x + face_normal.y * face_point[0].y + face_normal.z * face_point[0].z); +} + +} diff --git a/src/model/face.h b/src/model/face.h new file mode 100644 index 0000000..8c75522 --- /dev/null +++ b/src/model/face.h @@ -0,0 +1,113 @@ +/* + model/face.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_MODEL_FACE_H__ +#define __INCLUDED_MODEL_FACE_H__ + +#include + +#include "math/vector2f.h" +#include "math/vector3f.h" +#include "model/material.h" + +namespace model +{ + +/** @brief A class representing the plane of a single model face + * all points p(x, y, z) on the plane satisfy the general equation + * x*a() + y*b() + z*c() + d() = 0 + */ +class Face +{ +public: + /// a plane defined by 3 points in space + Face(math::Vector3f const & point0, math::Vector3f const &point1, math::Vector3f const &point2); + + /// copy constructor + Face(Face const & other); + + /// normal of the plane, not normalized to lenght 1 + inline math::Vector3f const & normal() const + { + return face_normal; + } + + /// the points defining the plane. + /// @param index 0 <= i < 3 + inline math::Vector3f const & point(size_t index) const + { + return face_point[index]; + } + + /// face material + inline Material *material() const + { + return face_material; + } + + /// first parameter of the general plane equation + inline float a() const + { + return face_normal[0]; + } + + /// second parameter of the general plane equation + inline float b() const + { + return face_normal[1]; + } + + /// third param of the general plane equation + inline float c() const + { + return face_normal[2]; + } + + /// fourth parameter of the general plane equation + inline float d() const + { + return pd; + } + + /// indidcates if this plane was generated from a detail brush + inline bool & detail() + { + return face_detail; + } + + /// surface flags + inline unsigned int & surface_flags() + { + return face_surface_flags; + } + + /// return texture transformation vectors + /// @param index 0 <= i < 2 + inline math::Vector3f &tex_vec(size_t index) { return face_tex_vec[index]; } + + /// return texture shift + inline math::Vector2f &tex_shift() { return face_tex_shift; } + + + inline void set_material(Material *material) { face_material = material; } + + +private: + math::Vector3f face_normal; + math::Vector3f face_point[3]; + + math::Vector3f face_tex_vec[2]; + math::Vector2f face_tex_shift; + + Material *face_material; + unsigned int face_surface_flags; + bool face_detail; + float pd; +}; + +} + +#endif // __INCLUDED_MODEL_FACE_H__ diff --git a/src/model/map.cc b/src/model/map.cc deleted file mode 100644 index 5d699e6..0000000 --- a/src/model/map.cc +++ /dev/null @@ -1,1212 +0,0 @@ -/* - model/map.cc - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#include "auxiliary/functions.h" -#include "filesystem/filesystem.h" -#include "math/mathlib.h" -#include "model/map.h" -#include "model/material.h" -#include "model/model.h" -#include "model/vertexarray.h" -#include "sys/sys.h" - -#include -#include -#include - -namespace model -{ - -// function to test spawnflags -inline bool spawnflag_isset(unsigned int spawnflags, unsigned int flag) -{ - return ((spawnflags & flag) == flag); -} - -// max geometry bounds -const float MAX_BOUNDS = 16384; - -const float MIN_DELTA = 10e-10; - -MapFile::MapFile() : map_center(0,0,0) -{ - mapfile_name.clear(); - map_brushes = 0; - map_faces = 0; - map_faces_detail = 0; - - warning_q2brush = false; -} - -MapFile::~MapFile() -{ - clear_materials(); -} - -void MapFile::clear_materials() -{ - for (Materials::iterator mit = map_materials.begin(); mit != map_materials.end(); mit++) { - // delete list of primitives - delete(*mit).second; - } - map_materials.clear(); -} - -bool MapFile::open(std::string const & mapname) -{ - warning_q2brush = false; - last_read_was_classname = false; - last_read_was_key = false; - key_current = ""; - value_current = ""; - classname_current = ""; - line_number = 0; - parse_level = 0; - - clear_materials(); - - mapfile_name.append(mapname); - mapfile_name.append(".map"); - - mapfile_ifs.open(mapfile_name); - if (!mapfile_ifs.is_open()) { - return false; - } - return true; -} - - -bool MapFile::got_classname() const -{ - return last_read_was_classname; -} - -bool MapFile::got_classname(const char * classnamelabel) const -{ - return (last_read_was_classname && (classname_current.compare(classnamelabel) == 0)); -} - -bool MapFile::got_classend(const char * classnamelabel) const -{ - return (last_read_was_classend && (classname_current.compare(classnamelabel) == 0)); -} - -bool MapFile::getline() -{ - using math::Vector3f; - - last_read_was_classname = false; - last_read_was_key = false; - last_read_was_classend = false; - - key_current = ""; - value_current = ""; - - if (!mapfile_ifs.is_open()) - return false; - - char data[1024]; - memset(data, 0, sizeof(data)); - - if (mapfile_ifs.getline(data, 1023)) { - line_number++; - std::istringstream linestream(data); - std::string firstword; - - if (linestream >> firstword) { - if (!firstword.size()) { - return true; - - } else if (firstword == "//") { - return true; - - } else if (firstword == "{") { - parse_level++; - - } else if (firstword == "}") { - if ((parse_level == 2) && (planes.size())) { - // end-of-brush - - // for every face - for (std::vector::iterator face = planes.begin(); face != planes.end(); face++) { - make_brushface((*face)); - } - - // clean planes - for (std::vector::iterator it = planes.begin(); it != planes.end(); it++) { - delete(*it); - } - planes.clear(); - - map_brushes++; - - value_current.clear(); - - } else if ((parse_level == 1)) { - // end-of-class - last_read_was_classend = true; - } - - parse_level--; - - } else if (parse_level == 1) { - - if (firstword == "\"classname\"") { - classname_current.clear(); - - if (linestream >> classname_current) { - if (classname_current.size() > 2) { - classname_current.erase(0,1); - classname_current.erase(classname_current.size()-1, 1); - last_read_was_classname = true; - } else { - classname_current.clear(); - } - } - - } else if ((firstword.size() > 2) && (firstword[0] == '\"') && (firstword[firstword.size()-1] == '\"')) { - - key_current.assign(firstword); - key_current.erase(0,1); - key_current.erase(key_current.size()-1, 1); - - value_current.clear(); - char c; - while ((linestream.get(c)) && (c != '"')); - while ((linestream.get(c)) && (c != '"')) - value_current += c; - - last_read_was_key = true; - } - - } else if (parse_level == 2) { - - if (firstword == "(") { - // brush plane - - Vector3f p1, p2, p3; - std::string tmp; - std::string texture; - int n = 0; - - linestream >> p1; - linestream >> tmp; // ) - linestream >> tmp; // ( - linestream >> p2; - linestream >> tmp; // ) - linestream >> tmp; // ( - linestream >> p3; - linestream >> tmp; // ) - linestream >> texture; - - // 5 numbers (texture alignment?) - for (int i=0; i < 5; i++) - linestream >> tmp; - - // content flags ? - if (!(linestream >> n)) - n = 0; - - Plane *plane = new Plane(p1, p2, p3); - aux::to_lowercase(texture); - plane->texture() = texture; - if (n > 0) - plane->detail() = true; - - // surface flags - if (!(linestream >> n)) { - n = 0; - warning_q2brush = true; - } - plane->surface_flags() = n; - - planes.push_back(plane); - - value_current.clear(); - } - } - } - } else { - - return false; - } - - return true; -} - -void MapFile::make_brushface(Plane *face) -{ - using math::Vector3f; - - // ignore caulk - if (face->texture() == "common/caulk") { - return; - } - - // FIXME clip should be parsed as collision blocks - if (face->texture() == "common/clip") { - return; - } - - // statistics - map_faces++; - if (face->detail()) { - map_faces_detail++; - } - - // using suggestions from - // http://www.flipcode.com/archives/Level_Editing.shtml - - // vertex list - std::vector vl; - - // calculate initial vertices on the bounding box - - // check if the face is x-axis oriented - if ((fabsf(face->normal().x) >= fabsf(face->normal().y)) && (fabsf(face->normal().x) >= fabsf(face->normal().z))) { - - if (face->normal().x > MIN_DELTA) { - vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, -MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, MAX_BOUNDS, MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, MAX_BOUNDS, -MAX_BOUNDS)); - } else { - vl.push_back(new math::Vector3f(0, MAX_BOUNDS, -MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, MAX_BOUNDS, MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, MAX_BOUNDS)); - vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, -MAX_BOUNDS)); - } - // calculate the x coordinate of each face vertex - for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { - (*it)->x = (-face->d() - - face->normal().z * (*it)->z - - face->normal().y * (*it)->y) / - face->normal().x; - } - } - - // check if the face is y-axis oriented - else if ((fabsf(face->normal().y) >= fabsf(face->normal().x)) && (fabsf(face->normal().y) >= fabsf(face->normal().z))) { - - if (face->normal().y > MIN_DELTA) { - vl.push_back(new Vector3f(MAX_BOUNDS, 0, -MAX_BOUNDS)); - vl.push_back(new Vector3f(MAX_BOUNDS, 0, MAX_BOUNDS)); - vl.push_back(new Vector3f(-MAX_BOUNDS, 0, MAX_BOUNDS)); - vl.push_back(new Vector3f(-MAX_BOUNDS, 0, -MAX_BOUNDS)); - } else { - vl.push_back(new Vector3f(-MAX_BOUNDS, 0, -MAX_BOUNDS)); - vl.push_back(new Vector3f(-MAX_BOUNDS, 0, MAX_BOUNDS)); - vl.push_back(new Vector3f(MAX_BOUNDS, 0, MAX_BOUNDS)); - vl.push_back(new Vector3f(MAX_BOUNDS, 0, -MAX_BOUNDS)); - } - - // calculate the x coordinate of each face vertex - for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { - (*it)->y = (-face->d() - - face->normal().z * (*it)->z - - face->normal().x * (*it)->x) / - face->normal().y; - } - } - - // face must be z-axis oriented - else { - - if (face->normal().z > MIN_DELTA) { - vl.push_back(new Vector3f(-MAX_BOUNDS, -MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(-MAX_BOUNDS, MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(MAX_BOUNDS, MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(MAX_BOUNDS, -MAX_BOUNDS, 0)); - } else { - vl.push_back(new Vector3f(MAX_BOUNDS, -MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(MAX_BOUNDS, MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(-MAX_BOUNDS, MAX_BOUNDS, 0)); - vl.push_back(new Vector3f(-MAX_BOUNDS, -MAX_BOUNDS, 0)); - } - - // calculate the x coordinate of each face vertex - for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { - (*it)->z = (-face->d() - - face->normal().x * (*it)->x - - face->normal().y * (*it)->y) / - face->normal().z; - } - } - - - // intersect the face with every plane - for (std::vector::iterator pit = planes.begin(); pit != planes.end(); pit++) { - - Plane *plane = (*pit); - if (plane == face) { - continue; - } - - Vector3f fn = crossproduct(face->point(1)-face->point(0), face->point(2)-face->point(0)); - Vector3f pn = crossproduct(plane->point(1)-plane->point(0), plane->point(2)-plane->point(0)); - - Vector3f t = crossproduct(fn, pn); - if ((t.x == 0) && (t.y == 0) && (t.z == 0)) { - continue; - } - - // intersect face with plane - for (int i=0; vl.size() - i > 0; i++) { - - Vector3f v(*vl.at(i)); - - Vector3f next; - if (vl.size() - i > 1) { - next = *vl.at(i+1); - } else { - next = *vl.front(); - } - - Vector3f prev; - if (i > 0) { - prev = *vl.at(i-1); - } else { - prev = *vl.back(); - } - - if ((v.x*plane->normal().x + v.y*plane->normal().y + v.z*plane->normal().z +plane->d()) < MIN_DELTA) { - - // find current - std::vector::iterator vit = vl.begin(); - while ((*vit) != vl.at(i)) { - vit++; - } - - // check if prev - v intersects with plane - if ((prev.x*plane->normal().x + prev.y*plane->normal().y + prev.z*plane->normal().z + plane->d()) > MIN_DELTA) { - - // calculate intersection - float t1 = -plane->normal().x * prev.x - plane->normal().y * prev.y - plane->normal().z * prev.z -plane->d(); - float t2 = (plane->normal().x * v.x - plane->normal().x * prev.x + - plane->normal().y * v.y - plane->normal().y * prev.y + - plane->normal().z * v.z - plane->normal().z * prev.z); - - Vector3f *s = new Vector3f; - - if (t2 == 0) { - *s = v; - } else { - for (int j = 0; j < 3; j++) - (*s)[j] = prev [j] + t1 * (v[j] - prev[j]) / t2; - } - - vit = vl.insert(vit,s); - vit++; - i++; - } - - // check if next - v intersects with plane - if ((next.x*plane->normal().x + next.y*plane->normal().y + next.z*plane->normal().z + plane->d()) > MIN_DELTA) { - - // calculate intersection - float t1 = -plane->normal().x * v.x - plane->normal().y * v.y - plane->normal().z * v.z -plane->d(); - float t2 = (plane->normal().x * next.x - plane->normal().x * v.x + - plane->normal().y * next.y - plane->normal().y * v.y + - plane->normal().z * next.z - plane->normal().z * v.z); - //cout << "next t2 " << t2 << std::endl; - Vector3f *s = new Vector3f; - - if (t2 == 0) { - *s = v; - } else { - for (int j = 0; j < 3; j++) - (*s)[j] = v [j] + t1 * (next[j] - v[j]) / t2; - } - - vit = vl.insert(vit,s); - vit++; - i++; - } - - // erase - delete *vit; - vl.erase(vit); - i--; - } - - } - } - - if (vl.size() > 2) { - - Material *material = Material::find("textures/" + face->texture()); - - if (!material) { - material = new Material("textures/" + face->texture()); - Material::add(material); - material->set_flags(Material::Texture); - material->set_texture(material->name()); - } - - // find the list if primitives for the current material, allocate a new one if necessary - Primitives *primitives = 0; - - Materials::iterator mit = map_materials.find(material); - if (mit == map_materials.end()) { - primitives = new Primitives(material); - map_materials[material] = primitives; - } else { - primitives = (*mit).second; - } - - // calculate bounding box - for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { - *(*it) *= SCALE; - for (int i=0; i < 3; i++) { - if (class_maxbbox[i] < (*(*it))[i]) - class_maxbbox[i] = (*(*it))[i]; - - if (class_minbbox[i] > (*(*it))[i]) - class_minbbox[i] = (*(*it))[i]; - } - } - - -#ifndef HAVE_BULLET - - // Quads are disable to use model data for bullet physics - - // split polygon into quads - while (vl.size() > 3) { - std::vector::iterator v0 = vl.begin(); - std::vector::reverse_iterator vn = vl.rbegin(); - std::vector::reverse_iterator vn1 = vl.rbegin(); - ++vn1; - std::vector::reverse_iterator vn2 = vl.rbegin(); - ++vn2; - ++vn2; - - Vector3f n(face->normal()*-1); - n.normalize(); - - primitives->add_quad(*(*vn2), *(*vn1), *(*vn), *(*v0), n, face->detail()); - delete(*vn); - delete(*vn1); - vl.pop_back(); - vl.pop_back(); - } -#endif - // split polygon into triangles - while (vl.size() > 2) { - std::vector::iterator v0 = vl.begin(); - std::vector::reverse_iterator vn = vl.rbegin(); - std::vector::reverse_iterator vn1 = vl.rbegin(); - ++vn1; - - Vector3f n(face->normal()*-1); - n.normalize(); - - primitives->add_triangle(*(*vn1), *(*vn), *(*v0), n, face->detail()); - delete(*vn); - vl.pop_back(); - } - } else { - con_debug << "Unresolved face!\n"; - } - - // clean up the vertex list - for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { - delete(*it); - } - - vl.clear(); -} - -bool MapFile::got_key_string(const char * keylabel, std::string & valuestring) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - valuestring.assign(value_current); - return true; - } else { - return false; - } -} - -bool MapFile::got_key_vector3f(const char * keylabel, math::Vector3f & v) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - std::istringstream is(value_current); - float x, y, z; - if ((is >> x) && (is >> y) && (is >> z)) { - v = math::Vector3f(x,y,z); - } else { - v= math::Vector3f(); - } - return true; - } else { - return false; - } -} - -bool MapFile::got_key_float(const char * keylabel, float & f) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - std::istringstream is(value_current); - if (!(is >> f)) { - f = 0; - } - return true; - } else { - return false; - } -} - -bool MapFile::got_key_int(const char * keylabel, unsigned int & u) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - std::istringstream is(value_current); - if (!(is >> u)) { - u = 0; - } - return true; - } else { - return false; - } -} - -bool MapFile::got_key(const char * keylabel) -{ - return (last_read_was_key && (key_current.compare(keylabel) == 0)); -} - -bool MapFile::got_key_angle(const char * keylabel, float & f) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - std::istringstream is(value_current); - if ((is >> f)) { - f = math::degrees360f(f); - } else { - f = 0; - } - return true; - } else { - return false; - } -} - -bool MapFile::got_key_color(const char * keylabel, math::Color & color) -{ - if (last_read_was_key && (key_current.compare(keylabel) == 0)) { - std::istringstream is(value_current); - float r, g, b; - if ((is >> r) && (is >> g) && (is >> b)) { - if ((r > 1) || (g > 1) || (b > 1)) { - r /= 255; - g /= 255; - b /= 255; - } - color = math::Color(r, g, b); - } else { - color = math::Color(); - } - return true; - } else { - return false; - } -} - -void MapFile::close() -{ - mapfile_ifs.close(); -} - -void MapFile::clear_bbox() -{ - for (int i=0; i < 3; i++) { - class_minbbox[i] = MAX_BOUNDS; - class_maxbbox[i] = -MAX_BOUNDS; - } - - class_axis.clear(); - class_speed = 0; -} - -void MapFile::load_worldspawn(Model *model) -{ - if (!map_materials.size()) - return; - - // FIXME center in maps without brushes - map_center = (class_minbbox + class_maxbbox) / 2.0f; - - model->model_minbbox = class_minbbox - map_center; - model->model_maxbbox = class_maxbbox - map_center; - - model->model_radius = model->model_maxbbox.length(); - - load_fragmentgroup(model, FragmentGroup::None); -} - -void MapFile::load_fragmentgroup(Model *model, const FragmentGroup::Type class_type) -{ - if (!VertexArray::instance() || VertexArray::instance()->overflow()) - return; - - if (!map_materials.size()) - return; - - FragmentGroup *group = new FragmentGroup(); - - if (class_type == FragmentGroup::Rotate) { - if (class_speed == 0) { - // default rotation speed 45 degrees per second - class_speed = 45.0f; - } - } - - if (class_type != FragmentGroup::None) { - group->set_transform(true); - } - - group->set_type(class_type); - group->set_location((class_minbbox + class_maxbbox) / 2.0f - map_center); - group->set_axis(class_axis); - group->set_speed(class_speed); - - math::Vector3f group_center(map_center); - if (group->transform()) { - group_center += group->location(); - } - - for (Materials::iterator mit = map_materials.begin(); mit != map_materials.end(); mit++) { - // split the Primitives with this material into fragments - Primitives *primitives = (*mit).second; - - // store triangles - if (primitives->triangles().size()) { - Fragment *fragment = new Fragment(Fragment::Triangles, primitives->material()); - - // add structural triangles to the fragment - for (Primitives::Triangles::iterator tris_it = primitives->triangles().begin(); tris_it != primitives->triangles().end(); tris_it++) { - Triangle *triangle = (*tris_it); - if (!triangle->detail()) { - size_t count = 0; - count += fragment->add_vertex(triangle->v0()-group_center, triangle->normal(), false); - count += fragment->add_vertex(triangle->v1()-group_center, triangle->normal(), false); - count += fragment->add_vertex(triangle->v2()-group_center, triangle->normal(), false); - if (count == 3) - model->model_tris_count++; - } - } - - // add detail triangles to the fragment - for (Primitives::Triangles::iterator tris_it = primitives->triangles().begin(); tris_it != primitives->triangles().end(); tris_it++) { - Triangle *triangle = (*tris_it); - if (triangle->detail()) { - size_t count = 0; - count += fragment->add_vertex(triangle->v0()-group_center, triangle->normal(), true); - count += fragment->add_vertex(triangle->v1()-group_center, triangle->normal(), true); - count += fragment->add_vertex(triangle->v2()-group_center, triangle->normal(), true); - if (count == 3) { - model->model_tris_count++; - model->model_tris_detail_count++; - } - } - } - - // add the fragment to the group - group->add_fragment(fragment); - } - - // store quads - if (primitives->quads().size()) { - Fragment *fragment = new Fragment(Fragment::Quads, primitives->material()); - - // add structural triangles to the fragment - for (Primitives::Quads::iterator quad_it = primitives->quads().begin(); quad_it != primitives->quads().end(); quad_it++) { - Quad *quad = (*quad_it); - if (!quad->detail()) { - size_t count = 0; - count += fragment->add_vertex(quad->v0()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v1()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v2()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v3()-group_center, quad->normal(), false); - if (count == 4) - model->model_quad_count++; - } - } - - // add detail triangles to the fragment - for (Primitives::Quads::iterator quad_it = primitives->quads().begin(); quad_it != primitives->quads().end(); quad_it++) { - Quad *quad = (*quad_it); - if (quad->detail()) { - size_t count = 0; - count += fragment->add_vertex(quad->v0()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v1()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v2()-group_center, quad->normal(), false); - count += fragment->add_vertex(quad->v3()-group_center, quad->normal(), false); - if (count == 4) { - model->model_quad_count++; - model->model_quad_detail_count++; - } - } - } - - // add the fragment to the group - group->add_fragment(fragment); - } - - } - - // add the group to the model - model->add_group(group); -} - -void MapFile::unknown_value() const -{ - con_warn << name() << " unknown value '" << value() << "' for '" << classname() << ":" << key() << "' at line " << line() << std::endl; -} - -void MapFile::unknown_key() const -{ - con_warn << name() << " unknown key '" << classname() << ":" << key() << "' at line " << line() << std::endl; -} - -void MapFile::unknown_class() const -{ - con_warn << name() << " unknown class '" << classname() << "' at line " << line() << std::endl; -} - -Model * MapFile::load(std::string const &name) -{ - // open the .map file - MapFile mapfile; - - if (!mapfile.open(name)) { - return 0; - } - - Model *model = new Model(name); - mapfile.clear_bbox(); - - Dock *dock = 0; - Particles *particles = 0; - Flare *flare = 0; - Light *light = 0; - SubModel *submodel = 0; - - std::string modelname; - math::Vector3f location; - - typedef std::list SubModelList; - SubModelList submodel_list; - - unsigned int u; - float angle; - float r, s; - std::string str; - - while (mapfile.getline()) { - - if (mapfile.got_classname("worldspawn")) { - mapfile.clear_bbox(); - - } else if (mapfile.got_classend("worldspawn")) { - mapfile.load_worldspawn(model); - mapfile.clear_materials(); - - } else if (mapfile.in_class("worldspawn")) { - - // worldspawn attributes - if (mapfile.got_key("name")) { - con_debug << " model name '" << name << "'" << std::endl; - - } else if (mapfile.got_key_int("enginesound", u)) { - model->model_enginesound = u; - continue; - - } else if (mapfile.got_key_int("impulsesound", u)) { - model->model_impulsesound = u; - continue; - - } else if (mapfile.got_key_color("enginecolor", model->model_enginecolor)) { - continue; - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - - } - - } else if (mapfile.got_classname("func_door")) { - mapfile.clear_bbox(); - - } else if (mapfile.got_classend("func_door")) { - mapfile.load_fragmentgroup(model, FragmentGroup::Door); - mapfile.clear_materials(); - - } else if (mapfile.in_class("func_door")) { - - } else if (mapfile.got_classname("func_group")) { - mapfile.clear_bbox(); - - } else if (mapfile.got_classend("func_group")) { - mapfile.load_fragmentgroup(model, FragmentGroup::None); - mapfile.clear_materials(); - - } else if (mapfile.got_classname("func_rotate")) { - mapfile.clear_bbox(); - - } else if (mapfile.got_classend("func_rotate")) { - mapfile.load_fragmentgroup(model, FragmentGroup::Rotate); - mapfile.clear_materials(); - - } else if (mapfile.in_class("func_rotate")) { - - if (mapfile.got_key_float("angle", angle)) { - if (angle == ANGLEUP) { - mapfile.class_axis.change_pitch(90.0f); - } else if (angle == ANGLEDOWN) { - mapfile.class_axis.change_pitch(-90.0f); - } else { - mapfile.class_axis.change_direction(angle); - } - } else if (mapfile.got_key_float("direction", angle)) { - mapfile.class_axis.change_direction(angle); - - } else if (mapfile.got_key_float("pitch", angle)) { - mapfile.class_axis.change_pitch(angle); - - } else if (mapfile.got_key_float("roll", angle)) { - mapfile.class_axis.change_roll(angle); - - } else if (mapfile.got_key_float("speed", mapfile.class_speed)) { - continue; - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - } - - } else if (mapfile.got_classend()) { - mapfile.clear_materials(); - - } else if (mapfile.got_classname("light")) { - - // new light - light = new Light(); - model->add_light(light); - - } else if (mapfile.classname().compare("light") == 0) { - - // light attributes - if (mapfile.got_key_vector3f("origin", light->light_location)) { - light->light_location *= SCALE; - continue; - - } else if (mapfile.got_key_color("_color", light->light_color)) { - continue; - - } else if (mapfile.got_key_int("spawnflags", u)) { - light->light_strobe = spawnflag_isset(u, 1); - light->light_entity = spawnflag_isset(u, 2); - - } else if (mapfile.got_key_float("light", light->light_radius)) { - light->light_radius *= LIGHTSCALE; - - } else if (mapfile.got_key_float("radius", light->light_radius)) { - light->light_radius *= LIGHTSCALE; - - } else if (mapfile.got_key_float("frequency", light->light_frequency)) { - continue; - - } else if (mapfile.got_key_float("offset", light->light_offset)) { - continue; - - } else if (mapfile.got_key_float("time", light->light_time)) { - continue; - - } else if (mapfile.got_key_int("flare", light->light_flare)) { - continue; - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - - } - - } else if (mapfile.got_classname("location_dock")) { - - // new docking location - dock = new Dock(); - model->add_dock(dock); - - } else if (mapfile.classname().compare("location_dock") == 0) { - - // dock attributes - if (mapfile.got_key_vector3f("origin", dock->dock_location)) { - dock->dock_location *= SCALE; - continue; - - } else if (mapfile.got_key_float("radius", dock->dock_radius)) { - dock->dock_radius *= SCALE; - continue; - - } else if (mapfile.got_key("angle")) { - continue; - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - - } - - } else if (mapfile.got_classname("location_cannon")) { - // new cannon - - } else if (mapfile.classname().compare("location_cannon") == 0) { - - } else if (mapfile.got_classname("location_turret")) { - // new turret - - } else if (mapfile.classname().compare("location_turret") == 0) { - - } else if (mapfile.got_classname("location_cockpit")) { - // cockpit location - - } else if (mapfile.classname().compare("location_cockpit") == 0) { - - } else if (mapfile.got_classname("fx_flare")) { - - // new flare - flare = new Flare(); - model->add_flare(flare); - - } else if (mapfile.classname().compare("fx_flare") == 0) { - - // flare attributes - if (mapfile.got_key_vector3f("origin", flare->light_location)) { - flare->light_location *= SCALE; - continue; - - } else if (mapfile.got_key_color("_color", flare->light_color)) { - continue; - - } else if (mapfile.got_key_int("spawnflags", u)) { - flare->light_strobe = spawnflag_isset(u, 1); - flare->light_entity = spawnflag_isset(u, 2); - flare->flare_engine = spawnflag_isset(u, 4); - - } else if (mapfile.got_key_float("radius", flare->light_radius)) { - flare->light_radius *= LIGHTSCALE; - - } else if (mapfile.got_key_float("frequency", flare->light_frequency)) { - continue; - - } else if (mapfile.got_key_float("offset", flare->light_offset)) { - continue; - - } else if (mapfile.got_key_float("time", flare->light_time)) { - continue; - - } else if (mapfile.got_key_int("flare", flare->light_flare)) { - continue; - - } else if (mapfile.got_key_float("angle", angle)) { - if (angle == ANGLEUP) { - flare->flare_axis.change_pitch(90.0f); - } else if (angle == ANGLEDOWN) { - flare->flare_axis.change_pitch(-90.0f); - } else { - flare->flare_axis.change_direction(angle); - } - } else if (mapfile.got_key_float("direction", angle)) { - flare->flare_axis.change_direction(angle); - - } else if (mapfile.got_key_float("pitch", angle)) { - flare->flare_axis.change_pitch(angle); - - } else if (mapfile.got_key_float("roll", angle)) { - flare->flare_axis.change_roll(angle); - - } else if (mapfile.got_key_string("cull", str)) { - - aux::to_lowercase(str); - if (str.compare("none") == 0) { - flare->flare_cull = CullNone; - } else if (str.compare("back") == 0) { - flare->flare_cull = CullBack; - } else if (str.compare("front") == 0) { - flare->flare_cull = CullFront; - } else { - mapfile.unknown_value(); - } - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - } - - } else if (mapfile.got_classname("misc_model")) { - - // new submodel - submodel = new SubModel(); - submodel_list.push_back(submodel); - - } else if (mapfile.classname().compare("misc_model") == 0) { - - // submodel attributes - if (mapfile.got_key_vector3f("origin", location)) { - submodel->set_location(location * SCALE); - continue; - - } else if (mapfile.got_key_string("model", modelname)) { - - // remove extension - if (modelname[modelname.size()-4] == '.') { - modelname.erase(modelname.size()-4); - } - - submodel->set_name(modelname); - continue; - - } else if (mapfile.got_key_float("angle", angle)) { - if (angle == ANGLEUP) { - submodel->axis().change_pitch(90.0f); - } else if (angle == ANGLEDOWN) { - submodel->axis().change_pitch(-90.0f); - } else { - submodel->axis().change_direction(angle); - } - - } else if (mapfile.got_key_float("modelscale", s)) { - if (s) { - submodel->set_scale(s); - } else { - submodel->set_scale(1.0f); - } - } - - } else if (mapfile.got_classname("fx_particles")) { - - // new particle system - particles = new Particles(); - model->add_particles(particles); - - } else if (mapfile.classname().compare("fx_particles") == 0) { - - // particle system attributes - if (mapfile.got_key_vector3f("origin", particles->particles_location)) { - particles->particles_location *= SCALE; - continue; - } else if (mapfile.got_key_string("script", particles->particles_script)) { - continue; - } else if (mapfile.got_key_float("angle", angle)) { - if (angle == ANGLEUP) { - particles->particles_axis.change_pitch(90.0f); - } else if (angle == ANGLEDOWN) { - particles->particles_axis.change_pitch(-90.0f); - } else { - particles->particles_axis.change_direction(angle); - } - } else if (mapfile.got_key_float("direction", angle)) { - particles->particles_axis.change_direction(angle); - - } else if (mapfile.got_key_float("pitch", angle)) { - particles->particles_axis.change_pitch(angle); - - } else if (mapfile.got_key_float("roll", angle)) { - particles->particles_axis.change_roll(angle); - - } else if (mapfile.got_key_int("spawnflags", u)) { - particles->particles_entity = spawnflag_isset(u, 2); - particles->particles_engine = spawnflag_isset(u, 4); - - } else if (mapfile.got_key_float("radius", r)) { - particles->set_radius(r * LIGHTSCALE); - - } else if (mapfile.got_key_string("cull", str)) { - - aux::to_lowercase(str); - if (str.compare("none") == 0) { - particles->particles_cull = CullNone; - } else if (str.compare("back") == 0) { - particles->particles_cull = CullBack; - } else if (str.compare("front") == 0) { - particles->particles_cull = CullFront; - } else { - mapfile.unknown_value(); - } - - } else if (mapfile.got_key()) { - mapfile.unknown_key(); - } - } else if (mapfile.got_classname()) { - mapfile.unknown_class(); - } - } - - mapfile.close(); - - // reposition docks, lights, flares and particles according to the model center - for (Model::Lights::iterator lit = model->lights().begin(); lit != model->lights().end(); lit++) { - (*lit)->light_location -= mapfile.map_center; - } - - for (Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) { - (*flit)->light_location -= mapfile.map_center; - } - - for (Model::ParticleSystems::iterator pit = model->particles().begin(); pit != model->particles().end(); pit++) { - (*pit)->particles_location -= mapfile.map_center; - } - - for (Model::Docks::iterator dit = model->docks().begin(); dit != model->docks().end(); dit++) { - (*dit)->dock_location -= mapfile.map_center; - } - - // FIXME this will go wrong if a Rotate group is imported as submodel - for (SubModelList::iterator smit = submodel_list.begin(); smit != submodel_list.end(); smit++) { - submodel = (*smit); - Model *submodel_model = 0; - - if (submodel->name().size()) { - submodel_model = Model::load(submodel->name()); - } - - if (submodel_model) { - // copy fragmentgroups - for (Model::Groups::iterator git = submodel_model->groups().begin(); git != submodel_model->groups().end(); git++) { - FragmentGroup *groupsrc = (*git); - FragmentGroup *groupdst = new FragmentGroup(); - - groupdst->set_transform(true); - groupdst->set_type(groupsrc->type()); - groupdst->set_scale(groupsrc->scale() * submodel->scale()); - groupdst->set_speed(groupsrc->speed()); - groupdst->set_location(groupsrc->location() + (submodel->location() - mapfile.map_center)); - groupdst->set_axis(groupsrc->axis() * submodel->axis()); - - // copy fragments - for (FragmentGroup::iterator fit = groupsrc->begin(); fit != groupsrc->end(); fit++) { - Fragment *fragmentdst = new Fragment(*(*fit)); - groupdst->add_fragment(fragmentdst); - } - - if (groupdst->size()) { - model->add_group(groupdst); - } else { - delete groupdst; - } - } - - con_debug << " imported submodel '" << submodel->name() << "'" << std::endl; - } - - delete submodel; - - } - - con_debug << " " << mapfile.name() << " " << mapfile.map_brushes << " brushes " << - mapfile.map_faces << "/" << mapfile.map_faces_detail << " faces/detail " << std::endl; - if (mapfile.warning_q2brush) - con_warn << " quake2 style brushes detected" << std::endl; - - return model; -} - -} - diff --git a/src/model/map.h b/src/model/map.h deleted file mode 100644 index ed0b3e1..0000000 --- a/src/model/map.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - model/map.h - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#ifndef __INCLUDED_MODEL_MAPFILE_H__ -#define __INCLUDED_MODEL_MAPFILE_H__ - -#include -#include - -#include "model/material.h" -#include "model/model.h" -#include "model/plane.h" -#include "model/primitives.h" -#include "filesystem/filestream.h" - -namespace model -{ - -/// class to parse the .map file structure and load geometry data into a model -class MapFile -{ - -public: - /** - * @brief load a .map file into a Model - * @param name name of the model to be loaded, without .map extension or maps/ prefix - * If the file can not be read, load() returns the NULL-pointer - */ - static Model *load(std::string const &name); - -private: - MapFile(); - ~MapFile(); - - /// tpye definition for a per-material list of Primitives - typedef std::map Materials; - - /// open the file for reading - /** the filename will get the "maps/" prefix and ".map" suffix - */ - bool open(std::string const & name); - - /// parse one line, returns false on end-of-file - bool getline(); - - /// current classname - inline std::string classname() const - { - return classname_current; - } - - /// current key - inline std::string key() const - { - return key_current; - } - - /// current value - inline std::string value() const - { - return value_current; - } - - /// true if the last read line contained a new classname - bool got_classname() const; - - /// true if we are inside the requested class - bool in_class(const char *name) { - return (classname_current.compare(name) == 0); - } - - /// true if the last read line contained a class closing bracket - inline bool got_classend() const - { - return last_read_was_classend; - } - - /// true if the last read line contained the closing bracket for the requested class - bool got_classend(const char*) const; - - /// true if the last read line contained a new classname - bool got_classname(const char*) const; - - /// true if the last read statement was a key=value pair - inline bool got_key() const - { - return last_read_was_key; - } - - bool got_key(const char * keylabel); - - /// check if the last read key=value pair matches keylabel and store the value in valuestring - bool got_key_string(const char * keylabel, std::string & valuestring); - - /// check if the last read key=value pair matches keylabel and store the value in color - bool got_key_color(const char * keylabel, math::Color & color); - - /// check if the last read key=value pair matches keylabel and store the value in f - bool got_key_float(const char * keylabel, float & f); - - /// check if the last read key=value pair matches keylabel and store the value in f - bool got_key_int(const char * keylabel, unsigned int & u); - - /// check if the last read key=value pair matches keylabel and store the value in valuestring - bool got_key_angle(const char * keylabel, float & f); - - bool got_key_vector3f(const char * keylabel, math::Vector3f & v); - - - /// return the number of lines read so far - inline unsigned int line() const - { - return line_number; - } - - /// return true of the map file is open for reading - inline bool is_open() - { - return mapfile_ifs.is_open(); - } - - /// current filename - inline std::string const & name() const - { - return mapfile_name; - } - - /// close the file - void close(); - - /// generate triangles for one plane in the plane list - void make_brushface(Plane *face); - - /// load parsed primitives into model worldspawn - void load_worldspawn(Model *model); - - /// load parsed primitives into a FragmentGroup - void load_fragmentgroup(Model *model, const FragmentGroup::Type class_type); - - /// clear the current list of per-material geometry - void clear_materials(); - - /// clear class bounding box - void clear_bbox(); - - void unknown_class() const; - - void unknown_key() const; - - void unknown_value() const; - - /// list of planes for the current brush - std::vector planes; - - - std::string classname_current; - std::string key_current; - std::string value_current; - - bool last_read_was_key; - bool last_read_was_classname; - bool last_read_was_classend; - - unsigned int map_brushes; - unsigned int map_faces; - unsigned int map_faces_detail; - - unsigned int parse_level; - unsigned int line_number; - filesystem::IFileStream mapfile_ifs; - std::string mapfile_name; - - math::Vector3f class_maxbbox; - math::Vector3f class_minbbox; - math::Axis class_axis; - float class_speed; - - math::Vector3f map_center; - Materials map_materials; - - bool warning_q2brush; -}; - -} - -#endif // __INCLUDED_MODEL_MAPFILE_H__ diff --git a/src/model/mapfile.cc b/src/model/mapfile.cc new file mode 100644 index 0000000..21d318b --- /dev/null +++ b/src/model/mapfile.cc @@ -0,0 +1,1353 @@ +/* + model/map.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "auxiliary/functions.h" +#include "filesystem/filesystem.h" +#include "math/mathlib.h" +#include "model/mapfile.h" +#include "model/material.h" +#include "model/model.h" +#include "model/vertexarray.h" +#include "sys/sys.h" + +#include +#include +#include + +namespace model +{ + +/* + from radiant tools/quake3/map.c +*/ + +math::Vector3f texture_baseaxis[18] = +{ + math::Vector3f(0,0,1), math::Vector3f(1,0,0), math::Vector3f(0,-1,0), // floor + math::Vector3f(0,0,-1), math::Vector3f(1,0,0), math::Vector3f(0,-1,0), // ceiling + math::Vector3f(1,0,0), math::Vector3f(0,1,0), math::Vector3f(0,0,-1), // west wall + math::Vector3f(-1,0,0), math::Vector3f(0,1,0), math::Vector3f(0,0,-1), // east wall + math::Vector3f(0,1,0), math::Vector3f(1,0,0), math::Vector3f(0,0,-1), // south wall + math::Vector3f(0,-1,0), math::Vector3f(1,0,0), math::Vector3f(0,0,-1) // north wall +}; + +// determines best orthagonal axis to project a texture onto a wall (must be identical in radiant!) +void texture_axis_from_plane(const Face &face, math::Vector3f &xv, math::Vector3f &yv) +{ + size_t best_axis = 0; + float dot = 0; + float best = 0; + + for (size_t i=0 ; i<6 ; i++) + { + dot = math::dotproduct(face.normal(), texture_baseaxis[i *3]); + if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */ + { + best = dot; + best_axis = i; + } + } + + xv.assign(texture_baseaxis[best_axis*3+1]); + yv.assign(texture_baseaxis[best_axis*3+2]); +} + +// creates world-to-texture mapping vecs for crappy quake plane arrangements +void face_texture_verts(Face &face, const math::Vector2f &tex_shift, const float tex_rotate, const math::Vector2f & tex_scale) +{ + math::Vector3f vecs[2]; + math::Vector2f scale(tex_scale); + + int sv, tv; + float ang, sinv, cosv; + float ns, nt; + int i, j; + + texture_axis_from_plane(face, vecs[0], vecs[1]); + + if (!scale[0]) + scale[0] = 1; + if (!scale[1]) + scale[1] = 1; + + // rotate axis + if (tex_rotate == 0.0f) + { sinv = 0.0f ; cosv = 1.0f; } + else if (tex_rotate == 90.0f) + { sinv = 1.0f ; cosv = 0.0f; } + else if (tex_rotate == 180.0f) + { sinv = 0.0f; cosv = -1.0f; } + else if (tex_rotate == 270.0f) + { sinv = -1.0f ; cosv = 0.0f; } + else + { + ang = tex_rotate / 180.0f * M_PI; + sinv = sinf(ang); + cosv = cosf(ang); + } + + if (vecs[0][0]) + sv = 0; + else if (vecs[0][1]) + sv = 1; + else + sv = 2; + + if (vecs[1][0]) + tv = 0; + else if (vecs[1][1]) + tv = 1; + else + tv = 2; + + for (i=0 ; i<2 ; i++) { + ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; + nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; + vecs[i][sv] = ns; + vecs[i][tv] = nt; + } + + for (i=0 ; i<2 ; i++) + for (j=0 ; j<3 ; j++) + face.tex_vec(i)[j] = vecs[i][j] / scale[i]; + + face.tex_shift().assign(tex_shift); +} + +const math::Vector2f map_texture_coords(Face *face, const math::Vector3f &v) +{ + math::Vector2f t ( + (face->tex_shift().x + math::dotproduct(face->tex_vec(0), v)) / face->material()->size().width(), + (face->tex_shift().y + math::dotproduct(face->tex_vec(1), v)) / face->material()->size().height() + ); + + //con_debug << " texture coords for vertex (" << v << ") set to (" << t.x << "," << t.y << ")" << std::endl; + return t; +} +/* + + // nearest-axial projection + dv->st[ 0 ] = s->vecs[ 0 ][ 3 ] + DotProduct( s->vecs[ 0 ], vTranslated ); + dv->st[ 1 ] = s->vecs[ 1 ][ 3 ] + DotProduct( s->vecs[ 1 ], vTranslated ); + dv->st[ 0 ] /= si->shaderWidth; + dv->st[ 1 ] /= si->shaderHeight; + +*/ + +// function to test spawnflags +inline bool spawnflag_isset(unsigned int spawnflags, unsigned int flag) +{ + return ((spawnflags & flag) == flag); +} + +// max geometry bounds +const float MAX_BOUNDS = 16384; + +const float MIN_DELTA = 10e-10; + +MapFile::MapFile() : map_center(0,0,0) +{ + mapfile_name.clear(); + map_brushes = 0; + map_faces = 0; + map_faces_detail = 0; + + warning_q2brush = false; +} + +MapFile::~MapFile() +{ + clear_materials(); +} + +void MapFile::clear_materials() +{ + for (Materials::iterator mit = map_materials.begin(); mit != map_materials.end(); mit++) { + // delete list of primitives + delete(*mit).second; + } + map_materials.clear(); +} + +bool MapFile::open(std::string const & mapname) +{ + warning_q2brush = false; + last_read_was_classname = false; + last_read_was_key = false; + key_current = ""; + value_current = ""; + classname_current = ""; + line_number = 0; + parse_level = 0; + + clear_materials(); + + mapfile_name.append(mapname); + mapfile_name.append(".map"); + + mapfile_ifs.open(mapfile_name); + if (!mapfile_ifs.is_open()) { + return false; + } + return true; +} + + +bool MapFile::got_classname() const +{ + return last_read_was_classname; +} + +bool MapFile::got_classname(const char * classnamelabel) const +{ + return (last_read_was_classname && (classname_current.compare(classnamelabel) == 0)); +} + +bool MapFile::got_classend(const char * classnamelabel) const +{ + return (last_read_was_classend && (classname_current.compare(classnamelabel) == 0)); +} + +bool MapFile::getline() +{ + using math::Vector3f; + + last_read_was_classname = false; + last_read_was_key = false; + last_read_was_classend = false; + + key_current = ""; + value_current = ""; + + if (!mapfile_ifs.is_open()) + return false; + + char data[1024]; + memset(data, 0, sizeof(data)); + + if (mapfile_ifs.getline(data, 1023)) { + line_number++; + std::istringstream linestream(data); + std::string firstword; + + if (linestream >> firstword) { + if (!firstword.size()) { + return true; + + } else if (firstword == "//") { + return true; + + } else if (firstword == "{") { + parse_level++; + + } else if (firstword == "}") { + if ((parse_level == 2) && (planes.size())) { + // end-of-brush + + // for every face + for (std::vector::iterator face = planes.begin(); face != planes.end(); face++) { + make_brushface((*face)); + } + + // clean planes + for (std::vector::iterator it = planes.begin(); it != planes.end(); it++) { + delete(*it); + } + planes.clear(); + + map_brushes++; + + value_current.clear(); + + } else if ((parse_level == 1)) { + // end-of-class + last_read_was_classend = true; + } + + parse_level--; + + } else if (parse_level == 1) { + + if (firstword == "\"classname\"") { + classname_current.clear(); + + if (linestream >> classname_current) { + if (classname_current.size() > 2) { + classname_current.erase(0,1); + classname_current.erase(classname_current.size()-1, 1); + last_read_was_classname = true; + } else { + classname_current.clear(); + } + } + + } else if ((firstword.size() > 2) && (firstword[0] == '\"') && (firstword[firstword.size()-1] == '\"')) { + + key_current.assign(firstword); + key_current.erase(0,1); + key_current.erase(key_current.size()-1, 1); + + value_current.clear(); + char c; + while ((linestream.get(c)) && (c != '"')); + while ((linestream.get(c)) && (c != '"')) + value_current += c; + + last_read_was_key = true; + } + + } else if (parse_level == 2) { + + if (firstword == "(") { + // brush plane + + Vector3f p1, p2, p3; + std::string tmp; + std::string texture; + int n = 0; + + linestream >> p1; + linestream >> tmp; // ) + linestream >> tmp; // ( + linestream >> p2; + linestream >> tmp; // ) + linestream >> tmp; // ( + linestream >> p3; + linestream >> tmp; // ) + + Face *face = new Face(p1, p2, p3); + + // material + linestream >> texture; + aux::to_lowercase(texture); + + Material *material = Material::find("textures/" + texture); + if (!material) { + material = new Material("textures/" + texture); + Material::add(material); + material->set_flags(Material::Texture); + material->set_texture(material->name()); + } + face->set_material(material); + + // texture alignment + float tx, ty, tr, tsx, tsy; + linestream >> tx >> ty; // texture translation + linestream >> tr; // texture rotation angle + linestream >> tsx >> tsy; // texture scale + /* + face->tex_translate().assign(tx, ty); + face->tex_rotate() = tr; + face->tex_scale().assign(tsx, tsy); + */ + + // from radiant: tools/quake3/q3map2map.c + tsx -= (floor( tsx / material->size().width()) * material->size().width()); + tsy -= (floor( tsy / material->size().height()) * material->size().height()); + + // store the texture transformation for this face + face_texture_verts((*face), math::Vector2f(tx, ty), tr, math::Vector2f(tsx, tsy)); + + // content flags + if (!(linestream >> n)) + n = 0; + if (n > 0) + face->detail() = true; + + // surface flags + if (!(linestream >> n)) { + n = 0; + warning_q2brush = true; + } + face->surface_flags() = n; + + + planes.push_back(face); + + value_current.clear(); + } + } + } + } else { + + return false; + } + + return true; +} + +void MapFile::make_brushface(Face *face) +{ + using math::Vector3f; + + // ignore materials with the 'Ignore' flag set + if ((face->material()->flags() & Material::Ignore) == Material::Ignore) { + return; + } + + // statistics + map_faces++; + + if (face->detail()) { + map_faces_detail++; + } + + // using suggestions from + // http://www.flipcode.com/archives/Level_Editing.shtml + + // vertex list + std::vector vl; + + // calculate initial vertices on the bounding box + + // check if the face is x-axis oriented + if ((fabsf(face->normal().x) >= fabsf(face->normal().y)) && (fabsf(face->normal().x) >= fabsf(face->normal().z))) { + + if (face->normal().x > MIN_DELTA) { + vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, -MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, MAX_BOUNDS, MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, MAX_BOUNDS, -MAX_BOUNDS)); + } else { + vl.push_back(new math::Vector3f(0, MAX_BOUNDS, -MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, MAX_BOUNDS, MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, MAX_BOUNDS)); + vl.push_back(new math::Vector3f(0, -MAX_BOUNDS, -MAX_BOUNDS)); + } + // calculate the x coordinate of each face vertex + for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { + (*it)->x = (-face->d() - + face->normal().z * (*it)->z - + face->normal().y * (*it)->y) / + face->normal().x; + } + } + + // check if the face is y-axis oriented + else if ((fabsf(face->normal().y) >= fabsf(face->normal().x)) && (fabsf(face->normal().y) >= fabsf(face->normal().z))) { + + if (face->normal().y > MIN_DELTA) { + vl.push_back(new Vector3f(MAX_BOUNDS, 0, -MAX_BOUNDS)); + vl.push_back(new Vector3f(MAX_BOUNDS, 0, MAX_BOUNDS)); + vl.push_back(new Vector3f(-MAX_BOUNDS, 0, MAX_BOUNDS)); + vl.push_back(new Vector3f(-MAX_BOUNDS, 0, -MAX_BOUNDS)); + } else { + vl.push_back(new Vector3f(-MAX_BOUNDS, 0, -MAX_BOUNDS)); + vl.push_back(new Vector3f(-MAX_BOUNDS, 0, MAX_BOUNDS)); + vl.push_back(new Vector3f(MAX_BOUNDS, 0, MAX_BOUNDS)); + vl.push_back(new Vector3f(MAX_BOUNDS, 0, -MAX_BOUNDS)); + } + + // calculate the x coordinate of each face vertex + for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { + (*it)->y = (-face->d() - + face->normal().z * (*it)->z - + face->normal().x * (*it)->x) / + face->normal().y; + } + } + + // face must be z-axis oriented + else { + + if (face->normal().z > MIN_DELTA) { + vl.push_back(new Vector3f(-MAX_BOUNDS, -MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(-MAX_BOUNDS, MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(MAX_BOUNDS, MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(MAX_BOUNDS, -MAX_BOUNDS, 0)); + } else { + vl.push_back(new Vector3f(MAX_BOUNDS, -MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(MAX_BOUNDS, MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(-MAX_BOUNDS, MAX_BOUNDS, 0)); + vl.push_back(new Vector3f(-MAX_BOUNDS, -MAX_BOUNDS, 0)); + } + + // calculate the x coordinate of each face vertex + for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { + (*it)->z = (-face->d() - + face->normal().x * (*it)->x - + face->normal().y * (*it)->y) / + face->normal().z; + } + } + + + // intersect the face with every plane + for (std::vector::iterator pit = planes.begin(); pit != planes.end(); pit++) { + + Face *plane = (*pit); + if (plane == face) { + continue; + } + + Vector3f fn = crossproduct(face->point(1)-face->point(0), face->point(2)-face->point(0)); + Vector3f pn = crossproduct(plane->point(1)-plane->point(0), plane->point(2)-plane->point(0)); + + Vector3f t = crossproduct(fn, pn); + if ((t.x == 0) && (t.y == 0) && (t.z == 0)) { + continue; + } + + // intersect face with plane + for (int i=0; vl.size() - i > 0; i++) { + + Vector3f v(*vl.at(i)); + + Vector3f next; + if (vl.size() - i > 1) { + next = *vl.at(i+1); + } else { + next = *vl.front(); + } + + Vector3f prev; + if (i > 0) { + prev = *vl.at(i-1); + } else { + prev = *vl.back(); + } + + if ((v.x*plane->normal().x + v.y*plane->normal().y + v.z*plane->normal().z +plane->d()) < MIN_DELTA) { + + // find current + std::vector::iterator vit = vl.begin(); + while ((*vit) != vl.at(i)) { + vit++; + } + + // check if prev - v intersects with plane + if ((prev.x*plane->normal().x + prev.y*plane->normal().y + prev.z*plane->normal().z + plane->d()) > MIN_DELTA) { + + // calculate intersection + float t1 = -plane->normal().x * prev.x - plane->normal().y * prev.y - plane->normal().z * prev.z -plane->d(); + float t2 = (plane->normal().x * v.x - plane->normal().x * prev.x + + plane->normal().y * v.y - plane->normal().y * prev.y + + plane->normal().z * v.z - plane->normal().z * prev.z); + + Vector3f *s = new Vector3f; + + if (t2 == 0) { + *s = v; + } else { + for (int j = 0; j < 3; j++) + (*s)[j] = prev [j] + t1 * (v[j] - prev[j]) / t2; + } + + vit = vl.insert(vit,s); + vit++; + i++; + } + + // check if next - v intersects with plane + if ((next.x*plane->normal().x + next.y*plane->normal().y + next.z*plane->normal().z + plane->d()) > MIN_DELTA) { + + // calculate intersection + float t1 = -plane->normal().x * v.x - plane->normal().y * v.y - plane->normal().z * v.z -plane->d(); + float t2 = (plane->normal().x * next.x - plane->normal().x * v.x + + plane->normal().y * next.y - plane->normal().y * v.y + + plane->normal().z * next.z - plane->normal().z * v.z); + //cout << "next t2 " << t2 << std::endl; + Vector3f *s = new Vector3f; + + if (t2 == 0) { + *s = v; + } else { + for (int j = 0; j < 3; j++) + (*s)[j] = v [j] + t1 * (next[j] - v[j]) / t2; + } + + vit = vl.insert(vit,s); + vit++; + i++; + } + + // erase + delete *vit; + vl.erase(vit); + i--; + } + + } + } + + if (vl.size() > 2) { + + // find the list if primitives for the current material, allocate a new one if necessary + Primitives *primitives = 0; + + Materials::iterator mit = map_materials.find(face->material()); + if (mit == map_materials.end()) { + primitives = new Primitives(face->material()); + map_materials[face->material()] = primitives; + } else { + primitives = (*mit).second; + } + + // scale vertices and calculate the bounding box + for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { + //*(*it) *= SCALE; + for (int i=0; i < 3; i++) { + if (class_maxbbox[i] < (*(*it))[i] * SCALE) + class_maxbbox[i] = (*(*it))[i] * SCALE; + + if (class_minbbox[i] > (*(*it))[i] * SCALE) + class_minbbox[i] = (*(*it))[i] * SCALE; + } + } + +/* +#ifndef HAVE_BULLET + + // Quads are disable to use model data for bullet physics + + // split polygon into quads + while (vl.size() > 3) { + std::vector::iterator v0 = vl.begin(); + std::vector::reverse_iterator vn = vl.rbegin(); + std::vector::reverse_iterator vn1 = vl.rbegin(); + ++vn1; + std::vector::reverse_iterator vn2 = vl.rbegin(); + ++vn2; + ++vn2; + + Vector3f n(face->normal()*-1); + n.normalize(); + + primitives->add_quad(*(*vn2), *(*vn1), *(*vn), *(*v0), n, face->detail()); + delete(*vn); + delete(*vn1); + vl.pop_back(); + vl.pop_back(); + } +#endif +*/ + // split polygon into triangles + while (vl.size() > 2) { + std::vector::iterator v0 = vl.begin(); + std::vector::reverse_iterator vn = vl.rbegin(); + std::vector::reverse_iterator vn1 = vl.rbegin(); + ++vn1; + + Vector3f n(face->normal()*-1); + n.normalize(); + + Triangle *triangle = primitives->add_triangle(*(*vn1) * SCALE, *(*vn) * SCALE, *(*v0) * SCALE, n, face->detail()); + + if (face->material()->flags() & Material::Texture) { + triangle->t0().assign(map_texture_coords(face, *(*vn1))); + triangle->t1().assign(map_texture_coords(face, *(*vn))); + triangle->t2().assign(map_texture_coords(face, *(*v0))); + } + /* + triangle->t0().assign(0, 0); + triangle->t1().assign(0, 1); + triangle->t2().assign(1, 1); + */ + delete(*vn); + vl.pop_back(); + } + } else { + con_debug << "Unresolved face!\n"; + } + + // clean up the vertex list + for (std::vector::iterator it = vl.begin(); it != vl.end(); it++) { + delete(*it); + } + + vl.clear(); +} + +bool MapFile::got_key_string(const char * keylabel, std::string & valuestring) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + valuestring.assign(value_current); + return true; + } else { + return false; + } +} + +bool MapFile::got_key_vector3f(const char * keylabel, math::Vector3f & v) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + std::istringstream is(value_current); + float x, y, z; + if ((is >> x) && (is >> y) && (is >> z)) { + v = math::Vector3f(x,y,z); + } else { + v= math::Vector3f(); + } + return true; + } else { + return false; + } +} + +bool MapFile::got_key_float(const char * keylabel, float & f) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + std::istringstream is(value_current); + if (!(is >> f)) { + f = 0; + } + return true; + } else { + return false; + } +} + +bool MapFile::got_key_int(const char * keylabel, unsigned int & u) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + std::istringstream is(value_current); + if (!(is >> u)) { + u = 0; + } + return true; + } else { + return false; + } +} + +bool MapFile::got_key(const char * keylabel) +{ + return (last_read_was_key && (key_current.compare(keylabel) == 0)); +} + +bool MapFile::got_key_angle(const char * keylabel, float & f) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + std::istringstream is(value_current); + if ((is >> f)) { + f = math::degrees360f(f); + } else { + f = 0; + } + return true; + } else { + return false; + } +} + +bool MapFile::got_key_color(const char * keylabel, math::Color & color) +{ + if (last_read_was_key && (key_current.compare(keylabel) == 0)) { + std::istringstream is(value_current); + float r, g, b; + if ((is >> r) && (is >> g) && (is >> b)) { + if ((r > 1) || (g > 1) || (b > 1)) { + r /= 255; + g /= 255; + b /= 255; + } + color = math::Color(r, g, b); + } else { + color = math::Color(); + } + return true; + } else { + return false; + } +} + +void MapFile::close() +{ + mapfile_ifs.close(); +} + +void MapFile::clear_bbox() +{ + for (int i=0; i < 3; i++) { + class_minbbox[i] = MAX_BOUNDS; + class_maxbbox[i] = -MAX_BOUNDS; + } + + class_axis.clear(); + class_speed = 0; +} + +void MapFile::load_worldspawn(Model *model) +{ + if (!map_materials.size()) + return; + + // FIXME center in maps without brushes + map_center = (class_minbbox + class_maxbbox) / 2.0f; + + model->model_minbbox = class_minbbox - map_center; + model->model_maxbbox = class_maxbbox - map_center; + + model->model_radius = model->model_maxbbox.length(); + + load_fragmentgroup(model, FragmentGroup::None); +} + +void MapFile::load_fragmentgroup(Model *model, const FragmentGroup::Type class_type) +{ + if (!VertexArray::instance() || VertexArray::instance()->overflow()) + return; + + if (!map_materials.size()) + return; + + FragmentGroup *group = new FragmentGroup(); + + if (class_type == FragmentGroup::Rotate) { + if (class_speed == 0) { + // default rotation speed 45 degrees per second + class_speed = 45.0f; + } + } + + if (class_type != FragmentGroup::None) { + group->set_transform(true); + } + + group->set_type(class_type); + group->set_location((class_minbbox + class_maxbbox) / 2.0f - map_center); + group->set_axis(class_axis); + group->set_speed(class_speed); + + math::Vector3f group_center(map_center); + if (group->transform()) { + group_center += group->location(); + } + + for (Materials::iterator mit = map_materials.begin(); mit != map_materials.end(); mit++) { + // split the Primitives with this material into fragments + Primitives *primitives = (*mit).second; + + // store triangles + if (primitives->triangles().size()) { + Fragment *fragment = new Fragment(Fragment::Triangles, primitives->material()); + + // add structural triangles to the fragment + for (Primitives::Triangles::iterator tris_it = primitives->triangles().begin(); tris_it != primitives->triangles().end(); tris_it++) { + Triangle *triangle = (*tris_it); + if (!triangle->detail()) { + size_t count = 0; + count += fragment->add_vertex(triangle->v0()-group_center, triangle->normal(), triangle->t0(), false); + count += fragment->add_vertex(triangle->v1()-group_center, triangle->normal(), triangle->t1(), false); + count += fragment->add_vertex(triangle->v2()-group_center, triangle->normal(), triangle->t2(), false); + if (count == 3) + model->model_tris_count++; + } + } + + // add detail triangles to the fragment + for (Primitives::Triangles::iterator tris_it = primitives->triangles().begin(); tris_it != primitives->triangles().end(); tris_it++) { + Triangle *triangle = (*tris_it); + if (triangle->detail()) { + size_t count = 0; + count += fragment->add_vertex(triangle->v0()-group_center, triangle->normal(), triangle->t0(), true); + count += fragment->add_vertex(triangle->v1()-group_center, triangle->normal(), triangle->t1(), true); + count += fragment->add_vertex(triangle->v2()-group_center, triangle->normal(), triangle->t2(), true); + if (count == 3) { + model->model_tris_count++; + model->model_tris_detail_count++; + } + } + } + + // add the fragment to the group + group->add_fragment(fragment); + } + + // store quads + if (primitives->quads().size()) { + Fragment *fragment = new Fragment(Fragment::Quads, primitives->material()); + + // add structural triangles to the fragment + for (Primitives::Quads::iterator quad_it = primitives->quads().begin(); quad_it != primitives->quads().end(); quad_it++) { + Quad *quad = (*quad_it); + if (!quad->detail()) { + size_t count = 0; + count += fragment->add_vertex(quad->v0()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v1()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v2()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v3()-group_center, quad->normal(), false); + if (count == 4) + model->model_quad_count++; + } + } + + // add detail triangles to the fragment + for (Primitives::Quads::iterator quad_it = primitives->quads().begin(); quad_it != primitives->quads().end(); quad_it++) { + Quad *quad = (*quad_it); + if (quad->detail()) { + size_t count = 0; + count += fragment->add_vertex(quad->v0()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v1()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v2()-group_center, quad->normal(), false); + count += fragment->add_vertex(quad->v3()-group_center, quad->normal(), false); + if (count == 4) { + model->model_quad_count++; + model->model_quad_detail_count++; + } + } + } + + // add the fragment to the group + group->add_fragment(fragment); + } + + } + + // add the group to the model + model->add_group(group); +} + +void MapFile::unknown_value() const +{ + con_warn << name() << " unknown value '" << value() << "' for '" << classname() << ":" << key() << "' at line " << line() << std::endl; +} + +void MapFile::unknown_key() const +{ + con_warn << name() << " unknown key '" << classname() << ":" << key() << "' at line " << line() << std::endl; +} + +void MapFile::unknown_class() const +{ + con_warn << name() << " unknown class '" << classname() << "' at line " << line() << std::endl; +} + +Model * MapFile::load(std::string const &name) +{ + // open the .map file + MapFile mapfile; + + if (!mapfile.open(name)) { + return 0; + } + + Model *model = new Model(name); + mapfile.clear_bbox(); + + Dock *dock = 0; + Particles *particles = 0; + Flare *flare = 0; + Light *light = 0; + SubModel *submodel = 0; + + std::string modelname; + math::Vector3f location; + + typedef std::list SubModelList; + SubModelList submodel_list; + + unsigned int u; + float angle; + float r, s; + std::string str; + + while (mapfile.getline()) { + + if (mapfile.got_classname("worldspawn")) { + mapfile.clear_bbox(); + + } else if (mapfile.got_classend("worldspawn")) { + mapfile.load_worldspawn(model); + mapfile.clear_materials(); + + } else if (mapfile.in_class("worldspawn")) { + + // worldspawn attributes + if (mapfile.got_key("name")) { + con_debug << " model name '" << name << "'" << std::endl; + + } else if (mapfile.got_key_int("enginesound", u)) { + model->model_enginesound = u; + continue; + + } else if (mapfile.got_key_int("impulsesound", u)) { + model->model_impulsesound = u; + continue; + + } else if (mapfile.got_key_color("enginecolor", model->model_enginecolor)) { + continue; + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + + } + + } else if (mapfile.got_classname("func_door")) { + mapfile.clear_bbox(); + + } else if (mapfile.got_classend("func_door")) { + mapfile.load_fragmentgroup(model, FragmentGroup::Door); + mapfile.clear_materials(); + + } else if (mapfile.in_class("func_door")) { + + } else if (mapfile.got_classname("func_group")) { + mapfile.clear_bbox(); + + } else if (mapfile.got_classend("func_group")) { + mapfile.load_fragmentgroup(model, FragmentGroup::None); + mapfile.clear_materials(); + + } else if (mapfile.got_classname("func_rotate")) { + mapfile.clear_bbox(); + + } else if (mapfile.got_classend("func_rotate")) { + mapfile.load_fragmentgroup(model, FragmentGroup::Rotate); + mapfile.clear_materials(); + + } else if (mapfile.in_class("func_rotate")) { + + if (mapfile.got_key_float("angle", angle)) { + if (angle == ANGLEUP) { + mapfile.class_axis.change_pitch(90.0f); + } else if (angle == ANGLEDOWN) { + mapfile.class_axis.change_pitch(-90.0f); + } else { + mapfile.class_axis.change_direction(angle); + } + } else if (mapfile.got_key_float("direction", angle)) { + mapfile.class_axis.change_direction(angle); + + } else if (mapfile.got_key_float("pitch", angle)) { + mapfile.class_axis.change_pitch(angle); + + } else if (mapfile.got_key_float("roll", angle)) { + mapfile.class_axis.change_roll(angle); + + } else if (mapfile.got_key_float("speed", mapfile.class_speed)) { + continue; + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + } + + } else if (mapfile.got_classend()) { + mapfile.clear_materials(); + + } else if (mapfile.got_classname("light")) { + + // new light + light = new Light(); + model->add_light(light); + + } else if (mapfile.classname().compare("light") == 0) { + + // light attributes + if (mapfile.got_key_vector3f("origin", light->light_location)) { + light->light_location *= SCALE; + continue; + + } else if (mapfile.got_key_color("_color", light->light_color)) { + continue; + + } else if (mapfile.got_key_int("spawnflags", u)) { + light->light_strobe = spawnflag_isset(u, 1); + light->light_entity = spawnflag_isset(u, 2); + + } else if (mapfile.got_key_float("light", light->light_radius)) { + light->light_radius *= LIGHTSCALE; + + } else if (mapfile.got_key_float("radius", light->light_radius)) { + light->light_radius *= LIGHTSCALE; + + } else if (mapfile.got_key_float("frequency", light->light_frequency)) { + continue; + + } else if (mapfile.got_key_float("offset", light->light_offset)) { + continue; + + } else if (mapfile.got_key_float("time", light->light_time)) { + continue; + + } else if (mapfile.got_key_int("flare", light->light_flare)) { + continue; + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + + } + + } else if (mapfile.got_classname("location_dock")) { + + // new docking location + dock = new Dock(); + model->add_dock(dock); + + } else if (mapfile.classname().compare("location_dock") == 0) { + + // dock attributes + if (mapfile.got_key_vector3f("origin", dock->dock_location)) { + dock->dock_location *= SCALE; + continue; + + } else if (mapfile.got_key_float("radius", dock->dock_radius)) { + dock->dock_radius *= SCALE; + continue; + + } else if (mapfile.got_key("angle")) { + continue; + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + + } + + } else if (mapfile.got_classname("location_cannon")) { + // new cannon + + } else if (mapfile.classname().compare("location_cannon") == 0) { + + } else if (mapfile.got_classname("location_turret")) { + // new turret + + } else if (mapfile.classname().compare("location_turret") == 0) { + + } else if (mapfile.got_classname("location_cockpit")) { + // cockpit location + + } else if (mapfile.classname().compare("location_cockpit") == 0) { + + } else if (mapfile.got_classname("fx_flare")) { + + // new flare + flare = new Flare(); + model->add_flare(flare); + + } else if (mapfile.classname().compare("fx_flare") == 0) { + + // flare attributes + if (mapfile.got_key_vector3f("origin", flare->light_location)) { + flare->light_location *= SCALE; + continue; + + } else if (mapfile.got_key_color("_color", flare->light_color)) { + continue; + + } else if (mapfile.got_key_int("spawnflags", u)) { + flare->light_strobe = spawnflag_isset(u, 1); + flare->light_entity = spawnflag_isset(u, 2); + flare->flare_engine = spawnflag_isset(u, 4); + + } else if (mapfile.got_key_float("radius", flare->light_radius)) { + flare->light_radius *= LIGHTSCALE; + + } else if (mapfile.got_key_float("frequency", flare->light_frequency)) { + continue; + + } else if (mapfile.got_key_float("offset", flare->light_offset)) { + continue; + + } else if (mapfile.got_key_float("time", flare->light_time)) { + continue; + + } else if (mapfile.got_key_int("flare", flare->light_flare)) { + continue; + + } else if (mapfile.got_key_float("angle", angle)) { + if (angle == ANGLEUP) { + flare->flare_axis.change_pitch(90.0f); + } else if (angle == ANGLEDOWN) { + flare->flare_axis.change_pitch(-90.0f); + } else { + flare->flare_axis.change_direction(angle); + } + } else if (mapfile.got_key_float("direction", angle)) { + flare->flare_axis.change_direction(angle); + + } else if (mapfile.got_key_float("pitch", angle)) { + flare->flare_axis.change_pitch(angle); + + } else if (mapfile.got_key_float("roll", angle)) { + flare->flare_axis.change_roll(angle); + + } else if (mapfile.got_key_string("cull", str)) { + + aux::to_lowercase(str); + if (str.compare("none") == 0) { + flare->flare_cull = CullNone; + } else if (str.compare("back") == 0) { + flare->flare_cull = CullBack; + } else if (str.compare("front") == 0) { + flare->flare_cull = CullFront; + } else { + mapfile.unknown_value(); + } + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + } + + } else if (mapfile.got_classname("misc_model")) { + + // new submodel + submodel = new SubModel(); + submodel_list.push_back(submodel); + + } else if (mapfile.classname().compare("misc_model") == 0) { + + // submodel attributes + if (mapfile.got_key_vector3f("origin", location)) { + submodel->set_location(location * SCALE); + continue; + + } else if (mapfile.got_key_string("model", modelname)) { + + // remove extension + if (modelname[modelname.size()-4] == '.') { + modelname.erase(modelname.size()-4); + } + + submodel->set_name(modelname); + continue; + + } else if (mapfile.got_key_float("angle", angle)) { + if (angle == ANGLEUP) { + submodel->axis().change_pitch(90.0f); + } else if (angle == ANGLEDOWN) { + submodel->axis().change_pitch(-90.0f); + } else { + submodel->axis().change_direction(angle); + } + + } else if (mapfile.got_key_float("modelscale", s)) { + if (s) { + submodel->set_scale(s); + } else { + submodel->set_scale(1.0f); + } + } + + } else if (mapfile.got_classname("fx_particles")) { + + // new particle system + particles = new Particles(); + model->add_particles(particles); + + } else if (mapfile.classname().compare("fx_particles") == 0) { + + // particle system attributes + if (mapfile.got_key_vector3f("origin", particles->particles_location)) { + particles->particles_location *= SCALE; + continue; + } else if (mapfile.got_key_string("script", particles->particles_script)) { + continue; + } else if (mapfile.got_key_float("angle", angle)) { + if (angle == ANGLEUP) { + particles->particles_axis.change_pitch(90.0f); + } else if (angle == ANGLEDOWN) { + particles->particles_axis.change_pitch(-90.0f); + } else { + particles->particles_axis.change_direction(angle); + } + } else if (mapfile.got_key_float("direction", angle)) { + particles->particles_axis.change_direction(angle); + + } else if (mapfile.got_key_float("pitch", angle)) { + particles->particles_axis.change_pitch(angle); + + } else if (mapfile.got_key_float("roll", angle)) { + particles->particles_axis.change_roll(angle); + + } else if (mapfile.got_key_int("spawnflags", u)) { + particles->particles_entity = spawnflag_isset(u, 2); + particles->particles_engine = spawnflag_isset(u, 4); + + } else if (mapfile.got_key_float("radius", r)) { + particles->set_radius(r * LIGHTSCALE); + + } else if (mapfile.got_key_string("cull", str)) { + + aux::to_lowercase(str); + if (str.compare("none") == 0) { + particles->particles_cull = CullNone; + } else if (str.compare("back") == 0) { + particles->particles_cull = CullBack; + } else if (str.compare("front") == 0) { + particles->particles_cull = CullFront; + } else { + mapfile.unknown_value(); + } + + } else if (mapfile.got_key()) { + mapfile.unknown_key(); + } + } else if (mapfile.got_classname()) { + mapfile.unknown_class(); + } + } + + mapfile.close(); + + // reposition docks, lights, flares and particles according to the model center + for (Model::Lights::iterator lit = model->lights().begin(); lit != model->lights().end(); lit++) { + (*lit)->light_location -= mapfile.map_center; + } + + for (Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) { + (*flit)->light_location -= mapfile.map_center; + } + + for (Model::ParticleSystems::iterator pit = model->particles().begin(); pit != model->particles().end(); pit++) { + (*pit)->particles_location -= mapfile.map_center; + } + + for (Model::Docks::iterator dit = model->docks().begin(); dit != model->docks().end(); dit++) { + (*dit)->dock_location -= mapfile.map_center; + } + + // FIXME this will go wrong if a Rotate group is imported as submodel + for (SubModelList::iterator smit = submodel_list.begin(); smit != submodel_list.end(); smit++) { + submodel = (*smit); + Model *submodel_model = 0; + + if (submodel->name().size()) { + submodel_model = Model::load(submodel->name()); + } + + if (submodel_model) { + // copy fragmentgroups + for (Model::Groups::iterator git = submodel_model->groups().begin(); git != submodel_model->groups().end(); git++) { + FragmentGroup *groupsrc = (*git); + FragmentGroup *groupdst = new FragmentGroup(); + + groupdst->set_transform(true); + groupdst->set_type(groupsrc->type()); + groupdst->set_scale(groupsrc->scale() * submodel->scale()); + groupdst->set_speed(groupsrc->speed()); + groupdst->set_location(groupsrc->location() + submodel->location() - mapfile.map_center); + groupdst->set_axis(groupsrc->axis() * submodel->axis()); + + // copy fragments + for (FragmentGroup::iterator fit = groupsrc->begin(); fit != groupsrc->end(); fit++) { + Fragment *fragmentdst = new Fragment(*(*fit)); + groupdst->add_fragment(fragmentdst); + } + + if (groupdst->size()) { + model->add_group(groupdst); + } else { + delete groupdst; + } + } + + con_debug << " imported submodel '" << submodel->name() << "'" << std::endl; + } + + delete submodel; + + } + + con_debug << " " << mapfile.name() << " " << mapfile.map_brushes << " brushes " << + mapfile.map_faces << "/" << mapfile.map_faces_detail << " faces/detail " << std::endl; + if (mapfile.warning_q2brush) + con_warn << " quake2 style brushes detected" << std::endl; + + return model; +} + +} + diff --git a/src/model/mapfile.h b/src/model/mapfile.h new file mode 100644 index 0000000..fd6bf48 --- /dev/null +++ b/src/model/mapfile.h @@ -0,0 +1,189 @@ +/* + model/map.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_MODEL_MAPFILE_H__ +#define __INCLUDED_MODEL_MAPFILE_H__ + +#include +#include + +#include "model/material.h" +#include "model/model.h" +#include "model/face.h" +#include "model/primitives.h" +#include "filesystem/filestream.h" + +namespace model +{ + +/// class to parse the .map file structure and load geometry data into a model +class MapFile +{ + +public: + /** + * @brief load a .map file into a Model + * @param name name of the model to be loaded, without .map extension or maps/ prefix + * If the file can not be read, load() returns the NULL-pointer + */ + static Model *load(std::string const &name); + +private: + MapFile(); + ~MapFile(); + + /// tpye definition for a per-material list of Primitives + typedef std::map Materials; + + /// open the file for reading + /** the filename will get the "maps/" prefix and ".map" suffix + */ + bool open(std::string const & name); + + /// parse one line, returns false on end-of-file + bool getline(); + + /// current classname + inline std::string classname() const + { + return classname_current; + } + + /// current key + inline std::string key() const + { + return key_current; + } + + /// current value + inline std::string value() const + { + return value_current; + } + + /// true if the last read line contained a new classname + bool got_classname() const; + + /// true if we are inside the requested class + bool in_class(const char *name) { + return (classname_current.compare(name) == 0); + } + + /// true if the last read line contained a class closing bracket + inline bool got_classend() const + { + return last_read_was_classend; + } + + /// true if the last read line contained the closing bracket for the requested class + bool got_classend(const char*) const; + + /// true if the last read line contained a new classname + bool got_classname(const char*) const; + + /// true if the last read statement was a key=value pair + inline bool got_key() const + { + return last_read_was_key; + } + + bool got_key(const char * keylabel); + + /// check if the last read key=value pair matches keylabel and store the value in valuestring + bool got_key_string(const char * keylabel, std::string & valuestring); + + /// check if the last read key=value pair matches keylabel and store the value in color + bool got_key_color(const char * keylabel, math::Color & color); + + /// check if the last read key=value pair matches keylabel and store the value in f + bool got_key_float(const char * keylabel, float & f); + + /// check if the last read key=value pair matches keylabel and store the value in f + bool got_key_int(const char * keylabel, unsigned int & u); + + /// check if the last read key=value pair matches keylabel and store the value in valuestring + bool got_key_angle(const char * keylabel, float & f); + + bool got_key_vector3f(const char * keylabel, math::Vector3f & v); + + + /// return the number of lines read so far + inline unsigned int line() const + { + return line_number; + } + + /// return true of the map file is open for reading + inline bool is_open() + { + return mapfile_ifs.is_open(); + } + + /// current filename + inline std::string const & name() const + { + return mapfile_name; + } + + /// close the file + void close(); + + /// generate triangles for one plane in the plane list + void make_brushface(Face *face); + + /// load parsed primitives into model worldspawn + void load_worldspawn(Model *model); + + /// load parsed primitives into a FragmentGroup + void load_fragmentgroup(Model *model, const FragmentGroup::Type class_type); + + /// clear the current list of per-material geometry + void clear_materials(); + + /// clear class bounding box + void clear_bbox(); + + void unknown_class() const; + + void unknown_key() const; + + void unknown_value() const; + + /// list of planes for the current brush + std::vector planes; + + + std::string classname_current; + std::string key_current; + std::string value_current; + + bool last_read_was_key; + bool last_read_was_classname; + bool last_read_was_classend; + + unsigned int map_brushes; + unsigned int map_faces; + unsigned int map_faces_detail; + + unsigned int parse_level; + unsigned int line_number; + filesystem::IFileStream mapfile_ifs; + std::string mapfile_name; + + math::Vector3f class_maxbbox; + math::Vector3f class_minbbox; + math::Axis class_axis; + float class_speed; + + math::Vector3f map_center; + Materials map_materials; + + bool warning_q2brush; +}; + +} + +#endif // __INCLUDED_MODEL_MAPFILE_H__ diff --git a/src/model/material.cc b/src/model/material.cc index c35740f..280ebba 100644 --- a/src/model/material.cc +++ b/src/model/material.cc @@ -15,11 +15,18 @@ namespace model { -Material::Material(const std::string &name) : material_name(name), material_color(1.0f) +Material::LoaderFuncPtr Material::material_loaderfunc = 0; +Material::Registry Material::material_registry; + +Material::Material(const std::string &name) : + material_name(name), + material_color(1.0f) , + material_size(64.0f, 64.0f) { aux::to_lowercase(material_name); material_flags = 0; material_texture_id = 0; + } Material::~Material() @@ -35,6 +42,9 @@ void Material::set_color(const math::Color &color) void Material::set_texture(const std::string &texture) { material_texture.assign(texture); + + if (material_loaderfunc) + material_loaderfunc(this); } void Material::set_texture_id(const size_t texture_id) @@ -42,7 +52,15 @@ void Material::set_texture_id(const size_t texture_id) material_texture_id = texture_id; } -Material::Registry Material::material_registry; +void Material::set_size(const float width, const float height) +{ + material_size.assign(width, height); +} + +void Material::set_size(const math::Vector2f &size) +{ + material_size.assign(size); +} void Material::init() { @@ -155,6 +173,8 @@ void Material::load_shader(const std::string &shadername) material->set_flags(Secondary); } else if (firstword.compare("entitythird") == 0) { material->set_flags(Tertiary); + } else if (firstword.compare("ignore") == 0) { + material->set_flags(Ignore); } else if (firstword.compare("qer_editorimage") == 0) { continue; } else if (firstword.compare("qer_trans") == 0) { @@ -163,7 +183,10 @@ void Material::load_shader(const std::string &shadername) // texture name should not contain spaces if (linestream >> firstword) { - // FIXME remve extension + // remove extension + if (firstword[firstword.size()-4] == '.') { + firstword.erase(firstword.size()-4); + } material->set_texture(firstword); material->set_flags(Material::Texture); } else { @@ -216,4 +239,9 @@ Material *Material::find(const std::string &name) return 0; } +void Material::set_loader_func(LoaderFuncPtr func) +{ + material_loaderfunc = func; +} + } diff --git a/src/model/material.h b/src/model/material.h index 9072a4d..9185fd2 100644 --- a/src/model/material.h +++ b/src/model/material.h @@ -11,6 +11,7 @@ #include #include "math/color.h" +#include "math/vector2f.h" namespace model { @@ -19,8 +20,11 @@ namespace model class Material { public: + /// function pointer type for local functions + typedef void(* LoaderFuncPtr)(Material *); + /// surface flags - enum SurfaceFlags { None=0, Primary=1, Secondary=2, Tertiary=3, Bright=4, Engine=8, Environment=16, Texture=32}; + enum SurfaceFlags { None=0, Primary=1, Secondary=2, Tertiary=3, Bright=4, Engine=8, Environment=16, Texture=32, Ignore=64}; /// type definition for the material registry typedef std::map Registry; @@ -41,14 +45,35 @@ public: inline const size_t texture_id() const { return material_texture_id; } + /** + * @brief returns the material texture size + */ + inline const math::Vector2f & size() const { return material_size; } + /* ---- mutators ------------------------------------------- */ void set_color(const math::Color &color); + /** + * @brief set the material texture name + */ void set_texture(const std::string &texture); + /** + * @brief set the material texture id + */ void set_texture_id(const size_t texture_id); + /** + * @brief set the material texture size + */ + void set_size(const float width, const float height); + + /** + * @brief set the material texture size + */ + void set_size(const math::Vector2f &size); + inline void set_flags(SurfaceFlags flags) { material_flags |= flags; @@ -92,6 +117,8 @@ public: */ static Material *find(const std::string &name); + static void set_loader_func(LoaderFuncPtr func); + private: std::string material_name; @@ -100,10 +127,15 @@ private: std::string material_texture; size_t material_texture_id; + /// size of the material + math::Vector2f material_size; + /// the materials registry static Registry material_registry; static void load_shader(const std::string &shadername); + + static LoaderFuncPtr material_loaderfunc; }; } diff --git a/src/model/model.cc b/src/model/model.cc index b02f127..1f9fcb5 100644 --- a/src/model/model.cc +++ b/src/model/model.cc @@ -5,9 +5,9 @@ */ #include "sys/sys.h" -#include "model/asefile.h" #include "model/model.h" -#include "model/map.h" +#include "model/asefile.h" +#include "model/mapfile.h" #include "model/vertexarray.h" namespace model @@ -112,7 +112,7 @@ Model *Model::load(std::string const & name) } if (!model) { - con_warn << "Could not open " << name << std::endl; + con_warn << "Could not open model " << name << std::endl; } else { model_registry[model->name()] = model; } diff --git a/src/model/model.h b/src/model/model.h index 6e9c5d8..2ca2734 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -12,7 +12,7 @@ #include #include "math/mathlib.h" -#include "model/classes.h" +#include "model/parts.h" #include "model/fragment.h" /// classes representing 3D geometry diff --git a/src/model/parts.cc b/src/model/parts.cc new file mode 100644 index 0000000..a4a2d1e --- /dev/null +++ b/src/model/parts.cc @@ -0,0 +1,87 @@ +/* + model/classes.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "model/parts.h" + +namespace model { + +/* ---- class Light ------------------------------------------------ */ + +Light::Light() : + light_location(), + light_color(1.0f, 1.0f, 1.0f) +{ + light_entity = false; + light_strobe = false; + light_radius = 1.0f; + light_frequency = 1.0f; + light_offset = 0.0f; + light_time = 0.5f; + light_flare = 0; + render_texture = 0; +} + +Light::~Light() +{} + +/* ---- class Flare ------------------------------------------------ */ + +Flare::Flare() : Light() +{ + flare_engine = false; + flare_cull = CullBack; +} + +Flare::~Flare() +{} + +/* ---- class Particles -------------------------------------------- */ + +Particles::Particles() : + particles_location() +{ + particles_entity = false; + particles_engine = false; + particles_radius = 0.0f; + particles_cull = CullNone; +} + +Particles::Particles(math::Vector3f const & location) : + particles_location(location) +{ +} + +Particles::~Particles() +{} + +void Particles::set_radius(const float radius) +{ + particles_radius = radius; +} + +/* ---- class Dock ------------------------------------------------- */ + +Dock::Dock() +{ + dock_radius = 0.01f; +} + +Dock::~Dock() +{ +} + +/* ---- class SubModel---------------------------------------------- */ + +SubModel::SubModel() +{ + submodel_scale = 1.0f; +} + +SubModel::~SubModel() +{ +} + +} diff --git a/src/model/parts.h b/src/model/parts.h new file mode 100644 index 0000000..8593147 --- /dev/null +++ b/src/model/parts.h @@ -0,0 +1,259 @@ +/* + model/classes.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_MODEL_PARTS_H__ +#define __INCLUDED_MODEL_PARTS_H__ + +#include "math/axis.h" +#include "math/color.h" +#include "math/vector3f.h" + +namespace model +{ + + +/** + * @brief + * culling parameter values + * Culling is a paremeter used by flares and particles to indicate + * with side of the polygons should be culled during rendering + */ +enum Cull { CullNone=0, CullBack=1, CullFront=2 }; + +/* ---- class Light ------------------------------------------------ */ + +/// an exterior light +class Light +{ +public: + Light(); + + Light(const math::Vector3f & location, const math::Color & color, bool strobe=false); + + ~Light(); + + inline const math::Vector3f & location() const + { + return light_location; + } + + inline const math::Color & color() const + { + return light_color; + }; + + /// true if this is a strobe light + inline bool strobe() const + { + return light_strobe; + } + + /// true if this light has entity color + inline bool entity() const + { + return light_entity; + } + + /// size of the light, default is 1.0f + inline float radius() const + { + return light_radius; + } + + /// strobe time offset, in seconds + inline float offset() const + { + return light_offset; + } + + /// frequency in strobes per second + inline float frequency() const + { + return light_frequency; + } + + /// fraction a strobe light will be on, default is 0.5f + inline float time() const + { + return light_time; + } + + /// flare texture number + inline unsigned int flare() const + { + return light_flare; + } + + /// render texture number + inline size_t texture() const + { + return render_texture; + } + + math::Vector3f light_location; + math::Color light_color; + bool light_strobe; + bool light_entity; + float light_radius; + float light_frequency; + float light_offset; + float light_time; + + unsigned int light_flare; + + size_t render_texture; +}; + +/* ---- class Flare ------------------------------------------------ */ + +/// a flare is a directional light +class Flare : public Light +{ +public: + Flare(); + ~Flare(); + + inline const math::Axis axis() const + { + return flare_axis; + } + + inline const bool engine() const + { + return flare_engine; + } + + inline const Cull cull() const + { + return flare_cull; + } + + math::Axis flare_axis; + bool flare_engine; + Cull flare_cull; +}; + +/* ---- class Particles -------------------------------------------- */ + +/// a particle system +class Particles +{ +public: + Particles(); + + Particles(const math::Vector3f & location); + ~Particles(); + + void set_radius(const float radius); + + inline const math::Vector3f & location() const + { + return particles_location; + } + + inline const math::Axis &axis() const + { + return particles_axis; + } + + inline const std::string & script() const + { + return particles_script; + } + + inline const bool entity() const + { + return particles_entity; + } + + inline const bool engine() const + { + return particles_engine; + } + + inline const float radius() const + { + return particles_radius; + } + + inline const Cull cull() const + { + return particles_cull; + } + + std::string particles_script; + math::Vector3f particles_location; + math::Axis particles_axis; + + bool particles_entity; + bool particles_engine; + + float particles_radius; + Cull particles_cull; +}; + +/* ---- class Dock ------------------------------------------------- */ + +/// a docking location +class Dock +{ +public: + Dock(); + ~Dock(); + + /// location of the dock + inline const math::Vector3f & location() const + { + return dock_location; + } + + /// trigger distance default is 0.01f + inline float radius() const + { + return dock_radius; + } + + + math::Vector3f dock_location; + float dock_radius; +}; + +/* ---- class SubModel --------------------------------------------- */ + +/// a submodel +class SubModel +{ +public: + SubModel(); + ~SubModel(); + + inline const std::string &name() const { return submodel_name; } + + inline const float scale() const { return submodel_scale; } + + inline const math::Vector3f &location() const { return submodel_location; } + + inline math::Axis &axis() { return submodel_axis; } + + inline void set_scale(const float scale) { submodel_scale = scale; } + + inline void set_name(const std::string &name) { submodel_name.assign(name); } + + inline void set_location(const math::Vector3f &location) { submodel_location.assign(location); } + + inline void set_axis(const math::Axis &axis) { submodel_axis.assign(axis); } + +private: + std::string submodel_name; + float submodel_scale; + math::Vector3f submodel_location; + math::Axis submodel_axis; +}; + +} + +#endif // __INCLUDED_MODEL_PARTS_H__ + diff --git a/src/model/plane.cc b/src/model/plane.cc deleted file mode 100644 index 6d80df6..0000000 --- a/src/model/plane.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - model/plane.cc - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#include - -#include "model/plane.h" - -namespace model -{ - -using math::Vector3f; -/* - * all points p(x, y, z) on the plane satisfy the general equation - * x*a() + y*b() + z*c() + d() = 0 - */ - -Plane::Plane(Vector3f const & point0, Vector3f const &point1, Vector3f const &point2) -{ - plane_detail = false; - plane_surface_flags = 0; - - plane_point[0] = point0; - plane_point[1] = point1; - plane_point[2] = point2; - - plane_normal = crossproduct((plane_point[1] - plane_point[0]) , (plane_point[2] - plane_point[0])); - pd = -1 * (plane_normal.x * plane_point[0].x + plane_normal.y * plane_point[0].y + plane_normal.z * plane_point[0].z); -} - -Plane::Plane(Plane const & other) -{ - for (size_t i=0; i < 3; i++) - this->plane_point[i] = other.plane_point[i]; - - plane_normal = crossproduct((plane_point[1] - plane_point[0]) , (plane_point[2] - plane_point[0])); - pd = -1 * (plane_normal.x * plane_point[0].x + plane_normal.y * plane_point[0].y + plane_normal.z * plane_point[0].z); -} - -} diff --git a/src/model/plane.h b/src/model/plane.h deleted file mode 100644 index 6c19a5a..0000000 --- a/src/model/plane.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - model/plane.h - This file is part of the Osirion project and is distributed under - the terms of the GNU General Public License version 2 -*/ - -#ifndef __INCLUDED_MODEL_PLANE_H__ -#define __INCLUDED_MODEL_PLANE_H__ - -#include - -#include "math/vector3f.h" - -namespace model -{ - -/** @brief A class representing a plane in 3d space - * all points p(x, y, z) on the plane satisfy the general equation - * x*a() + y*b() + z*c() + d() = 0 - */ -class Plane -{ -public: - /// a plane defined by 3 points in space - Plane(math::Vector3f const & point0, math::Vector3f const &point1, math::Vector3f const &point2); - /// copy constructor - Plane(Plane const & other); - - /// normal of the plane, not normalized to lenght 1 - inline math::Vector3f const & normal() const - { - return plane_normal; - } - /// the points defining the plane. - /// @param i 0 <= i < 3 - inline math::Vector3f const & point(size_t i) const - { - return plane_point[i]; - } - /// plane texture - inline std::string & texture() - { - return plane_texture; - } - /// first parameter of the general equation - inline float a() const - { - return plane_normal[0]; - } - /// second parameter of the general equation - inline float b() const - { - return plane_normal[1]; - } - /// third param of the general equation - inline float c() const - { - return plane_normal[2]; - } - /// fourth parameter of the general equation - inline float d() const - { - return pd; - } - /// indidcates if this plane was generated from a detail brush - inline bool & detail() - { - return plane_detail; - } - /// surface flags - inline unsigned int & surface_flags() - { - return plane_surface_flags; - } - - -private: - math::Vector3f plane_point[3]; - math::Vector3f plane_normal; - std::string plane_texture; - unsigned int plane_surface_flags; - bool plane_detail; - float pd; -}; - -} - -#endif // __INCLUDED_MODEL_PLANE_H__ diff --git a/src/model/primitives.cc b/src/model/primitives.cc index 5e58b50..8eec0f4 100644 --- a/src/model/primitives.cc +++ b/src/model/primitives.cc @@ -27,18 +27,20 @@ Primitives::~Primitives() primitives_quads.clear(); } -void Primitives::add_triangle(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, +Triangle *Primitives::add_triangle(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &normal, bool detail) { Triangle *triangle = new Triangle(v0, v1, v2, normal, detail); primitives_triangles.push_back(triangle); + return triangle; } -void Primitives::add_quad(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &v3, +Quad *Primitives::add_quad(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &v3, math::Vector3f const &normal, bool detail) { Quad *quad = new Quad(v0, v1, v2, v3, normal, detail); primitives_quads.push_back(quad); + return quad; } } diff --git a/src/model/primitives.h b/src/model/primitives.h index d113961..116d01b 100644 --- a/src/model/primitives.h +++ b/src/model/primitives.h @@ -49,11 +49,11 @@ public: } /// add a Triangle primitive - void add_triangle(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, + Triangle *add_triangle(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &normal, bool detail); /// add a Quad primitive - void add_quad(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &v3, + Quad *add_quad(math::Vector3f const &v0, math::Vector3f const &v1, math::Vector3f const &v2, math::Vector3f const &v3, math::Vector3f const &normal, bool detail); private: Triangles primitives_triangles; diff --git a/src/render/particles.h b/src/render/particles.h index a36b142..cf0f5eb 100644 --- a/src/render/particles.h +++ b/src/render/particles.h @@ -13,7 +13,7 @@ #include "math/color.h" #include "math/vector3f.h" #include "core/entity.h" -#include "model/classes.h" +#include "model/parts.h" namespace render { diff --git a/src/render/textures.cc b/src/render/textures.cc index 5845bb6..fe85c8d 100644 --- a/src/render/textures.cc +++ b/src/render/textures.cc @@ -23,6 +23,12 @@ namespace render std::map Textures::registry; GLuint Textures::textures[MAXTEXTURES]; +math::Vector2f Textures::texture_size[MAXTEXTURES]; + +void material_loader_func(model::Material *material) +{ + Textures::material_loader(material); +} void Textures::init() { @@ -49,10 +55,13 @@ void Textures::init() load("bitmaps/pointers/center"); load("bitmaps/pointers/control"); load("bitmaps/pointers/target"); + + model::Material::set_loader_func(Textures::material_loader); } void Textures::shutdown() { + model::Material::set_loader_func(0); clear(); } @@ -159,7 +168,7 @@ size_t Textures::load(const std::string &name, const bool filter) if (!image) { // add to the registry with id 0 (texture not found) - con_warn << "Could not open " << filename << std::endl; + con_warn << "Could not open texture " << name << std::endl; registry[name] = 0; return 0; } @@ -181,6 +190,10 @@ size_t Textures::load(const std::string &name, const bool filter) // hardware generated mipmaps (requires OpenGL 1.4) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } + + // enable texture wrapping + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { // scaling functions glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); @@ -210,6 +223,7 @@ size_t Textures::load(const std::string &name, const bool filter) // add to the registry registry[name] = id; + texture_size[id].assign((float) image->width(), (float) image->height()); // delete image data delete image; @@ -258,4 +272,13 @@ size_t Textures::bind(const size_t texture, const bool filter) return id; } +void Textures::material_loader(model::Material *material) +{ + if ((material->flags() & model::Material::Texture) & (material->texture().size())) { + size_t id = load(material->texture()); + material->set_texture_id(id); + material->set_size(texture_size[id]); + } +} + } diff --git a/src/render/textures.h b/src/render/textures.h index 84d5c9a..c1ccabe 100644 --- a/src/render/textures.h +++ b/src/render/textures.h @@ -10,6 +10,7 @@ #include #include +#include "model/material.h" #include "render/gl.h" namespace render @@ -56,11 +57,15 @@ public: /// list loaded textures static void list(); + /// material loader func + static void material_loader(model::Material *material); + private: static void clear(); typedef std::map::iterator iterator; + static math::Vector2f texture_size[MAXTEXTURES]; static std::map registry; static GLuint textures[MAXTEXTURES]; }; -- cgit v1.2.3