Converting c++ lookup tables to java

Hello, i am currently trying to implement the Transvoxel algortithm to java.
Since i have no deeper experience with c# or c++ i currently don’t know whats the best practice to convert a lookup table something similar in java.

This is the lookup table
https://github.com/qwertzui11/voxel-Terrain/blob/master/blub/procedural/voxelMap/transVoxel.hpp

First think which comes into my mind is to create a HashMap for each of the entries. But in generall. what would you do?

1 Like

HashMap i would do, at least its (nearly) constant time. And if thats to slow i would look further.

I would probably just use the array data structures that they already have. They are definitely constant time and will have slightly less overhead than a HashMap.

…it will also make the algorithm easier to port.

Yeah, i used also standart array’s. Took a bit to figue out why there are no constructors in the c++ lookups. This pointer and direct memory stuff i kind of hard to port for me :frowning:

You mean how it uses this stuff to set the bytes directly instead of a constructor?
{0x62, {0, 1, 2, 3, 4, 5}}

C++ programmers often prefer to type a few characters less and sacrifice all kinds of safety.

Yeah that was the first issue. The bigger problem currently is that i don’t get why/how the algorithm does the stuff he is supposed to to. I find it always particularly hard to follow binary operation. Since i am not able to do it i have to go trough the code with with a paper and pen in my hand and do all the binary calculations by hand.

Things like:

[java]
byte directionMask = (byte) ((position.x > 0 ? 1 : 0) | ((position.z > 0 ? 1 : 0) < 0 ? 1 : 0) <> 8);
byte reuseIndex = (byte) (edge & 0xF); //Vertex id which should be created or reused 1,2 or 3
byte rDir = (byte) (edge >> 4); //the direction to go to reach a previous cell for reusing
byte v1 = (byte) ((vertexLocations[i]) & 0x0F); //Second Corner Index
byte v0 = (byte) ((vertexLocations[i] >> 4) & 0x0F); //First Corner Index
[/java]
are not readable to me. With readable i mean i cannot follow why the results are correct by reading the code. I always have to do the damn math myself. Seems this port is going to be a longer story then i tought.

Also i am not sure the reference implementation is complete and the author does not respond.

@zzuegg said: Yeah that was the first issue. The bigger problem currently is that i don't get why/how the algorithm does the stuff he is supposed to to. I find it always particularly hard to follow binary operation. Since i am not able to do it i have to go trough the code with with a paper and pen in my hand and do all the binary calculations by hand.

Things like:

[java]
byte directionMask = (byte) ((position.x > 0 ? 1 : 0) | ((position.z > 0 ? 1 : 0) < 0 ? 1 : 0) <> 8);
byte reuseIndex = (byte) (edge & 0xF); //Vertex id which should be created or reused 1,2 or 3
byte rDir = (byte) (edge >> 4); //the direction to go to reach a previous cell for reusing
byte v1 = (byte) ((vertexLocations[i]) & 0x0F); //Second Corner Index
byte v0 = (byte) ((vertexLocations[i] >> 4) & 0x0F); //First Corner Index
[/java]
are not readable to me. With readable i mean i cannot follow why the results are correct by reading the code. I always have to do the damn math myself. Seems this port is going to be a longer story then i tought.

Also i am not sure the reference implementation is complete and the author does not respond.

Looks like the first line was mangled… I can translate the others, though.
[java]
// This first line makes no sense as written. The last condition will never be true…ever.
byte directionMask = (byte) ((position.x > 0 ? 1 : 0) | ((position.z > 0 ? 1 : 0) < 0 ? 1 : 0) <> 8);

// looks like reuseIndex and rDir are packed together in a byte… each using 4 bits.
// This line extracts the index from the low 4 bits
byte reuseIndex = (byte) (edge & 0xF); //Vertex id which should be created or reused 1,2 or 3

// This line extracts the dir from the upper 4 bits and shifts them into a regular number (0-15)
byte rDir = (byte) (edge >> 4); //the direction to go to reach a previous cell for reusing

