I want to make a pyramid mesh shape. So far I have:
[java]public class Pyramid extends Mesh {
public Pyramid() {
Vector3f [] vertices = new Vector3f[5];
vertices[0] = new Vector3f(0, 5, 0);
vertices[1] = new Vector3f(-1, -3, -1);
vertices[2] = new Vector3f(1, -3, -1);
vertices[3] = new Vector3f(-1, -3, 1);
vertices[4] = new Vector3f(1, -3, 1);
Vector2f[] texCoord = new Vector2f[3];
texCoord[0] = new Vector2f(0,0);
texCoord[1] = new Vector2f(1,0);
texCoord[2] = new Vector2f(0.5f ,1);
int [] indexes = { 0,3,4, 0,4,2, 0,2,1, 0,1,3, 2,4,3, 3,1,2 };
setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes));
updateBound();
}
}
[/java]
It works, but there are two glitches. One, when I move the camera so all four of the bottom vertices go out of view, but the top one should still be in view, the whole thing disappears. Two, the texture coordinates are messed up. (I don’t really understand how texture coordinates are supposed to work… I want the same triangle texture to be applied to the four triangular faces.) How can I fix this?
Quick explanation of how shaders handle texture coordinates.
No matter what the texture size… (we’ll take a 512 x 512 texture as an example)
pixel 0,0 = texCoord 0,0
pixel 0,512 = texCoord 0,1
pixel 512,0 = texCoord 1,0
pixel 512,512 = texCoord 1,1
samples are taken is steps between 0.0f,0.0f and 1.0f,1.0f (the bounds of the texture)
If the texture isn’t square… the shader still handles it as a square… between 0.0f,0.0f and 1.0f,1.0f;
say you had a grid that was 10,10… you would map the texCoords like this:
[java]
int gridSize = 10;
for (int x =0; x < gridSize; x++) {
for (int y =0; y < gridSize; y++) {
texCoord[x*gridSize+y] = Vector2f( (1.0f / gridSize * x ), (1.0f / gridSize * y ) );
}
}
[/java]
This would map each vertex to 0.1 * x, 0.1 * y - a fraction of 1.0f, 1.0f (the dimensions of the texture.
So… for you pyramid… the point of the pyramid is going to be half way through the x texCoord… (0.5f, 0f)
The bottom left vert is going to be at (1f, 0f)
And the bottom left will be at (1f, 1f)
To map the pyramid with sharp angles… you are going to need more than 5 verts. You’ll need 3 for each side… a total of 12.
I suggest setting up templates for you vert position, texCoords, indexes and normals… like this:
This is for a box… but I’m sure you can figure out how to convert it…
[java]
package mygame;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
/**
*
-
@author t0neg0d
/
public class MeshTest extends Mesh {
public static enum SIDES {
FRONT,
TOP,
BACK,
BOTTOM,
LEFT,
RIGHT
};
protected int faceCount = 0;
protected float size = .1f;
protected Vector3f startVec;
//private List<SIDES> map = new ArrayList();
private Vector3f[] finVertices = new Vector3f[0];
private Vector2f[] finTexCoord = new Vector2f[0];
private int[] finIndexes = new int[0];
private float[] finNormals = new float[0];
private Vector3f[][] vertTemplate = new Vector3f[][] {
{ new Vector3f(0, 0, 0), new Vector3f(size, 0, 0), new Vector3f(0, size, 0), new Vector3f(size, size, 0) },
{ new Vector3f(0, size, 0), new Vector3f(size, size, 0), new Vector3f(0, size, -size), new Vector3f(size, size, -size) },
{ new Vector3f(0, size, -size), new Vector3f(size, size, -size), new Vector3f(0, 0, -size), new Vector3f(size, 0, -size) },
{ new Vector3f(0, 0, -size), new Vector3f(size, 0, -size), new Vector3f(0, 0, 0), new Vector3f(size, 0, 0) },
{ new Vector3f(size, 0, 0), new Vector3f(size, 0, -size), new Vector3f(size, size, 0), new Vector3f(size, size, -size) },
{ new Vector3f(0, 0, -size), new Vector3f(0, 0, 0), new Vector3f(0, size, -size), new Vector3f(0, size, 0) }
};
private int[] indTemplate = {
2, 0, 1, 1, 3, 2
};
private float[][] normTemplate = new float[][] {
{ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 },
{ 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1 },
{ 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 },
{ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
{-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0 }
};
public MeshTest(float size, Vector3f startVec) {
this.size = size;
for (int i = 0; i < vertTemplate.length; i++) {
for (int x = 0; x < vertTemplate.length; x++) {
if (vertTemplate[x].x > 0) vertTemplate[x].x = size;
if (vertTemplate[x].x < 0) vertTemplate[x].x = -size;
if (vertTemplate[x].y > 0) vertTemplate[x].y = size;
if (vertTemplate[x].y < 0) vertTemplate[x].y = -size;
if (vertTemplate[x].z > 0) vertTemplate[x].z = size;
if (vertTemplate[x].z < 0) vertTemplate[x].z = -size;
}
}
this.startVec = startVec;
}
public void addSide(SIDES side, Vector3f startVec) {
// Update vertices
int index = 0;
Vector3f[] tempVerts = new Vector3f[this.finVertices.length+4];
for (int i = 0; i < finVertices.length; i++) {
tempVerts = finVertices;
index++;
}
for (int i = 0; i < 4; i++)
tempVerts[index+i] = vertTemplate[side.ordinal()].add(startVec);
finVertices = tempVerts;
// Update indexes
int[] tempInds = new int[this.finIndexes.length+6];
index = 0;
for (int i = 0; i < finIndexes.length; i++) {
tempInds = finIndexes;
index++;
}
if (index > 0)
index = index + 1 / 6 * 4;
for (int i = 0; i < 6; i++)
tempInds[index+i] = (faceCount4)+indTemplate;
finIndexes = tempInds;
// Update texCoords
Vector2f[] tempTexCoord = new Vector2f[this.finTexCoord.length+4];
index = 0;
for (int i = 0; i < finTexCoord.length; i++) {
tempTexCoord = finTexCoord;
index++;
}
tempTexCoord[index] = new Vector2f(0,0);
tempTexCoord[index+1] = new Vector2f(0,1);
tempTexCoord[index+2] = new Vector2f(1,0);
tempTexCoord[index+3] = new Vector2f(1,1);
finTexCoord = tempTexCoord;
// Update normals
float[] tempNormals = new float[this.finNormals.length+12];
index = 0;
for (int i = 0; i < finNormals.length; i++) {
tempNormals = finNormals;
index++;
}
for (int i = 0; i < 12; i++)
tempNormals[index+i] = normTemplate[side.ordinal()];
finNormals = tempNormals;
faceCount++;
}
public void buildMesh() {
//Rebuild mesh buffers
this.clearBuffer(Type.Position);
this.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(finVertices));
this.clearBuffer(Type.TexCoord);
this.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(finTexCoord));
this.clearBuffer(Type.Index);
this.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(finIndexes));
this.clearBuffer(Type.Normal);
this.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(finNormals));
this.updateBound();
}
}
[/java]
Oh… to draw a box using the code above… you would…
[java]
Vector3f startVec = new Vector3f(0f,0f,0f);
MeshTest mesh = new MeshTest(0.1f, startVec);
// Draw a box
for (int i = 0; i < MeshTest.SIDES.values().length; i++)
mesh.addSide(MeshTest.SIDES.values(), startVec);
mesh.buildMesh();
// Add this to a Geometry, apply texture and then to the scene
// making steps with the same
Vector3f startVec = new Vector3f(0f,0f,0f);
MeshTest mesh = new MeshTest(0.1f, );
mesh.addSide(MeshTest.SIDES.FRONT, startVec);
mesh.addSide(MeshTest.SIDES.TOP, startVec);
startVec = new Vector3f(1f,0f,0f);
mesh.addSide(MeshTest.SIDES.FRONT, startVec);
mesh.addSide(MeshTest.SIDES.TOP, startVec);
startVec = new Vector3f(0f,1f,-1f);
mesh.addSide(MeshTest.SIDES.FRONT, startVec);
mesh.addSide(MeshTest.SIDES.TOP, startVec);
startVec = new Vector3f(1f,1f,-1f);
mesh.addSide(MeshTest.SIDES.FRONT, startVec);
mesh.addSide(MeshTest.SIDES.TOP, startVec);
mesh.buildMesh();
// Add this to a Geometry, apply texture and then to the scene
[/java]
For a pyramid you could map the centre point to 0.5, 0.5 and then the corners to 0,0; 0,1; 1,1 and 1,0. The bottom might look a little funny (it would map the whole texture) but you can then put whatever you like around the top.
Alternatively make the texture twice as wide as it is high, halve all the X mappings above, add new vertices around the bottom corners and uv map them to a new square to the right of the ones mapped above.
You have as many options as you can imagine
But really unless you have a specific reason to use a custom mesh you are going to be better off just modelling it in blender and doing the uv mappings there…
@zarch said:
For a pyramid you could map the centre point to 0.5, 0.5 and then the corners to 0,0; 0,1; 1,1 and 1,0. The bottom might look a little funny (it would map the whole texture) but you can then put whatever you like around the top.
If he adds lighting to a pyramid made with 5 verts... it'll look like a roundamid when rendered :(
@zarch said:
Alternatively make the texture twice as wide as it is high, halve all the X mappings above, add new vertices around the bottom corners and uv map them to a new square to the right of the ones mapped above.
I'd be careful doing this... Not all hardware supports non-square textures... power of 2 baby!
Roundamid indeed, but as he has no normals at all at the moment that’s something for him to sort out later
x and y have to be powers of two - I knew that…but I thought non-square was valid. i.e. 512 * 256.
@zarch said:
Roundamid indeed, but as he has no normals at all at the moment that's something for him to sort out later :)
x and y have to be powers of two - I knew that...but I thought non-square was valid. i.e. 512 * 256.
hahaha... I think I'm going to see if I can trademark roundamid. I just assumed he/she hadn't gotten around to adding normals yet.
And... you may be right about the non-square textures... I always kept them proportionate to be on the safe side. Maybe someone else here knows the correct answer to that. And if not... I'll try and find the answer and post it when I do.
@zarch said:
Roundamid indeed, but as he has no normals at all at the moment that's something for him to sort out later :)
x and y have to be powers of two - I knew that...but I thought non-square was valid. i.e. 512 * 256.
A quick google search showed that not all video cards supports non-square textures. Of course this didn't sound like it was even remotely close to a high percentage.... better safe then sorry I guess.
@erikuhlmann said:
I want to make a pyramid mesh shape. So far I have:
[java]public class Pyramid extends Mesh {
public Pyramid() {
Vector3f [] vertices = new Vector3f[5];
vertices[0] = new Vector3f(0, 5, 0);
vertices[1] = new Vector3f(-1, -3, -1);
vertices[2] = new Vector3f(1, -3, -1);
vertices[3] = new Vector3f(-1, -3, 1);
vertices[4] = new Vector3f(1, -3, 1);
Vector2f[] texCoord = new Vector2f[3];
texCoord[0] = new Vector2f(0,0);
texCoord[1] = new Vector2f(1,0);
texCoord[2] = new Vector2f(0.5f ,1);
int [] indexes = { 0,3,4, 0,4,2, 0,2,1, 0,1,3, 2,4,3, 3,1,2 };
setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes));
updateBound();
}
}
[/java]
It works, but there are two glitches. One, when I move the camera so all four of the bottom vertices go out of view, but the top one should still be in view, the whole thing disappears. Two, the texture coordinates are messed up. (I don't really understand how texture coordinates are supposed to work.... :( I want the same triangle texture to be applied to the four triangular faces.) How can I fix this?
The others sort of hinted at this more or less directly, but the simple answer is that each triangle needs its own vertexes. You cannot share them because they need different normals and texture coordinates. You can only share any on the bottom quad.
So given 4 triangular sides and a single quad bottom... you need 3 + 3 + 3 + 3 + 4 = 16 vertexes.
@pspeed said:
The others sort of hinted at this more or less directly, but the simple answer is that each triangle needs its own vertexes. You cannot share them because they need different normals and texture coordinates. You can only share any on the bottom quad.
So given 4 triangular sides and a single quad bottom... you need 3 + 3 + 3 + 3 + 4 = 16 vertexes.
Oh yeah... I guess a bottom is helpful
Wow, I didn’t know such a noob question would get so much response…