1 /*
2  Copyright (C) 2001-2006, William Joseph.
3  All Rights Reserved.
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 #ifndef INCLUDED_TEXTURELIB_H
23 #define INCLUDED_TEXTURELIB_H
24 
25 #include "debugging/debugging.h"
26 #include "math/Vector3.h"
27 #include "math/matrix.h"
28 #include "math/Plane3.h"
29 
30 typedef Vector3 Colour3;
31 typedef unsigned int GLuint;
32 class LoadImageCallback;
33 class Image;
34 
35 enum ProjectionAxis
36 {
37 	eProjectionAxisX = 0, eProjectionAxisY = 1, eProjectionAxisZ = 2
38 };
39 
40 // describes a GL texture
41 class GLTexture
42 {
43 	private:
44 
45 		/**
46 		 * @brief This function does the actual processing of raw RGBA data into a GL texture.
47 		 * @note It will also resample to power-of-two dimensions, generate the mipmaps and adjust gamma.
48 		 */
49 		void LoadTextureRGBA (Image& image);
50 
51 		const std::string name;
52 
53 	public:
54 		GLTexture (const LoadImageCallback& load, const std::string& name);
55 
56 		void realise ();
57 		void unrealise ();
58 
59 		const std::string& getName () const;
60 
61 		const LoadImageCallback& load;
62 		std::size_t width, height;
63 		GLuint texture_number; // gl bind number
64 		Colour3 color; // for flat shade mode
65 		int surfaceFlags, contentFlags, value;
66 		bool hasAlpha;
67 };
68 
matrix4_rotation_for_vector3(const Vector3 & x,const Vector3 & y,const Vector3 & z)69 inline Matrix4 matrix4_rotation_for_vector3 (const Vector3& x, const Vector3& y, const Vector3& z)
70 {
71 	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);
72 }
73 
matrix4_swap_axes(const Vector3 & from,const Vector3 & to)74 inline Matrix4 matrix4_swap_axes (const Vector3& from, const Vector3& to)
75 {
76 	if (from.x() != 0 && to.y() != 0) {
77 		return matrix4_rotation_for_vector3(to, from, g_vector3_axis_z);
78 	}
79 
80 	if (from.x() != 0 && to.z() != 0) {
81 		return matrix4_rotation_for_vector3(to, g_vector3_axis_y, from);
82 	}
83 
84 	if (from.y() != 0 && to.z() != 0) {
85 		return matrix4_rotation_for_vector3(g_vector3_axis_x, to, from);
86 	}
87 
88 	if (from.y() != 0 && to.x() != 0) {
89 		return matrix4_rotation_for_vector3(from, to, g_vector3_axis_z);
90 	}
91 
92 	if (from.z() != 0 && to.x() != 0) {
93 		return matrix4_rotation_for_vector3(from, g_vector3_axis_y, to);
94 	}
95 
96 	if (from.z() != 0 && to.y() != 0) {
97 		return matrix4_rotation_for_vector3(g_vector3_axis_x, from, to);
98 	}
99 
100 	ERROR_MESSAGE("unhandled axis swap case");
101 
102 	return Matrix4::getIdentity();
103 }
104 
matrix4_reflection_for_plane(const Plane3 & plane)105 inline Matrix4 matrix4_reflection_for_plane (const Plane3& plane)
106 {
107 	return Matrix4::byColumns(1 - (2 * plane.normal().x() * plane.normal().x()), -2 * plane.normal().x()
108 			* plane.normal().y(), -2 * plane.normal().x() * plane.normal().z(), 0, -2 * plane.normal().y()
109 			* plane.normal().x(), 1 - (2 * plane.normal().y() * plane.normal().y()), -2 * plane.normal().y()
110 			* plane.normal().z(), 0, -2 * plane.normal().z() * plane.normal().x(), -2 * plane.normal().z()
111 			* plane.normal().y(), 1 - (2 * plane.normal().z() * plane.normal().z()), 0, -2 * plane.dist()
112 			* plane.normal().x(), -2 * plane.dist() * plane.normal().y(), -2 * plane.dist() * plane.normal().z(), 1);
113 }
114 
matrix4_reflection_for_plane45(const Plane3 & plane,const Vector3 & from,const Vector3 & to)115 inline Matrix4 matrix4_reflection_for_plane45 (const Plane3& plane, const Vector3& from, const Vector3& to)
116 {
117 	Vector3 first = from;
118 	Vector3 second = to;
119 
120 	if ((from.dot(plane.normal()) > 0) == (to.dot(plane.normal()) > 0)) {
121 		first = -first;
122 		second = -second;
123 	}
124 
125 	Matrix4 swap = matrix4_swap_axes(first, second);
126 
127 	//Matrix4 tmp = matrix4_reflection_for_plane(plane);
128 
129 	swap.tx() = -(-2 * plane.normal().x() * plane.dist());
130 	swap.ty() = -(-2 * plane.normal().y() * plane.dist());
131 	swap.tz() = -(-2 * plane.normal().z() * plane.dist());
132 
133 	return swap;
134 }
135 
136 const float ProjectionAxisEpsilon = static_cast<float> (0.0001);
137 
projectionaxis_better(float axis,float other)138 inline bool projectionaxis_better (float axis, float other)
139 {
140 	return fabs(axis) > fabs(other) + ProjectionAxisEpsilon;
141 }
142 
143 /// \brief Texture axis precedence: Z > X > Y
projectionaxis_for_normal(const Vector3 & normal)144 inline ProjectionAxis projectionaxis_for_normal (const Vector3& normal)
145 {
146 	return (projectionaxis_better(normal[eProjectionAxisY], normal[eProjectionAxisX])) ? (projectionaxis_better(
147 			normal[eProjectionAxisY], normal[eProjectionAxisZ])) ? eProjectionAxisY : eProjectionAxisZ
148 			: (projectionaxis_better(normal[eProjectionAxisX], normal[eProjectionAxisZ])) ? eProjectionAxisX
149 					: eProjectionAxisZ;
150 }
151 
152 /*!
153  \brief Construct a transform from XYZ space to ST space (3d to 2d).
154  This will be one of three axis-aligned spaces, depending on the surface normal.
155  NOTE: could also be done by swapping values.
156  */
Normal_GetTransform(const Vector3 & normal,Matrix4 & transform)157 inline void Normal_GetTransform (const Vector3& normal, Matrix4& transform)
158 {
159 	switch (projectionaxis_for_normal(normal)) {
160 	case eProjectionAxisZ:
161 		transform[0] = 1;
162 		transform[1] = 0;
163 		transform[2] = 0;
164 
165 		transform[4] = 0;
166 		transform[5] = 1;
167 		transform[6] = 0;
168 
169 		transform[8] = 0;
170 		transform[9] = 0;
171 		transform[10] = 1;
172 		break;
173 	case eProjectionAxisY:
174 		transform[0] = 1;
175 		transform[1] = 0;
176 		transform[2] = 0;
177 
178 		transform[4] = 0;
179 		transform[5] = 0;
180 		transform[6] = -1;
181 
182 		transform[8] = 0;
183 		transform[9] = 1;
184 		transform[10] = 0;
185 		break;
186 	case eProjectionAxisX:
187 		transform[0] = 0;
188 		transform[1] = 0;
189 		transform[2] = 1;
190 
191 		transform[4] = 1;
192 		transform[5] = 0;
193 		transform[6] = 0;
194 
195 		transform[8] = 0;
196 		transform[9] = 1;
197 		transform[10] = 0;
198 		break;
199 	}
200 	transform[3] = transform[7] = transform[11] = transform[12] = transform[13] = transform[14] = 0;
201 	transform[15] = 1;
202 }
203 
204 /* greebo: This method calculates the normalised basis vectors of the texture plane as defined by <normal>
205  *
206  * If the normal vector points to the z-direction, the basis vectors are part
207  * of the xy-plane: texS = <0,1,0> and texT = <1,0,0>
208  *
209  * If normal vector points to the negative z-direction, the above case applies, but with
210  * the x-direction inversed: texS = <0,1,0> and texT = <-1,0,0> (note the minus)
211  *
212  * If none of the two above cases apply, the basis is calculated via cross products
213  * that result in vectors perpendicular to <normal>. These lie within the plane
214  * that is defined by the normal vector itself.
215  *
216  * Note: the vector <normal> MUST be normalised for this to function correctly.
217  */
ComputeAxisBase(const Vector3 & normal,Vector3 & texS,Vector3 & texT)218 inline void ComputeAxisBase (const Vector3& normal, Vector3& texS, Vector3& texT)
219 {
220 	const Vector3 up(0, 0, 1);
221 	const Vector3 down(0, 0, -1);
222 
223 	if (vector3_equal_epsilon(normal, up, float(1e-6))) {
224 		texS = Vector3(0, 1, 0);
225 		texT = Vector3(1, 0, 0);
226 	} else if (vector3_equal_epsilon(normal, down, float(1e-6))) {
227 		texS = Vector3(0, 1, 0);
228 		texT = Vector3(-1, 0, 0);
229 	} else {
230 		texS = normal.crossProduct(up).getNormalised();
231 		texT = normal.crossProduct(texS).getNormalised();
232 		texS = -texS;
233 	}
234 }
235 
236 // greebo: Helper function
237 // handles degenerate cases, just in case library atan2 doesn't
arctangent_yx(double y,double x)238 inline double arctangent_yx (double y, double x)
239 {
240 	if (fabs(x) > 1.0E-6) {
241 		return atan2(y, x);
242 	} else if (y > 0) {
243 		return c_half_pi;
244 	} else {
245 		return -c_half_pi;
246 	}
247 }
248 
249 #endif
250