1 /***************************************************************************
2 collisionmodel.cpp - A collision model
3 -------------------
4 begin : wo sep 24 2003
5 copyright : (C) 2003 by CJP
6 email : cornware-cjp@users.sourceforge.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include <cstdio>
19 #include <cmath>
20 #include "collisionmodel.h"
21 #include "datafile.h"
22
23 #include "lconfig.h"
24 #include "glbfile.h"
25
CCollisionModel(CDataManager * manager)26 CCollisionModel::CCollisionModel(CDataManager *manager) : CDataObject(manager, CDataObject::eCollisionModel)
27 {}
28
~CCollisionModel()29 CCollisionModel::~CCollisionModel(){
30 }
31
getSubset() const32 CString CCollisionModel::getSubset() const
33 {
34 return m_Subset;
35 }
36
getGLBFilename() const37 CString CCollisionModel::getGLBFilename() const
38 {
39 return m_GLBFilename;
40 }
41
getConfFilename() const42 CString CCollisionModel::getConfFilename() const
43 {
44 return m_ConfFilename;
45 }
46
load(const CString & filename,const CParamList & list)47 bool CCollisionModel::load(const CString &filename, const CParamList &list)
48 {
49 CDataObject::load(filename, list);
50
51 if(filename.mid(filename.length()-4) == ".glb")
52 {
53 m_Subset = m_ParamList.getValue("subset", "");
54 m_GLBFilename = filename;
55 m_ConfFilename = "";
56 return loadGLB(filename, list);
57 }
58
59 //Default for other extensions: .conf loader
60 m_ConfFilename = filename;
61 return loadConf(filename, list);
62 }
63
loadConf(const CString & filename,const CParamList & list)64 bool CCollisionModel::loadConf(const CString &filename, const CParamList &list)
65 {
66 CDataFile df(filename);
67 CLConfig file(df.useExtern());
68
69 m_GLBFilename = file.getValue("model", "glbfile");
70 if(m_GLBFilename == "")
71 {
72 printf("Error: conf file \"%s\" does not contain a model\\glbfile value\n",
73 filename.c_str());
74 return false;
75 }
76
77 CString textures = file.getValue("model", "textures");
78 m_Subset = m_DataManager->loadFilesFromString(CDataObject::eMaterial, textures);
79
80 //Override flags with value from conf file:
81 CString flags = file.getValue("model", "flags");
82 m_ParamList.setValue("flags", flags);
83
84 return loadGLB(m_GLBFilename, list);
85 }
86
loadGLB(const CString & filename,const CParamList & list)87 bool CCollisionModel::loadGLB(const CString &filename, const CParamList &list)
88 {
89 CGLBFile f;
90 if(!f.load(filename)) return false;
91
92 vector<CDataObject *> matarray;
93 if(m_Subset != "")
94 matarray = m_DataManager->getSubset(CDataObject::eMaterial, m_Subset);
95
96 //Initial bounding volume state
97 m_BSphere_r = 0.0;
98 m_OBB_min = CVector(0,0,0);
99 m_OBB_max = CVector(0,0,0);
100
101 //initial state
102 CMaterial *mat = NULL;
103
104 for(unsigned int p=0; p < f.m_Primitives.size(); p++)
105 {
106 CGLBFile::SPrimitive &pr = f.m_Primitives[p];
107
108 if((pr.material.LODs & (16+32)) == 0) continue; //only collision+surface primitives
109
110 //printf(" Processing primitive %s\n", pr.Name.c_str());
111
112 if(pr.material.Texture < 0)
113 {
114 mat = NULL;
115 } //TODO: assign a number for non-texturised materials
116 else
117 {
118 if(pr.material.Texture < int(matarray.size()))
119 {mat = (CMaterial *)(matarray[pr.material.Texture]);}
120 else
121 {mat = NULL;}
122 }
123
124 for(unsigned int t=0; t < pr.index.size()/3; t++)
125 {
126 CVector v1 = pr.vertex[pr.index[3*t]].pos;
127 CVector v2 = pr.vertex[pr.index[3*t+1]].pos;
128 CVector v3 = pr.vertex[pr.index[3*t+2]].pos;
129
130 m_Faces.push_back(CCollisionFace());
131 CCollisionFace &theFace = m_Faces.back();
132
133 theFace.push_back(v1);
134 theFace.push_back(v2);
135 theFace.push_back(v3);
136
137 theFace.isSurface = (pr.material.LODs & 32) != 0;
138 //if(theFace.isSurface) printf("%d is is a surface\n", t);
139 theFace.isWater = (pr.material.LODs & 32) != 0 && (pr.material.LODs & 16) != 0;
140 if(theFace.isWater) theFace.isSurface = false;
141
142 theFace.material = mat;
143 theFace.reverse = false;
144
145 //printf(" Face %d: %d points\n", t, theFace.size());
146 }
147 }
148
149 determineOBVs();
150 determinePlaneEquations();
151
152 return true;
153 }
154
determineOBVs()155 void CCollisionModel::determineOBVs()
156 {
157 //Generating oriented bounding volumes
158 for(unsigned int i=0; i<m_Faces.size(); i++)
159 {
160 CCollisionFace &theFace = m_Faces[i];
161
162 for(unsigned int j=0; j<theFace.size(); j++)
163 {
164 CVector &v = theFace[j];
165
166 if(v.abs() > m_BSphere_r)
167 m_BSphere_r = v.abs();
168
169 if(v.x > m_OBB_max.x) m_OBB_max.x = v.x;
170 if(v.y > m_OBB_max.y) m_OBB_max.y = v.y;
171 if(v.z > m_OBB_max.z) m_OBB_max.z = v.z;
172 if(v.x < m_OBB_min.x) m_OBB_min.x = v.x;
173 if(v.y < m_OBB_min.y) m_OBB_min.y = v.y;
174 if(v.z < m_OBB_min.z) m_OBB_min.z = v.z;
175 }
176 }
177 }
178
determinePlaneEquations()179 void CCollisionModel::determinePlaneEquations()
180 {
181 //Determine a plane equation for each face
182 for(unsigned int i=0; i<m_Faces.size(); i++)
183 {
184 CCollisionFace &theFace = m_Faces[i];
185
186 //reverse order correction
187 if(theFace.reverse)
188 {
189 unsigned int n = theFace.size();
190 for(unsigned int j=0; j < n/2; j++)
191 {
192 CVector t = theFace[j];
193 theFace[j] = theFace[n-j-1];
194 theFace[n-j-1] = t;
195 }
196 theFace.reverse = false;
197 }
198
199 //initial:
200 theFace.d = 0.0;
201 theFace.nor = CVector(0,0,0);
202
203 if(theFace.size() < 3) continue; //not a plane
204
205 //the first point
206 CVector p1 = theFace[0];
207
208 //the second point: as far away from 1 as possible
209 unsigned int index_2nd = 1;
210 CVector p2 = theFace[index_2nd];
211 float dist2 = (p2-p1).abs2();
212 for(unsigned int j=2; j<theFace.size(); j++)
213 {
214 float d2 = (theFace[j] - p1).abs2();
215 if(d2 > dist2)
216 {p2 = theFace[j]; dist2 = d2; index_2nd = j;}
217 }
218
219 //the third point: try to maximise cross product
220 CVector crosspr;
221 float crosspr_abs2 = 0.0;
222 bool reverse = false; //2 and 3 in reverse order
223 CVector p1p2 = p2 - p1;
224 for(unsigned int j=1; j<theFace.size(); j++)
225 {
226 const CVector &p3 = theFace[j];
227 CVector cp = (p3-p1).crossProduct(p1p2);
228 float abs2 = cp.abs2();
229
230 if(abs2 > crosspr_abs2)
231 {
232 crosspr = cp;
233 crosspr_abs2 = abs2;
234 reverse = (j<index_2nd);
235 }
236 }
237
238 if(crosspr_abs2 < 0.00001) continue; //too small
239
240 if(reverse) //2 and 3 in reverse order
241 theFace.nor = crosspr.normal();
242 else
243 theFace.nor = -crosspr.normal();
244
245 theFace.d = p1.dotProduct(theFace.nor);
246
247 /*
248 printf(" Face %d: p1=(%.2f,%.2f,%.2f) p2=(%.2f,%.2f,%.2f)\n"
249 " p3=(%.2f,%.2f,%.2f) nor=(%.3f,%.3f,%.3f) d=%.2f\n",
250 i,
251 //p1.x, p1.y, p1.z,
252 //p2.x, p2.y, p2.z,
253 //p3.x, p3.y, p3.z,
254 theFace[0].x, theFace[0].y, theFace[0].z,
255 theFace[1].x, theFace[1].y, theFace[1].z,
256 theFace[2].x, theFace[2].y, theFace[2].z,
257 theFace.nor.x, theFace.nor.y, theFace.nor.z,
258 theFace.d
259 );
260 */
261 }
262 }
263