package loon.opengl;
import java.util.StringTokenizer;
import loon.BaseIO;
import loon.canvas.LColor;
import loon.canvas.Pixmap;
import loon.geom.Vector3f;
import loon.utils.MathUtils;
import loon.utils.StringUtils;
import loon.utils.TArray;
/**这是一个3D对象预览用组件,可以导出3D对象的基本骨骼图,另外,此组件允许直接把3D图像输出在Pixmap上.方便用户静态使用**/
public class PreBoxViewer3D {
private boolean showGrid = true;
private LColor gridColor = new LColor(LColor.red);
public class PreFace {
int v1, v2, v3;
int t1, t2, t3;
int n1, n2, n3;
PreFace() {
v1 = 0;
v2 = 0;
v3 = 0;
t1 = 0;
t2 = 0;
t3 = 0;
n1 = 0;
n2 = 0;
n3 = 0;
}
void setVertices(int v1, int v2, int v3) {
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
void setTextureVertices(int t1, int t2, int t3) {
this.t1 = t1;
this.t2 = t2;
this.t3 = t3;
}
void setNormalVertices(int n1, int n2, int n3) {
this.n1 = n1;
this.n2 = n2;
this.n3 = n3;
}
}
public class PreTextureVertex {
Vector3f position;
PreTextureVertex() {
position = new Vector3f(0.0f, 0.0f, 0.0f);
}
void setPosition(float u, float v, float w) {
position.x = u;
position.y = v;
position.z = w;
}
}
public class PreVertex {
Vector3f position;
PreVertex() {
position = new Vector3f(0.0f, 0.0f, 0.0f);
}
void setPosition(float x, float y, float z) {
position.x = x;
position.y = y;
position.z = z;
}
}
PreVertex[] vertex;
PreTextureVertex[] textureVertex;
PreVertex[] vertexNormal;
PreFace[] face;
PreVertex[] rotatedVertex;
PreVertex[] faceNormal;
PreVertex[] rotatedFaceNormal;
LColor color;
PreVertex ambientLight;
int vertices;
int textureVertices;
int vertexNormals;
int faces;
private int zValue[];
PreBoxViewer3D() {
vertex = null;
textureVertex = null;
vertexNormal = null;
face = null;
rotatedVertex = null;
faceNormal = null;
rotatedFaceNormal = null;
ambientLight = new PreVertex();
setAmbientLight(1.0f, 1.0f, 1.0f, 64.0f);
color = new LColor(0.5f, 0.5f, 0.5f);
vertices = 0;
textureVertices = 0;
vertexNormals = 0;
faces = 0;
}
private float[][] createRotationMatrix(float x, float y, float z, float ax,
float ay, float az) {
float[][] matrix = new float[4][4];
matrix[0][0] = (MathUtils.cos(ay) * MathUtils.cos(az));
matrix[1][0] = (MathUtils.cos(ay) * MathUtils.sin(az));
matrix[2][0] = (-MathUtils.sin(ay));
matrix[3][0] = 0.0f;
matrix[0][1] = (MathUtils.sin(ax) * MathUtils.sin(ay)
* MathUtils.cos(az) - MathUtils.cos(ax) * MathUtils.sin(az));
matrix[1][1] = (MathUtils.sin(ax) * MathUtils.sin(ay)
* MathUtils.sin(az) + MathUtils.cos(ax) * MathUtils.cos(az));
matrix[2][1] = (MathUtils.sin(ax) * MathUtils.cos(ay));
matrix[3][1] = 0.0f;
matrix[0][2] = (MathUtils.cos(ax) * MathUtils.sin(ay)
* MathUtils.cos(az) + MathUtils.sin(ax) * MathUtils.sin(az));
matrix[1][2] = (MathUtils.cos(ax) * MathUtils.sin(ay)
* MathUtils.sin(az) - MathUtils.sin(ax) * MathUtils.cos(az));
matrix[2][2] = (MathUtils.cos(ax) * MathUtils.cos(ay));
matrix[3][2] = 0.0f;
matrix[0][3] = x;
matrix[1][3] = y;
matrix[2][3] = z;
matrix[3][3] = 1.0f;
return matrix;
}
private boolean isPolygonVisible(PreVertex v1, PreVertex v2, PreVertex v3) {
boolean returnValue = false;
float dx1 = v3.position.x - v1.position.x;
float dy1 = v3.position.y - v1.position.y;
float dx2 = v3.position.x - v2.position.x;
float dy2 = v3.position.y - v2.position.y;
if ((dx1 * (dy2 - dy1) - (dx2 - dx1) * dy1) > 0) {
returnValue = true;
}
return returnValue;
}
private void sort(int top, int bottom) {
int i, j;
int x, tmp;
i = top;
if (i < 0) {
i = 0;
}
j = bottom;
if (j < 0) {
j = 0;
}
x = zValue[(top + bottom) / 2];
do {
while (zValue[i] < x) {
i++;
}
while (x < zValue[j]) {
j--;
}
if (i < j) {
tmp = zValue[i];
zValue[i] = zValue[j];
zValue[j] = tmp;
}
if (i <= j) {
i++;
j--;
}
} while (i <= j);
if (top < j)
sort(top, j);
if (i < bottom)
sort(i, bottom);
}
private int limitColor(int value) {
if (value < 0) {
value = 0;
} else if (value > 255) {
value = 255;
}
return value;
}
private void initLambertFlat(GLEx g, PreVertex n) {
int delta = 0;
int red = 0;
int green = 0;
int blue = 0;
delta = (int) (ambientLight.position.x * n.position.x
+ ambientLight.position.y * n.position.y + ambientLight.position.z
* n.position.z);
red = (int) (color.r * 255f) + delta;
green = (int) (color.g * 255f) + delta;
blue = (int) (color.b * 255f) + delta;
g.setColor(limitColor(red), limitColor(green), limitColor(blue));
}
private void initLambertFlat(Pixmap g, PreVertex n) {
int delta = 0;
int red = 0;
int green = 0;
int blue = 0;
delta = (int) (ambientLight.position.x * n.position.x
+ ambientLight.position.y * n.position.y + ambientLight.position.z
* n.position.z);
red = (int) (color.r * 255f) + delta;
green = (int) (color.g * 255f) + delta;
blue = (int) (color.b * 255f) + delta;
g.setColor(limitColor(red), limitColor(green), limitColor(blue));
}
final float[] xPoints = new float[3];
final float[] yPoints = new float[3];
private void drawLambertFlat(GLEx g, PreVertex v1, PreVertex v2,
PreVertex v3) {
xPoints[0] = v1.position.x;
yPoints[0] = v1.position.y;
xPoints[1] = v2.position.x;
yPoints[1] = v2.position.y;
xPoints[2] = v3.position.x;
yPoints[2] = v3.position.y;
if (showGrid) {
g.setColor(gridColor);
g.drawPolygon(xPoints, yPoints, 3);
} else {
g.fillPolygon(xPoints, yPoints, 3);
}
}
final int[] ixPoints = new int[3];
final int[] iyPoints = new int[3];
private void drawLambertFlat(Pixmap g, PreVertex v1, PreVertex v2,
PreVertex v3) {
ixPoints[0] = (int) v1.position.x;
iyPoints[0] = (int) v1.position.y;
ixPoints[1] = (int) v2.position.x;
iyPoints[1] = (int) v2.position.y;
ixPoints[2] = (int) v3.position.x;
iyPoints[2] = (int) v3.position.y;
if (showGrid) {
g.setColor(gridColor);
g.drawPolygon(ixPoints, iyPoints, 3);
} else {
g.fillPolygon(ixPoints, iyPoints, 3);
}
}
public void rotate(float x, float y, float z, float ax, float ay, float az) {
float rz = 0.0f;
float[][] matrix = null;
matrix = createRotationMatrix(0, 0, 0, ax, ay, az);
for (int i = 0; i < getFaces(); i++) {
rotatedFaceNormal[i].setPosition(matrix[0][0]
* faceNormal[i].position.x + matrix[0][1]
* faceNormal[i].position.y + matrix[0][2]
* faceNormal[i].position.z + matrix[0][3], matrix[1][0]
* faceNormal[i].position.x + matrix[1][1]
* faceNormal[i].position.y + matrix[1][2]
* faceNormal[i].position.z + matrix[1][3], matrix[2][0]
* faceNormal[i].position.x + matrix[2][1]
* faceNormal[i].position.y + matrix[2][2]
* faceNormal[i].position.z + matrix[2][3]);
}
matrix = createRotationMatrix(x, y, z, ax, ay, az);
for (int i = 0; i < getVertices(); i++) {
rz = matrix[2][0] * vertex[i].position.x + matrix[2][1]
* vertex[i].position.y + matrix[2][2]
* vertex[i].position.z + matrix[2][3];
rotatedVertex[i].setPosition((1024 * (matrix[0][0]
* vertex[i].position.x + matrix[0][1]
* vertex[i].position.y + matrix[0][2]
* vertex[i].position.z + matrix[0][3]))
/ rz, (1024 * (matrix[1][0] * vertex[i].position.x
+ matrix[1][1] * vertex[i].position.y + matrix[1][2]
* vertex[i].position.z + matrix[1][3]))
/ rz, rz);
}
}
public void draw(GLEx g) {
int n = 0;
zValue = new int[faces];
for (int i = 0; i < getFaces(); i++) {
if (true == isPolygonVisible(rotatedVertex[face[i].v1],
rotatedVertex[face[i].v2], rotatedVertex[face[i].v3])) {
zValue[n] = i;
zValue[n] += (int) (rotatedVertex[face[i].v1].position.z
+ rotatedVertex[face[i].v2].position.z + rotatedVertex[face[i].v3].position.z) << 16;
n++;
}
}
sort(0, n - 1);
int tmp = g.color();
for (int i = 0; i < n; i++) {
initLambertFlat(g, rotatedFaceNormal[zValue[i] & 65535]);
drawLambertFlat(g, rotatedVertex[face[zValue[i] & 65535].v1],
rotatedVertex[face[zValue[i] & 65535].v2],
rotatedVertex[face[zValue[i] & 65535].v3]);
}
g.setColor(tmp);
}
void draw(Pixmap g) {
int n = 0;
zValue = new int[faces];
for (int i = 0; i < getFaces(); i++) {
if (true == isPolygonVisible(rotatedVertex[face[i].v1],
rotatedVertex[face[i].v2], rotatedVertex[face[i].v3])) {
zValue[n] = i;
zValue[n] += (int) (rotatedVertex[face[i].v1].position.z
+ rotatedVertex[face[i].v2].position.z + rotatedVertex[face[i].v3].position.z) << 16;
n++;
}
}
sort(0, n - 1);
int tmp = g.color();
for (int i = 0; i < n; i++) {
initLambertFlat(g, rotatedFaceNormal[zValue[i] & 65535]);
drawLambertFlat(g, rotatedVertex[face[zValue[i] & 65535].v1],
rotatedVertex[face[zValue[i] & 65535].v2],
rotatedVertex[face[zValue[i] & 65535].v3]);
}
g.setColor(tmp);
}
void allocateVertices(int vertices) {
vertex = new PreVertex[vertices];
rotatedVertex = new PreVertex[vertices];
this.vertices = vertices;
for (int i = 0; i < vertices; i++) {
vertex[i] = new PreVertex();
rotatedVertex[i] = new PreVertex();
}
}
void allocateTextureVertices(int textureVertices) {
textureVertex = new PreTextureVertex[textureVertices];
this.textureVertices = textureVertices;
for (int i = 0; i < textureVertices; i++) {
textureVertex[i] = new PreTextureVertex();
}
}
void allocateVertexNormals(int vertexNormals) {
vertexNormal = new PreVertex[vertexNormals];
this.vertexNormals = vertexNormals;
for (int i = 0; i < vertexNormals; i++) {
vertexNormal[i] = new PreVertex();
}
}
void allocateFaces(int faces) {
face = new PreFace[faces];
faceNormal = new PreVertex[faces];
rotatedFaceNormal = new PreVertex[faces];
this.faces = faces;
for (int i = 0; i < faces; i++) {
face[i] = new PreFace();
faceNormal[i] = new PreVertex();
rotatedFaceNormal[i] = new PreVertex();
}
}
int getVertices() {
return vertices;
}
int getTextureVertices() {
return textureVertices;
}
public int getVertexNormals() {
return vertexNormals;
}
public int getFaces() {
return faces;
}
public void calculateFaceNormals() {
for (int i = 0; i < faces; i++) {
float x1 = vertex[face[i].v2].position.x
- vertex[face[i].v1].position.x;
float y1 = vertex[face[i].v2].position.y
- vertex[face[i].v1].position.y;
float z1 = vertex[face[i].v2].position.z
- vertex[face[i].v1].position.z;
float x2 = vertex[face[i].v3].position.x
- vertex[face[i].v1].position.x;
float y2 = vertex[face[i].v3].position.y
- vertex[face[i].v1].position.y;
float z2 = vertex[face[i].v3].position.z
- vertex[face[i].v1].position.z;
float nx = y1 * z2 - z1 * y2;
float ny = z1 * x2 - x1 * z2;
float nz = x1 * y2 - y1 * x2;
float d = MathUtils.sqrt(nx * nx + ny * ny + nz * nz);
faceNormal[i].setPosition(nx / d, ny / d, nz / d);
}
setAmbientLight(ambientLight.position.x, ambientLight.position.y,
ambientLight.position.z, 64.0f);
}
public void setAmbientLight(float x, float y, float z, float f) {
float d = MathUtils.sqrt(x * x + y * y + z * z);
ambientLight.setPosition(x * f / d, y * f / d, z * f / d);
}
public void setColor(float r, float g, float b) {
color.setColor(r, g, b);
}
private static void parseVertex(String line, float[] vertex) {
StringTokenizer stk = new StringTokenizer(line, " ");
float w = 1.0f;
if (true == stk.hasMoreTokens() && 0 == stk.nextToken().compareTo("v")) {
if (true == stk.hasMoreTokens()) {
vertex[0] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[1] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[2] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
w = Float.valueOf(stk.nextToken());
}
vertex[0] = vertex[0] / w;
vertex[1] = vertex[1] / w;
vertex[2] = vertex[2] / w;
}
}
private static void parseTextureVertex(String line, float[] vertex) {
StringTokenizer stk = new StringTokenizer(line, " ");
if (true == stk.hasMoreTokens() && 0 == stk.nextToken().compareTo("vt")) {
if (true == stk.hasMoreTokens()) {
vertex[0] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[1] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[2] = Float.valueOf(stk.nextToken());
} else {
vertex[2] = 0.0f;
}
}
}
private static void parseVertexNormal(String line, float[] vertex) {
StringTokenizer stk = new StringTokenizer(line, " ");
float w = 1.0f;
if (true == stk.hasMoreTokens() && 0 == stk.nextToken().compareTo("vn")) {
if (true == stk.hasMoreTokens()) {
vertex[0] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[1] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
vertex[2] = Float.valueOf(stk.nextToken());
}
if (true == stk.hasMoreTokens()) {
w = Float.valueOf(stk.nextToken());
}
vertex[0] = vertex[0] / w;
vertex[1] = vertex[1] / w;
vertex[2] = vertex[2] / w;
}
}
private static void parseFace(String line, int[] face) {
StringTokenizer stk = new StringTokenizer(line, " ");
if (stk.hasMoreTokens() && stk.nextToken().startsWith("f")) {
for (int i = 0; i < 3; i++) {
if (stk.hasMoreTokens()) {
String s[] = stk.nextToken().split("/");
face[i] = Integer.valueOf(s[0]) - 1;
if (2 == s.length) {
face[i + 3] = Integer.valueOf(s[1]) - 1;
} else if (3 == s.length) {
if (0 != s[1].length()) {
face[i + 3] = Integer.valueOf(s[1]) - 1;
}
face[i + 6] = Integer.valueOf(s[2]) - 1;
}
}
}
}
}
private static boolean isVertexList(String line) {
boolean returnValue = false;
if (true == line.startsWith("v ")) {
returnValue = true;
}
return returnValue;
}
private static boolean isTextureVertexList(String line) {
boolean returnValue = false;
if (true == line.startsWith("vt ")) {
returnValue = true;
}
return returnValue;
}
private static boolean isVertexNormalList(String line) {
boolean returnValue = false;
if (true == line.startsWith("vn ")) {
returnValue = true;
}
return returnValue;
}
private static boolean isFaceList(String line) {
boolean returnValue = false;
if (true == line.startsWith("f ")) {
returnValue = true;
}
return returnValue;
}
private static PreBoxViewer3D parseObj(String text) {
TArray<Float> vertexList = null;
TArray<Float> textureVertexList = null;
TArray<Float> vertexNormalList = null;
TArray<Integer> faceList = null;
String[] lines = StringUtils.split(text, '\n');
PreBoxViewer3D mesh = null;
float[] vertex = new float[3];
int[] face = new int[9];
int vertices = 0;
int textureVertices = 0;
int vertexNormals = 0;
int faces = 0;
for (String line : lines)
if (null != line) {
if (true == isVertexList(line)) {
parseVertex(line, vertex);
if (null == vertexList) {
vertexList = new TArray<Float>();
}
vertexList.add(vertex[0]);
vertexList.add(vertex[1]);
vertexList.add(vertex[2]);
vertices++;
}
if (true == isTextureVertexList(line)) {
parseTextureVertex(line, vertex);
if (null == textureVertexList) {
textureVertexList = new TArray<Float>();
}
textureVertexList.add(vertex[0]);
textureVertexList.add(vertex[1]);
textureVertexList.add(vertex[2]);
textureVertices++;
}
if (true == isVertexNormalList(line)) {
parseVertexNormal(line, vertex);
if (null == vertexNormalList) {
vertexNormalList = new TArray<Float>();
}
vertexNormalList.add(vertex[0]);
vertexNormalList.add(vertex[1]);
vertexNormalList.add(vertex[2]);
vertexNormals++;
}
if (true == isFaceList(line)) {
parseFace(line, face);
if (null == faceList) {
faceList = new TArray<Integer>();
}
faceList.add(face[0]);
faceList.add(face[1]);
faceList.add(face[2]);
faceList.add(face[3]);
faceList.add(face[4]);
faceList.add(face[5]);
faceList.add(face[6]);
faceList.add(face[7]);
faceList.add(face[8]);
faces++;
}
}
mesh = new PreBoxViewer3D();
mesh.allocateVertices(vertices);
if (0 != textureVertices) {
mesh.allocateTextureVertices(textureVertices);
}
if (0 != vertexNormals) {
mesh.allocateVertexNormals(vertexNormals);
}
mesh.allocateFaces(faces);
for (int i = 0; i < vertices; i++) {
mesh.vertex[i].setPosition(vertexList.get(i * 3),
vertexList.get(i * 3 + 1), vertexList.get(i * 3 + 2));
}
for (int i = 0; i < vertexNormals; i++) {
mesh.vertexNormal[i].setPosition(vertexNormalList.get(i * 3),
vertexNormalList.get(i * 3 + 1),
vertexNormalList.get(i * 3 + 2));
}
for (int i = 0; i < textureVertices; i++) {
mesh.textureVertex[i].setPosition(textureVertexList.get(i * 3),
textureVertexList.get(i * 3 + 1),
textureVertexList.get(i * 3 + 2));
}
for (int i = 0; i < faces; i++) {
mesh.face[i].setVertices(faceList.get(i * 9),
faceList.get(i * 9 + 1), faceList.get(i * 9 + 2));
mesh.face[i].setTextureVertices(faceList.get(i * 9 + 3),
faceList.get(i * 9 + 4), faceList.get(i * 9 + 5));
mesh.face[i].setNormalVertices(faceList.get(i * 9 + 6),
faceList.get(i * 9 + 7), faceList.get(i * 9 + 8));
}
return mesh;
}
public static PreBoxViewer3D load(String filename) {
PreBoxViewer3D mesh = null;
String text = BaseIO.loadText(filename);
if (text != null) {
mesh = parseObj(text);
}
return mesh;
}
public boolean isShowGrid() {
return showGrid;
}
public void setShowGrid(boolean showGrid) {
this.showGrid = showGrid;
}
public LColor getGridColor() {
return gridColor;
}
public void setGridColor(LColor gridColor) {
this.gridColor = gridColor;
}
}