1package com.jme3.scene.plugins.blender.textures;
2
3import com.jme3.bounding.BoundingBox;
4import com.jme3.bounding.BoundingSphere;
5import com.jme3.math.FastMath;
6import com.jme3.math.Triangle;
7import com.jme3.math.Vector3f;
8import com.jme3.scene.Mesh;
9import com.jme3.scene.VertexBuffer;
10import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube;
11import java.nio.FloatBuffer;
12
13/**
14 * This class helps with projection calculations.
15 *
16 * @author Marcin Roguski (Kaelthas)
17 */
18/* package */class UVProjectionGenerator {
19	/**
20	 * Flat projection for 2D textures.
21	 *
22	 * @param mesh
23	 *            mesh that is to be projected
24	 * @param bb
25	 *            the bounding box for projecting
26	 * @return UV coordinates after the projection
27	 */
28	public static float[] flatProjection(Mesh mesh, BoundingBox bb) {
29		if (bb == null) {
30			bb = UVCoordinatesGenerator.getBoundingBox(mesh);
31		}
32		Vector3f min = bb.getMin(null);
33		float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f };
34		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
35		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
36		for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
37			uvCoordinates[j] = (positions.get(i) - min.x) / ext[0];
38			uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1];
39			// skip the Z-coordinate
40		}
41		return uvCoordinates;
42	}
43
44	/**
45	 * Cube projection for 2D textures.
46	 *
47	 * @param mesh
48	 *            mesh that is to be projected
49	 * @param bb
50	 *            the bounding box for projecting
51	 * @return UV coordinates after the projection
52	 */
53	public static float[] cubeProjection(Mesh mesh, BoundingBox bb) {
54		Triangle triangle = new Triangle();
55		Vector3f x = new Vector3f(1, 0, 0);
56		Vector3f y = new Vector3f(0, 1, 0);
57		Vector3f z = new Vector3f(0, 0, 1);
58		Vector3f min = bb.getMin(null);
59		float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
60
61		float[] uvCoordinates = new float[mesh.getTriangleCount() * 6];// 6 == 3 * 2
62		float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
63		for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) {
64			mesh.getTriangle(i, triangle);
65			Vector3f n = triangle.getNormal();
66			float dotNX = Math.abs(n.dot(x));
67			float dorNY = Math.abs(n.dot(y));
68			float dotNZ = Math.abs(n.dot(z));
69			if (dotNX > borderAngle) {
70				if (dotNZ < borderAngle) {// discard X-coordinate
71					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
72					uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
73					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
74					uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
75					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
76					uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
77				} else {// discard Z-coordinate
78					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
79					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
80					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
81					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
82					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
83					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
84				}
85			} else {
86				if (dorNY > borderAngle) {// discard Y-coordinate
87					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
88					uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
89					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
90					uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
91					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
92					uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
93				} else {// discard Z-coordinate
94					uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
95					uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
96					uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
97					uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
98					uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
99					uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
100				}
101			}
102			triangle.setNormal(null);// clear the previous normal vector
103		}
104		return uvCoordinates;
105	}
106
107	/**
108	 * Tube projection for 2D textures.
109	 *
110	 * @param mesh
111	 *            mesh that is to be projected
112	 * @param bt
113	 *            the bounding tube for projecting
114	 * @return UV coordinates after the projection
115	 */
116	public static float[] tubeProjection(Mesh mesh, BoundingTube bt) {
117		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
118		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
119		Vector3f v = new Vector3f();
120		float cx = bt.getCenter().x, cy = bt.getCenter().y;
121		Vector3f uBase = new Vector3f(0, -1, 0);
122
123		float vBase = bt.getCenter().z - bt.getHeight() * 0.5f;
124		for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
125			// calculating U
126			v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
127			v.normalizeLocal();
128			float angle = v.angleBetween(uBase);// result between [0; PI]
129			if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
130				angle = FastMath.TWO_PI - angle;
131			}
132			uvCoordinates[j] = angle / FastMath.TWO_PI;
133
134			// calculating V
135			float z = positions.get(i + 2);
136			uvCoordinates[j + 1] = (z - vBase) / bt.getHeight();
137		}
138
139		//looking for splitted triangles
140		Triangle triangle = new Triangle();
141		for(int i=0;i<mesh.getTriangleCount();++i) {
142			mesh.getTriangle(i, triangle);
143			float sgn1 = Math.signum(triangle.get1().x-cx);
144			float sgn2 = Math.signum(triangle.get2().x-cx);
145			float sgn3 = Math.signum(triangle.get3().x-cx);
146			float xSideFactor = sgn1 + sgn2 + sgn3;
147			float ySideFactor = Math.signum(triangle.get1().y-cy)+
148					   Math.signum(triangle.get2().y-cy)+
149					   Math.signum(triangle.get3().y-cy);
150			if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
151				//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
152				if(sgn1==1.0f) {
153					uvCoordinates[i*3*2] += 1.0f;
154				}
155				if(sgn2==1.0f) {
156					uvCoordinates[(i*3+1)*2] += 1.0f;
157				}
158				if(sgn3==1.0f) {
159					uvCoordinates[(i*3+2)*2] += 1.0f;
160				}
161			}
162		}
163		return uvCoordinates;
164	}
165
166	/**
167	 * Sphere projection for 2D textures.
168	 *
169	 * @param mesh
170	 *            mesh that is to be projected
171	 * @param bb
172	 *            the bounding box for projecting
173	 * @return UV coordinates after the projection
174	 */
175	public static float[] sphereProjection(Mesh mesh, BoundingSphere bs) {
176		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
177		float[] uvCoordinates = new float[positions.limit() / 3 * 2];
178		Vector3f v = new Vector3f();
179		float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
180		Vector3f uBase = new Vector3f(0, -1, 0);
181		Vector3f vBase = new Vector3f(0, 0, -1);
182
183		for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
184			// calculating U
185			v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
186			v.normalizeLocal();
187			float angle = v.angleBetween(uBase);// result between [0; PI]
188			if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
189				angle = FastMath.TWO_PI - angle;
190			}
191			uvCoordinates[j] = angle / FastMath.TWO_PI;
192
193			// calculating V
194			v.set(positions.get(i)-cx, positions.get(i + 1)-cy, positions.get(i + 2)-cz);
195			v.normalizeLocal();
196			angle = v.angleBetween(vBase);// result between [0; PI]
197			uvCoordinates[j+1] = angle / FastMath.PI;
198		}
199
200		//looking for splitted triangles
201		Triangle triangle = new Triangle();
202		for(int i=0;i<mesh.getTriangleCount();++i) {
203			mesh.getTriangle(i, triangle);
204			float sgn1 = Math.signum(triangle.get1().x-cx);
205			float sgn2 = Math.signum(triangle.get2().x-cx);
206			float sgn3 = Math.signum(triangle.get3().x-cx);
207			float xSideFactor = sgn1 + sgn2 + sgn3;
208			float ySideFactor = Math.signum(triangle.get1().y-cy)+
209					   Math.signum(triangle.get2().y-cy)+
210					   Math.signum(triangle.get3().y-cy);
211			if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
212				//indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
213				if(sgn1==1.0f) {
214					uvCoordinates[i*3*2] += 1.0f;
215				}
216				if(sgn2==1.0f) {
217					uvCoordinates[(i*3+1)*2] += 1.0f;
218				}
219				if(sgn3==1.0f) {
220					uvCoordinates[(i*3+2)*2] += 1.0f;
221				}
222			}
223		}
224		return uvCoordinates;
225	}
226}
227