/* Copyright (C) 2003, 2010 - Wolfire Games Copyright (C) 2010-2017 - Lugaru contributors (see AUTHORS file) This file is part of Lugaru. Lugaru is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Lugaru is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Lugaru. If not, see . */ #include "Graphic/Models.hpp" #include "Game.hpp" #include "Utils/Folders.hpp" extern float multiplier; extern float viewdistance; extern XYZ viewer; extern float fadestart; extern float texdetail; extern bool decalstoggle; int Model::LineCheck(XYZ* p1, XYZ* p2, XYZ* p, XYZ* move, float* rotate) { static float distance; static float olddistance; static int intersecting; static int firstintersecting; static XYZ point; *p1 = *p1 - *move; *p2 = *p2 - *move; if (*rotate) { *p1 = DoRotation(*p1, 0, -*rotate, 0); } if (*rotate) { *p2 = DoRotation(*p2, 0, -*rotate, 0); } if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) { return -1; } firstintersecting = -1; for (unsigned int j = 0; j < Triangles.size(); j++) { intersecting = LineFacetd(p1, p2, &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], &Triangles[j].facenormal, &point); distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); if ((distance < olddistance || firstintersecting == -1) && intersecting) { olddistance = distance; firstintersecting = j; *p = point; } } if (*rotate) { *p = DoRotation(*p, 0, *rotate, 0); } *p = *p + *move; return firstintersecting; } int Model::LineCheckPossible(XYZ* p1, XYZ* p2, XYZ* p, XYZ* move, float* rotate) { static float distance; static float olddistance; static int intersecting; static int firstintersecting; static XYZ point; *p1 = *p1 - *move; *p2 = *p2 - *move; if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) { return -1; } firstintersecting = -1; if (*rotate) { *p1 = DoRotation(*p1, 0, -*rotate, 0); } if (*rotate) { *p2 = DoRotation(*p2, 0, -*rotate, 0); } for (unsigned int j = 0; j < possible.size(); j++) { if (possible[j] < Triangles.size()) { intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &Triangles[possible[j]].facenormal, &point); distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); if ((distance < olddistance || firstintersecting == -1) && intersecting) { olddistance = distance; firstintersecting = possible[j]; *p = point; } } } if (*rotate) { *p = DoRotation(*p, 0, *rotate, 0); } *p = *p + *move; return firstintersecting; } int Model::LineCheckSlidePossible(XYZ* p1, XYZ* p2, XYZ* move, float* rotate) { static float distance; static float olddistance; static int intersecting; static int firstintersecting; static XYZ point; *p1 = *p1 - *move; *p2 = *p2 - *move; if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) { return -1; } firstintersecting = -1; if (*rotate) { *p1 = DoRotation(*p1, 0, -*rotate, 0); } if (*rotate) { *p2 = DoRotation(*p2, 0, -*rotate, 0); } for (unsigned int j = 0; j < possible.size(); j++) { if (possible[j] < Triangles.size()) { intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &Triangles[possible[j]].facenormal, &point); distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); if ((distance < olddistance || firstintersecting == -1) && intersecting) { olddistance = distance; firstintersecting = possible[j]; } } } if (firstintersecting > 0) { distance = abs((Triangles[firstintersecting].facenormal.x * p2->x) + (Triangles[firstintersecting].facenormal.y * p2->y) + (Triangles[firstintersecting].facenormal.z * p2->z) - ((Triangles[firstintersecting].facenormal.x * vertex[Triangles[firstintersecting].vertex[0]].x) + (Triangles[firstintersecting].facenormal.y * vertex[Triangles[firstintersecting].vertex[0]].y) + (Triangles[firstintersecting].facenormal.z * vertex[Triangles[firstintersecting].vertex[0]].z))); *p2 -= Triangles[firstintersecting].facenormal * distance; } if (*rotate) { *p2 = DoRotation(*p2, 0, *rotate, 0); } *p2 = *p2 + *move; return firstintersecting; } int Model::SphereCheck(XYZ* p1, float radius, XYZ* p, XYZ* move, float* rotate) { static int i; static float distance; static float olddistance; static int intersecting; static int firstintersecting; static XYZ point; static XYZ oldp1; firstintersecting = -1; oldp1 = *p1; *p1 = *p1 - *move; if (*rotate) { *p1 = DoRotation(*p1, 0, -*rotate, 0); } if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) { return -1; } for (i = 0; i < 4; i++) { for (unsigned int j = 0; j < Triangles.size(); j++) { intersecting = 0; distance = abs((Triangles[j].facenormal.x * p1->x) + (Triangles[j].facenormal.y * p1->y) + (Triangles[j].facenormal.z * p1->z) - ((Triangles[j].facenormal.x * vertex[Triangles[j].vertex[0]].x) + (Triangles[j].facenormal.y * vertex[Triangles[j].vertex[0]].y) + (Triangles[j].facenormal.z * vertex[Triangles[j].vertex[0]].z))); if (distance < radius) { point = *p1 - Triangles[j].facenormal * distance; if (PointInTriangle(&point, Triangles[j].facenormal, &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) { intersecting = 1; } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); } if (intersecting) { *p1 += Triangles[j].facenormal * (distance - radius); } } if ((distance < olddistance || firstintersecting == -1) && intersecting) { olddistance = distance; firstintersecting = j; *p = point; } } } if (*rotate) { *p = DoRotation(*p, 0, *rotate, 0); } *p = *p + *move; if (*rotate) { *p1 = DoRotation(*p1, 0, *rotate, 0); } *p1 += *move; return firstintersecting; } int Model::SphereCheckPossible(XYZ* p1, float radius, XYZ* move, float* rotate) { static float distance; static float olddistance; static int intersecting; static int firstintersecting; static XYZ point; static XYZ oldp1; firstintersecting = -1; oldp1 = *p1; *p1 = *p1 - *move; possible.clear(); if (*rotate) { *p1 = DoRotation(*p1, 0, -*rotate, 0); } if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) { *p1 = oldp1; return -1; } for (unsigned int j = 0; j < Triangles.size(); j++) { intersecting = 0; distance = abs((Triangles[j].facenormal.x * p1->x) + (Triangles[j].facenormal.y * p1->y) + (Triangles[j].facenormal.z * p1->z) - ((Triangles[j].facenormal.x * vertex[Triangles[j].vertex[0]].x) + (Triangles[j].facenormal.y * vertex[Triangles[j].vertex[0]].y) + (Triangles[j].facenormal.z * vertex[Triangles[j].vertex[0]].z))); if (distance < radius) { point = *p1 - Triangles[j].facenormal * distance; if (PointInTriangle(&point, Triangles[j].facenormal, &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) { intersecting = 1; } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); } if (!intersecting) { intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); } if (intersecting) { possible.push_back(j); } } if ((distance < olddistance || firstintersecting == -1) && intersecting) { olddistance = distance; firstintersecting = j; } } if (*rotate) { *p1 = DoRotation(*p1, 0, *rotate, 0); } *p1 += *move; return firstintersecting; } void Model::UpdateVertexArray() { if (type != normaltype && type != decalstype) { return; } if (flat) { for (unsigned int i = 0; i < Triangles.size(); i++) { unsigned int j = i * 24; vArray[j + 0] = Triangles[i].gx[0]; vArray[j + 1] = Triangles[i].gy[0]; vArray[j + 2] = Triangles[i].facenormal.x * -1; vArray[j + 3] = Triangles[i].facenormal.y * -1; vArray[j + 4] = Triangles[i].facenormal.z * -1; vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; vArray[j + 8] = Triangles[i].gx[1]; vArray[j + 9] = Triangles[i].gy[1]; vArray[j + 10] = Triangles[i].facenormal.x * -1; vArray[j + 11] = Triangles[i].facenormal.y * -1; vArray[j + 12] = Triangles[i].facenormal.z * -1; vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; vArray[j + 16] = Triangles[i].gx[2]; vArray[j + 17] = Triangles[i].gy[2]; vArray[j + 18] = Triangles[i].facenormal.x * -1; vArray[j + 19] = Triangles[i].facenormal.y * -1; vArray[j + 20] = Triangles[i].facenormal.z * -1; vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; } } else { for (unsigned int i = 0; i < Triangles.size(); i++) { unsigned int j = i * 24; vArray[j + 0] = Triangles[i].gx[0]; vArray[j + 1] = Triangles[i].gy[0]; vArray[j + 2] = normals[Triangles[i].vertex[0]].x; vArray[j + 3] = normals[Triangles[i].vertex[0]].y; vArray[j + 4] = normals[Triangles[i].vertex[0]].z; vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; vArray[j + 8] = Triangles[i].gx[1]; vArray[j + 9] = Triangles[i].gy[1]; vArray[j + 10] = normals[Triangles[i].vertex[1]].x; vArray[j + 11] = normals[Triangles[i].vertex[1]].y; vArray[j + 12] = normals[Triangles[i].vertex[1]].z; vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; vArray[j + 16] = Triangles[i].gx[2]; vArray[j + 17] = Triangles[i].gy[2]; vArray[j + 18] = normals[Triangles[i].vertex[2]].x; vArray[j + 19] = normals[Triangles[i].vertex[2]].y; vArray[j + 20] = normals[Triangles[i].vertex[2]].z; vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; } } } void Model::UpdateVertexArrayNoTex() { if (type != normaltype && type != decalstype) { return; } if (flat) { for (unsigned int i = 0; i < Triangles.size(); i++) { unsigned int j = i * 24; vArray[j + 2] = Triangles[i].facenormal.x * -1; vArray[j + 3] = Triangles[i].facenormal.y * -1; vArray[j + 4] = Triangles[i].facenormal.z * -1; vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; vArray[j + 10] = Triangles[i].facenormal.x * -1; vArray[j + 11] = Triangles[i].facenormal.y * -1; vArray[j + 12] = Triangles[i].facenormal.z * -1; vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; vArray[j + 18] = Triangles[i].facenormal.x * -1; vArray[j + 19] = Triangles[i].facenormal.y * -1; vArray[j + 20] = Triangles[i].facenormal.z * -1; vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; } } else { for (unsigned int i = 0; i < Triangles.size(); i++) { unsigned int j = i * 24; vArray[j + 2] = normals[Triangles[i].vertex[0]].x; vArray[j + 3] = normals[Triangles[i].vertex[0]].y; vArray[j + 4] = normals[Triangles[i].vertex[0]].z; vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; vArray[j + 10] = normals[Triangles[i].vertex[1]].x; vArray[j + 11] = normals[Triangles[i].vertex[1]].y; vArray[j + 12] = normals[Triangles[i].vertex[1]].z; vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; vArray[j + 18] = normals[Triangles[i].vertex[2]].x; vArray[j + 19] = normals[Triangles[i].vertex[2]].y; vArray[j + 20] = normals[Triangles[i].vertex[2]].z; vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; } } } void Model::UpdateVertexArrayNoTexNoNorm() { if (type != normaltype && type != decalstype) { return; } for (unsigned int i = 0; i < Triangles.size(); i++) { unsigned int j = i * 24; vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; } } bool Model::loadnotex(const std::string& filename) { FILE* tfile; long i; short triangleNum; type = notextype; color = 0; tfile = Folders::openMandatoryFile(Folders::getResourcePath(filename), "rb"); // read model settings fseek(tfile, 0, SEEK_SET); funpackf(tfile, "Bs Bs", &vertexNum, &triangleNum); // read the model data deallocate(); possible.clear(); owner = (int*)malloc(sizeof(int) * vertexNum); vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); Triangles.resize(triangleNum); vArray = (GLfloat*)malloc(sizeof(GLfloat) * triangleNum * 24); for (i = 0; i < vertexNum; i++) { funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); } for (i = 0; i < triangleNum; i++) { short vertex[6]; funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[0], &vertex[1], &vertex[2], &vertex[3], &vertex[4], &vertex[5]); Triangles[i].vertex[0] = vertex[0]; Triangles[i].vertex[1] = vertex[2]; Triangles[i].vertex[2] = vertex[4]; funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); } fclose(tfile); UpdateVertexArray(); for (i = 0; i < vertexNum; i++) { owner[i] = -1; } boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (int j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); return true; } bool Model::load(const std::string& filename) { FILE* tfile; long i; short triangleNum; LOGFUNC; LOG(std::string("Loading model...") + filename); Game::LoadingScreen(); type = normaltype; color = 0; tfile = Folders::openMandatoryFile(Folders::getResourcePath(filename), "rb"); // read model settings fseek(tfile, 0, SEEK_SET); funpackf(tfile, "Bs Bs", &vertexNum, &triangleNum); // read the model data deallocate(); possible.clear(); owner = (int*)malloc(sizeof(int) * vertexNum); vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); Triangles.resize(triangleNum); vArray = (GLfloat*)malloc(sizeof(GLfloat) * triangleNum * 24); for (i = 0; i < vertexNum; i++) { funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); } for (i = 0; i < triangleNum; i++) { short vertex[6]; funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[0], &vertex[1], &vertex[2], &vertex[3], &vertex[4], &vertex[5]); Triangles[i].vertex[0] = vertex[0]; Triangles[i].vertex[1] = vertex[2]; Triangles[i].vertex[2] = vertex[4]; funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); } modelTexture.xsz = 0; fclose(tfile); UpdateVertexArray(); for (i = 0; i < vertexNum; i++) { owner[i] = -1; } static int j; boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); return true; } bool Model::loaddecal(const std::string& filename) { FILE* tfile; long i, j; short triangleNum; LOGFUNC; LOG(std::string("Loading decal...") + Folders::getResourcePath(filename)); type = decalstype; color = 0; tfile = Folders::openMandatoryFile(Folders::getResourcePath(filename), "rb"); // read model settings fseek(tfile, 0, SEEK_SET); funpackf(tfile, "Bs Bs", &vertexNum, &triangleNum); // read the model data deallocate(); possible.clear(); owner = (int*)malloc(sizeof(int) * vertexNum); vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); Triangles.resize(triangleNum); vArray = (GLfloat*)malloc(sizeof(GLfloat) * triangleNum * 24); for (i = 0; i < vertexNum; i++) { funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); } for (i = 0; i < triangleNum; i++) { short vertex[6]; funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[0], &vertex[1], &vertex[2], &vertex[3], &vertex[4], &vertex[5]); Triangles[i].vertex[0] = vertex[0]; Triangles[i].vertex[1] = vertex[2]; Triangles[i].vertex[2] = vertex[4]; funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); } modelTexture.xsz = 0; fclose(tfile); UpdateVertexArray(); for (i = 0; i < vertexNum; i++) { owner[i] = -1; } boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); return true; } bool Model::loadraw(const std::string& filename) { FILE* tfile; long i; short triangleNum; LOGFUNC; LOG(std::string("Loading raw...") + filename); type = rawtype; color = 0; tfile = Folders::openMandatoryFile(Folders::getResourcePath(filename), "rb"); // read model settings fseek(tfile, 0, SEEK_SET); funpackf(tfile, "Bs Bs", &vertexNum, &triangleNum); // read the model data deallocate(); possible.clear(); owner = (int*)malloc(sizeof(int) * vertexNum); vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); Triangles.resize(triangleNum); vArray = (GLfloat*)malloc(sizeof(GLfloat) * triangleNum * 24); for (i = 0; i < vertexNum; i++) { funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); } for (i = 0; i < triangleNum; i++) { short vertex[6]; funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[0], &vertex[1], &vertex[2], &vertex[3], &vertex[4], &vertex[5]); Triangles[i].vertex[0] = vertex[0]; Triangles[i].vertex[1] = vertex[2]; Triangles[i].vertex[2] = vertex[4]; funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); } fclose(tfile); for (i = 0; i < vertexNum; i++) { owner[i] = -1; } return true; } void Model::UniformTexCoords() { for (unsigned int i = 0; i < Triangles.size(); i++) { Triangles[i].gy[0] = vertex[Triangles[i].vertex[0]].y; Triangles[i].gy[1] = vertex[Triangles[i].vertex[1]].y; Triangles[i].gy[2] = vertex[Triangles[i].vertex[2]].y; Triangles[i].gx[0] = vertex[Triangles[i].vertex[0]].x; Triangles[i].gx[1] = vertex[Triangles[i].vertex[1]].x; Triangles[i].gx[2] = vertex[Triangles[i].vertex[2]].x; } UpdateVertexArray(); } void Model::FlipTexCoords() { for (unsigned int i = 0; i < Triangles.size(); i++) { Triangles[i].gy[0] = -Triangles[i].gy[0]; Triangles[i].gy[1] = -Triangles[i].gy[1]; Triangles[i].gy[2] = -Triangles[i].gy[2]; } UpdateVertexArray(); } void Model::ScaleTexCoords(float howmuch) { for (unsigned int i = 0; i < Triangles.size(); i++) { Triangles[i].gx[0] *= howmuch; Triangles[i].gx[1] *= howmuch; Triangles[i].gx[2] *= howmuch; Triangles[i].gy[0] *= howmuch; Triangles[i].gy[1] *= howmuch; Triangles[i].gy[2] *= howmuch; } UpdateVertexArray(); } void Model::Scale(float xscale, float yscale, float zscale) { static int i; for (i = 0; i < vertexNum; i++) { vertex[i].x *= xscale; vertex[i].y *= yscale; vertex[i].z *= zscale; } UpdateVertexArray(); static int j; boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); } void Model::ScaleNormals(float xscale, float yscale, float zscale) { if (type != normaltype && type != decalstype) { return; } for (int i = 0; i < vertexNum; i++) { normals[i].x *= xscale; normals[i].y *= yscale; normals[i].z *= zscale; } for (unsigned int i = 0; i < Triangles.size(); i++) { Triangles[i].facenormal.x *= xscale; Triangles[i].facenormal.y *= yscale; Triangles[i].facenormal.z *= zscale; } UpdateVertexArray(); } void Model::Translate(float xtrans, float ytrans, float ztrans) { static int i; for (i = 0; i < vertexNum; i++) { vertex[i].x += xtrans; vertex[i].y += ytrans; vertex[i].z += ztrans; } UpdateVertexArray(); static int j; boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); } void Model::Rotate(float xang, float yang, float zang) { static int i; for (i = 0; i < vertexNum; i++) { vertex[i] = DoRotation(vertex[i], xang, yang, zang); } UpdateVertexArray(); static int j; boundingsphereradius = 0; for (i = 0; i < vertexNum; i++) { for (j = 0; j < vertexNum; j++) { if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; boundingspherecenter = (vertex[i] + vertex[j]) / 2; } } } boundingsphereradius = fast_sqrt(boundingsphereradius); } void Model::CalculateNormals(bool facenormalise) { Game::LoadingScreen(); if (type != normaltype && type != decalstype) { return; } for (int i = 0; i < vertexNum; i++) { normals[i].x = 0; normals[i].y = 0; normals[i].z = 0; } for (unsigned int i = 0; i < Triangles.size(); i++) { CrossProduct(vertex[Triangles[i].vertex[1]] - vertex[Triangles[i].vertex[0]], vertex[Triangles[i].vertex[2]] - vertex[Triangles[i].vertex[0]], &Triangles[i].facenormal); normals[Triangles[i].vertex[0]].x += Triangles[i].facenormal.x; normals[Triangles[i].vertex[0]].y += Triangles[i].facenormal.y; normals[Triangles[i].vertex[0]].z += Triangles[i].facenormal.z; normals[Triangles[i].vertex[1]].x += Triangles[i].facenormal.x; normals[Triangles[i].vertex[1]].y += Triangles[i].facenormal.y; normals[Triangles[i].vertex[1]].z += Triangles[i].facenormal.z; normals[Triangles[i].vertex[2]].x += Triangles[i].facenormal.x; normals[Triangles[i].vertex[2]].y += Triangles[i].facenormal.y; normals[Triangles[i].vertex[2]].z += Triangles[i].facenormal.z; if (facenormalise) { Normalise(&Triangles[i].facenormal); } } for (int i = 0; i < vertexNum; i++) { Normalise(&normals[i]); normals[i] *= -1; } UpdateVertexArrayNoTex(); } void Model::drawimmediate() { textureptr.bind(); glBegin(GL_TRIANGLES); for (unsigned int i = 0; i < Triangles.size(); i++) { glTexCoord2f(Triangles[i].gx[0], Triangles[i].gy[0]); if (color) { glColor3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); } else if (flat) { glNormal3f(Triangles[i].facenormal.x, Triangles[i].facenormal.y, Triangles[i].facenormal.y); } else { glNormal3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); } glVertex3f(vertex[Triangles[i].vertex[0]].x, vertex[Triangles[i].vertex[0]].y, vertex[Triangles[i].vertex[0]].z); glTexCoord2f(Triangles[i].gx[1], Triangles[i].gy[1]); if (color) { glColor3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); } else if (flat) { glNormal3f(Triangles[i].facenormal.x, Triangles[i].facenormal.y, Triangles[i].facenormal.y); } else { glNormal3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); } glVertex3f(vertex[Triangles[i].vertex[1]].x, vertex[Triangles[i].vertex[1]].y, vertex[Triangles[i].vertex[1]].z); glTexCoord2f(Triangles[i].gx[2], Triangles[i].gy[2]); if (color) { glColor3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); } else if (flat) { glNormal3f(Triangles[i].facenormal.x, Triangles[i].facenormal.y, Triangles[i].facenormal.y); } else { glNormal3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); } glVertex3f(vertex[Triangles[i].vertex[2]].x, vertex[Triangles[i].vertex[2]].y, vertex[Triangles[i].vertex[2]].z); } glEnd(); } void Model::draw() { if (type != normaltype && type != decalstype) { return; } glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (color) { glInterleavedArrays(GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); } else { glInterleavedArrays(GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); } textureptr.bind(); glDrawArrays(GL_TRIANGLES, 0, Triangles.size() * 3); if (color) { glDisableClientState(GL_COLOR_ARRAY); } else { glDisableClientState(GL_NORMAL_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } void Model::drawdifftex(Texture texture) { glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (color) { glInterleavedArrays(GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); } else { glInterleavedArrays(GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); } texture.bind(); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glDrawArrays(GL_TRIANGLES, 0, Triangles.size() * 3); if (color) { glDisableClientState(GL_COLOR_ARRAY); } else { glDisableClientState(GL_NORMAL_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } void Model::drawdecals(Texture shadowtexture, Texture bloodtexture, Texture bloodtexture2, Texture breaktexture) { if (decalstoggle) { if (type != decalstype) { return; } bool blend = true; int lasttype = -1; glEnable(GL_BLEND); glDisable(GL_LIGHTING); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(0); for (unsigned int i = 0; i < decals.size(); i++) { if (decals[i].type == blooddecalfast && decals[i].alivetime < 2) { decals[i].alivetime = 2; } if (decals[i].type != lasttype) { if (decals[i].type == shadowdecal) { shadowtexture.bind(); if (!blend) { blend = 1; glAlphaFunc(GL_GREATER, 0.0001); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } if (decals[i].type == breakdecal) { breaktexture.bind(); if (!blend) { blend = 1; glAlphaFunc(GL_GREATER, 0.0001); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } if (decals[i].type == blooddecal || decals[i].type == blooddecalslow) { bloodtexture.bind(); if (blend) { blend = 0; glAlphaFunc(GL_GREATER, 0.15); glBlendFunc(GL_ONE, GL_ZERO); } } if (decals[i].type == blooddecalfast) { bloodtexture2.bind(); if (blend) { blend = 0; glAlphaFunc(GL_GREATER, 0.15); glBlendFunc(GL_ONE, GL_ZERO); } } } if (decals[i].type == shadowdecal) { glColor4f(1, 1, 1, decals[i].opacity); } if (decals[i].type == breakdecal) { glColor4f(1, 1, 1, decals[i].opacity); if (decals[i].alivetime > 58) { glColor4f(1, 1, 1, decals[i].opacity * (60 - decals[i].alivetime) / 2); } } if ((decals[i].type == blooddecal || decals[i].type == blooddecalfast || decals[i].type == blooddecalslow)) { glColor4f(1, 1, 1, decals[i].opacity); if (decals[i].alivetime < 4) { glColor4f(1, 1, 1, decals[i].opacity * decals[i].alivetime * .25); } if (decals[i].alivetime > 58) { glColor4f(1, 1, 1, decals[i].opacity * (60 - decals[i].alivetime) / 2); } } lasttype = decals[i].type; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glBegin(GL_TRIANGLES); for (int j = 0; j < 3; j++) { glTexCoord2f(decals[i].texcoords[j][0], decals[i].texcoords[j][1]); glVertex3f(decals[i].vertex[j].x, decals[i].vertex[j].y, decals[i].vertex[j].z); } glEnd(); glPopMatrix(); } for (int i = decals.size() - 1; i >= 0; i--) { decals[i].alivetime += multiplier; if (decals[i].type == blooddecalslow) { decals[i].alivetime -= multiplier * 2 / 3; } if (decals[i].type == blooddecalfast) { decals[i].alivetime += multiplier * 4; } if (decals[i].type == shadowdecal) { DeleteDecal(i); } if ((decals[i].type == blooddecal || decals[i].type == blooddecalfast || decals[i].type == blooddecalslow) && decals[i].alivetime >= 60) { DeleteDecal(i); } } glAlphaFunc(GL_GREATER, 0.0001); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } void Model::DeleteDecal(int which) { if (decalstoggle) { if (type != decalstype) { return; } decals.erase(decals.begin() + which); } } void Model::MakeDecal(decal_type atype, XYZ* where, float* size, float* opacity, float* rotation) { if (decalstoggle) { if (type != decalstype) { return; } static XYZ rot; static float distance; if (*opacity > 0) { if (distsq(where, &boundingspherecenter) < (boundingsphereradius + *size) * (boundingsphereradius + *size)) { for (unsigned int i = 0; i < Triangles.size(); i++) { if (Triangles[i].facenormal.y < -.1 && (vertex[Triangles[i].vertex[0]].y < where->y || vertex[Triangles[i].vertex[1]].y < where->y || vertex[Triangles[i].vertex[2]].y < where->y)) { distance = abs(((Triangles[i].facenormal.x * where->x) + (Triangles[i].facenormal.y * where->y) + (Triangles[i].facenormal.z * where->z) - ((Triangles[i].facenormal.x * vertex[Triangles[i].vertex[0]].x) + (Triangles[i].facenormal.y * vertex[Triangles[i].vertex[0]].y) + (Triangles[i].facenormal.z * vertex[Triangles[i].vertex[0]].z))) / Triangles[i].facenormal.y); if ((*opacity - distance / 10) > 0) { Decal decal(*where, atype, *opacity - distance / 10, *rotation, *size, *this, i, 0); if (!(decal.texcoords[0][0] < 0 && decal.texcoords[1][0] < 0 && decal.texcoords[2][0] < 0)) { if (!(decal.texcoords[0][1] < 0 && decal.texcoords[1][1] < 0 && decal.texcoords[2][1] < 0)) { if (!(decal.texcoords[0][0] > 1 && decal.texcoords[1][0] > 1 && decal.texcoords[2][0] > 1)) { if (!(decal.texcoords[0][1] > 1 && decal.texcoords[1][1] > 1 && decal.texcoords[2][1] > 1)) { if (decal.rotation) { for (int j = 0; j < 3; j++) { rot.y = 0; rot.x = decal.texcoords[j][0] - .5; rot.z = decal.texcoords[j][1] - .5; rot = DoRotation(rot, 0, -decal.rotation, 0); decal.texcoords[j][0] = rot.x + .5; decal.texcoords[j][1] = rot.z + .5; } } if (decals.size() < max_model_decals - 1) { decals.push_back(decal); } } } } } } } } } } } } void Model::MakeDecal(decal_type atype, XYZ where, float size, float opacity, float rotation) { if (decalstoggle) { if (type != decalstype) { return; } static XYZ rot; static float distance; if (opacity > 0) { if (distsq(&where, &boundingspherecenter) < (boundingsphereradius + size) * (boundingsphereradius + size)) { for (unsigned int i = 0; i < Triangles.size(); i++) { distance = abs(((Triangles[i].facenormal.x * where.x) + (Triangles[i].facenormal.y * where.y) + (Triangles[i].facenormal.z * where.z) - ((Triangles[i].facenormal.x * vertex[Triangles[i].vertex[0]].x) + (Triangles[i].facenormal.y * vertex[Triangles[i].vertex[0]].y) + (Triangles[i].facenormal.z * vertex[Triangles[i].vertex[0]].z)))); if (distance < .02 && abs(Triangles[i].facenormal.y) > abs(Triangles[i].facenormal.x) && abs(Triangles[i].facenormal.y) > abs(Triangles[i].facenormal.z)) { if ((opacity - distance / 10) > 0) { Decal decal(where, atype, opacity - distance / 10, rotation, size, *this, i, 0); if (!(decal.texcoords[0][0] < 0 && decal.texcoords[1][0] < 0 && decal.texcoords[2][0] < 0)) { if (!(decal.texcoords[0][1] < 0 && decal.texcoords[1][1] < 0 && decal.texcoords[2][1] < 0)) { if (!(decal.texcoords[0][0] > 1 && decal.texcoords[1][0] > 1 && decal.texcoords[2][0] > 1)) { if (!(decal.texcoords[0][1] > 1 && decal.texcoords[1][1] > 1 && decal.texcoords[2][1] > 1)) { if (decal.rotation) { for (int j = 0; j < 3; j++) { rot.y = 0; rot.x = decal.texcoords[j][0] - .5; rot.z = decal.texcoords[j][1] - .5; rot = DoRotation(rot, 0, -decal.rotation, 0); decal.texcoords[j][0] = rot.x + .5; decal.texcoords[j][1] = rot.z + .5; } } if (decals.size() < max_model_decals - 1) { decals.push_back(decal); } } } } } } } else if (distance < .02 && abs(Triangles[i].facenormal.x) > abs(Triangles[i].facenormal.y) && abs(Triangles[i].facenormal.x) > abs(Triangles[i].facenormal.z)) { if ((opacity - distance / 10) > 0) { Decal decal(where, atype, opacity - distance / 10, rotation, size, *this, i, 1); if (!(decal.texcoords[0][0] < 0 && decal.texcoords[1][0] < 0 && decal.texcoords[2][0] < 0)) { if (!(decal.texcoords[0][1] < 0 && decal.texcoords[1][1] < 0 && decal.texcoords[2][1] < 0)) { if (!(decal.texcoords[0][0] > 1 && decal.texcoords[1][0] > 1 && decal.texcoords[2][0] > 1)) { if (!(decal.texcoords[0][1] > 1 && decal.texcoords[1][1] > 1 && decal.texcoords[2][1] > 1)) { if (decal.rotation) { for (int j = 0; j < 3; j++) { rot.y = 0; rot.x = decal.texcoords[j][0] - .5; rot.z = decal.texcoords[j][1] - .5; rot = DoRotation(rot, 0, -decal.rotation, 0); decal.texcoords[j][0] = rot.x + .5; decal.texcoords[j][1] = rot.z + .5; } } if (decals.size() < max_model_decals - 1) { decals.push_back(decal); } } } } } } } else if (distance < .02 && abs(Triangles[i].facenormal.z) > abs(Triangles[i].facenormal.y) && abs(Triangles[i].facenormal.z) > abs(Triangles[i].facenormal.x)) { if ((opacity - distance / 10) > 0) { Decal decal(where, atype, opacity - distance / 10, rotation, size, *this, i, 2); if (!(decal.texcoords[0][0] < 0 && decal.texcoords[1][0] < 0 && decal.texcoords[2][0] < 0)) { if (!(decal.texcoords[0][1] < 0 && decal.texcoords[1][1] < 0 && decal.texcoords[2][1] < 0)) { if (!(decal.texcoords[0][0] > 1 && decal.texcoords[1][0] > 1 && decal.texcoords[2][0] > 1)) { if (!(decal.texcoords[0][1] > 1 && decal.texcoords[1][1] > 1 && decal.texcoords[2][1] > 1)) { if (decal.rotation) { for (int j = 0; j < 3; j++) { rot.y = 0; rot.x = decal.texcoords[j][0] - .5; rot.z = decal.texcoords[j][1] - .5; rot = DoRotation(rot, 0, -decal.rotation, 0); decal.texcoords[j][0] = rot.x + .5; decal.texcoords[j][1] = rot.z + .5; } } if (decals.size() < max_model_decals - 1) { decals.push_back(decal); } } } } } } } } } } } } const XYZ& Model::getTriangleVertex(unsigned triangleId, unsigned vertexId) const { return vertex[Triangles[triangleId].vertex[vertexId]]; } void Model::deleteDeadDecals() { for (int i = decals.size() - 1; i >= 0; i--) { if ((decals[i].type == blooddecal || decals[i].type == blooddecalslow) && decals[i].alivetime < 2) { DeleteDecal(i); } } } Model::~Model() { deallocate(); } void Model::deallocate() { if (owner) { free(owner); } owner = 0; if (vertex) { free(vertex); } vertex = 0; if (normals) { free(normals); } normals = 0; if (vArray) { free(vArray); } vArray = 0; decals.clear(); } Model::Model() : vertexNum(0) , type(nothing) , owner(0) , vertex(0) , normals(0) , vArray(0) , color(0) , boundingspherecenter() , boundingsphereradius(0) , flat(false) { memset(&modelTexture, 0, sizeof(modelTexture)); }