2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
34 #include "autocaulk.h"
36 // Note: the code in here looks pretty goofy in places, and probably doesn't use the new Q4 class stuff fully,
37 // but I just got it in and compiling from the JK2/SOF2 Radiants via some ugly code replaces, and it works, so there.
38 // Also, a bunch of Radiant fields no longer exist in this codebase, likewise the whole point of passing in the bool
39 // to this code, but I've just left it as-is. A designer tested it and pronounced it fine.
41 //#pragma warning( disable : 4786)
43 //using namespace std;
44 //#pragma warning( disable : 4786)
47 #define strnicmp idStr::Icmpn
52 //extern void ClearBounds (idVec3 mins, idVec3 maxs);
53 //extern void AddPointToBounds (const idVec3 v, idVec3 mins, idVec3 maxs);
54 void ClearBounds (idVec3 &mins, idVec3 &maxs)
56 mins[0] = mins[1] = mins[2] = 99999;
57 maxs[0] = maxs[1] = maxs[2] = -99999;
60 void AddPointToBounds( const idVec3 &v, idVec3 &mins, idVec3 &maxs )
76 static void FloorBounds(idVec3 &mins, idVec3 &maxs)
78 for (int i=0 ; i<3 ; i++)
80 mins[i] = floor(mins[i] + 0.5);
81 maxs[i] = floor(maxs[i] + 0.5);
86 static LPCSTR vtos(idVec3 &v3)
88 return va("%.3ff,%.3f,%.3f",v3[0],v3[1],v3[2]);
90 struct PairBrushFace_t
95 idList < PairBrushFace_t > FacesToCaulk;
96 void Select_AutoCaulk()
98 /*Sys_Printf*/common->Printf("Caulking...\n");
100 FacesToCaulk.Clear();
102 int iSystemBrushesSkipped = 0;
103 face_t *pSelectedFace;
106 for (brush_t *pSelectedBrush = selected_brushes.next ; pSelectedBrush != &selected_brushes ; pSelectedBrush = next)
108 next = pSelectedBrush->next;
110 if (pSelectedBrush->owner->eclass->fixedsize)
111 continue; // apparently this means it's a model, so skip it...
113 // new check, we can't caulk a brush that has any "system/" faces...
115 bool bSystemFacePresent = false;
116 for ( pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next)
118 if (!strnicmp(pSelectedFace->d_texture->GetName(),"system/",7))
120 bSystemFacePresent = true;
124 if (bSystemFacePresent)
126 iSystemBrushesSkipped++;
127 continue; // verboten to caulk this.
130 for (int iBrushListToScan = 0; iBrushListToScan<2; iBrushListToScan++)
133 for (brush_t *pScannedBrush = (iBrushListToScan?active_brushes.next:selected_brushes.next); pScannedBrush != (iBrushListToScan?&active_brushes:&selected_brushes) ; pScannedBrush = snext)
135 snext = pScannedBrush->next;
137 if ( pScannedBrush == pSelectedBrush)
140 if (pScannedBrush->owner->eclass->fixedsize || pScannedBrush->pPatch || pScannedBrush->hiddenBrush)
143 if (FilterBrush(pScannedBrush))
146 // idMaterial stuff no longer support this, not sure what else to do.
147 // Searching for other occurences of QER_NOCARVE just shows people REMing the code and ignoring ths issue...
149 // if (pScannedBrush->brush_faces->d_texture->bFromShader && (pScannedBrush->brush_faces->d_texture->TestMaterialFlag(QER_NOCARVE)))
152 // basic-reject first to see if brushes can even possibly touch (coplanar counts as touching)
155 for (i=0 ; i<3 ; i++)
157 if (pSelectedBrush->mins[i] > pScannedBrush->maxs[i] ||
158 pSelectedBrush->maxs[i] < pScannedBrush->mins[i])
164 continue; // can't be touching
166 // ok, now for the clever stuff, we need to detect only those faces that are both coplanar and smaller
167 // or equal to the face they're coplanar with...
169 for (pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next)
171 idWinding *pSelectedWinding = pSelectedFace->face_winding;
173 if (!pSelectedWinding)
174 continue; // freed face, probably won't happen here, but who knows with this program?
176 // SquaredFace_t SelectedSquaredFace;
177 // WindingToSquaredFace( &SelectedSquaredFace, pSelectedWinding);
179 for (face_t *pScannedFace = pScannedBrush->brush_faces; pScannedFace; pScannedFace = pScannedFace->next)
181 // don't even try caulking against a system face, because these are often transparent and will leave holes
183 if (!strnicmp(pScannedFace->d_texture->GetName(),"system/",7))
186 // and don't try caulking against something inherently transparent...
188 if (pScannedFace->d_texture->TestMaterialFlag(QER_TRANS))
191 idWinding *pScannedWinding = pScannedFace->face_winding;
193 if (!pScannedWinding)
194 continue; // freed face, probably won't happen here, but who knows with this program?
196 // SquaredFace_t ScannedSquaredFace;
197 // WindingToSquaredFace( &ScannedSquaredFace, pScannedWinding);
199 /* if (VectorCompare(ScannedSquaredFace.v3NormalisedRotationVector, SelectedSquaredFace.v3NormalisedRotationVector)
201 VectorCompare(ScannedSquaredFace.v3NormalisedElevationVector, SelectedSquaredFace.v3NormalisedElevationVector)
205 // brush faces are in parallel planes to each other, so check that their normals
206 // are opposite, by adding them together and testing for zero...
207 // (if normals are opposite, then faces can be against/touching each other?)
210 idVec3 v3Zero;v3Zero.Zero(); //static idVec3 v3Zero={0,0,0};
212 VectorAdd(pSelectedFace->plane.Normal(),pScannedFace->plane.Normal(),v3ZeroTest);
213 if (v3ZeroTest == v3Zero)
215 // planes are facing each other...
217 // coplanar? (this is some maths of Gil's, which I don't even pretend to understand)
219 float fTotalDist = 0;
220 for (int _i=0; _i<3; _i++)
222 fTotalDist += fabs( DotProduct(pSelectedFace->plane.Normal(),(*pSelectedWinding)[0])
224 DotProduct(pSelectedFace->plane.Normal(),(*pScannedWinding)[i])
227 //OutputDebugString(va("Dist = %g\n",fTotalDist));
229 if (fTotalDist > 0.01)
232 // every point in the selected face must be within (or equal to) the bounds of the
235 // work out the bounds first...
237 idVec3 v3ScannedBoundsMins, v3ScannedBoundsMaxs;
238 ClearBounds (v3ScannedBoundsMins, v3ScannedBoundsMaxs);
240 for (iPoint=0; iPoint<pScannedWinding->GetNumPoints(); iPoint++)
242 AddPointToBounds( (*pScannedWinding)[iPoint].ToVec3(), v3ScannedBoundsMins, v3ScannedBoundsMaxs);
244 // floor 'em... (or .001 differences mess things up...
246 FloorBounds(v3ScannedBoundsMins, v3ScannedBoundsMaxs);
249 // now check points from selected face...
252 for (iPoint=0; iPoint < pSelectedWinding->GetNumPoints(); iPoint++)
254 for (int iXYZ=0; iXYZ<3; iXYZ++)
256 float f = floor((*pSelectedWinding)[iPoint][iXYZ] + 0.5);
259 f >= v3ScannedBoundsMins[iXYZ]
261 f <= v3ScannedBoundsMaxs[iXYZ]
272 PairBrushFace_t PairBrushFace;
273 PairBrushFace.pFace = pSelectedFace;
274 PairBrushFace.pBrush= pSelectedBrush;
275 FacesToCaulk.Append(PairBrushFace);
288 int iFacesCaulked = 0;
289 if (FacesToCaulk.Num())
291 LPCSTR psCaulkName = "textures/common/caulk";
292 const idMaterial *pCaulk = Texture_ForName(psCaulkName);
297 // and call some other junk that Radiant wants so so we can use it later...
300 memset (&tex, 0, sizeof(tex));
303 //tex.flags = pCaulk->flags; // field missing in Q4
304 //tex.value = pCaulk->value; // ditto
305 //tex.contents = pCaulk->contents; // ditto
306 tex.SetName( pCaulk->GetName() );
308 //Texture_SetTexture (&tex);
310 for (int iListEntry = 0; iListEntry < FacesToCaulk.Num(); iListEntry++)
312 PairBrushFace_t &PairBrushFace = FacesToCaulk[iListEntry];
313 face_t *pFace = PairBrushFace.pFace;
314 brush_t*pBrush= PairBrushFace.pBrush;
316 pFace->d_texture = pCaulk;
319 Face_FitTexture(pFace, 1, 1); // this doesn't work here for some reason... duh.
327 /*Sys_Printf*/common->Printf(" Unable to locate caulk texture at: \"%s\"!\n",psCaulkName);
331 /*Sys_Printf*/common->Printf("( %d faces caulked )\n",iFacesCaulked);
333 if (iSystemBrushesSkipped)
335 /*Sys_Printf*/common->Printf("( %d system-faced brushes skipped )\n",iSystemBrushesSkipped);
338 Sys_UpdateWindows (W_ALL);