]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/brush_primit.cpp
added buffering to minimise GtkTextBuffer insert calls
[xonotic/netradiant.git] / radiant / brush_primit.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "brush_primit.h"
23
24 #include "debugging/debugging.h"
25
26 #include "itexdef.h"
27 #include "itextures.h"
28
29 #include <algorithm>
30
31 #include "stringio.h"
32 #include "texturelib.h"
33 #include "math/matrix.h"
34 #include "math/plane.h"
35
36 #include "winding.h"
37 #include "preferences.h"
38
39
40 /*!
41 \brief Construct a transform from XYZ space to ST space (3d to 2d).
42 This will be one of three axis-aligned spaces, depending on the surface normal.
43 NOTE: could also be done by swapping values.
44 */
45 void Normal_GetTransform(const Vector3& normal, Matrix4& transform)
46 {
47   switch (projectionaxis_for_normal(normal))
48   {
49   case eProjectionAxisZ:
50     transform[0]  =  1;
51     transform[1]  =  0;
52     transform[2]  =  0;
53     
54     transform[4]  =  0;
55     transform[5]  =  1;
56     transform[6]  =  0;
57     
58     transform[8]  =  0;
59     transform[9]  =  0;
60     transform[10] =  1;
61     break;
62   case eProjectionAxisY:
63     transform[0]  =  1;
64     transform[1]  =  0;
65     transform[2]  =  0;
66     
67     transform[4]  =  0;
68     transform[5]  =  0;
69     transform[6]  = -1;
70     
71     transform[8]  =  0;
72     transform[9]  =  1;
73     transform[10] =  0;
74     break;
75   case eProjectionAxisX:
76     transform[0]  =  0;
77     transform[1]  =  0;
78     transform[2]  =  1;
79     
80     transform[4]  =  1;
81     transform[5]  =  0;
82     transform[6]  =  0;
83     
84     transform[8]  =  0;
85     transform[9]  =  1;
86     transform[10] =  0;
87     break;
88   }
89   transform[3] = transform[7] = transform[11] = transform[12] = transform[13] = transform[14] = 0;
90   transform[15] = 1;
91 }
92
93 /*!
94 \brief Construct a transform in ST space from the texdef.
95 Transforms constructed from quake's texdef format are (-shift)*(1/scale)*(-rotate) with x translation sign flipped.
96 This would really make more sense if it was inverseof(shift*rotate*scale).. oh well.
97 */
98 inline void Texdef_toTransform(const texdef_t& texdef, float width, float height, Matrix4& transform)
99 {
100   double inverse_scale[2];
101   
102   // transform to texdef shift/scale/rotate
103   inverse_scale[0] = 1 / (texdef.scale[0] * width);
104   inverse_scale[1] = 1 / (texdef.scale[1] * -height);
105   transform[12] = texdef.shift[0] / width;
106   transform[13] = -texdef.shift[1] / -height;
107   double c = cos(degrees_to_radians(-texdef.rotate));
108   double s = sin(degrees_to_radians(-texdef.rotate));
109   transform[0] = static_cast<float>(c * inverse_scale[0]);
110   transform[1] = static_cast<float>(s * inverse_scale[1]);
111   transform[4] = static_cast<float>(-s * inverse_scale[0]);
112   transform[5] = static_cast<float>(c * inverse_scale[1]);
113   transform[2] = transform[3] = transform[6] = transform[7] = transform[8] = transform[9] = transform[11] = transform[14] = 0;
114   transform[10] = transform[15] = 1;
115 }
116
117 inline void BPTexdef_toTransform(const brushprimit_texdef_t& bp_texdef, Matrix4& transform)
118 {
119   transform = g_matrix4_identity;
120   transform.xx() = bp_texdef.coords[0][0];
121   transform.yx() = bp_texdef.coords[0][1];
122   transform.tx() = bp_texdef.coords[0][2];
123   transform.xy() = bp_texdef.coords[1][0];
124   transform.yy() = bp_texdef.coords[1][1];
125   transform.ty() = bp_texdef.coords[1][2];
126 }
127
128 inline void Texdef_toTransform(const TextureProjection& projection, float width, float height, Matrix4& transform)
129 {
130   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
131   {
132     BPTexdef_toTransform(projection.m_brushprimit_texdef, transform);
133   }
134   else
135   {
136     Texdef_toTransform(projection.m_texdef, width, height, transform);
137   }
138 }
139
140 // handles degenerate cases, just in case library atan2 doesn't
141 inline double arctangent_yx(double y, double x)
142 {
143   if(fabs(x) > 1.0E-6)
144   {
145     return atan2(y, x);
146   }
147   else if(y > 0)
148   {
149                 return c_half_pi;
150   }
151   else
152   {
153                 return -c_half_pi;
154   }
155 }
156
157 inline void Texdef_fromTransform(texdef_t& texdef, float width, float height, const Matrix4& transform)
158 {
159   texdef.scale[0] = static_cast<float>((1.0 / vector2_length(Vector2(transform[0], transform[4]))) / width);
160   texdef.scale[1] = static_cast<float>((1.0 / vector2_length(Vector2(transform[1], transform[5]))) / height);
161
162   texdef.rotate = static_cast<float>(-radians_to_degrees(arctangent_yx(-transform[4], transform[0])));
163
164   if(texdef.rotate == -180.0f)
165   {
166     texdef.rotate = 180.0f;
167   }
168
169   texdef.shift[0] = transform[12] * width;
170   texdef.shift[1] = transform[13] * height;
171
172   // If the 2d cross-product of the x and y axes is positive, one of the axes has a negative scale.
173   if(vector2_cross(Vector2(transform[0], transform[4]), Vector2(transform[1], transform[5])) > 0)
174   {
175     if(texdef.rotate >= 180.0f)
176     {
177       texdef.rotate -= 180.0f;
178       texdef.scale[0] = -texdef.scale[0];
179     }
180     else
181     {
182       texdef.scale[1] = -texdef.scale[1];
183     }
184   }
185   //globalOutputStream() << "fromTransform: " << texdef.shift[0] << " " << texdef.shift[1] << " " << texdef.scale[0] << " " << texdef.scale[1] << " " << texdef.rotate << "\n";
186 }
187
188 inline void BPTexdef_fromTransform(brushprimit_texdef_t& bp_texdef, const Matrix4& transform)
189 {
190   bp_texdef.coords[0][0] = transform.xx();
191   bp_texdef.coords[0][1] = transform.yx();
192   bp_texdef.coords[0][2] = transform.tx();
193   bp_texdef.coords[1][0] = transform.xy();
194   bp_texdef.coords[1][1] = transform.yy();
195   bp_texdef.coords[1][2] = transform.ty();
196 }
197
198 inline void Texdef_fromTransform(TextureProjection& projection, float width, float height, const Matrix4& transform)
199 {
200   ASSERT_MESSAGE((transform[0] != 0 || transform[4] != 0)
201     && (transform[1] != 0 || transform[5] != 0), "invalid texture matrix");
202
203   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
204   {
205     BPTexdef_fromTransform(projection.m_brushprimit_texdef, transform);
206   }
207   else
208   {
209     Texdef_fromTransform(projection.m_texdef, width, height, transform);
210   }
211 }
212
213 inline void Texdef_normalise(texdef_t& texdef, float width, float height)
214 {
215   // it may be useful to also normalise the rotation here, if this function is used elsewhere.
216   texdef.shift[0] = float_mod(texdef.shift[0], width);
217   texdef.shift[1] = float_mod(texdef.shift[1], height);
218   //globalOutputStream() << "normalise: " << texdef.shift[0] << " " << texdef.shift[1] << " " << texdef.scale[0] << " " << texdef.scale[1] << " " << texdef.rotate << "\n";
219 }
220
221 inline void BPTexdef_normalise(brushprimit_texdef_t& bp_texdef, float width, float height)
222 {
223   bp_texdef.coords[0][2] = float_mod(bp_texdef.coords[0][2], width);
224   bp_texdef.coords[1][2] = float_mod(bp_texdef.coords[1][2], height);
225 }
226
227 /// \brief Normalise \p projection for a given texture \p width and \p height.
228 ///
229 /// All texture-projection translation (shift) values are congruent modulo the dimensions of the texture.
230 /// This function normalises shift values to the smallest positive congruent values.
231 void Texdef_normalise(TextureProjection& projection, float width, float height)
232 {
233   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
234   {
235     BPTexdef_normalise(projection.m_brushprimit_texdef, width, height);
236   }
237   else
238   {
239     Texdef_normalise(projection.m_texdef, width, height);
240   }
241 }
242
243 void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT);
244
245 inline void DebugAxisBase(const Vector3& normal)
246 {
247   Vector3 x, y;
248   ComputeAxisBase(normal, x, y);
249   globalOutputStream() << "BP debug: " << x << y << normal << "\n";
250 }
251
252 void Texdef_basisForNormal(const TextureProjection& projection, const Vector3& normal, Matrix4& basis)
253 {
254   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
255   {
256     basis = g_matrix4_identity;
257     ComputeAxisBase(normal, basis.x(), basis.y());
258     static_cast<Vector3&>(basis.z()) = normal;
259     matrix4_transpose(basis);
260     //DebugAxisBase(normal);
261   }
262   else if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE)
263   {
264     basis = g_matrix4_identity;
265     static_cast<Vector3&>(basis.x()) = projection.m_basis_s;
266     static_cast<Vector3&>(basis.y()) = vector3_negated(projection.m_basis_t);
267     static_cast<Vector3&>(basis.z()) = vector3_normalised(vector3_cross(static_cast<Vector3&>(basis.x()), static_cast<Vector3&>(basis.y())));
268     matrix4_multiply_by_matrix4(basis, matrix4_rotation_for_z_degrees(-projection.m_texdef.rotate));
269     //globalOutputStream() << "debug: " << projection.m_basis_s << projection.m_basis_t << normal << "\n";
270     matrix4_transpose(basis);
271   }
272   else
273   {
274     Normal_GetTransform(normal, basis);
275   }
276 }
277
278 void Texdef_EmitTextureCoordinates(const TextureProjection& projection, std::size_t width, std::size_t height, Winding& w, const Vector3& normal, const Matrix4& localToWorld)
279 {
280   if(w.numpoints < 3)
281   {
282     return;
283   }
284   //globalOutputStream() << "normal: " << normal << "\n";
285
286   Matrix4 local2tex;
287   Texdef_toTransform(projection, (float)width, (float)height, local2tex);
288   //globalOutputStream() << "texdef: " << static_cast<const Vector3&>(local2tex.x()) << static_cast<const Vector3&>(local2tex.y()) << "\n";
289
290 #if 0
291   {
292     TextureProjection tmp;
293     Texdef_fromTransform(tmp, (float)width, (float)height, local2tex);
294     Matrix4 tmpTransform;
295     Texdef_toTransform(tmp, (float)width, (float)height, tmpTransform);
296     ASSERT_MESSAGE(matrix4_equal_epsilon(local2tex, tmpTransform, 0.0001f), "bleh");
297   }
298 #endif
299   
300   {
301     Matrix4 xyz2st; 
302     // we don't care if it's not normalised...
303     Texdef_basisForNormal(projection, matrix4_transformed_direction(localToWorld, normal), xyz2st);
304     //globalOutputStream() << "basis: " << static_cast<const Vector3&>(xyz2st.x()) << static_cast<const Vector3&>(xyz2st.y()) << static_cast<const Vector3&>(xyz2st.z()) << "\n";
305     matrix4_multiply_by_matrix4(local2tex, xyz2st);
306   }
307
308   Vector3 tangent(vector3_normalised(vector4_to_vector3(matrix4_transposed(local2tex).x())));
309   Vector3 bitangent(vector3_normalised(vector4_to_vector3(matrix4_transposed(local2tex).y())));
310   
311   matrix4_multiply_by_matrix4(local2tex, localToWorld);
312
313   for(Winding::iterator i = w.begin(); i != w.end(); ++i)
314   {
315     Vector3 texcoord = matrix4_transformed_point(local2tex, (*i).vertex);
316     (*i).texcoord[0] = texcoord[0];
317     (*i).texcoord[1] = texcoord[1];
318
319     (*i).tangent = tangent;
320     (*i).bitangent = bitangent;
321   }
322 }
323
324 /*!
325 \brief Provides the axis-base of the texture ST space for this normal,
326 as they had been transformed to world XYZ space.
327 */
328 void TextureAxisFromNormal(const Vector3& normal, Vector3& s, Vector3& t)
329 {
330   switch (projectionaxis_for_normal(normal))
331   {
332   case eProjectionAxisZ:
333     s[0]  =  1;
334     s[1]  =  0;
335     s[2]  =  0;
336     
337     t[0]  =  0;
338     t[1]  = -1;
339     t[2]  =  0;
340
341     break;
342   case eProjectionAxisY:
343     s[0]  =  1;
344     s[1]  =  0;
345     s[2]  =  0;
346     
347     t[0]  =  0;
348     t[1]  =  0;
349     t[2]  = -1;
350
351     break;
352   case eProjectionAxisX:
353     s[0]  =  0;
354     s[1]  =  1;
355     s[2]  =  0;
356     
357     t[0]  =  0;
358     t[1]  =  0;
359     t[2]  = -1;
360
361     break;
362   }
363 }
364
365 void Texdef_Assign(texdef_t& td, const texdef_t& other)
366 {
367   td = other;
368 }
369
370 void Texdef_Shift(texdef_t& td, float s, float t)
371 {
372         td.shift[0] += s;
373         td.shift[1] += t;
374 }
375
376 void Texdef_Scale(texdef_t& td, float s, float t)
377 {
378         td.scale[0] += s;
379         td.scale[1] += t;
380 }
381
382 void Texdef_Rotate(texdef_t& td, float angle)
383 {
384         td.rotate += angle;
385         td.rotate = static_cast<float>(float_to_integer(td.rotate) % 360);
386 }
387
388 // NOTE: added these from Ritual's Q3Radiant
389 void ClearBounds(Vector3& mins, Vector3& maxs)
390 {
391         mins[0] = mins[1] = mins[2] = 99999;
392         maxs[0] = maxs[1] = maxs[2] = -99999;
393 }
394
395 void AddPointToBounds(const Vector3& v, Vector3& mins, Vector3& maxs)
396 {
397         int             i;
398         float   val;
399         
400         for (i=0 ; i<3 ; i++)
401         {
402                 val = v[i];
403                 if (val < mins[i])
404                         mins[i] = val;
405                 if (val > maxs[i])
406                         maxs[i] = val;
407         }
408 }
409
410 void Texdef_FitTexture(texdef_t& td, std::size_t width, std::size_t height, const Vector3& normal, const Winding& w, float s_repeat, float t_repeat)
411 {
412   float temp;
413   float rot_width, rot_height;
414   float cosv,sinv;
415   float min_t, min_s, max_t, max_s;
416   float s,t;
417   Vector3       vecs[2];
418   Vector3 coords[4];
419
420   Vector3 mins, maxs;
421   
422   if(s_repeat == 0)
423     s_repeat = 1;
424    if(t_repeat == 0)
425     t_repeat = 1;
426  
427   {
428     ClearBounds(mins, maxs);
429     for(Winding::const_iterator i = w.begin(); i != w.end(); ++i)
430     {
431       AddPointToBounds((*i).vertex, mins, maxs);
432     }
433   }
434   
435   //
436   // get the current angle
437   //
438   {
439     double ang = degrees_to_radians(td.rotate);
440     sinv = static_cast<float>(sin(ang));
441     cosv = static_cast<float>(cos(ang));
442   }
443     
444   // get natural texture axis
445   TextureAxisFromNormal(normal, vecs[0], vecs[1]);
446   
447   min_s = static_cast<float>(vector3_dot(mins, vecs[0]));
448   min_t = static_cast<float>(vector3_dot(mins, vecs[1]));
449   max_s = static_cast<float>(vector3_dot(maxs, vecs[0]));
450   max_t = static_cast<float>(vector3_dot(maxs, vecs[1]));
451   coords[0][0] = min_s;
452   coords[0][1] = min_t;
453   coords[1][0] = max_s;
454   coords[1][1] = min_t;
455   coords[2][0] = min_s;
456   coords[2][1] = max_t;
457   coords[3][0] = max_s;
458   coords[3][1] = max_t;
459   min_s = min_t = 99999;
460   max_s = max_t = -99999;
461   for (int i=0; i<4; i++)
462   {
463     s = cosv * coords[i][0] - sinv * coords[i][1];
464     t = sinv * coords[i][0] + cosv * coords[i][1];
465     if (i&1)
466     {
467       if (s > max_s)
468       {
469         max_s = s;
470       }
471     }
472     else
473     {
474       if (s < min_s)
475       {
476         min_s = s;
477       }
478       if (i<2)
479       {
480         if (t < min_t)
481         {
482           min_t = t;
483         }
484       }
485       else
486       {
487         if (t > max_t)
488         {
489           max_t = t;
490         }
491       }
492     }
493   }
494   rot_width =  (max_s - min_s);
495   rot_height = (max_t - min_t);
496   td.scale[0] = -(rot_width/(static_cast<float>(width) * s_repeat));
497   td.scale[1] = -(rot_height/(static_cast<float>(height) * t_repeat));
498   
499   td.shift[0] = min_s/td.scale[0];
500   temp = static_cast<float>(float_to_integer(td.shift[0] / (static_cast<float>(width) * s_repeat)));
501   temp = (temp+1)*static_cast<float>(width) * s_repeat;
502   td.shift[0] = static_cast<float>(float_to_integer(temp - td.shift[0]) % static_cast<int>(static_cast<float>(width) * s_repeat));
503   
504   td.shift[1] = min_t/td.scale[1];
505   temp = static_cast<float>(float_to_integer(td.shift[1] / (static_cast<float>(height) * t_repeat)));
506   temp = (temp+1)*(static_cast<float>(height) * t_repeat);
507   td.shift[1] = static_cast<float>(float_to_integer(temp - td.shift[1]) % static_cast<int>(static_cast<float>(height) * t_repeat));
508 }
509
510
511 // low level functions .. put in mathlib?
512 #define BPMatCopy(a,b) {b[0][0] = a[0][0]; b[0][1] = a[0][1]; b[0][2] = a[0][2]; b[1][0] = a[1][0]; b[1][1] = a[1][1]; b[1][2] = a[1][2];}
513 // apply a scale transformation to the BP matrix
514 #define BPMatScale(m,sS,sT) {m[0][0]*=sS; m[1][0]*=sS; m[0][1]*=sT; m[1][1]*=sT;}
515 // apply a translation transformation to a BP matrix
516 #define BPMatTranslate(m,s,t) {m[0][2] += m[0][0]*s + m[0][1]*t; m[1][2] += m[1][0]*s+m[1][1]*t;}
517 // 2D homogeneous matrix product C = A*B
518 void BPMatMul(float A[2][3], float B[2][3], float C[2][3]);
519 // apply a rotation (degrees)
520 void BPMatRotate(float A[2][3], float theta);
521 #ifdef _DEBUG
522 void BPMatDump(float A[2][3]);
523 #endif
524
525 #ifdef _DEBUG
526 //#define DBG_BP
527 #endif
528
529
530 bp_globals_t g_bp_globals;
531 float g_texdef_default_scale;
532
533 // compute a determinant using Sarrus rule
534 //++timo "inline" this with a macro
535 // NOTE : the three vectors are understood as columns of the matrix
536 inline float SarrusDet(const Vector3& a, const Vector3& b, const Vector3& c)
537 {
538         return a[0]*b[1]*c[2]+b[0]*c[1]*a[2]+c[0]*a[1]*b[2]
539                 -c[0]*b[1]*a[2]-a[1]*b[0]*c[2]-a[0]*b[2]*c[1];
540 }
541
542 // in many case we know three points A,B,C in two axis base B1 and B2
543 // and we want the matrix M so that A(B1) = T * A(B2)
544 // NOTE: 2D homogeneous space stuff
545 // NOTE: we don't do any check to see if there's a solution or we have a particular case .. need to make sure before calling
546 // NOTE: the third coord of the A,B,C point is ignored
547 // NOTE: see the commented out section to fill M and D
548 //++timo TODO: update the other members to use this when possible
549 void MatrixForPoints( Vector3 M[3], Vector3 D[2], brushprimit_texdef_t *T )
550 {
551 //      Vector3 M[3]; // columns of the matrix .. easier that way (the indexing is not standard! it's column-line .. later computations are easier that way)
552         float det;
553 //      Vector3 D[2];
554         M[2][0]=1.0f; M[2][1]=1.0f; M[2][2]=1.0f;
555 #if 0
556         // fill the data vectors
557         M[0][0]=A2[0]; M[0][1]=B2[0]; M[0][2]=C2[0];
558         M[1][0]=A2[1]; M[1][1]=B2[1]; M[1][2]=C2[1];
559         M[2][0]=1.0f; M[2][1]=1.0f; M[2][2]=1.0f;
560         D[0][0]=A1[0];
561         D[0][1]=B1[0];
562         D[0][2]=C1[0];
563         D[1][0]=A1[1];
564         D[1][1]=B1[1];
565         D[1][2]=C1[1];
566 #endif
567         // solve
568         det = SarrusDet( M[0], M[1], M[2] );
569         T->coords[0][0] = SarrusDet( D[0], M[1], M[2] ) / det;
570         T->coords[0][1] = SarrusDet( M[0], D[0], M[2] ) / det;
571         T->coords[0][2] = SarrusDet( M[0], M[1], D[0] ) / det;
572         T->coords[1][0] = SarrusDet( D[1], M[1], M[2] ) / det;
573         T->coords[1][1] = SarrusDet( M[0], D[1], M[2] ) / det;
574         T->coords[1][2] = SarrusDet( M[0], M[1], D[1] ) / det;
575 }
576
577 //++timo replace everywhere texX by texS etc. ( ----> and in q3map !) 
578 // NOTE : ComputeAxisBase here and in q3map code must always BE THE SAME !
579 // WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
580 // rotation by (0,RotY,RotZ) assigns X to normal
581 void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT)
582 {
583 #if 1
584   const Vector3 up(0, 0, 1);
585   const Vector3 down(0, 0, -1);
586
587   if(vector3_equal_epsilon(normal, up, float(1e-6)))
588   {
589     texS = Vector3(0, 1, 0);
590     texT = Vector3(1, 0, 0);
591   }
592   else if(vector3_equal_epsilon(normal, down, float(1e-6)))
593   {
594     texS = Vector3(0, 1, 0);
595     texT = Vector3(-1, 0, 0);
596   }
597   else
598   {
599     texS = vector3_normalised(vector3_cross(normal, up));
600     texT = vector3_normalised(vector3_cross(normal, texS));
601     vector3_negate(texS);
602   }
603
604 #else
605         float RotY,RotZ;
606         // do some cleaning
607   /*
608         if (fabs(normal[0])<1e-6)
609                 normal[0]=0.0f;
610         if (fabs(normal[1])<1e-6)
611                 normal[1]=0.0f;
612         if (fabs(normal[2])<1e-6)
613                 normal[2]=0.0f;
614     */
615         RotY=-atan2(normal[2],sqrt(normal[1]*normal[1]+normal[0]*normal[0]));
616         RotZ=atan2(normal[1],normal[0]);
617         // rotate (0,1,0) and (0,0,1) to compute texS and texT
618         texS[0]=-sin(RotZ);
619         texS[1]=cos(RotZ);
620         texS[2]=0;
621         // the texT vector is along -Z ( T texture coorinates axis )
622         texT[0]=-sin(RotY)*cos(RotZ);
623         texT[1]=-sin(RotY)*sin(RotZ);
624         texT[2]=-cos(RotY);
625 #endif
626 }
627
628 #if 0 // texdef conversion
629 void FaceToBrushPrimitFace(face_t *f)
630 {
631         Vector3 texX,texY;
632         Vector3 proj;
633         // ST of (0,0) (1,0) (0,1)
634         float ST[3][5]; // [ point index ] [ xyz ST ]
635         //++timo not used as long as brushprimit_texdef and texdef are static
636 /*      f->brushprimit_texdef.contents=f->texdef.contents;
637         f->brushprimit_texdef.flags=f->texdef.flags;
638         f->brushprimit_texdef.value=f->texdef.value;
639         strcpy(f->brushprimit_texdef.name,f->texdef.name); */
640 #ifdef DBG_BP
641         if ( f->plane.normal[0]==0.0f && f->plane.normal[1]==0.0f && f->plane.normal[2]==0.0f )
642         {
643                 globalOutputStream() << "Warning : f->plane.normal is (0,0,0) in FaceToBrushPrimitFace\n";
644         }
645         // check d_texture
646         if (!f->d_texture)
647         {
648                 globalOutputStream() << "Warning : f.d_texture is 0 in FaceToBrushPrimitFace\n";
649                 return;
650         }
651 #endif
652         // compute axis base
653         ComputeAxisBase(f->plane.normal,texX,texY);
654         // compute projection vector
655         VectorCopy(f->plane.normal,proj);
656         VectorScale(proj,f->plane.dist,proj);
657         // (0,0) in plane axis base is (0,0,0) in world coordinates + projection on the affine plane
658         // (1,0) in plane axis base is texX in world coordinates + projection on the affine plane
659         // (0,1) in plane axis base is texY in world coordinates + projection on the affine plane
660         // use old texture code to compute the ST coords of these points
661         VectorCopy(proj,ST[0]);
662         EmitTextureCoordinates(ST[0], f->pShader->getTexture(), f);
663         VectorCopy(texX,ST[1]);
664         VectorAdd(ST[1],proj,ST[1]);
665         EmitTextureCoordinates(ST[1], f->pShader->getTexture(), f);
666         VectorCopy(texY,ST[2]);
667         VectorAdd(ST[2],proj,ST[2]);
668         EmitTextureCoordinates(ST[2], f->pShader->getTexture(), f);
669         // compute texture matrix
670         f->brushprimit_texdef.coords[0][2]=ST[0][3];
671         f->brushprimit_texdef.coords[1][2]=ST[0][4];
672         f->brushprimit_texdef.coords[0][0]=ST[1][3]-f->brushprimit_texdef.coords[0][2];
673         f->brushprimit_texdef.coords[1][0]=ST[1][4]-f->brushprimit_texdef.coords[1][2];
674         f->brushprimit_texdef.coords[0][1]=ST[2][3]-f->brushprimit_texdef.coords[0][2];
675         f->brushprimit_texdef.coords[1][1]=ST[2][4]-f->brushprimit_texdef.coords[1][2];
676 }
677
678 // compute texture coordinates for the winding points
679 void EmitBrushPrimitTextureCoordinates(face_t * f, Winding * w)
680 {
681         Vector3 texX,texY;
682         float x,y;
683         // compute axis base
684         ComputeAxisBase(f->plane.normal,texX,texY);
685         // in case the texcoords matrix is empty, build a default one
686         // same behaviour as if scale[0]==0 && scale[1]==0 in old code
687         if (f->brushprimit_texdef.coords[0][0]==0 && f->brushprimit_texdef.coords[1][0]==0 && f->brushprimit_texdef.coords[0][1]==0 && f->brushprimit_texdef.coords[1][1]==0)
688         {
689                 f->brushprimit_texdef.coords[0][0] = 1.0f;
690                 f->brushprimit_texdef.coords[1][1] = 1.0f;
691                 ConvertTexMatWithQTexture( &f->brushprimit_texdef, 0, &f->brushprimit_texdef, f->pShader->getTexture() );
692         }
693         int i;
694     for (i=0 ; i<w.numpoints ; i++)
695         {
696                 x=vector3_dot(w.point_at(i),texX);
697                 y=vector3_dot(w.point_at(i),texY);
698 #if 0
699 #ifdef DBG_BP
700                 if (g_bp_globals.bNeedConvert)
701                 {
702                         // check we compute the same ST as the traditional texture computation used before
703                         float S=f->brushprimit_texdef.coords[0][0]*x+f->brushprimit_texdef.coords[0][1]*y+f->brushprimit_texdef.coords[0][2];
704                         float T=f->brushprimit_texdef.coords[1][0]*x+f->brushprimit_texdef.coords[1][1]*y+f->brushprimit_texdef.coords[1][2];
705                         if ( fabs(S-w.point_at(i)[3])>1e-2 || fabs(T-w.point_at(i)[4])>1e-2 )
706                         {
707                                 if ( fabs(S-w.point_at(i)[3])>1e-4 || fabs(T-w.point_at(i)[4])>1e-4 )
708                                         globalOutputStream() << "Warning : precision loss in brush -> brush primitive texture computation\n";
709                                 else
710                                         globalOutputStream() << "Warning : brush -> brush primitive texture computation bug detected\n";
711                         }
712                 }
713 #endif
714 #endif
715                 w.point_at(i)[3]=f->brushprimit_texdef.coords[0][0]*x+f->brushprimit_texdef.coords[0][1]*y+f->brushprimit_texdef.coords[0][2];
716                 w.point_at(i)[4]=f->brushprimit_texdef.coords[1][0]*x+f->brushprimit_texdef.coords[1][1]*y+f->brushprimit_texdef.coords[1][2];
717         }
718 }
719 #endif
720
721 typedef float texmat_t[2][3];
722
723 void TexMat_Scale(texmat_t texmat, float s, float t)
724 {
725         texmat[0][0] *= s;
726         texmat[0][1] *= s;
727         texmat[0][2] *= s;
728         texmat[1][0] *= t;
729         texmat[1][1] *= t;
730         texmat[1][2] *= t;
731 }
732
733 void TexMat_Assign(texmat_t texmat, const texmat_t other)
734 {
735         texmat[0][0] = other[0][0];
736         texmat[0][1] = other[0][1];
737         texmat[0][2] = other[0][2];
738         texmat[1][0] = other[1][0];
739         texmat[1][1] = other[1][1];
740         texmat[1][2] = other[1][2];
741 }
742
743 void ConvertTexMatWithDimensions(const texmat_t texmat1, std::size_t w1, std::size_t h1,
744                                  texmat_t texmat2, std::size_t w2, std::size_t h2)
745 {
746   TexMat_Assign(texmat2, texmat1);
747   TexMat_Scale(texmat2, static_cast<float>(w1) / static_cast<float>(w2), static_cast<float>(h1) / static_cast<float>(h2));
748 }
749
750 #if 0
751 // convert a texture matrix between two qtexture_t
752 // if 0 for qtexture_t, basic 2x2 texture is assumed ( straight mapping between s/t coordinates and geometric coordinates )
753 void ConvertTexMatWithQTexture( const float texMat1[2][3], const qtexture_t *qtex1, float texMat2[2][3], const qtexture_t *qtex2 )
754 {
755   ConvertTexMatWithDimensions(texMat1, (qtex1) ? qtex1->width : 2, (qtex1) ? qtex1->height : 2,
756                               texMat2, (qtex2) ? qtex2->width : 2, (qtex2) ? qtex2->height : 2);
757 }
758
759 void ConvertTexMatWithQTexture( const brushprimit_texdef_t *texMat1, const qtexture_t *qtex1, brushprimit_texdef_t *texMat2, const qtexture_t *qtex2 )
760 {
761   ConvertTexMatWithQTexture(texMat1->coords, qtex1, texMat2->coords, qtex2);
762 }
763 #endif
764
765 // compute a fake shift scale rot representation from the texture matrix
766 // these shift scale rot values are to be understood in the local axis base
767 // Note: this code looks similar to Texdef_fromTransform, but the algorithm is slightly different.
768
769 void TexMatToFakeTexCoords(const brushprimit_texdef_t& bp_texdef, texdef_t& texdef)
770 {
771   texdef.scale[0] = static_cast<float>(1.0 / vector2_length(Vector2(bp_texdef.coords[0][0], bp_texdef.coords[1][0])));
772   texdef.scale[1] = static_cast<float>(1.0 / vector2_length(Vector2(bp_texdef.coords[0][1], bp_texdef.coords[1][1])));
773
774   texdef.rotate = -static_cast<float>(radians_to_degrees(arctangent_yx(bp_texdef.coords[1][0], bp_texdef.coords[0][0])));
775
776   texdef.shift[0] = -bp_texdef.coords[0][2];
777   texdef.shift[1] = bp_texdef.coords[1][2];
778
779   // determine whether or not an axis is flipped using a 2d cross-product
780   double cross = vector2_cross(Vector2(bp_texdef.coords[0][0], bp_texdef.coords[0][1]), Vector2(bp_texdef.coords[1][0], bp_texdef.coords[1][1]));
781   if(cross < 0)
782   {
783     // This is a bit of a compromise when using BPs--since we don't know *which* axis was flipped,
784     // we pick one (rather arbitrarily) using the following convention: If the X-axis is between
785     // 0 and 180, we assume it's the Y-axis that flipped, otherwise we assume it's the X-axis and
786     // subtract out 180 degrees to compensate.
787     if(texdef.rotate >= 180.0f)
788     {
789       texdef.rotate -= 180.0f;
790       texdef.scale[0] = -texdef.scale[0];
791     }
792     else
793     {
794       texdef.scale[1] = -texdef.scale[1];
795     }
796   }
797 }
798
799 // compute back the texture matrix from fake shift scale rot
800 void FakeTexCoordsToTexMat(const texdef_t& texdef, brushprimit_texdef_t& bp_texdef)
801 {
802   double r = degrees_to_radians(-texdef.rotate);
803   double c = cos(r);
804   double s = sin(r);
805   double x = 1.0f / texdef.scale[0];
806   double y = 1.0f / texdef.scale[1];
807   bp_texdef.coords[0][0] = static_cast<float>(x * c);
808   bp_texdef.coords[1][0] = static_cast<float>(x * s);
809   bp_texdef.coords[0][1] = static_cast<float>(y * -s);
810   bp_texdef.coords[1][1] = static_cast<float>(y * c);
811   bp_texdef.coords[0][2] = -texdef.shift[0];
812   bp_texdef.coords[1][2] = texdef.shift[1];
813 }
814
815 #if 0 // texture locking (brush primit)
816 // used for texture locking
817 // will move the texture according to a geometric vector
818 void ShiftTextureGeometric_BrushPrimit(face_t *f, Vector3& delta)
819 {
820         Vector3 texS,texT;
821         float tx,ty;
822         Vector3 M[3]; // columns of the matrix .. easier that way
823         float det;
824         Vector3 D[2];
825         // compute plane axis base ( doesn't change with translation )
826         ComputeAxisBase( f->plane.normal, texS, texT );
827         // compute translation vector in plane axis base
828         tx = vector3_dot( delta, texS );
829         ty = vector3_dot( delta, texT );
830         // fill the data vectors
831         M[0][0]=tx; M[0][1]=1.0f+tx; M[0][2]=tx;
832         M[1][0]=ty; M[1][1]=ty; M[1][2]=1.0f+ty;
833         M[2][0]=1.0f; M[2][1]=1.0f; M[2][2]=1.0f;
834         D[0][0]=f->brushprimit_texdef.coords[0][2];
835         D[0][1]=f->brushprimit_texdef.coords[0][0]+f->brushprimit_texdef.coords[0][2];
836         D[0][2]=f->brushprimit_texdef.coords[0][1]+f->brushprimit_texdef.coords[0][2];
837         D[1][0]=f->brushprimit_texdef.coords[1][2];
838         D[1][1]=f->brushprimit_texdef.coords[1][0]+f->brushprimit_texdef.coords[1][2];
839         D[1][2]=f->brushprimit_texdef.coords[1][1]+f->brushprimit_texdef.coords[1][2];
840         // solve
841         det = SarrusDet( M[0], M[1], M[2] );
842         f->brushprimit_texdef.coords[0][0] = SarrusDet( D[0], M[1], M[2] ) / det;
843         f->brushprimit_texdef.coords[0][1] = SarrusDet( M[0], D[0], M[2] ) / det;
844         f->brushprimit_texdef.coords[0][2] = SarrusDet( M[0], M[1], D[0] ) / det;
845         f->brushprimit_texdef.coords[1][0] = SarrusDet( D[1], M[1], M[2] ) / det;
846         f->brushprimit_texdef.coords[1][1] = SarrusDet( M[0], D[1], M[2] ) / det;
847         f->brushprimit_texdef.coords[1][2] = SarrusDet( M[0], M[1], D[1] ) / det;
848 }
849
850 // shift a texture (texture adjustments) along it's current texture axes
851 // x and y are geometric values, which we must compute as ST increments
852 // this depends on the texture size and the pixel/texel ratio
853 void ShiftTextureRelative_BrushPrimit( face_t *f, float x, float y)
854 {
855   float s,t;
856   // as a ratio against texture size
857   // the scale of the texture is not relevant here (we work directly on a transformation from the base vectors)
858   s = (x * 2.0) / (float)f->pShader->getTexture().width;
859   t = (y * 2.0) / (float)f->pShader->getTexture().height;
860   f->brushprimit_texdef.coords[0][2] -= s;
861   f->brushprimit_texdef.coords[1][2] -= t;
862 }
863 #endif
864
865 // TTimo: FIXME: I don't like that, it feels broken
866 //   (and it's likely that it's not used anymore)
867 // best fitted 2D vector is x.X+y.Y
868 void ComputeBest2DVector( Vector3& v, Vector3& X, Vector3& Y, int &x, int &y )
869 {
870         double sx,sy;
871         sx = vector3_dot( v, X );
872         sy = vector3_dot( v, Y );
873         if ( fabs(sy) > fabs(sx) )
874   {
875                 x = 0;
876                 if ( sy > 0.0 )
877                         y =  1;
878                 else
879                         y = -1;
880         }
881         else
882         {
883                 y = 0;
884                 if ( sx > 0.0 )
885                         x =  1;
886                 else
887                         x = -1;
888         }
889 }
890
891
892 #if 0 // texdef conversion
893 void BrushPrimitFaceToFace(face_t *face)
894 {
895   // we have parsed brush primitives and need conversion back to standard format
896   // NOTE: converting back is a quick hack, there's some information lost and we can't do anything about it
897   // FIXME: if we normalize the texture matrix to a standard 2x2 size, we end up with wrong scaling
898   // I tried various tweaks, no luck .. seems shifting is lost
899   brushprimit_texdef_t aux;
900   ConvertTexMatWithQTexture( &face->brushprimit_texdef, face->pShader->getTexture(), &aux, 0 );
901   TexMatToFakeTexCoords( aux.coords, face->texdef.shift, &face->texdef.rotate, face->texdef.scale );
902   face->texdef.scale[0]/=2.0;
903   face->texdef.scale[1]/=2.0;
904 }
905 #endif
906
907
908 #if 0 // texture locking (brush primit)
909 // TEXTURE LOCKING -----------------------------------------------------------------------------------------------------
910 // (Relevant to the editor only?)
911
912 // internally used for texture locking on rotation and flipping
913 // the general algorithm is the same for both lockings, it's only the geometric transformation part that changes
914 // so I wanted to keep it in a single function
915 // if there are more linear transformations that need the locking, going to a C++ or code pointer solution would be best
916 // (but right now I want to keep brush_primit.cpp striclty C)
917
918 bool txlock_bRotation;
919
920 // rotation locking params
921 int txl_nAxis;
922 float txl_fDeg;
923 Vector3 txl_vOrigin;
924
925 // flip locking params
926 Vector3 txl_matrix[3];
927 Vector3 txl_origin;
928
929 void TextureLockTransformation_BrushPrimit(face_t *f)
930 {
931   Vector3 Orig,texS,texT;        // axis base of initial plane
932   // used by transformation algo
933   Vector3 temp; int j;
934         Vector3 vRotate;                                        // rotation vector
935
936   Vector3 rOrig,rvecS,rvecT;     // geometric transformation of (0,0) (1,0) (0,1) { initial plane axis base }
937   Vector3 rNormal,rtexS,rtexT;   // axis base for the transformed plane
938         Vector3 lOrig,lvecS,lvecT;      // [2] are not used ( but usefull for debugging )
939         Vector3 M[3];
940         float det;
941         Vector3 D[2];
942
943         // compute plane axis base
944         ComputeAxisBase( f->plane.normal, texS, texT );
945   VectorSet(Orig, 0.0f, 0.0f, 0.0f);
946
947         // compute coordinates of (0,0) (1,0) (0,1) ( expressed in initial plane axis base ) after transformation
948         // (0,0) (1,0) (0,1) ( expressed in initial plane axis base ) <-> (0,0,0) texS texT ( expressed world axis base )
949   // input: Orig, texS, texT (and the global locking params)
950   // ouput: rOrig, rvecS, rvecT, rNormal
951   if (txlock_bRotation) {
952     // rotation vector
953         VectorSet( vRotate, 0.0f, 0.0f, 0.0f );
954         vRotate[txl_nAxis]=txl_fDeg;
955         VectorRotateOrigin ( Orig, vRotate, txl_vOrigin, rOrig );
956         VectorRotateOrigin ( texS, vRotate, txl_vOrigin, rvecS );
957         VectorRotateOrigin ( texT, vRotate, txl_vOrigin, rvecT );
958         // compute normal of plane after rotation
959         VectorRotate ( f->plane.normal, vRotate, rNormal );
960   }
961   else
962   {
963     for (j=0 ; j<3 ; j++)
964       rOrig[j] = vector3_dot(vector3_subtracted(Orig, txl_origin), txl_matrix[j]) + txl_origin[j];
965     for (j=0 ; j<3 ; j++)
966       rvecS[j] = vector3_dot(vector3_subtracted(texS, txl_origin), txl_matrix[j]) + txl_origin[j];
967     for (j=0 ; j<3 ; j++)
968       rvecT[j] = vector3_dot(vector3_subtracted(texT, txl_origin), txl_matrix[j]) + txl_origin[j];
969     // we also need the axis base of the target plane, apply the transformation matrix to the normal too..
970     for (j=0 ; j<3 ; j++)
971       rNormal[j] = vector3_dot(f->plane.normal, txl_matrix[j]);
972   }
973
974         // compute rotated plane axis base
975         ComputeAxisBase( rNormal, rtexS, rtexT );
976         // compute S/T coordinates of the three points in rotated axis base ( in M matrix )
977         lOrig[0] = vector3_dot( rOrig, rtexS );
978         lOrig[1] = vector3_dot( rOrig, rtexT );
979         lvecS[0] = vector3_dot( rvecS, rtexS );
980         lvecS[1] = vector3_dot( rvecS, rtexT );
981         lvecT[0] = vector3_dot( rvecT, rtexS );
982         lvecT[1] = vector3_dot( rvecT, rtexT );
983         M[0][0] = lOrig[0]; M[1][0] = lOrig[1]; M[2][0] = 1.0f;
984         M[0][1] = lvecS[0]; M[1][1] = lvecS[1]; M[2][1] = 1.0f;
985         M[0][2] = lvecT[0]; M[1][2] = lvecT[1]; M[2][2] = 1.0f;
986         // fill data vector
987         D[0][0]=f->brushprimit_texdef.coords[0][2];
988         D[0][1]=f->brushprimit_texdef.coords[0][0]+f->brushprimit_texdef.coords[0][2];
989         D[0][2]=f->brushprimit_texdef.coords[0][1]+f->brushprimit_texdef.coords[0][2];
990         D[1][0]=f->brushprimit_texdef.coords[1][2];
991         D[1][1]=f->brushprimit_texdef.coords[1][0]+f->brushprimit_texdef.coords[1][2];
992         D[1][2]=f->brushprimit_texdef.coords[1][1]+f->brushprimit_texdef.coords[1][2];
993         // solve
994         det = SarrusDet( M[0], M[1], M[2] );
995         f->brushprimit_texdef.coords[0][0] = SarrusDet( D[0], M[1], M[2] ) / det;
996         f->brushprimit_texdef.coords[0][1] = SarrusDet( M[0], D[0], M[2] ) / det;
997         f->brushprimit_texdef.coords[0][2] = SarrusDet( M[0], M[1], D[0] ) / det;
998         f->brushprimit_texdef.coords[1][0] = SarrusDet( D[1], M[1], M[2] ) / det;
999         f->brushprimit_texdef.coords[1][1] = SarrusDet( M[0], D[1], M[2] ) / det;
1000         f->brushprimit_texdef.coords[1][2] = SarrusDet( M[0], M[1], D[1] ) / det;
1001 }
1002
1003 // texture locking
1004 // called before the points on the face are actually rotated
1005 void RotateFaceTexture_BrushPrimit(face_t *f, int nAxis, float fDeg, Vector3& vOrigin )
1006 {
1007   // this is a placeholder to call the general texture locking algorithm
1008   txlock_bRotation = true;
1009   txl_nAxis = nAxis;
1010   txl_fDeg = fDeg;
1011   VectorCopy(vOrigin, txl_vOrigin);
1012   TextureLockTransformation_BrushPrimit(f);
1013 }
1014
1015 // compute the new brush primit texture matrix for a transformation matrix and a flip order flag (change plane orientation)
1016 // this matches the select_matrix algo used in select.cpp
1017 // this needs to be called on the face BEFORE any geometric transformation
1018 // it will compute the texture matrix that will represent the same texture on the face after the geometric transformation is done
1019 void ApplyMatrix_BrushPrimit(face_t *f, Vector3 matrix[3], Vector3& origin)
1020 {
1021   // this is a placeholder to call the general texture locking algorithm
1022   txlock_bRotation = false;
1023   VectorCopy(matrix[0], txl_matrix[0]);
1024   VectorCopy(matrix[1], txl_matrix[1]);
1025   VectorCopy(matrix[2], txl_matrix[2]);
1026   VectorCopy(origin, txl_origin);
1027   TextureLockTransformation_BrushPrimit(f);
1028 }
1029 #endif
1030
1031 // don't do C==A!
1032 void BPMatMul(float A[2][3], float B[2][3], float C[2][3])
1033 {
1034   C[0][0] = A[0][0]*B[0][0]+A[0][1]*B[1][0];
1035   C[1][0] = A[1][0]*B[0][0]+A[1][1]*B[1][0];
1036   C[0][1] = A[0][0]*B[0][1]+A[0][1]*B[1][1];
1037   C[1][1] = A[1][0]*B[0][1]+A[1][1]*B[1][1];
1038   C[0][2] = A[0][0]*B[0][2]+A[0][1]*B[1][2]+A[0][2];
1039   C[1][2] = A[1][0]*B[0][2]+A[1][1]*B[1][2]+A[1][2];
1040 }
1041
1042 void BPMatDump(float A[2][3])
1043 {
1044   globalOutputStream() << "" << A[0][0]
1045     << " " << A[0][1]
1046     << " " << A[0][2]
1047     << "\n" << A[1][0]
1048     << " " << A[1][2]
1049     << " " << A[1][2]
1050     << "\n0 0 1\n";
1051 }
1052
1053 void BPMatRotate(float A[2][3], float theta)
1054 {
1055   float m[2][3];
1056   float aux[2][3];
1057   memset(&m, 0, sizeof(float)*6);
1058   m[0][0] = static_cast<float>(cos(degrees_to_radians(theta)));
1059   m[0][1] = static_cast<float>(-sin(degrees_to_radians(theta)));
1060   m[1][0] = -m[0][1];
1061   m[1][1] = m[0][0];
1062   BPMatMul(A, m, aux);
1063   BPMatCopy(aux,A);
1064 }
1065
1066 #if 0 // camera-relative texture shift
1067 // get the relative axes of the current texturing
1068 void BrushPrimit_GetRelativeAxes(face_t *f, Vector3& vecS, Vector3& vecT)
1069 {
1070   float vS[2],vT[2];
1071   // first we compute them as expressed in plane axis base
1072   // BP matrix has coordinates of plane axis base expressed in geometric axis base
1073   // so we use the line vectors
1074   vS[0] = f->brushprimit_texdef.coords[0][0];
1075   vS[1] = f->brushprimit_texdef.coords[0][1];
1076   vT[0] = f->brushprimit_texdef.coords[1][0];
1077   vT[1] = f->brushprimit_texdef.coords[1][1];
1078   // now compute those vectors in geometric space
1079   Vector3 texS, texT; // axis base of the plane (geometric)
1080   ComputeAxisBase(f->plane.normal, texS, texT);
1081   // vecS[] = vS[0].texS[] + vS[1].texT[]
1082   // vecT[] = vT[0].texS[] + vT[1].texT[]
1083   vecS[0] = vS[0]*texS[0] + vS[1]*texT[0];
1084   vecS[1] = vS[0]*texS[1] + vS[1]*texT[1];
1085   vecS[2] = vS[0]*texS[2] + vS[1]*texT[2];
1086   vecT[0] = vT[0]*texS[0] + vT[1]*texT[0];
1087   vecT[1] = vT[0]*texS[1] + vT[1]*texT[1];
1088   vecT[2] = vT[0]*texS[2] + vT[1]*texT[2];
1089 }
1090
1091 // brush primitive texture adjustments, use the camera view to map adjustments
1092 // ShiftTextureRelative_BrushPrimit ( s , t ) will shift relative to the texture
1093 void ShiftTextureRelative_Camera(face_t *f, int x, int y)
1094 {
1095   Vector3 vecS, vecT;
1096   float XY[2]; // the values we are going to send for translation
1097   float sgn[2]; // +1 or -1
1098   int axis[2];
1099   CamWnd* pCam;
1100
1101   // get the two relative texture axes for the current texturing
1102   BrushPrimit_GetRelativeAxes(f, vecS, vecT);
1103
1104   // center point of the face, project it on the camera space
1105   Vector3 C;
1106   VectorClear(C);
1107   int i;
1108   for (i=0; i<f->face_winding->numpoints; i++)
1109   {
1110     VectorAdd(C,f->face_winding->point_at(i),C);
1111   }
1112   VectorScale(C,1.0/f->face_winding->numpoints,C);
1113
1114   pCam = g_pParentWnd->GetCamWnd();
1115   pCam->MatchViewAxes(C, vecS, axis[0], sgn[0]);
1116   pCam->MatchViewAxes(C, vecT, axis[1], sgn[1]);
1117   
1118   // this happens when the two directions can't be mapped on two different directions on the screen
1119   // then the move will occur against a single axis
1120   // (i.e. the user is not positioned well enough to send understandable shift commands)
1121   // NOTE: in most cases this warning is not very relevant because the user would use one of the two axes
1122   // for which the solution is easy (the other one being unknown)
1123   // so this warning could be removed
1124   if (axis[0] == axis[1])
1125     globalOutputStream() << "Warning: degenerate in ShiftTextureRelative_Camera\n";
1126
1127   // compute the X Y geometric increments
1128   // those geometric increments will be applied along the texture axes (the ones we computed above)
1129   XY[0] = 0;
1130   XY[1] = 0;
1131   if (x!=0)
1132   {
1133     // moving right/left
1134     XY[axis[0]] += sgn[0]*x;
1135   }
1136   if (y!=0)
1137   {
1138     XY[axis[1]] += sgn[1]*y;
1139   }
1140   // we worked out a move along vecS vecT, and we now it's geometric amplitude
1141   // apply it
1142   ShiftTextureRelative_BrushPrimit(f, XY[0], XY[1]);
1143 }
1144 #endif
1145
1146
1147 void BPTexdef_Assign(brushprimit_texdef_t& bp_td, const brushprimit_texdef_t& bp_other)
1148 {
1149         bp_td = bp_other;
1150 }
1151
1152 void BPTexdef_Shift(brushprimit_texdef_t& bp_td, float s, float t)
1153 {
1154   // shift a texture (texture adjustments) along it's current texture axes
1155   // x and y are geometric values, which we must compute as ST increments
1156   // this depends on the texture size and the pixel/texel ratio
1157   // as a ratio against texture size
1158   // the scale of the texture is not relevant here (we work directly on a transformation from the base vectors)
1159   bp_td.coords[0][2] -= s;
1160   bp_td.coords[1][2] += t;
1161 }
1162
1163 void BPTexdef_Scale(brushprimit_texdef_t& bp_td, float s, float t)
1164 {
1165         // apply same scale as the spinner button of the surface inspector
1166         texdef_t texdef;
1167         // compute fake shift scale rot
1168         TexMatToFakeTexCoords( bp_td, texdef );
1169         // update
1170         texdef.scale[0] += s;
1171         texdef.scale[1] += t;
1172         // compute new normalized texture matrix
1173         FakeTexCoordsToTexMat( texdef, bp_td );
1174 }
1175
1176 void BPTexdef_Rotate(brushprimit_texdef_t& bp_td, float angle)
1177 {
1178         // apply same scale as the spinner button of the surface inspector
1179         texdef_t texdef;
1180         // compute fake shift scale rot
1181         TexMatToFakeTexCoords( bp_td, texdef );
1182         // update
1183         texdef.rotate += angle;
1184         // compute new normalized texture matrix
1185         FakeTexCoordsToTexMat( texdef, bp_td );
1186 }
1187
1188 void BPTexdef_Construct(brushprimit_texdef_t& bp_td, std::size_t width, std::size_t height)
1189 {
1190         bp_td.coords[0][0] = 1.0f;
1191         bp_td.coords[1][1] = 1.0f;
1192         ConvertTexMatWithDimensions(bp_td.coords, 2, 2, bp_td.coords, width, height);
1193 }
1194
1195 //++timo FIXME quick'n dirty hack, doesn't care about current texture settings (angle)
1196 // can be improved .. bug #107311
1197 void BPTexdef_FitTexture(brushprimit_texdef_t& bp_td, std::size_t width, std::size_t height, const Vector3& normal, const Winding& w, float s_repeat, float t_repeat)
1198 {
1199         Vector3 BBoxSTMin, BBoxSTMax;
1200         Vector3 M[3],D[2];
1201 //      Vector3 N[2],Mf[2];
1202         brushprimit_texdef_t N;
1203   Vector3 Mf[2];
1204
1205   //qtexture_t texture;
1206   //texture.width = width;
1207   //texture.height = height;
1208
1209
1210         // we'll be working on a standardized texture size
1211 //      ConvertTexMatWithQTexture( &bp_td, &texture, &bp_td, 0 );
1212         // compute the BBox in ST coords
1213   {
1214     Winding tmp(w);
1215           Texdef_EmitTextureCoordinates(TextureProjection(texdef_t(), bp_td, Vector3(0, 0, 0), Vector3(0, 0, 0)), width, height, tmp, normal, g_matrix4_identity);
1216
1217           ClearBounds( BBoxSTMin, BBoxSTMax );
1218     for(Winding::const_iterator i = tmp.begin(); i != tmp.end(); ++i)
1219                 {
1220                   // AddPointToBounds in 2D on (S,T) coordinates
1221                   for(int j=0 ; j<2 ; j++)
1222                   {
1223                           float val = (*i).texcoord[j];
1224                           if (val < BBoxSTMin[j])
1225                                   BBoxSTMin[j] = val;
1226                           if (val > BBoxSTMax[j])
1227                                   BBoxSTMax[j] = val;
1228       }
1229                 }
1230         }
1231         // we have the three points of the BBox (BBoxSTMin[0].BBoxSTMin[1]) (BBoxSTMax[0],BBoxSTMin[1]) (BBoxSTMin[0],BBoxSTMax[1]) in ST space
1232   // the BP matrix we are looking for gives (0,0) (nwidth,0) (0,t_repeat) coordinates in (Sfit,Tfit) space to these three points
1233         // we have A(Sfit,Tfit) = (0,0) = Mf * A(TexS,TexT) = N * M * A(TexS,TexT) = N * A(S,T)
1234         // so we solve the system for N and then Mf = N * M
1235         M[0][0] = BBoxSTMin[0]; M[0][1] = BBoxSTMax[0]; M[0][2] = BBoxSTMin[0];
1236         M[1][0] = BBoxSTMin[1]; M[1][1] = BBoxSTMin[1]; M[1][2] = BBoxSTMax[1];
1237         D[0][0] = 0.0f; D[0][1] = s_repeat; D[0][2] = 0.0f;
1238         D[1][0] = 0.0f; D[1][1] = 0.0f; D[1][2] = t_repeat;
1239   MatrixForPoints( M, D, &N );
1240
1241 #if 0
1242   // FIT operation gives coordinates of three points of the bounding box in (S',T'), our target axis base
1243         // A(S',T')=(0,0) B(S',T')=(s_repeat,0) C(S',T')=(0,t_repeat)
1244         // and we have them in (S,T) axis base: A(S,T)=(BBoxSTMin[0],BBoxSTMin[1]) B(S,T)=(BBoxSTMax[0],BBoxSTMin[1]) C(S,T)=(BBoxSTMin[0],BBoxSTMax[1])
1245         // we compute the N transformation so that: A(S',T') = N * A(S,T)
1246   VectorSet( N[0], (BBoxSTMax[0]-BBoxSTMin[0])/s_repeat, 0.0f, BBoxSTMin[0] );
1247         VectorSet( N[1], 0.0f, (BBoxSTMax[1]-BBoxSTMin[1])/t_repeat, BBoxSTMin[1] );
1248 #endif
1249
1250         // the final matrix is the product (Mf stands for Mfit)
1251   Mf[0][0] = N.coords[0][0] * bp_td.coords[0][0] + N.coords[0][1] * bp_td.coords[1][0];
1252         Mf[0][1] = N.coords[0][0] * bp_td.coords[0][1] + N.coords[0][1] * bp_td.coords[1][1];
1253         Mf[0][2] = N.coords[0][0] * bp_td.coords[0][2] + N.coords[0][1] * bp_td.coords[1][2] + N.coords[0][2];
1254   Mf[1][0] = N.coords[1][0] * bp_td.coords[0][0] + N.coords[1][1] * bp_td.coords[1][0];
1255         Mf[1][1] = N.coords[1][0] * bp_td.coords[0][1] + N.coords[1][1] * bp_td.coords[1][1];
1256         Mf[1][2] = N.coords[1][0] * bp_td.coords[0][2] + N.coords[1][1] * bp_td.coords[1][2] + N.coords[1][2];
1257         // copy back
1258         bp_td.coords[0][0] = Mf[0][0];
1259         bp_td.coords[0][1] = Mf[0][1];
1260         bp_td.coords[0][2] = Mf[0][2];
1261         bp_td.coords[1][0] = Mf[1][0];
1262         bp_td.coords[1][1] = Mf[1][1];
1263         bp_td.coords[1][2] = Mf[1][2];
1264         // handle the texture size
1265 //      ConvertTexMatWithQTexture( &bp_td, 0, &bp_td, &texture );
1266 }
1267
1268
1269
1270 void Texdef_Assign(TextureProjection& projection, const TextureProjection& other)
1271 {
1272   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1273   {
1274     BPTexdef_Assign(projection.m_brushprimit_texdef, other.m_brushprimit_texdef);
1275   }
1276   else
1277   {
1278     Texdef_Assign(projection.m_texdef, other.m_texdef);
1279     if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE)
1280     {
1281       projection.m_basis_s = other.m_basis_s;
1282       projection.m_basis_t = other.m_basis_t;
1283     }
1284   }
1285 }
1286
1287 void Texdef_Shift(TextureProjection& projection, float s, float t)
1288 {
1289   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1290   {
1291     BPTexdef_Shift(projection.m_brushprimit_texdef, s, t);
1292   }
1293   else
1294   {
1295                 Texdef_Shift(projection.m_texdef, s, t);
1296   }
1297 }
1298
1299 void Texdef_Scale(TextureProjection& projection, float s, float t)
1300 {
1301         if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1302         {
1303     BPTexdef_Scale(projection.m_brushprimit_texdef, s, t);
1304         }
1305         else
1306         {
1307                 Texdef_Scale(projection.m_texdef, s, t);
1308         }
1309 }
1310
1311 void Texdef_Rotate(TextureProjection& projection, float angle)
1312 {
1313         if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1314         {
1315     BPTexdef_Rotate(projection.m_brushprimit_texdef, angle);
1316         }
1317         else
1318         {
1319                 Texdef_Rotate(projection.m_texdef, angle);
1320         }
1321 }
1322
1323 void Texdef_FitTexture(TextureProjection& projection, std::size_t width, std::size_t height, const Vector3& normal, const Winding& w, float s_repeat, float t_repeat)
1324 {
1325   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1326   {
1327     BPTexdef_FitTexture(projection.m_brushprimit_texdef, width, height, normal, w, s_repeat, t_repeat);
1328   }
1329   else
1330   {
1331     Texdef_FitTexture(projection.m_texdef, width, height, normal, w, s_repeat, t_repeat);
1332   }
1333 }
1334
1335 float Texdef_getDefaultTextureScale()
1336 {
1337   return g_texdef_default_scale;
1338 }
1339
1340 void TexDef_Construct_Default(TextureProjection& projection)
1341 {
1342   projection.m_texdef.scale[0] = Texdef_getDefaultTextureScale();
1343   projection.m_texdef.scale[1] = Texdef_getDefaultTextureScale();
1344
1345   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1346   {
1347     FakeTexCoordsToTexMat(projection.m_texdef, projection.m_brushprimit_texdef);
1348   }
1349 }
1350
1351
1352
1353 void ShiftScaleRotate_fromFace(texdef_t& shiftScaleRotate, const TextureProjection& projection)
1354 {
1355   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1356   {
1357     TexMatToFakeTexCoords(projection.m_brushprimit_texdef, shiftScaleRotate);
1358   }
1359   else
1360   {
1361     shiftScaleRotate = projection.m_texdef;
1362   }
1363 }
1364
1365 void ShiftScaleRotate_toFace(const texdef_t& shiftScaleRotate, TextureProjection& projection)
1366 {
1367   if (g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES)
1368   {
1369     // compute texture matrix
1370     // the matrix returned must be understood as a qtexture_t with width=2 height=2
1371     FakeTexCoordsToTexMat( shiftScaleRotate, projection.m_brushprimit_texdef );
1372   }
1373   else
1374   {
1375     projection.m_texdef = shiftScaleRotate;
1376   }
1377 }
1378
1379
1380 inline void print_vector3(const Vector3& v)
1381 {
1382   globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n";
1383 }
1384
1385 inline void print_3x3(const Matrix4& m)
1386 {
1387   globalOutputStream() << "( " << m.xx() << " " << m.xy() << " " << m.xz() << " ) "
1388     << "( " << m.yx() << " " << m.yy() << " " << m.yz() << " ) "
1389     << "( " << m.zx() << " " << m.zy() << " " << m.zz() << " )\n";
1390 }
1391
1392
1393 inline Matrix4 matrix4_rotation_for_vector3(const Vector3& x, const Vector3& y, const Vector3& z)
1394 {
1395   return Matrix4(
1396     x.x(), x.y(), x.z(), 0,
1397     y.x(), y.y(), y.z(), 0,
1398     z.x(), z.y(), z.z(), 0,
1399     0, 0, 0, 1
1400   );
1401 }
1402
1403 inline Matrix4 matrix4_swap_axes(const Vector3& from, const Vector3& to)
1404 {
1405   if(from.x() != 0 && to.y() != 0)
1406   {
1407     return matrix4_rotation_for_vector3(to, from, g_vector3_axis_z);
1408   }
1409
1410   if(from.x() != 0 && to.z() != 0)
1411   {
1412     return matrix4_rotation_for_vector3(to, g_vector3_axis_y, from);
1413   }
1414
1415   if(from.y() != 0 && to.z() != 0)
1416   {
1417     return matrix4_rotation_for_vector3(g_vector3_axis_x, to, from);
1418   }
1419
1420   if(from.y() != 0 && to.x() != 0)
1421   {
1422     return matrix4_rotation_for_vector3(from, to, g_vector3_axis_z);
1423   }
1424
1425   if(from.z() != 0 && to.x() != 0)
1426   {
1427     return matrix4_rotation_for_vector3(from, g_vector3_axis_y, to);
1428   }
1429
1430   if(from.z() != 0 && to.y() != 0)
1431   {
1432     return matrix4_rotation_for_vector3(g_vector3_axis_x, from, to);
1433   }
1434
1435   ERROR_MESSAGE("unhandled axis swap case");
1436
1437   return g_matrix4_identity;
1438 }
1439
1440 inline Matrix4 matrix4_reflection_for_plane(const Plane3& plane)
1441 {
1442   return Matrix4(
1443     static_cast<float>(1 - (2 * plane.a * plane.a)),
1444     static_cast<float>(-2 * plane.a * plane.b),
1445     static_cast<float>(-2 * plane.a * plane.c),
1446     0,
1447     static_cast<float>(-2 * plane.b * plane.a),
1448     static_cast<float>(1 - (2 * plane.b * plane.b)),
1449     static_cast<float>(-2 * plane.b * plane.c),
1450     0,
1451     static_cast<float>(-2 * plane.c * plane.a),
1452     static_cast<float>(-2 * plane.c * plane.b),
1453     static_cast<float>(1 - (2 * plane.c * plane.c)),
1454     0,
1455     static_cast<float>(-2 * plane.d * plane.a),
1456     static_cast<float>(-2 * plane.d * plane.b),
1457     static_cast<float>(-2 * plane.d * plane.c),
1458     1
1459   );
1460 }
1461
1462 inline Matrix4 matrix4_reflection_for_plane45(const Plane3& plane, const Vector3& from, const Vector3& to)
1463 {
1464   Vector3 first = from;
1465   Vector3 second = to;
1466
1467   if(vector3_dot(from, plane.normal()) > 0 == vector3_dot(to, plane.normal()) > 0)
1468   {
1469     first = vector3_negated(first);
1470     second = vector3_negated(second);
1471   }
1472
1473 #if 0
1474   globalOutputStream() << "normal: ";
1475   print_vector3(plane.normal());
1476
1477   globalOutputStream() << "from: ";
1478   print_vector3(first);
1479
1480   globalOutputStream() << "to: ";
1481   print_vector3(second);
1482 #endif
1483
1484   Matrix4 swap = matrix4_swap_axes(first, second);
1485
1486   Matrix4 tmp = matrix4_reflection_for_plane(plane);
1487
1488   swap.tx() = -static_cast<float>(-2 * plane.a * plane.d);
1489   swap.ty() = -static_cast<float>(-2 * plane.b * plane.d);
1490   swap.tz() = -static_cast<float>(-2 * plane.c * plane.d);
1491
1492   return swap;
1493 }
1494
1495 void Texdef_transformLocked(TextureProjection& projection, std::size_t width, std::size_t height, const Plane3& plane, const Matrix4& identity2transformed)
1496 {
1497   //globalOutputStream() << "identity2transformed: " << identity2transformed << "\n";
1498
1499   //globalOutputStream() << "plane.normal(): " << plane.normal() << "\n";
1500
1501   Vector3 normalTransformed(matrix4_transformed_direction(identity2transformed, plane.normal()));
1502
1503   //globalOutputStream() << "normalTransformed: " << normalTransformed << "\n";
1504
1505   // identity: identity space
1506   // transformed: transformation
1507   // stIdentity: base st projection space before transformation
1508   // stTransformed: base st projection space after transformation
1509   // stOriginal: original texdef space
1510
1511   // stTransformed2stOriginal = stTransformed -> transformed -> identity -> stIdentity -> stOriginal
1512
1513   Matrix4 identity2stIdentity;
1514   Texdef_basisForNormal(projection, plane.normal(), identity2stIdentity);
1515   //globalOutputStream() << "identity2stIdentity: " << identity2stIdentity << "\n";
1516
1517   if(g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE)
1518   {
1519     matrix4_transform_direction(identity2transformed, projection.m_basis_s);
1520     matrix4_transform_direction(identity2transformed, projection.m_basis_t);
1521   }
1522
1523   Matrix4 transformed2stTransformed;
1524   Texdef_basisForNormal(projection, normalTransformed, transformed2stTransformed);
1525
1526   Matrix4 stTransformed2identity(matrix4_affine_inverse(matrix4_multiplied_by_matrix4(transformed2stTransformed, identity2transformed)));
1527
1528   Vector3 originalProjectionAxis(vector4_to_vector3(matrix4_affine_inverse(identity2stIdentity).z()));
1529
1530   Vector3 transformedProjectionAxis(vector4_to_vector3(stTransformed2identity.z()));
1531
1532   Matrix4 stIdentity2stOriginal;
1533   Texdef_toTransform(projection, (float)width, (float)height, stIdentity2stOriginal);
1534   Matrix4 identity2stOriginal(matrix4_multiplied_by_matrix4(stIdentity2stOriginal, identity2stIdentity));
1535
1536   //globalOutputStream() << "originalProj: " << originalProjectionAxis << "\n";
1537   //globalOutputStream() << "transformedProj: " << transformedProjectionAxis << "\n";
1538   double dot = vector3_dot(originalProjectionAxis, transformedProjectionAxis);
1539   //globalOutputStream() << "dot: " << dot << "\n";
1540   if(dot == 0)
1541   {
1542     // The projection axis chosen for the transformed normal is at 90 degrees
1543     // to the transformed projection axis chosen for the original normal.
1544     // This happens when the projection axis is ambiguous - e.g. for the plane
1545     // 'X == Y' the projection axis could be either X or Y.
1546     //globalOutputStream() << "flipped\n";
1547 #if 0
1548     globalOutputStream() << "projection off by 90\n";
1549     globalOutputStream() << "normal: ";
1550     print_vector3(plane.normal());
1551     globalOutputStream() << "original projection: ";
1552     print_vector3(originalProjectionAxis);
1553     globalOutputStream() << "transformed projection: ";
1554     print_vector3(transformedProjectionAxis);
1555 #endif
1556
1557     Matrix4 identityCorrected = matrix4_reflection_for_plane45(plane, originalProjectionAxis, transformedProjectionAxis);
1558
1559     identity2stOriginal = matrix4_multiplied_by_matrix4(identity2stOriginal, identityCorrected);
1560   }
1561
1562   Matrix4 stTransformed2stOriginal = matrix4_multiplied_by_matrix4(identity2stOriginal, stTransformed2identity);
1563
1564   Texdef_fromTransform(projection, (float)width, (float)height, stTransformed2stOriginal);
1565   Texdef_normalise(projection, (float)width, (float)height);
1566 }
1567
1568 #if 1
1569 void Q3_to_matrix(const texdef_t& texdef, float width, float height, const Vector3& normal, Matrix4& matrix)
1570 {
1571   Normal_GetTransform(normal, matrix);
1572
1573   Matrix4 transform;
1574   
1575   Texdef_toTransform(texdef, width, height, transform);
1576
1577   matrix4_multiply_by_matrix4(matrix, transform);
1578 }
1579
1580 void BP_from_matrix(brushprimit_texdef_t& bp_texdef, const Vector3& normal, const Matrix4& transform)
1581 {
1582   Matrix4 basis;
1583   basis = g_matrix4_identity;
1584   ComputeAxisBase(normal, basis.x(), basis.y());
1585   static_cast<Vector3&>(basis.z()) = normal;
1586   matrix4_transpose(basis);
1587   matrix4_affine_invert(basis);
1588
1589   Matrix4 basis2texture = matrix4_multiplied_by_matrix4(basis, transform);
1590
1591   BPTexdef_fromTransform(bp_texdef, basis2texture);
1592 }
1593
1594 void Q3_to_BP(const texdef_t& texdef, float width, float height, const Vector3& normal, brushprimit_texdef_t& bp_texdef)
1595 {
1596   Matrix4 matrix;
1597   Q3_to_matrix(texdef, width, height, normal, matrix);
1598   BP_from_matrix(bp_texdef, normal, matrix);
1599 }
1600 #endif