+
+void BPTexdef_Construct( brushprimit_texdef_t& bp_td, std::size_t width, std::size_t height ){
+ bp_td.coords[0][0] = 1.0f;
+ bp_td.coords[1][1] = 1.0f;
+ ConvertTexMatWithDimensions( bp_td.coords, 2, 2, bp_td.coords, width, height );
+}
+
+void Texdef_Assign( TextureProjection& projection, const TextureProjection& other ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_Assign( projection.m_brushprimit_texdef, other.m_brushprimit_texdef );
+ }
+ else
+ {
+ Texdef_Assign( projection.m_texdef, other.m_texdef );
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE ) {
+ projection.m_basis_s = other.m_basis_s;
+ projection.m_basis_t = other.m_basis_t;
+ }
+ }
+}
+
+void Texdef_Shift( TextureProjection& projection, float s, float t ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_Shift( projection.m_brushprimit_texdef, s, t );
+ }
+ else
+ {
+ Texdef_Shift( projection.m_texdef, s, t );
+ }
+}
+
+void Texdef_Scale( TextureProjection& projection, float s, float t ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_Scale( projection.m_brushprimit_texdef, s, t );
+ }
+ else
+ {
+ Texdef_Scale( projection.m_texdef, s, t );
+ }
+}
+
+void Texdef_Rotate( TextureProjection& projection, float angle ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ BPTexdef_Rotate( projection.m_brushprimit_texdef, angle );
+ }
+ else
+ {
+ Texdef_Rotate( projection.m_texdef, angle );
+ }
+}
+
+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 ){
+ if ( w.numpoints < 3 ) {
+ return;
+ }
+
+ Matrix4 st2tex;
+ Texdef_toTransform( projection, (float)width, (float)height, st2tex );
+
+ // the current texture transform
+ Matrix4 local2tex = st2tex;
+ {
+ Matrix4 xyz2st;
+ Texdef_basisForNormal( projection, normal, xyz2st );
+ matrix4_multiply_by_matrix4( local2tex, xyz2st );
+ }
+
+ // the bounds of the current texture transform
+ AABB bounds;
+ for ( Winding::const_iterator i = w.begin(); i != w.end(); ++i )
+ {
+ Vector3 texcoord = matrix4_transformed_point( local2tex, ( *i ).vertex );
+ aabb_extend_by_point_safe( bounds, texcoord );
+ }
+ bounds.origin.z() = 0;
+ bounds.extents.z() = 1;
+
+ // the bounds of a perfectly fitted texture transform
+ AABB perfect( Vector3( s_repeat * 0.5, t_repeat * 0.5, 0 ), Vector3( s_repeat * 0.5, t_repeat * 0.5, 1 ) );
+
+ // the difference between the current texture transform and the perfectly fitted transform
+ Matrix4 matrix( matrix4_translation_for_vec3( bounds.origin - perfect.origin ) );
+ matrix4_pivoted_scale_by_vec3( matrix, bounds.extents / perfect.extents, perfect.origin );
+ matrix4_affine_invert( matrix );
+
+ // apply the difference to the current texture transform
+ matrix4_premultiply_by_matrix4( st2tex, matrix );
+
+ Texdef_fromTransform( projection, (float)width, (float)height, st2tex );
+ Texdef_normalise( projection, (float)width, (float)height );
+}
+
+float Texdef_getDefaultTextureScale(){
+ return g_texdef_default_scale;
+}
+
+void TexDef_Construct_Default( TextureProjection& projection ){
+ projection.m_texdef.scale[0] = Texdef_getDefaultTextureScale();
+ projection.m_texdef.scale[1] = Texdef_getDefaultTextureScale();
+
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ FakeTexCoordsToTexMat( projection.m_texdef, projection.m_brushprimit_texdef );
+ }
+}
+
+
+
+void ShiftScaleRotate_fromFace( texdef_t& shiftScaleRotate, const TextureProjection& projection ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ TexMatToFakeTexCoords( projection.m_brushprimit_texdef, shiftScaleRotate );
+ }
+ else
+ {
+ shiftScaleRotate = projection.m_texdef;
+ }
+}
+
+void ShiftScaleRotate_toFace( const texdef_t& shiftScaleRotate, TextureProjection& projection ){
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
+ // compute texture matrix
+ // the matrix returned must be understood as a qtexture_t with width=2 height=2
+ FakeTexCoordsToTexMat( shiftScaleRotate, projection.m_brushprimit_texdef );
+ }
+ else
+ {
+ projection.m_texdef = shiftScaleRotate;
+ }
+}
+
+
+inline void print_vector3( const Vector3& v ){
+ globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n";
+}
+
+inline void print_3x3( const Matrix4& m ){
+ globalOutputStream() << "( " << m.xx() << " " << m.xy() << " " << m.xz() << " ) "
+ << "( " << m.yx() << " " << m.yy() << " " << m.yz() << " ) "
+ << "( " << m.zx() << " " << m.zy() << " " << m.zz() << " )\n";
+}
+
+
+inline Matrix4 matrix4_rotation_for_vector3( const Vector3& x, const Vector3& y, const Vector3& z ){
+ return Matrix4(
+ x.x(), x.y(), x.z(), 0,
+ y.x(), y.y(), y.z(), 0,
+ z.x(), z.y(), z.z(), 0,
+ 0, 0, 0, 1
+ );
+}
+
+inline Matrix4 matrix4_swap_axes( const Vector3& from, const Vector3& to ){
+ if ( from.x() != 0 && to.y() != 0 ) {
+ return matrix4_rotation_for_vector3( to, from, g_vector3_axis_z );
+ }
+
+ if ( from.x() != 0 && to.z() != 0 ) {
+ return matrix4_rotation_for_vector3( to, g_vector3_axis_y, from );
+ }
+
+ if ( from.y() != 0 && to.z() != 0 ) {
+ return matrix4_rotation_for_vector3( g_vector3_axis_x, to, from );
+ }
+
+ if ( from.y() != 0 && to.x() != 0 ) {
+ return matrix4_rotation_for_vector3( from, to, g_vector3_axis_z );
+ }
+
+ if ( from.z() != 0 && to.x() != 0 ) {
+ return matrix4_rotation_for_vector3( from, g_vector3_axis_y, to );
+ }
+
+ if ( from.z() != 0 && to.y() != 0 ) {
+ return matrix4_rotation_for_vector3( g_vector3_axis_x, from, to );
+ }
+
+ ERROR_MESSAGE( "unhandled axis swap case" );
+
+ return g_matrix4_identity;
+}
+
+inline Matrix4 matrix4_reflection_for_plane( const Plane3& plane ){
+ return Matrix4(
+ static_cast<float>( 1 - ( 2 * plane.a * plane.a ) ),
+ static_cast<float>( -2 * plane.a * plane.b ),
+ static_cast<float>( -2 * plane.a * plane.c ),
+ 0,
+ static_cast<float>( -2 * plane.b * plane.a ),
+ static_cast<float>( 1 - ( 2 * plane.b * plane.b ) ),
+ static_cast<float>( -2 * plane.b * plane.c ),
+ 0,
+ static_cast<float>( -2 * plane.c * plane.a ),
+ static_cast<float>( -2 * plane.c * plane.b ),
+ static_cast<float>( 1 - ( 2 * plane.c * plane.c ) ),
+ 0,
+ static_cast<float>( -2 * plane.d * plane.a ),
+ static_cast<float>( -2 * plane.d * plane.b ),
+ static_cast<float>( -2 * plane.d * plane.c ),
+ 1
+ );
+}
+
+inline Matrix4 matrix4_reflection_for_plane45( const Plane3& plane, const Vector3& from, const Vector3& to ){
+ Vector3 first = from;
+ Vector3 second = to;
+
+ if ( vector3_dot( from, plane.normal() ) > 0 == vector3_dot( to, plane.normal() ) > 0 ) {
+ first = vector3_negated( first );
+ second = vector3_negated( second );
+ }
+
+#if 0
+ globalOutputStream() << "normal: ";
+ print_vector3( plane.normal() );
+
+ globalOutputStream() << "from: ";
+ print_vector3( first );
+
+ globalOutputStream() << "to: ";
+ print_vector3( second );
+#endif
+
+ Matrix4 swap = matrix4_swap_axes( first, second );
+
+ Matrix4 tmp = matrix4_reflection_for_plane( plane );
+
+ swap.tx() = -static_cast<float>( -2 * plane.a * plane.d );
+ swap.ty() = -static_cast<float>( -2 * plane.b * plane.d );
+ swap.tz() = -static_cast<float>( -2 * plane.c * plane.d );
+
+ return swap;
+}
+
+void Texdef_transformLocked( TextureProjection& projection, std::size_t width, std::size_t height, const Plane3& plane, const Matrix4& identity2transformed ){
+ //globalOutputStream() << "identity2transformed: " << identity2transformed << "\n";
+
+ //globalOutputStream() << "plane.normal(): " << plane.normal() << "\n";
+
+ Vector3 normalTransformed( matrix4_transformed_direction( identity2transformed, plane.normal() ) );
+
+ //globalOutputStream() << "normalTransformed: " << normalTransformed << "\n";
+
+ // identity: identity space
+ // transformed: transformation
+ // stIdentity: base st projection space before transformation
+ // stTransformed: base st projection space after transformation
+ // stOriginal: original texdef space
+
+ // stTransformed2stOriginal = stTransformed -> transformed -> identity -> stIdentity -> stOriginal
+
+ Matrix4 identity2stIdentity;
+ Texdef_basisForNormal( projection, plane.normal(), identity2stIdentity );
+ //globalOutputStream() << "identity2stIdentity: " << identity2stIdentity << "\n";
+
+ if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_HALFLIFE ) {
+ matrix4_transform_direction( identity2transformed, projection.m_basis_s );
+ matrix4_transform_direction( identity2transformed, projection.m_basis_t );
+ }
+
+ Matrix4 transformed2stTransformed;
+ Texdef_basisForNormal( projection, normalTransformed, transformed2stTransformed );
+
+ Matrix4 stTransformed2identity( matrix4_affine_inverse( matrix4_multiplied_by_matrix4( transformed2stTransformed, identity2transformed ) ) );
+
+ Vector3 originalProjectionAxis( vector4_to_vector3( matrix4_affine_inverse( identity2stIdentity ).z() ) );
+
+ Vector3 transformedProjectionAxis( vector4_to_vector3( stTransformed2identity.z() ) );
+
+ Matrix4 stIdentity2stOriginal;
+ Texdef_toTransform( projection, (float)width, (float)height, stIdentity2stOriginal );
+ Matrix4 identity2stOriginal( matrix4_multiplied_by_matrix4( stIdentity2stOriginal, identity2stIdentity ) );
+
+ //globalOutputStream() << "originalProj: " << originalProjectionAxis << "\n";
+ //globalOutputStream() << "transformedProj: " << transformedProjectionAxis << "\n";
+ double dot = vector3_dot( originalProjectionAxis, transformedProjectionAxis );
+ //globalOutputStream() << "dot: " << dot << "\n";
+ if ( dot == 0 ) {
+ // The projection axis chosen for the transformed normal is at 90 degrees
+ // to the transformed projection axis chosen for the original normal.
+ // This happens when the projection axis is ambiguous - e.g. for the plane
+ // 'X == Y' the projection axis could be either X or Y.
+ //globalOutputStream() << "flipped\n";
+#if 0
+ globalOutputStream() << "projection off by 90\n";
+ globalOutputStream() << "normal: ";
+ print_vector3( plane.normal() );
+ globalOutputStream() << "original projection: ";
+ print_vector3( originalProjectionAxis );
+ globalOutputStream() << "transformed projection: ";
+ print_vector3( transformedProjectionAxis );
+#endif
+
+ Matrix4 identityCorrected = matrix4_reflection_for_plane45( plane, originalProjectionAxis, transformedProjectionAxis );
+
+ identity2stOriginal = matrix4_multiplied_by_matrix4( identity2stOriginal, identityCorrected );
+ }
+
+ Matrix4 stTransformed2stOriginal = matrix4_multiplied_by_matrix4( identity2stOriginal, stTransformed2identity );
+
+ Texdef_fromTransform( projection, (float)width, (float)height, stTransformed2stOriginal );
+ Texdef_normalise( projection, (float)width, (float)height );
+}
+
+#if 1
+void Q3_to_matrix( const texdef_t& texdef, float width, float height, const Vector3& normal, Matrix4& matrix ){
+ Normal_GetTransform( normal, matrix );
+
+ Matrix4 transform;
+
+ Texdef_toTransform( texdef, width, height, transform );
+
+ matrix4_multiply_by_matrix4( matrix, transform );
+}
+
+void BP_from_matrix( brushprimit_texdef_t& bp_texdef, const Vector3& normal, const Matrix4& transform ){
+ Matrix4 basis;
+ 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 );
+ matrix4_affine_invert( basis );
+
+ Matrix4 basis2texture = matrix4_multiplied_by_matrix4( basis, transform );
+
+ BPTexdef_fromTransform( bp_texdef, basis2texture );
+}
+
+void Q3_to_BP( const texdef_t& texdef, float width, float height, const Vector3& normal, brushprimit_texdef_t& bp_texdef ){
+ Matrix4 matrix;
+ Q3_to_matrix( texdef, width, height, normal, matrix );
+ BP_from_matrix( bp_texdef, normal, matrix );
+}
+#endif