+#include "brush_primit.h"
+#include "globaldefs.h"
+
+#include "debugging/debugging.h"
+
+#include "itexdef.h"
+#include "itextures.h"
+
+#include <algorithm>
+
+#include "stringio.h"
+#include "texturelib.h"
+#include "math/matrix.h"
+#include "math/plane.h"
+#include "math/aabb.h"
+
+#include "winding.h"
+#include "preferences.h"
+
+
+/*!
+ \brief Construct a transform from XYZ space to ST space (3d to 2d).
+ This will be one of three axis-aligned spaces, depending on the surface normal.
+ NOTE: could also be done by swapping values.
+ */
+void Normal_GetTransform( const Vector3& normal, Matrix4& transform ){
+ switch ( projectionaxis_for_normal( normal ) )
+ {
+ case eProjectionAxisZ:
+ transform[0] = 1;
+ transform[1] = 0;
+ transform[2] = 0;
+
+ transform[4] = 0;
+ transform[5] = 1;
+ transform[6] = 0;
+
+ transform[8] = 0;
+ transform[9] = 0;
+ transform[10] = 1;
+ break;
+ case eProjectionAxisY:
+ transform[0] = 1;
+ transform[1] = 0;
+ transform[2] = 0;
+
+ transform[4] = 0;
+ transform[5] = 0;
+ transform[6] = -1;
+
+ transform[8] = 0;
+ transform[9] = 1;
+ transform[10] = 0;
+ break;
+ case eProjectionAxisX:
+ transform[0] = 0;
+ transform[1] = 0;
+ transform[2] = 1;
+
+ transform[4] = 1;
+ transform[5] = 0;
+ transform[6] = 0;
+
+ transform[8] = 0;
+ transform[9] = 1;
+ transform[10] = 0;
+ break;
+ }
+ transform[3] = transform[7] = transform[11] = transform[12] = transform[13] = transform[14] = 0;
+ transform[15] = 1;
+}
+
+/*!
+ \brief Construct a transform in ST space from the texdef.
+ Transforms constructed from quake's texdef format are (-shift)*(1/scale)*(-rotate) with x translation sign flipped.
+ This would really make more sense if it was inverseof(shift*rotate*scale).. oh well.
+ */
+inline void Texdef_toTransform( const texdef_t& texdef, float width, float height, Matrix4& transform ){
+ double inverse_scale[2];
+
+ // transform to texdef shift/scale/rotate
+ inverse_scale[0] = 1 / ( texdef.scale[0] * width );
+ inverse_scale[1] = 1 / ( texdef.scale[1] * -height );
+ transform[12] = texdef.shift[0] / width;
+ transform[13] = -texdef.shift[1] / -height;
+ double c = cos( degrees_to_radians( -texdef.rotate ) );
+ double s = sin( degrees_to_radians( -texdef.rotate ) );
+ transform[0] = static_cast<float>( c * inverse_scale[0] );
+ transform[1] = static_cast<float>( s * inverse_scale[1] );
+ transform[4] = static_cast<float>( -s * inverse_scale[0] );
+ transform[5] = static_cast<float>( c * inverse_scale[1] );
+ transform[2] = transform[3] = transform[6] = transform[7] = transform[8] = transform[9] = transform[11] = transform[14] = 0;
+ transform[10] = transform[15] = 1;
+}
+
+inline void BPTexdef_toTransform( const brushprimit_texdef_t& bp_texdef, Matrix4& transform ){
+ transform = g_matrix4_identity;
+ transform.xx() = bp_texdef.coords[0][0];
+ transform.yx() = bp_texdef.coords[0][1];
+ transform.tx() = bp_texdef.coords[0][2];
+ transform.xy() = bp_texdef.coords[1][0];
+ transform.yy() = bp_texdef.coords[1][1];
+ transform.ty() = bp_texdef.coords[1][2];
+}
+
+inline void Texdef_toTransform( const TextureProjection& projection, float width, float height, Matrix4& transform ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_toTransform( projection.m_brushprimit_texdef, transform );
+ }
+ else
+ {
+ Texdef_toTransform( projection.m_texdef, width, height, transform );
+ }
+}
+
+// handles degenerate cases, just in case library atan2 doesn't
+inline double arctangent_yx( double y, double x ){
+ if ( fabs( x ) > 1.0E-6 ) {
+ return atan2( y, x );
+ }
+ else if ( y > 0 ) {
+ return c_half_pi;
+ }
+ else
+ {
+ return -c_half_pi;
+ }
+}
+
+inline void Texdef_fromTransform( texdef_t& texdef, float width, float height, const Matrix4& transform ){
+ texdef.scale[0] = static_cast<float>( ( 1.0 / vector2_length( Vector2( transform[0], transform[4] ) ) ) / width );
+ texdef.scale[1] = static_cast<float>( ( 1.0 / vector2_length( Vector2( transform[1], transform[5] ) ) ) / height );
+
+ texdef.rotate = static_cast<float>( -radians_to_degrees( arctangent_yx( -transform[4], transform[0] ) ) );
+
+ if ( texdef.rotate == -180.0f ) {
+ texdef.rotate = 180.0f;
+ }
+
+ texdef.shift[0] = transform[12] * width;
+ texdef.shift[1] = transform[13] * height;
+
+ // If the 2d cross-product of the x and y axes is positive, one of the axes has a negative scale.
+ if ( vector2_cross( Vector2( transform[0], transform[4] ), Vector2( transform[1], transform[5] ) ) > 0 ) {
+ if ( texdef.rotate >= 180.0f ) {
+ texdef.rotate -= 180.0f;
+ texdef.scale[0] = -texdef.scale[0];
+ }
+ else
+ {
+ texdef.scale[1] = -texdef.scale[1];
+ }
+ }
+ //globalOutputStream() << "fromTransform: " << texdef.shift[0] << " " << texdef.shift[1] << " " << texdef.scale[0] << " " << texdef.scale[1] << " " << texdef.rotate << "\n";
+}
+
+inline void BPTexdef_fromTransform( brushprimit_texdef_t& bp_texdef, const Matrix4& transform ){
+ bp_texdef.coords[0][0] = transform.xx();
+ bp_texdef.coords[0][1] = transform.yx();
+ bp_texdef.coords[0][2] = transform.tx();
+ bp_texdef.coords[1][0] = transform.xy();
+ bp_texdef.coords[1][1] = transform.yy();
+ bp_texdef.coords[1][2] = transform.ty();
+}
+
+inline void Texdef_fromTransform( TextureProjection& projection, float width, float height, const Matrix4& transform ){
+ ASSERT_MESSAGE( ( transform[0] != 0 || transform[4] != 0 )
+ && ( transform[1] != 0 || transform[5] != 0 ), "invalid texture matrix" );
+
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_fromTransform( projection.m_brushprimit_texdef, transform );
+ }
+ else
+ {
+ Texdef_fromTransform( projection.m_texdef, width, height, transform );
+ }
+}
+
+inline void Texdef_normalise( texdef_t& texdef, float width, float height ){
+ // it may be useful to also normalise the rotation here, if this function is used elsewhere.
+ texdef.shift[0] = float_mod( texdef.shift[0], width );
+ texdef.shift[1] = float_mod( texdef.shift[1], height );
+ //globalOutputStream() << "normalise: " << texdef.shift[0] << " " << texdef.shift[1] << " " << texdef.scale[0] << " " << texdef.scale[1] << " " << texdef.rotate << "\n";
+}
+
+inline void BPTexdef_normalise( brushprimit_texdef_t& bp_texdef, float width, float height ){
+ bp_texdef.coords[0][2] = float_mod( bp_texdef.coords[0][2], width );
+ bp_texdef.coords[1][2] = float_mod( bp_texdef.coords[1][2], height );
+}
+
+/// \brief Normalise \p projection for a given texture \p width and \p height.
+///
+/// All texture-projection translation (shift) values are congruent modulo the dimensions of the texture.
+/// This function normalises shift values to the smallest positive congruent values.
+void Texdef_normalise( TextureProjection& projection, float width, float height ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_normalise( projection.m_brushprimit_texdef, width, height );
+ }
+ else
+ {
+ Texdef_normalise( projection.m_texdef, width, height );
+ }
+}
+
+void ComputeAxisBase( const Vector3& normal, Vector3& texS, Vector3& texT );
+
+inline void DebugAxisBase( const Vector3& normal ){
+ Vector3 x, y;
+ ComputeAxisBase( normal, x, y );
+ globalOutputStream() << "BP debug: " << x << y << normal << "\n";
+}
+
+void Texdef_basisForNormal( const TextureProjection& projection, const Vector3& normal, Matrix4& basis ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ basis = g_matrix4_identity;
+ ComputeAxisBase( normal, vector4_to_vector3( basis.x() ), vector4_to_vector3( basis.y() ) );
+ vector4_to_vector3( basis.z() ) = normal;
+ matrix4_transpose( basis );
+ //DebugAxisBase(normal);
+ }
+ else if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE ) {
+ basis = g_matrix4_identity;
+ vector4_to_vector3( basis.x() ) = projection.m_basis_s;
+ vector4_to_vector3( basis.y() ) = vector3_negated( projection.m_basis_t );
+ vector4_to_vector3( basis.z() ) = vector3_normalised( vector3_cross( vector4_to_vector3( basis.x() ), vector4_to_vector3( basis.y() ) ) );
+ matrix4_multiply_by_matrix4( basis, matrix4_rotation_for_z_degrees( -projection.m_texdef.rotate ) );
+ //globalOutputStream() << "debug: " << projection.m_basis_s << projection.m_basis_t << normal << "\n";
+ matrix4_transpose( basis );
+ }
+ else
+ {
+ Normal_GetTransform( normal, basis );
+ }
+}
+
+void Texdef_EmitTextureCoordinates( const TextureProjection& projection, std::size_t width, std::size_t height, Winding& w, const Vector3& normal, const Matrix4& localToWorld ){
+ if ( w.numpoints < 3 ) {
+ return;
+ }
+ //globalOutputStream() << "normal: " << normal << "\n";
+
+ Matrix4 local2tex;
+ Texdef_toTransform( projection, (float)width, (float)height, local2tex );
+ //globalOutputStream() << "texdef: " << static_cast<const Vector3&>(local2tex.x()) << static_cast<const Vector3&>(local2tex.y()) << "\n";
+
+#if 0
+ {
+ TextureProjection tmp;
+ Texdef_fromTransform( tmp, (float)width, (float)height, local2tex );
+ Matrix4 tmpTransform;
+ Texdef_toTransform( tmp, (float)width, (float)height, tmpTransform );
+ ASSERT_MESSAGE( matrix4_equal_epsilon( local2tex, tmpTransform, 0.0001f ), "bleh" );
+ }
+#endif
+
+ {
+ Matrix4 xyz2st;
+ // we don't care if it's not normalised...
+ Texdef_basisForNormal( projection, matrix4_transformed_direction( localToWorld, normal ), xyz2st );
+ //globalOutputStream() << "basis: " << static_cast<const Vector3&>(xyz2st.x()) << static_cast<const Vector3&>(xyz2st.y()) << static_cast<const Vector3&>(xyz2st.z()) << "\n";
+ matrix4_multiply_by_matrix4( local2tex, xyz2st );
+ }
+
+ Vector3 tangent( vector3_normalised( vector4_to_vector3( matrix4_transposed( local2tex ).x() ) ) );
+ Vector3 bitangent( vector3_normalised( vector4_to_vector3( matrix4_transposed( local2tex ).y() ) ) );
+
+ matrix4_multiply_by_matrix4( local2tex, localToWorld );
+
+ for ( Winding::iterator i = w.begin(); i != w.end(); ++i )
+ {
+ Vector3 texcoord = matrix4_transformed_point( local2tex, ( *i ).vertex );
+ ( *i ).texcoord[0] = texcoord[0];
+ ( *i ).texcoord[1] = texcoord[1];
+
+ ( *i ).tangent = tangent;
+ ( *i ).bitangent = bitangent;
+ }
+}
+
+/*!
+ \brief Provides the axis-base of the texture ST space for this normal,
+ as they had been transformed to world XYZ space.
+ */
+void TextureAxisFromNormal( const Vector3& normal, Vector3& s, Vector3& t ){
+ switch ( projectionaxis_for_normal( normal ) )
+ {
+ case eProjectionAxisZ:
+ s[0] = 1;
+ s[1] = 0;
+ s[2] = 0;
+
+ t[0] = 0;
+ t[1] = -1;
+ t[2] = 0;
+
+ break;
+ case eProjectionAxisY:
+ s[0] = 1;
+ s[1] = 0;
+ s[2] = 0;
+
+ t[0] = 0;
+ t[1] = 0;
+ t[2] = -1;
+
+ break;
+ case eProjectionAxisX:
+ s[0] = 0;
+ s[1] = 1;
+ s[2] = 0;
+
+ t[0] = 0;
+ t[1] = 0;
+ t[2] = -1;
+
+ break;
+ }
+}
+
+void Texdef_Assign( texdef_t& td, const texdef_t& other ){
+ td = other;
+}
+
+void Texdef_Shift( texdef_t& td, float s, float t ){
+ td.shift[0] += s;
+ td.shift[1] += t;
+}
+
+void Texdef_Scale( texdef_t& td, float s, float t ){
+ td.scale[0] += s;
+ td.scale[1] += t;
+}
+
+void Texdef_Rotate( texdef_t& td, float angle ){
+ td.rotate += angle;
+ td.rotate = static_cast<float>( float_to_integer( td.rotate ) % 360 );
+}
+
+// NOTE: added these from Ritual's Q3Radiant
+void ClearBounds( Vector3& mins, Vector3& maxs ){
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+}
+
+void AddPointToBounds( const Vector3& v, Vector3& mins, Vector3& maxs ){
+ int i;
+ float val;
+
+ for ( i = 0 ; i < 3 ; i++ )
+ {
+ val = v[i];
+ if ( val < mins[i] ) {
+ mins[i] = val;
+ }
+ if ( val > maxs[i] ) {
+ maxs[i] = val;
+ }
+ }
+}
+
+template<typename Element>
+inline BasicVector3<Element> vector3_inverse( const BasicVector3<Element>& self ){
+ return BasicVector3<Element>(
+ Element( 1.0 / self.x() ),
+ Element( 1.0 / self.y() ),
+ Element( 1.0 / self.z() )
+ );
+}
+
+// low level functions .. put in mathlib?
+#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]; }
+// apply a scale transformation to the BP matrix
+#define BPMatScale( m,sS,sT ) {m[0][0] *= sS; m[1][0] *= sS; m[0][1] *= sT; m[1][1] *= sT; }
+// apply a translation transformation to a BP matrix
+#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; }
+// 2D homogeneous matrix product C = A*B
+void BPMatMul( float A[2][3], float B[2][3], float C[2][3] );
+// apply a rotation (degrees)
+void BPMatRotate( float A[2][3], float theta );
+#if GDEF_DEBUG
+void BPMatDump( float A[2][3] );
+#endif
+
+#if GDEF_DEBUG
+//#define DBG_BP
+#endif
+
+
+bp_globals_t g_bp_globals;
+float g_texdef_default_scale;