Hey I faced a strange behaviour on my custom mesh and I cant figure out why that is.
I have a VoxelObject that I load an array VoxelData[size][size][size] to. I then calculate density values for each VoxelData via a parametric function. For now its the simple sphere function x² + y² + z² = r² or rather distanceToIsoSurface = r - Sqrt((x - x0)² + (y - y0)² + (z - z0)²). After that I calculate the faces depending on the voxel density. Everything works quite perfect…I can build an object with size = 300 without any problems with acceptable time and memory. So I wanted to test how big I can make it before getting performance issues. The strange thing is that I dont get performance issues till like size = 512 but first i encountered the prob was with size > 315 suddenly the mesh just completely disappears while the node is still in the scene and the triangle count also rises if i look where the object should be.
So I tried play around with face culling → nothing.
UpdateBounds → did that; not the problem it seems.
Is there a maximum scene size?afaik Not only wouldnt make sense but also why then first not show 320 sized but when increasing by 1 everytime I start it does. And i also set my coord grid to 15000 and added jme sphere with size 800 … no problem.
Further research showed that setting size from 300 to 350 will make it disappear but if I slowly increase it, it works. By now I have a 346(weird number 347 is definately end) sized object. Note that all is done on starting the application so between 2 different sizes I restart the application…that makes me question does it remember things from the last call…wouldnt make sense either.
Also I can of course create several of these objects … then it gets really weird, they are somehow connected it seems. So while 1 300 sized no prob if i make 2 of them both dont show up so the more of them i create the smaller they need to be to show all up (sometimes just 2 of 4 or somthing)
Does anybody have an idea what this could be?
Another small thing that I think is independent from this but I dont want to make an extra post for that:
The docs say i have to use
mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes)); and not
mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes)); (what i dont know why but i did it like that so far, guess i thought well a pos vector has 3 values so the Type.Position Buffer gets 3 while an index is only 1 value so just 1)
but it actually doesnt change anything on the first look…and if everything consists of triangles (like the doc says) anyways whats the point of telling it that its actually 3? I think is something about the primitive to use to draw faces…but then with 1 it shouldnt show anything ?! Or at least only points. Or is it for collision detection or something?
Hm, 3 on the index buffer makes no sense at all, but i assume the number is not used on type index.
For all other buffers this makes absolutly sense since you can pass vec2,vec3 or vec4 to the shaders-.
One thing i have notices at a first look, i always use Buffer.flip() before setting them to the mesh.
I actually have no problem creating voxel worlds far bigger then 512, are your sure you build the mesh right?
You could check for example: Positionbuffer.size == IndexBuffer.size3
On non correctly filled buffers anything can happen
I mostly use this two classes for building custom meshes:
[java]
/
- To change this template, choose Tools | Templates
- and open the template in the editor.
*/
package com.respublic.core.MapSystems;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import java.util.ArrayList;
/**
*
-
@author MichealZuegg
/
public class MeshInformation {
ArrayList<Vector3f> position;
ArrayList<Vector3f> normal;
ArrayList<Vector2f> texCoord1;
public MeshInformation() {
position = new ArrayList<Vector3f>();
normal = new ArrayList<Vector3f>();
texCoord1 = new ArrayList<Vector2f>();
}
public void addPosition(Vector3f position){
this.position.add(position);
}
public void addNormal(Vector3f normal){
this.normal.add(normal);
}
public void addTexCoord(int type,Vector2f texCoord){
switch(type){
case 1:{
texCoord1.add(texCoord);
return;
}
}
}
public ArrayList<Vector3f> getPosition() {
return position;
}
public ArrayList<Vector3f> getNormal() {
return normal;
}
public ArrayList<Vector2f> getTexCoord1() {
return texCoord1;
}
}
[/java]
And then:
[java]
/
- To change this template, choose Tools | Templates
- and open the template in the editor.
*/
package com.respublic.core.MapSystems;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
/**
*
-
@author MichealZuegg
*/
public class MeshBuilder {
public static Mesh buildMeshFromMeshInfo(MeshInformation meshInfo,boolean debug){
Mesh mesh=new Mesh();
ArrayList<Vector3f> position = meshInfo.getPosition();
ArrayList<Vector3f> normals = meshInfo.getNormal();
ArrayList<Vector2f> texCoord1= meshInfo.getTexCoord1();
FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(position.size()*3);
IntBuffer indexBuffer = BufferUtils.createIntBuffer(position.size());
FloatBuffer normalBuffer = BufferUtils.createFloatBuffer(normals.size()*3);
FloatBuffer texCoord1Buffer = BufferUtils.createFloatBuffer(texCoord1.size()*2);
int count=0;
for(Vector3f pos:position){
indexBuffer.put(count);
count++;
positionBuffer.put(pos.x);
positionBuffer.put(pos.y);
positionBuffer.put(pos.z);
}
for(Vector3f pos:normals){
normalBuffer.put(pos.x);
normalBuffer.put(pos.y);
normalBuffer.put(pos.z);
}
for(Vector2f pos:texCoord1){
texCoord1Buffer.put(pos.x);
texCoord1Buffer.put(pos.y);
}
indexBuffer.flip();
positionBuffer.flip();
normalBuffer.flip();
texCoord1Buffer.flip();
mesh.setBuffer(Type.Index, 1, indexBuffer);
mesh.setBuffer(Type.Position, 3, positionBuffer);
mesh.setBuffer(Type.Normal, 3, normalBuffer);
mesh.setBuffer(Type.TexCoord, 2, texCoord1Buffer);
mesh.updateBound();
if(!debug){
TangentBinormalGenerator.generate(mesh);
}
return mesh;
}
}
[/java]
Then for example:
[java]
MeshInformation meshInfo = new MeshInformation();
meshInfo.addPosition(new Vector3f(0,0,0));
meshInfo.addPosition(new Vector3f(0,1,0));
meshInfo.addPosition(new Vector3f(0,1,0));
meshInfo.addPosition(new Vector3f(1,1,0));
meshInfo.addPosition(new Vector3f(1,1,0));
meshInfo.addPosition(new Vector3f(1,0,0));
meshInfo.addPosition(new Vector3f(1,0,0));
meshInfo.addPosition(new Vector3f(0,0,0));
mesh=MeshBuilder.buildMeshFromMeshInfo(meshInfo, true);
mesh.setMode(Mesh.Mode.Lines);
[/java]
makes you a line quad
Any chance you could post the custom mesh class giving you issues? It’s kinda hard to guess what the problem may be without seeing the code
@zzeugg Quick suggestion for the above code. Using arraylists to store the buffer info is a fine idea (especially if you need to edit the meshes prior to creating the buffers. The question/suggestion would be… are you clearing/nullifying the arraylists after you create the buffers? Depending on the size & number of the objects being created, you can eat through memory rather quickly using them.
Adding a flag to disable editing/build call once the buffers are created and nullifying the arraylists will more than likely show you a noticeable performance increase on large scenes.
@t0neg0d said:
@zzeugg re you clearing/nullifying the arraylists after you create the buffers? Depending on the size & number of the objects being created, you can eat through memory rather quickly using them.
Adding a flag to disable editing/build call once the buffers are created and nullifying the arraylists will more than likely show you a noticeable performance increase on large scenes.
Once i return from the function which created the mesh the arraylists should get automatically marked for GC since no reference to them exists anymore. (At least i assume this). I am never keeping 'global' reference to MeshInfo, since i use them only as helper. Writing buffer.put(x), buffer.put(y) buffer.put(z) makes more work then MeshInfo.addPosition(new Vector3f(x,y,z)). Additionally i can more easily read/debug my code-
I am going to run a test to see if it's makes any difference.
@t0neg0d said:
you can eat through memory rather quickly using them.
Lucky me i have plenty of memory ;)
@zzuegg said:
Once i return from the function which created the mesh the arraylists should get automatically marked for GC since no reference to them exists anymore. (At least i assume this). I am never keeping 'global' reference to MeshInfo, since i use them only as helper. Writing buffer.put(x), buffer.put(y) buffer.put(z) makes more work then MeshInfo.addPosition(new Vector3f(x,y,z)). Additionally i can more easily read/debug my code-
I am going to run a test to see if it's makes any difference.
This should be the case... I was holding reference to these because they were part of the custom Mesh class. I really like this approach!
@zzuegg said:
Lucky me i have plenty of memory ;)
Grrr... my machine sucks so bad >.<
@zzuegg said:
Hm, 3 on the index buffer makes no sense at all, but i assume the number is not used on type index.
For all other buffers this makes absolutly sense since you can pass vec2,vec3 or vec4 to the shaders-.
hmm ok so my assumption and understanding concerning that are correct so far. thx for confirming that.
The other thing with flipping does not work for me. There is just nothing then. But I fill the buffer in a different way. And also I am quite sure everything is correct because I really just change the size value and restart. Within range 0 - 346 there is no problem and I checked the buffers and everything on size 0 - 5.
I think or hope :) I will get easily above size 512 too, but for the moment I have no optimization in it so at the moment during creation of size 350 i need about 700mb just for the VoxelData[][][] array (VoxelData mainly a float density, int index and a flag if it has been touched (21bytes inc class information etc)). For the moment I concentrate on the functionality I want to have. So for now I maybe using classes I wont need or data that can be reduced and optimized. And also there is nothing like splitting the data so I dont need so much memory at once. After creation and the gc collecting everything its only 20mb or so. @zzuegg thx for the code some parts gave me some new ideas on optimization.
And also I am doing some calculation on the data before adding it to the mesh and do not just fill the buffers so at the moment takes about 20sec which of course is much too long but with threads it will be better. Also later not the hole object needs to be calculated as most parts not visible at all with that size or even bigger. My final goal is to implement CubicalMarchingSquares and realtime editing.
Btw for some reason -XmxM where v > 1536 wont work for me any ideas(yes i do have more than that)? (Unrecognized Operation it says)
@t0neg0d
I will try to find some important code parts but actually code is not well sorted and a little more complex.
@xeratos said:
hmm ok so my assumption and understanding concerning that are correct so far. thx for confirming that.
The other thing with flipping does not work for me. There is just nothing then. But I fill the buffer in a different way. And also I am quite sure everything is correct because I really just change the size value and restart. Within range 0 - 346 there is no problem and I checked the buffers and everything on size 0 - 5.
I think or hope :) I will get easily above size 512 too, but for the moment I have no optimization in it so at the moment during creation of size 350 i need about 700mb just for the VoxelData[][][] array (VoxelData mainly a float density, int index and a flag if it has been touched (21bytes inc class information etc)). For the moment I concentrate on the functionality I want to have. So for now I maybe using classes I wont need or data that can be reduced and optimized. And also there is nothing like splitting the data so I dont need so much memory at once. After creation and the gc collecting everything its only 20mb or so. @zzuegg thx for the code some parts gave me some new ideas on optimization.
And also I am doing some calculation on the data before adding it to the mesh and do not just fill the buffers so at the moment takes about 20sec which of course is much too long but with threads it will be better. Also later not the hole object needs to be calculated as most parts not visible at all with that size or even bigger. My final goal is to implement CubicalMarchingSquares and realtime editing.
Btw for some reason -XmxM where v > 1536 wont work for me any ideas(yes i do have more than that)? (Unrecognized Operation it says)
@t0neg0d
I will try to find some important code parts but actually code is not well sorted and a little more complex.
Seriously, don't worry about the implementation of the code. You should see my absolute hackery before cleaning up and optimizing (not that it is great after that either). If you aren't able to sort this out... feel free to post just the custom mesh... someone might be able to spot the problem right away.
Here is some code reduced to the important parts:
[java]
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
some stuff
VoxelObject obj = new VoxelObject();
obj.setData(loadVoxelData(346)); //the only thing I change between 2 starts. 346 - perfect, 347 - nothing
voxelObjects.attachChild(obj);
myApp.getRootNode().attachChild(voxelObjects);
some stuff
}
[/java]
[java]
public float getIsoLevel(int x, int y, int z, float offset) {
return (offset - 1) - (float) Math.sqrt(Math.pow(x - offset, 2)
+ Math.pow(y - offset, 2)
+ Math.pow(z - offset, 2));
}
public VoxelData[][][] loadVoxelData(int size) {
long t1 = System.currentTimeMillis();
float d;
VoxelData[][][] voxels = new VoxelData[size][size][size];
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
for (int z = 0; z < size; z++) {
d = getIsoLevel(x, y, z, (size - 1) / 2);
if (d < -1) d = -1; else if (d > 1) d = 1;
else
if (d < 1)
d = 0;
if (d < 1)
voxels[x][y][z] = new VoxelData(d);
// System.out.println(voxels[x][y][z].toString());
}
}
}
long t2 = System.currentTimeMillis();
System.out.println("load voxel data " + (t2 - t1) + "ms");
return voxels;
}
[/java]
The VoxelObject.setData VoxelObject.cubicalMarchingSquares method
[java]
public void setData(VoxelData[][][] data) {
voxels = data;
size = data.length;
center = new Vector3f(size / 2, size / 2, size / 2);
geom.setMesh(cubicalMarchingSquares(voxels));
attachChild(geom);
}
private Mesh cubicalMarchingSquares(VoxelData[][][] data) {
long sumD1;
long sumD2 = 0;
long verts1;
long verts2 = 0;
long indexes1;
long indexes2 = 0;
long tNormVert1;
long tNormVert2 = 0;
Mesh mesh = new Mesh();
int lod = 5;
ArrayList<Float> verts = new ArrayList<>();
ArrayList<Float> norm = new ArrayList<>();
ArrayList<Integer> indexList = new ArrayList<>();
VoxelData[] block = new VoxelData[8];
int voxelIndex = 0;
long t1 = System.currentTimeMillis();
System.out.print("marchin squares ");
for (int x = 0; x < size - 1; x++) {
for (int y = 0; y < size - 1; y++) {
for (int z = 0; z < size - 1; z++) {
tNormVert1 = System.currentTimeMillis();
block[0] = data[x][y][z];
block[1] = data[x][y][z + 1];
block[2] = data[x][y + 1][z];
block[3] = data[x][y + 1][z + 1];
block[4] = data[x + 1][y][z];
block[5] = data[x + 1][y][z + 1];
block[6] = data[x + 1][y + 1][z];
block[7] = data[x + 1][y + 1][z + 1];
tNormVert2 += System.currentTimeMillis() - tNormVert1;
// sum up the density of the 8 block voxels
sumD1 = System.currentTimeMillis();
float blockDensity = 0;
for (VoxelData v : block) {
if (lod == 5)
if (v != null) {
if (v.d <= -0.5f)
v.d = -1;
else
if (v.d >= 0.5f)
v.d = 1;
else v.d = 0;
blockDensity += v.d;
} else blockDensity--;
// System.out.println(v.toString());
}
sumD2 += System.currentTimeMillis() - sumD1;
// if the current block cuts the isosurface proceed
// that is if at least one voxel of the block has a density
// value greater/smaller than -1/1 so the sum is between -8/8
// else proceed with the next block
verts1 = System.currentTimeMillis();
if (blockDensity > -8 && blockDensity < 8) {
if (lod < 5) {
} else {
// collect needed voxels that are not yet in the list
// and set the indexes
for (int bx = 0; bx < 2; bx++) {
for (int by = 0; by < 2; by++) {
for (int bz = 0; bz < 2; bz++) {
VoxelData v = data[x + bx][y + by][z + bz];
if (v != null && !v.added) {
v.added = true;
v.index = voxelIndex++;
verts.add((float) x + bx);
verts.add((float) y + by);
verts.add((float) z + bz);
norm.add(0f);
norm.add(0f);
norm.add(0f);
// attachChild(new VoxelGeom(v.x, v.y,
// v.z, v.d));
}
}
}
}
verts2 += System.currentTimeMillis() - verts1;
indexes1 = System.currentTimeMillis();
int complementFace = 1;
//process each face and check whether to set or not
for (int faceIndex = 0; faceIndex < 6; faceIndex++) {
float faceDensity = 0;
float faceDensity2 = 0;
boolean face2Neg = false;
//check 4 face voxels
for (int i = 0; i < 4; i++) {
if (block[faceVertices[faceIndex]] == null) {
faceDensity = -1;
break;
}
if (block[faceVertices[faceIndex + complementFace]] == null) {
// face2Neg = true;
faceDensity2++;
} else {
faceDensity2 +=
block[faceVertices[faceIndex
+ complementFace]].d;
}
faceDensity +=
block[faceVertices[faceIndex]].d;
}
if (faceDensity >= 0
&& (faceDensity2 < 0 || face2Neg)) {
//set normals
if (faceIndex < 2) {
norm.set(block[faceVertices[faceIndex][0]].index * 3 + 2,
(float) complementFace);
// norm.set(block[faceVertices[faceIndex][1]].index
// * 3 + 2,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][2]].index
// * 3 + 2,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][3]].index
// * 3 + 2,
// (float) complementFace);
}
else
if (faceIndex < 4) {
norm.set(block[faceVertices[faceIndex][0]].index * 3 + 1,
(float) complementFace);
// norm.set(block[faceVertices[faceIndex][1]].index
// * 3 + 1,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][2]].index
// * 3 + 1,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][3]].index
// * 3 + 1,
// (float) complementFace);
}
else
if (faceIndex < 6) {
norm.set(block[faceVertices[faceIndex][0]].index * 3,
(float) complementFace);
// norm.set(block[faceVertices[faceIndex][1]].index
// * 3,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][2]].index
// * 3,
// (float) complementFace);
// norm.set(block[faceVertices[faceIndex][3]].index
// * 3,
// (float) complementFace);
}
indexList.add(block[faceVertices[faceIndex][1]].index);
indexList.add(block[faceVertices[faceIndex][0]].index);
indexList.add(block[faceVertices[faceIndex][2]].index);
indexList.add(block[faceVertices[faceIndex][2]].index);
indexList.add(block[faceVertices[faceIndex][3]].index);
indexList.add(block[faceVertices[faceIndex][1]].index);
}
complementFace -= complementFace * 2;
}
indexes2 += System.currentTimeMillis() - indexes1;
}
}
data[x][y][z] = null;
}
}
}
long mesh1 = System.currentTimeMillis();
float[] normA = new float[verts.size()];
float[] vertA = new float[verts.size()];
int[] indexes = new int[indexList.size()];
for (int i = 0; i < vertA.length; i += 3) {
vertA = verts.get(i);
vertA = verts.get(i + 1);
vertA = verts.get(i + 2);
normA = norm.get(i);
normA = norm.get(i + 1);
normA = norm.get(i + 2);
}
for (int i = 0; i < indexList.size(); i++) {
indexes = indexList.get(i);
}
FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(vertA);
FloatBuffer normalBuffer = BufferUtils.createFloatBuffer(normA);
IntBuffer indexBuffer = BufferUtils.createIntBuffer(indexes);
// positionBuffer.flip();
// normalBuffer.flip();
// indexBuffer.flip();
mesh.setBuffer(Type.Position, 3, positionBuffer);
mesh.setBuffer(Type.Normal, 3, normalBuffer);
mesh.setBuffer(Type.Index, 1, indexBuffer);
mesh.updateBound();
long mesh2 = System.currentTimeMillis() - mesh1;
long t2 = System.currentTimeMillis() - t1;
System.out.println("CMS " + t2);
System.out.println("summ density " + sumD2);
System.out.println("collect verts " + verts2 + ", " + vertA.length);
System.out.println("set indexes " + indexes2);
System.out.println("get blocks " + tNormVert2);
System.out.println("create mesh " + mesh2);
return mesh;
}
}
[/java]
@xeratos
Quick thought of the top of my head. Add some bogus texCoords to ensure that the mesh isn’t disappearing due to not applying the material correctly during rendering. Can’t guarantee this will fix the issue, but it’s the first thing that jumped out at me.
@t0neg0d
hmm i am using normals mat at the moment so i think shouldnt matter but i will try.
edit – napp nothing changes except size 346 not working anymore 300 still no prob
@xeratos said:
@t0neg0d
hmm i am using normals mat at the moment so i think shouldnt matter but i will try.
edit -- napp nothing changes except size 346 not working anymore ;) 300 still no prob
Does it disappear the same way it might if the bounds weren't being updated? Or does it just not show up at all? And, can you verify that the object is being placed where you expect it to be?
Yeah, I know... all simple things... but I am notorious for overlooking the obvious and I'm trying to break the habit. :)
Ugh… another really simple thought. Where did you find the implementation of marching cubes you’re basing this on? And does the problem persist with other uses of even/odd numbers? i.e. does 200 work and 201 not work?
@xeratos said:
I will try to find some important code parts but actually code is not well sorted and a little more complex.
That piece of code looks pretty nice compared to the messed up code i am currently trying to debug :(
The only difference is that i work with surface nets and you with marching cubes :)
If you get bored looking at your code we could exchange the source for bugfixing ;)
@zzuegg said:
That piece of code looks pretty nice compared to the messed up code i am currently trying to debug :(
HAHAHA.. that is EXACTLY what I thought. I'm almost afraid to ask what his/her optimized code looks like >.<
@xeratos
Another question… sorta related/unrelated. When things are working well… what’s the performance like using marching cubes? I hear that it’s fairly slow for real-time rendering and shied away from it most because my machine sucks atm and it would more frustrate me than not…
What’s your thoughts on performance thus far (when things render correctly that is)
Puh, after 4 hours i now found my bug
I had actually two bugs, one time i was using ‘key’ instead of ‘this.key’ and the other one i was using ‘key’ instead of ‘eKey’
I should write ‘USE FUCING* UNIQUE VARIABLE NAMES’ with big letters behind my screens (But unfortunatly both variables where a key for the same hashmap)
Now only a small normal calculation error left and i am finnished with terrain generation. Yippy now terrain painting
The even more strange thing about this bug is that it happend only where two tiles met.
@xeratos, i actually have an idea, do you use somewhere a counter for the vertices?
Because:
Integer.MaxValue == 2.147.483.647
2.147.483.647 / 4 (Vertices per side) = 536 870 912
536 870 912 / 6 Sides = 89 478 485
CubicSquare of 9478485 = 447
I have not checked, but maybe your indexBuffer System might need a rework
@zzuegg said:
Puh, after 4 hours i now found my bug :)
Congratz!
@zzuegg said:
Yippy now terrain painting ;)
I'd love to hear your thoughts for approaching this. I have been putting it off for quite a while due to not being overly happy with any ideas I've had thus far. I have yet to see an approach that doesn't look overly uniform. :( Not to mention there always seems to be a sticking point with the ideas I've had and I can't decide what is the lesser of all evils. Anyways! If you'd be so kind as to start a thread about it when you start as a place to kick around ideas and approaches, it would be ultra-appreciated!
@zzuegg said:
Integer.MaxValue == 2.147.483.647
2.147.483.647 / 4 (Vertices per side) = 536 870 912
536 870 912 / 6 Sides = 89 478 485
CubicSquare of 9478485 = 447
I have not checked, but maybe your indexBuffer System might need a rework
What ways are there around this? Do you have to split objects once you reach this cap? Kinda hard to create an array with a max index that isn't a valid integer :(
@t0neg0d said:
I'd love to hear your thoughts for approaching this. I have been putting it off for quite a while due to not being overly happy with any ideas I've had thus far. I have yet to see an approach that doesn't look overly uniform. :( Not to mention there always seems to be a sticking point with the ideas I've had and I can't decide what is the lesser of all evils. Anyways! If you'd be so kind as to start a thread about it when you start as a place to kick around ideas and approaches, it would be ultra-appreciated!
I have already many ideas, i will open a thread once i have played with them a bit..
@t0neg0d said:
What ways are there around this? Do you have to split objects once you reach this cap? Kinda hard to create an array with a max index that isn't a valid integer :(
Since no current card is able to draw that amount of triangle indexing all of them is kind of useless. In the above calculation every possible vertex would get it's own index, but for drawing them only a small subset is required.
Indexing them only when they are actually drawn would already drop your limit.
Since the OP want real time editing with marching cubes, tiling is not anymore an option, but a must. Regenerating the whole mesh in realtime is not possible.
If that all is not possible, switching to long might help :P
Damn I had to go…ok lets start answering everything
@t0neg0d said:
Does it disappear the same way it might if the bounds weren't being updated? Or does it just not show up at all? And, can you verify that the object is being placed where you expect it to be?
Yeah, I know... all simple things... but I am notorious for overlooking the obvious and I'm trying to break the habit. :)
@t0neg0d
Haha yea no problem it is good. I know sometimes it is one simple f***ing thing you just oversee all the time.
But unfortunateley ... the thing is maybe i didnt point that out enough .... it actually just never appears(camera instantly after start looks on it. Sometimes if i move and look around i get a small lag and it seems it updates....well something but model doesnt appear). If I leave out bound update it shows the "normal" behaviour for not being updated (with smaller sizes then 346 of course ;) ). And yes it is actually where it should be I guess. I mean everything is the same but one value and I have not found one value below 300 where I have the same issue. And also the scene obj and tri count behaviour is correct as i look around the scene where i intended to put it.
@t0neg0d said:
Ugh... another really simple thought. Where did you find the implementation of marching cubes you're basing this on? And does the problem persist with other uses of even/odd numbers? i.e. does 200 work and 201 not work?
Some interpolation errors in the model on changes like 200 to 201 but thats ok i know why they are.
Nearly everything I know so far and took into account can be found here (or rather the links in there):
http://hub.jmonkeyengine.org/groups/user-code-projects/forum/topic/dynamic-infinite-procedural-voxel-terrain/
and in addition to that, i've found this one. Unfortunately for jme2 but is a clean overview on the alghorithm.
http://hub.jmonkeyengine.org/groups/user-code-projects/forum/topic/my-marhcing-cube-algorithm
@zzuegg said:
That piece of code looks pretty nice compared to the messed up code i am currently trying to debug :(
The only difference is that i work with surface nets and you with marching cubes :)
If you get bored looking at your code we could exchange the source for bugfixing ;)
@t0neg0d said:
HAHAHA.. that is EXACTLY what I thought. I'm almost afraid to ask what his/her optimized code looks like >.<
Well thank you :D but by optimized I actually mean memory/threading/splitting etc. I removed lots of commented stuff and what you dont see is that everything is in my teststate declared as inner classes and I want to get rid of the VoxelData class if possible as its overhead data is nearly bigger than the data it is holding ;) And also I hate looking at unstructured code even if I know it is not finished yet or only temporarily. So I really try to stick to most conventions as much as possible.
@t0neg0d said:
Another question... sorta related/unrelated. When things are working well... what's the performance like using marching cubes? I hear that it's fairly slow for real-time rendering and shied away from it most because my machine sucks atm and it would more frustrate me than not...
What's your thoughts on performance thus far (when things render correctly that is)
This introduces the next part concerning performance and I think this is also the moment to point out that I dont want to implement MarchingCubes but CubicalMarchingSquares (from this nice overview on surface extraction http://hub.jmonkeyengine.org/groups/user-code-projects/forum/topic/my-marhcing-cube-algorithm) because you all often refer to the cubes. I think it is kind of easier cause the problem is mostly reduced to 2d and its capabilities are very good AFAIK compared to the others especially original MarchingCubes. But actually my implementation is far away from that. Just slowly getting the surrounding structure and filling it step by step.
You are right for real-time usage all of the approaches are kind of useless in there native form. But it is just that AFAIK nobody really has considered it yet as a possibility (Except these guys maybe http://www.euclideon.com/, i know seems unbelievable and some concerns are true but I must say I am not that pessimistic and god would that be great, may be a little too exaggerated but in earlier times nobody ever thought the earth could be a sphere or that we are not the center of the universe. And in a way thats what they claim - to reinvent the way graphics work....so lets give em a chance). CPU and especially GPU and relatively new programming techniques first of all multithreading combined with multiprocessors etc. are making it possible by now....I guess, hope. But everything I learned at university so far makes me feel like "Yep, should be possible in some way".
@zzuegg said:
Puh, after 4 hours i now found my bug :)
I had actually two bugs, one time i was using 'key' instead of 'this.key' and the other one i was using 'key' instead of 'eKey'
I should write 'USE FU*C*ING* UNIQUE VARIABLE NAMES' with big letters behind my screens :( (But unfortunatly both variables where a key for the same hashmap)
Now only a small normal calculation error left and i am finnished with terrain generation. Yippy now terrain painting ;)
The even more strange thing about this bug is that it happend only where two tiles met.
Great job and yea looking at each others code might be a good thing. But I want to check a little more on atm.
:) I know that problem just too good...one more thing why I love conventions and especially my eclipse configuration....took me some time but now on every save action it automatically formats the code and organizes imports. Just like you mention I like to declare statics/enum in upper cases. And to distinguish between var visibility I set eclipse to colorize member vars blue, local vars orange and param vars red. That really really helps a lot. Another way I mainly know from C/C++ is declaring member vars with _ in front of it. That is probably better, because it is IDE independent but I kind of threw that away.
@zzuegg said:
i actually have an idea, do you use somewhere a counter for the vertices?
Because:
Integer.MaxValue == 2.147.483.647
2.147.483.647 / 4 (Vertices per side) = 536 870 912
536 870 912 / 6 Sides = 89 478 485
CubicSquare of 9478485 = 447
I have not checked, but maybe your indexBuffer System might need a rework
First of all, thanks really a great idea could have been the problem. I have not thought about that till now. Unfortunately its too early to think about that. Not only did you miss the fact that 350 < 447. Also I have a different approach which lets me reuse a voxel so that two "blocks" dont have 16 but only 12. And I have some kind of optimization concerning the voxel count so that fully inner blocks are not indexed. So some statistics [remember I use a parametric sphere function and radius ((size - 1) / 2) - 1]:
300x300x300 = 27 000 000 voxels as base data
Times: LoadData ~4900ms, CMS ~6700ms
Memory: VoxelData[][][] ~440mb, max during creation ~650mb, after creation and gc ~120mb
Blocks: 962868, Faces 414792, Voxels: 972051
350x350x350 = 42 875 000 voxels as base data
Times: LoadData ~7800ms, CMS ~10500ms
Memory: VoxelData[][][] ~700mb, max during creation ~1,2gb, after creation and gc ~200mb
Blocks: 1316308, Faces 566376, Voxels: 1327027
And finally reached the end the last quotes I swear ;)
@t0neg0d said:
What ways are there around this? Do you have to split objects once you reach this cap? Kinda hard to create an array with a max index that isn't a valid integer :(
@zzuegg said:
Since no current card is able to draw that amount of triangle indexing all of them is kind of useless. In the above calculation every possible vertex would get it's own index, but for drawing them only a small subset is required.
Indexing them only when they are actually drawn would already drop your limit.
Since the OP want real time editing with marching cubes, tiling is not anymore an option, but a must. Regenerating the whole mesh in realtime is not possible.
If that all is not possible, switching to long might help :P
Yea indeed that is what is inevitable. I absoluteley have to break one object into several smaller ones depending on distance to charackter or if they are about to be edited. The good thing is that I think in a way I can combine that with LODs. Before implementing the complete alghorithm I will do some tests on my basic blocky version and check out the capabilities of these kinds of surface generation... as soon as I get rid of this weird error. But I think it is all to great data structure, splitting and multithreading....and hopefully a little GPU as soon as tesselation shaders are being implemented for jme.
Yea made it through...thanks guys for all your feedback and ideas so far. But now time to sleep a little and then try out some more tomorrow.
Put a coloured background (just a quad or something) behind the object that isn’t appearing.
That will let you distinguish between it not appearing at all and appearing but appearing black. (First probably a mesh issue, second probably a normal/lighting/texturing issue).