/******************************************************************************* * Copyright (c) 2013 Philip Collin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Philip Collin - initial API and implementation ******************************************************************************/ package com.lyeeedar.Roguelike3D.Graphics.Models; import java.util.ArrayList; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector3; import com.lyeeedar.Roguelike3D.Graphics.Lights.LightManager; public class Shapes { public static float[] genIcosahedronVertices(float X, float Z) { return new float[]{ -X, 0f, Z, X, 0f, Z, -X, 0f, -Z, X, 0f, -Z, 0f, Z, X, 0f, Z, -X, 0f, -Z, X, 0f, -Z, -X, Z, X, 0f, -Z, X, 0f, Z, -X, 0f, -Z, -X, 0f }; } public static short[] genIcosahedronIndicies() { return new short[] { 0,4,1, 0,9,4, 9,5,4, 4,5,8, 4,8,1, 8,10,1, 8,3,10, 5,3,8, 5,2,3, 2,7,3, 7,10,3, 7,6,10, 7,11,6, 11,0,6, 0,1,6, 6,1,10, 9,0,11, 9,11,2, 9,2,5, 7,2,11 }; } public static Mesh genIcosahedronMesh(float x, float z) { Mesh mesh = new Mesh(true, 12, 60, new VertexAttribute(Usage.Position, 3, "a_position")); mesh.setVertices(genIcosahedronVertices(x, z)); mesh.setIndices(genIcosahedronIndicies()); return mesh; } public static float[] genSphereVertices(float sphereRadius, float heightStep, float degreeStep) { ArrayList<Vector3> points = new ArrayList<Vector3>(); for (float y = sphereRadius; y <= sphereRadius; y+=heightStep) { float radius = SphereRadiusAtHeight(sphereRadius, y); //get the radius of the sphere at this height if (radius == 0) {//for the top and bottom points of the sphere add a single point points.add(new Vector3(((float) Math.sin(0) * radius), y, ((float) Math.cos(0) * radius))); } else { //otherwise step around the circle and add points at the specified degrees for (float d = 0; d <= 360; d += degreeStep) { points.add(new Vector3(((float) Math.sin(d) * radius), y, ((float) Math.cos(d) * radius))); } } } float[] vertices = new float[points.size()*3]; for (int i = 0; i < points.size(); i++) { vertices[(i*3)+0] = points.get(i).x; vertices[(i*3)+1] = points.get(i).y; vertices[(i*3)+2] = points.get(i).z; } return vertices; } public static Mesh genSphereMesh(float radius, float hstep, float dstep) { float[] vertices = genSphereVertices(radius, hstep, dstep); Mesh mesh = new Mesh(true, vertices.length/3, 0, new VertexAttribute(Usage.Position, 3, "a_position")); mesh.setVertices(vertices); return mesh; } public static float SphereRadiusAtHeight(float SphereRadius, float Height) { return (float) Math.sqrt((SphereRadius * SphereRadius) - (Height * Height)); } public static Mesh genCuboid(Vector3 dimensions) { return genCuboid(dimensions.x, dimensions.y, dimensions.z); } public static float[] genCubeVertices(float x, float y, float z, float offsetx, float offsety, float offsetz) { x /= 2.0f; y /= 2.0f; z /= 2.0f; float[] cubeVerts = { -x+offsetx, -y+offsety, -z+offsetz, // bottom -x+offsetx, -y+offsety, z+offsetz, x+offsetx, -y+offsety, z+offsetz, x+offsetx, -y+offsety, -z+offsetz, -x+offsetx, y+offsety, -z+offsetz, // top -x+offsetx, y+offsety, z+offsetz, x+offsetx, y+offsety, z+offsetz, x+offsetx, y+offsety, -z+offsetz, -x+offsetx, -y+offsety, -z+offsetz, // back -x+offsetx, y+offsety, -z+offsetz, x+offsetx, y+offsety, -z+offsetz, x+offsetx, -y+offsety, -z+offsetz, -x+offsetx, -y+offsety, z+offsetz, // front -x+offsetx, y+offsety, z+offsetz, x+offsetx, y+offsety, z+offsetz, x+offsetx, -y+offsety, z+offsetz, -x+offsetx, -y+offsety, -z+offsetz, // left -x+offsetx, -y+offsety, z+offsetz, -x+offsetx, y+offsety, z+offsetz, -x+offsetx, y+offsety, -z+offsetz, x+offsetx, -y+offsety, -z+offsetz, // right x+offsetx, -y+offsety, z+offsetz, x+offsetx, y+offsety, z+offsetz, x+offsetx, y+offsety, -z+offsetz}; float[] cubeNormals = { 0.0f, -1.0f, 0.0f, // bottom 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, // back 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, //front 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, // left -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // right 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; float[] cubeTex = { 0.0f, 0.0f, // bottom 0.0f, z, x, z, x, 0.0f, x, 0.0f, // top x, z, 0.0f, z, 0.0f, 0.0f, x, y, // back x, 0.0f, 0.0f, 0.0f, 0.0f, y, x, y, // front x, 0.0f, 0.0f, 0.0f, 0.0f, y, z, y, // left 0.0f, y, 0.0f, 0.0f, z, 0.0f, z, y, // right 0.0f, y, 0.0f, 0.0f, z, 0.0f }; float[] vertices = new float[24 * 8]; int pIdx = 0; int nIdx = 0; int tIdx = 0; for (int i = 0; i < vertices.length;) { vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeTex[tIdx++]; vertices[i++] = cubeTex[tIdx++]; } return vertices; } public static float[] genCubeVertices(float x, float y, float z) { x /= 2.0f; y /= 2.0f; z /= 2.0f; float[] cubeVerts = { -x, -y, -z, // bottom -x, -y, z, x, -y, z, x, -y, -z, -x, y, -z, // top -x, y, z, x, y, z, x, y, -z, -x, -y, -z, // back -x, y, -z, x, y, -z, x, -y, -z, -x, -y, z, // front -x, y, z, x, y, z, x, -y, z, -x, -y, -z, // left -x, -y, z, -x, y, z, -x, y, -z, x, -y, -z, // right x, -y, z, x, y, z, x, y, -z}; float[] cubeNormals = { 0.0f, -1.0f, 0.0f, // bottom 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, // back 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, //front 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, // left -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // right 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; float[] cubeTex = { 0.0f, 0.0f, // bottom 0.0f, z, x, z, x, 0.0f, x, 0.0f, // top x, z, 0.0f, z, 0.0f, 0.0f, x, y, // back x, 0.0f, 0.0f, 0.0f, 0.0f, y, x, y, // front x, 0.0f, 0.0f, 0.0f, 0.0f, y, z, y, // left 0.0f, y, 0.0f, 0.0f, z, 0.0f, z, y, // right 0.0f, y, 0.0f, 0.0f, z, 0.0f }; float[] vertices = new float[24 * 8]; int pIdx = 0; int nIdx = 0; int tIdx = 0; for (int i = 0; i < vertices.length;) { vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeVerts[pIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeNormals[nIdx++]; vertices[i++] = cubeTex[tIdx++]; vertices[i++] = cubeTex[tIdx++]; } return vertices; } public static short[] genCubeIndices() { return new short[] { 0, 2, 1, // bottom 0, 3, 2, 4, 5, 6, // top 4, 6, 7, 8, 9, 10, // back 8, 10, 11, 12, 15, 14, // front 12, 14, 13, 16, 17, 18, // left 16, 18, 19, 20, 23, 22, // right 20, 22, 21 }; } public static Mesh genCuboid (float x, float y, float z) { Mesh mesh = new Mesh(true, 24, 36, new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0")); float[] vertices = genCubeVertices(x, y, z); short[] indices = genCubeIndices(); mesh.setVertices(vertices); mesh.setIndices(indices); return mesh; } public static Mesh genCuboid (float x, float y, float z, float offsetx, float offsety, float offsetz) { Mesh mesh = new Mesh(true, 24, 36, new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0")); float[] vertices = genCubeVertices(x, y, z, offsetx, offsety, offsetz); short[] indices = genCubeIndices(); mesh.setVertices(vertices); mesh.setIndices(indices); return mesh; } public static TempMesh genTempCuboid (float x, float y, float z) { float[] vertices = genCubeVertices(x, y, z); short[] indices = genCubeIndices(); VertexAttribute[] attributes = { new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0") }; TempMesh tmpMesh = new TempMesh(vertices, indices, 36, 8, 24, attributes); return tmpMesh; } public static void translateMesh(Mesh mesh, float x, float y, float z) { final int vertexSize = mesh.getVertexAttributes().vertexSize / 4; float[] newPos = new float[mesh.getMaxVertices()*vertexSize]; mesh.getVertices(newPos); int positionOffset = mesh.getVertexAttributes().getOffset(Usage.Position); for (int i = 0; i < mesh.getMaxVertices(); i++) { newPos[(i*vertexSize)+0+positionOffset] += x; newPos[(i*vertexSize)+1+positionOffset] += y; newPos[(i*vertexSize)+2+positionOffset] += z; } mesh.setVertices(newPos); } public static void translateCubeVertices(int vertexNum, int vertexSize, float[] vertices, float x, float y, float z) { for (int i = 0; i < vertexNum; i++) { vertices[(i*vertexSize)] += x; vertices[(i*vertexSize)+1] += y; vertices[(i*vertexSize)+2] += z; } } public static Mesh merge(final TempMesh[] tmpMeshes) { final int vertCount = tmpMeshes[0].vertexNum * tmpMeshes.length; final int idxCount = tmpMeshes[0].indiceNum * tmpMeshes.length; final int vertexSize = tmpMeshes[0].vertexSize; final float vertices[] = new float[vertCount * vertexSize]; final short indices[] = new short[idxCount]; int voffset = 0; int ioffset = 0; for (int i = 0; i < tmpMeshes.length; i++) { final TempMesh tmpMesh = tmpMeshes[i]; final int vsize = tmpMesh.vertexNum * vertexSize; final int isize = tmpMesh.indiceNum; final float[] tempVerts = tmpMesh.vertices; final short[] tempIdxs = tmpMesh.indices; for (int j = 0; j < vsize; j++) { vertices[voffset+j] = tempVerts[j]; } for (int j = 0; j < isize; j++) { indices[ioffset+j] = (short)(tempIdxs[j] + (i*tmpMesh.vertexNum)); } voffset += vsize; ioffset += isize; } final Mesh result = new Mesh(true, vertCount, idxCount, tmpMeshes[0].attributes); result.setVertices(vertices); result.setIndices(indices); return result; } public static Mesh insertLight(Mesh mesh, LightManager lights, boolean bakeStatics, Matrix4 model_matrix) { VertexAttributes attributes = mesh.getVertexAttributes(); final int vertCount = mesh.getNumVertices(); final int vertexSize = attributes.vertexSize / 4; VertexAttribute[] newAttributes = new VertexAttribute[attributes.size()+1]; for (int i = 0; i < attributes.size(); i++) { newAttributes[i] = attributes.get(i); } newAttributes[attributes.size()] = new VertexAttribute(Usage.Generic, 3, "a_baked_light"); final int newVertexSize = vertexSize + 3; float[] verts = new float[vertexSize * vertCount]; mesh.getVertices(verts); short[] indices = new short[mesh.getNumIndices()]; mesh.getIndices(indices); float[] newVerts = new float[newVertexSize * vertCount]; int positionOffset = attributes.getOffset(Usage.Position); int normalOffset = attributes.getOffset(Usage.Normal); Matrix4 normal_matrix = new Matrix4(); normal_matrix.set(model_matrix); Vector3 position = new Vector3(); for (int i = 0; i < vertCount; i++) { int j = 0; for (; j < vertexSize; j++) { newVerts[ (i*newVertexSize) + j ] = verts[ (i*vertexSize) + j ]; } position.set(verts[(i*vertexSize)+positionOffset], verts[(i*vertexSize)+positionOffset+1], verts[(i*vertexSize)+positionOffset+2]).mul(model_matrix); Vector3 normal = new Vector3(verts[(i*vertexSize)+normalOffset], verts[(i*vertexSize)+normalOffset+1], verts[(i*vertexSize)+normalOffset+2]); normal.rot(normal_matrix).nor(); Color light_colour = lights.calculateLightAtPoint(position, normal, bakeStatics); newVerts[ (i*newVertexSize) + j + 0 ] = light_colour.r; newVerts[ (i*newVertexSize) + j + 1 ] = light_colour.g; newVerts[ (i*newVertexSize) + j + 2 ] = light_colour.b; } Mesh newMesh = new Mesh(true, mesh.getNumVertices(), mesh.getNumIndices(), newAttributes); newMesh.setVertices(newVerts); newMesh.setIndices(indices); return newMesh; } public static Mesh insertTangents(Mesh mesh) { VertexAttributes attributes = mesh.getVertexAttributes(); final int vertCount = mesh.getNumVertices(); final int vertexSize = attributes.vertexSize / 4; VertexAttribute[] newAttributes = new VertexAttribute[attributes.size()+1]; for (int i = 0; i < attributes.size(); i++) { newAttributes[i] = attributes.get(i); } newAttributes[attributes.size()] = new VertexAttribute(Usage.Generic, 4, "a_tangent"); final int newVertexSize = vertexSize + 4; float[] verts = new float[vertexSize * vertCount]; mesh.getVertices(verts); short[] indices = new short[mesh.getNumIndices()]; mesh.getIndices(indices); float[] newVerts = new float[newVertexSize * vertCount]; int positionOffset = attributes.getOffset(Usage.Position); int normalOffset = attributes.getOffset(Usage.Normal); int textureOffset = attributes.getOffset(Usage.TextureCoordinates); int tangentOffset = 0; for (int i = 0; i < vertCount; i += 3) { int j = 0; for (; j < vertexSize; j++) { newVerts[ (i*newVertexSize) + j ] = verts[ (i*vertexSize) + j ]; newVerts[ ((i+1)*newVertexSize) + j ] = verts[ ((i+1)*vertexSize) + j ]; newVerts[ ((i+2)*newVertexSize) + j ] = verts[ ((i+2)*vertexSize) + j ]; } tangentOffset = j; } for (int i = 0; i < mesh.getNumIndices(); i += 3) { int i1 = indices[i]; int i2 = indices[i+1]; int i3 = indices[i+2]; Vector3 v1 = new Vector3(verts[(i1*vertexSize)+positionOffset], verts[(i1*vertexSize)+positionOffset]+1, verts[(i1*vertexSize)+positionOffset]+2); Vector3 v2 = new Vector3(verts[(i2*vertexSize)+positionOffset], verts[(i2*vertexSize)+positionOffset]+1, verts[(i2*vertexSize)+positionOffset]+2); Vector3 v3 = new Vector3(verts[(i3*vertexSize)+positionOffset], verts[(i3*vertexSize)+positionOffset]+1, verts[(i3*vertexSize)+positionOffset]+2); float[] w1 = {verts[(i1*vertexSize)+textureOffset], verts[(i1*vertexSize)+textureOffset]+1}; float[] w2 = {verts[(i2*vertexSize)+textureOffset], verts[(i2*vertexSize)+textureOffset]+1}; float[] w3 = {verts[(i3*vertexSize)+textureOffset], verts[(i3*vertexSize)+textureOffset]+1}; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2[0] - w1[0]; float s2 = w3[0] - w1[0]; float t1 = w2[1] - w1[1]; float t2 = w3[1] - w1[1]; float div = s1 * t2 - s2 * t1; float r = div == 0.0f ? 0.0f : 1.0f / div; Vector3 t = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 n = new Vector3(verts[(i1*vertexSize)+normalOffset], verts[(i1*vertexSize)+normalOffset]+1, verts[(i1*vertexSize)+normalOffset]+2); //Vector3 tangent = t.cpy().sub(n).mul(n.tmp().dot(t)).nor(); Vector3 tangent = orthoNormalize(n, t); System.out.println(t2); Vector3 tan2 = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); float handedness = ( n.tmp().crs(t).dot(tan2) < 0.0f ) ? -1.0f : 1.0f; Vector3 c1 = n.cpy().crs(0.0f, 0.0f, 1.0f); Vector3 c2 = n.cpy().crs(0.0f, 1.0f, 0.0f); if( c1.len2()>c2.len2() ) { tangent = c1; } else { tangent = c2; } newVerts[ (i1*newVertexSize) + tangentOffset ] = tangent.x; newVerts[ (i1*newVertexSize) + tangentOffset + 1 ] = tangent.y; newVerts[ (i1*newVertexSize) + tangentOffset + 2 ] = tangent.z; newVerts[ (i1*newVertexSize) + tangentOffset + 3 ] = handedness; newVerts[ (i2*newVertexSize) + tangentOffset ] = tangent.x; newVerts[ (i2*newVertexSize) + tangentOffset + 1 ] = tangent.y; newVerts[ (i2*newVertexSize) + tangentOffset + 2 ] = tangent.z; newVerts[ (i2*newVertexSize) + tangentOffset + 3 ] = handedness; newVerts[ (i3*newVertexSize) + tangentOffset ] = tangent.x; newVerts[ (i3*newVertexSize) + tangentOffset + 1 ] = tangent.y; newVerts[ (i3*newVertexSize) + tangentOffset + 2 ] = tangent.z; newVerts[ (i3*newVertexSize) + tangentOffset + 3 ] = handedness; } Mesh newMesh = new Mesh(true, mesh.getNumVertices(), mesh.getNumIndices(), newAttributes); newMesh.setVertices(newVerts); newMesh.setIndices(indices); return newMesh; } public static Vector3 orthoNormalize(Vector3 vec1, Vector3 vec2) { vec1.nor(); vec1.mul(vec2.dot(vec1)); vec2.sub(vec1); vec2.nor(); return vec2; } public static Mesh copyMesh(Mesh mesh) { VertexAttributes attributes = mesh.getVertexAttributes(); final int vertCount = mesh.getNumVertices(); final int vertexSize = attributes.vertexSize / 4; float[] verts = new float[vertexSize * vertCount]; mesh.getVertices(verts); short[] indices = new short[mesh.getNumIndices()]; mesh.getIndices(indices); Mesh newMesh = new Mesh(true, mesh.getNumVertices(), mesh.getNumIndices(), attributes); newMesh.setVertices(verts); newMesh.setIndices(indices); return newMesh; } }