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