diff options
Diffstat (limited to 'src/model/mapfile.cc')
-rw-r--r-- | src/model/mapfile.cc | 1353 |
1 files changed, 1353 insertions, 0 deletions
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 <sstream> +#include <string> +#include <cstring> + +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<Face *>::iterator face = planes.begin(); face != planes.end(); face++) { + make_brushface((*face)); + } + + // clean planes + for (std::vector<Face *>::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<math::Vector3f *> 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<Vector3f *>::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<Vector3f *>::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<Vector3f *>::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<Face *>::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<Vector3f *>::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<Vector3f *>::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<Vector3f *>::iterator v0 = vl.begin(); + std::vector<Vector3f *>::reverse_iterator vn = vl.rbegin(); + std::vector<Vector3f *>::reverse_iterator vn1 = vl.rbegin(); + ++vn1; + std::vector<Vector3f *>::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<Vector3f *>::iterator v0 = vl.begin(); + std::vector<Vector3f *>::reverse_iterator vn = vl.rbegin(); + std::vector<Vector3f *>::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<Vector3f *>::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<SubModel *> 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; +} + +} + |