// Same here. vertexLocations[i] has v1 packed in the low 4 bits and v0 packed in the high 4 bits.
byte v1 = (byte) ((vertexLocations[i]) & 0x0F); //Second Corner Index
byte v0 = (byte) ((vertexLocations[i] >> 4) & 0x0F); //First Corner Index
[/java]

Note: there is a potential translation error since if edge was negative before (bit 7 set) then rDir will be negative. Maybe that’s right but I doubt it.

May need to be something like (byte)((edge >> 4) & 0xf)

//edit. this forum is really not usable as a code sharing forum currently.
here is the previous post at pastebin: http://pastebin.com/gaDVwpZz

“where vertexLocation is a ushort from the lookup table”

…and you say you used int. That’s fine. All of the bit operations will be done in int anyway.

…which is why you sometimes need to be careful when casting to byte to chop off any sign extension. It’s sometimes easier just to add an & 0xff before casting to byte rather than making sure you handle the sign extension everywhere.

“The first line has <<2); instead of the diamond operator and the following img tag”

…except as written that doesn’t really help unless the parenthesis were in the wrong place, also.

((position.z > 0 ? 1 : 0) < 0 ? 1 : 0)
…always evaluates to 0 because…
(position.z > 0 ? 1 : 0)

will never be less than 0.

@pspeed said: "where vertexLocation is a ushort from the lookup table"

…and you say you used int. That’s fine. All of the bit operations will be done in int anyway.

…which is why you sometimes need to be careful when casting to byte to chop off any sign extension. It’s sometimes easier just to add an & 0xff before casting to byte rather than making sure you handle the sign extension everywhere.

And truthfully, this matters more going the other direction since byte always has a sign and when you convert it to int it will sign extend.

If you are unfamiliar with binary operations you have a long road ahead. I keep thinking of more advice to give but it’s all just going to swim around at some point. :wink:

Steps to grokking bit operations:

  1. Understand that hexadecimal values translate to bits. 0x1 = 0001, 0x2 = 0010, 0x4 = 0100, 0x8 = 1000. Additions give you the combination: 0x3 = 0x1 + 0x2 = 0001 + 0010 = 0011. Hexadecimal counts up using letters after 0x9: 0xa, 0xb, 0xc, 0xd, 0xe, 0xf. I don’t add these but know some values by heart: 0xa = 1010, 0xc = 1100, 0xf = 1111.
  2. Understand a few standard operations. & (AND) can be used to mask bits. Say you use a mask of 0110 and apply it via & to four bits, will let the middle bits stand and replace the border bits with zeroes: 0110 & pqrs = 0qr0.
    | (OR) can set bits to 1, but it’s rarely used.
    ^ (XOR) inverts bits where the mask is 1, and keeps them unchanged where the mask is 0.
  3. Understand bit shifting. <>> will move every bit to the right, moving in 0 bits from the left. This is equivalent to >> in C where the integer is unsigned.
    Signed-integer right shift in C is >> in Java. This is a bit complicated but usually not necessary because signed right shifts aren’t usually useful for bit twiddling, so I’ll leave that out for now.

I’ve not looked at the library but depending on what it is doing you might be better off just doing a proper port though and get rid of all the bit twiddling, converting it to a enummap (for bitfields) and separate data for other operations.

Heh. If he’s after understanding the algorithm, he could and should implement it in Java by himself.
If he’s just after getting it to run, bit twiddling is fine.

Lol, I had the idea of transfering exactly the same c++ transvoxel algorithm some days ago. :smiley:
The c++ code looks a bit ugly, I found another project GitHub - BinaryConstruct/Transvoxel-XNA: Transvoxel implementation in C# for XNA but i didnt have a look at it right how.
I hope you are succesfull because I like the idea of voxels and LOD and this lookes easier than the dual marching cubes.

@zzuegg
Any news?

Nah sorry, i am currently sick. That damn flu got me.
Beside that this is probably going to take a bit since i currently have plenty of work todo and not much sparetime for ‘fun project’