Hi again!
I have contacted the author Danilo Balby, it is his graduation work on public domain!
I am adding here the files I created to integrate it with JME3. Thx vm!
This is a very initial work. As I said, it is not deeply integrated with JME3 (mesh); but is something that works and you can play with
.
1) First, download this package: j3dbool-1.1.zip
2) Extract this folder “src/unbboolean/j3dbool” to “jme3test/unbboolean/j3dbool”
I personally created that package on my project, not at jme3 sources as I update it frequently.
After extracting you will have to go on each file and modify the package, or just extract it to “unbboolean/j3dbool”.
3) now, copy this code over Solid.java, (I made a few simple changes to it, but not changing the way it works).
package jme3test.unbboolean.j3dbool;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.StringTokenizer;
import javax.media.j3d.Shape3D;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
/**
* Class representing a 3D solid.
*
* @author Danilo Balby Silva Castanheira (danbalby@yahoo.com)
* @author teique, added a few methods to rotate and position
*/
public class Solid extends Shape3D
{
// translation
private double dx = 0;
private double dy = 0;
private double dz = 0;
/** array of indices for the vertices from the 'vertices' attribute */
protected int[] indices;
/** array of points defining the solid's vertices */
protected Point3d[] vertices;
/** array of color defining the vertices colors */
protected Color3f[] colors;
//--------------------------------CONSTRUCTORS----------------------------------//
/** Constructs an empty solid. */
public Solid()
{
super();
setInitialFeatures();
}
/**
* Construct a solid based on data arrays. An exception may occur in the case of
* abnormal arrays (indices making references to inexistent vertices, there are less
* colors than vertices...)
*
* @param vertices array of points defining the solid vertices
* @param indices array of indices for a array of vertices
* @param colors array of colors defining the vertices colors
*/
public Solid(Point3d[] vertices, int[] indices, Color3f[] colors)
{
this();
setData(vertices, indices, colors);
}
/**
* Constructs a solid based on a coordinates file. It contains vertices and indices,
* and its format is like this:
*
* <br><br>4
* <br>0 -5.00000000000000E-0001 -5.00000000000000E-0001 -5.00000000000000E-0001
* <br>1 5.00000000000000E-0001 -5.00000000000000E-0001 -5.00000000000000E-0001
* <br>2 -5.00000000000000E-0001 5.00000000000000E-0001 -5.00000000000000E-0001
* <br>3 5.00000000000000E-0001 5.00000000000000E-0001 -5.00000000000000E-0001
*
* <br><br>2
* <br>0 0 2 3
* <br>1 3 1 0
*
* @param solidFile file containing the solid coordinates
* @param color solid color
*/
public Solid(File solidFile, Color3f color)
{
this();
loadCoordinateFile(solidFile, color);
}
/** Sets the initial features common to all constructors */
protected void setInitialFeatures()
{
vertices = new Point3d[0];
colors = new Color3f[0];
indices = new int[0];
setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
setCapability(Shape3D.ALLOW_APPEARANCE_READ);
}
//---------------------------------------GETS-----------------------------------//
/**
* Gets the solid vertices
*
* @return solid vertices
*/
public Point3d[] getVertices()
{
Point3d[] newVertices = new Point3d[vertices.length];
for(int i=0;i<newVertices.length;i++)
{
newVertices[i] = (Point3d)vertices[i].clone();
}
return newVertices;
}
/** Gets the solid indices for its vertices
*
* @return solid indices for its vertices
*/
public int[] getIndices()
{
int[] newIndices = new int[indices.length];
System.arraycopy(indices,0,newIndices,0,indices.length);
return newIndices;
}
/** Gets the vertices colors
*
* @return vertices colors
*/
public Color3f[] getColors()
{
Color3f[] newColors = new Color3f[colors.length];
for(int i=0;i<newColors.length;i++)
{
newColors[i] = colors[i];
}
return newColors;
}
/**
* Gets if the solid is empty (without any vertex)
*
* @return true if the solid is empty, false otherwise
*/
public boolean isEmpty()
{
if(indices.length==0)
{
return true;
}
else
{
return false;
}
}
//---------------------------------------SETS-----------------------------------//
/**
* Sets the solid data. Each vertex may have a different color. An exception may
* occur in the case of abnormal arrays (e.g., indices making references to
* inexistent vertices, there are less colors than vertices...)
*
* @param vertices array of points defining the solid vertices
* @param indices array of indices for a array of vertices
* @param colors array of colors defining the vertices colors
*/
public void setData(Point3d[] vertices, int[] indices, Color3f[] colors)
{
this.vertices = new Point3d[vertices.length];
this.colors = new Color3f[colors.length];
this.indices = new int[indices.length];
if(indices.length!=0)
{
for(int i=0;i<vertices.length;i++)
{
this.vertices[i] = (Point3d)vertices[i].clone();
this.colors[i] = colors[i];
}
System.arraycopy(indices, 0, this.indices, 0, indices.length);
defineGeometry();
}
}
/**
* Sets the solid data. Defines the same color to all the vertices. An exception may
* may occur in the case of abnormal arrays (e.g., indices making references to
* inexistent vertices...)
*
* @param vertices array of points defining the solid vertices
* @param indices array of indices for a array of vertices
* @param color the color of the vertices (the solid color)
*/
public void setData(Point3d[] vertices, int[] indices, Color3f color)
{
Color3f[] colors = new Color3f[vertices.length];
Arrays.fill(colors, color);
setData(vertices, indices, colors);
}
//-------------------------GEOMETRICAL_TRANSFORMATIONS-------------------------//
/**
* Applies a translation into a solid
*
* @param dx translation on the x axis
* @param dy translation on the y axis
*/
public void translate(double dx, double dy, double dz)
{
if(dx!=0||dy!=0||dz!=0)
{
for(int i=0;i= 90, application will freeze... keep visibility private...
rotate(Math.toRadians(dx),Math.toRadians(dy),Math.toRadians(dz));
}
/**
* Applies a rotation into a solid
*
* @param dx rotation on the x axis
* @param dy rotation on the y axis
* @param dz rotation on the z axis
*/
public void rotate(double dx, double dy, double dz)
{
double cosX = Math.cos(dx);
double cosY = Math.cos(dy);
double cosZ = Math.cos(dz);
double sinX = Math.sin(dx);
double sinY = Math.sin(dy);
double sinZ = Math.sin(dz);
if(dx!=0||dy!=0||dz!=0)
{
//get mean
Point3d mean = getMean();
double newX, newY, newZ;
for(int i=0;i<vertices.length;i++)
{
vertices[i].x -= mean.x;
vertices[i].y -= mean.y;
vertices[i].z -= mean.z;
//x rotation
if(dx!=0)
{
newY = vertices[i].y*cosX - vertices[i].z*sinX;
newZ = vertices[i].y*sinX + vertices[i].z*cosX;
vertices[i].y = newY;
vertices[i].z = newZ;
}
//y rotation
if(dy!=0)
{
newX = vertices[i].x*cosY + vertices[i].z*sinY;
newZ = -vertices[i].x*sinY + vertices[i].z*cosY;
vertices[i].x = newX;
vertices[i].z = newZ;
}
//z rotation
if(dz!=0)
{
newX = vertices[i].x*cosZ - vertices[i].y*sinZ;
newY = vertices[i].x*sinZ + vertices[i].y*cosZ;
vertices[i].x = newX;
vertices[i].y = newY;
}
vertices[i].x += mean.x;
vertices[i].y += mean.y;
vertices[i].z += mean.z;
}
}
defineGeometry();
}
/**
* Applies a zoom into a solid
*
* @param dz translation on the z axis
*/
public void zoom(double dz)
{
if(dz!=0)
{
for(int i=0;i<vertices.length;i++)
{
vertices[i].z += dz;
}
defineGeometry();
}
}
/**
* Applies a scale changing into the solid
*
* @param dx scale changing for the x axis
* @param dy scale changing for the y axis
* @param dz scale changing for the z axis
*/
public void scale(double dx, double dy, double dz)
{
for(int i=0;i<vertices.length;i++)
{
vertices[i].x*=dx;
vertices[i].y*=dy;
vertices[i].z*=dz;
}
defineGeometry();
}
//-----------------------------------PRIVATES--------------------------------//
/** Creates a geometry based on the indexes and vertices set for the solid */
protected void defineGeometry()
{
GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
gi.setCoordinateIndices(indices);
gi.setCoordinates(vertices);
NormalGenerator ng = new NormalGenerator();
ng.generateNormals(gi);
gi.setColors(colors);
gi.setColorIndices(indices);
gi.recomputeIndices();
setGeometry(gi.getIndexedGeometryArray());
}
/**
* Loads a coordinates file, setting vertices and indices
*
* @param solidFile file used to create the solid
* @param color solid color
*/
protected void loadCoordinateFile(File solidFile, Color3f color)
{
try
{
BufferedReader reader = new BufferedReader(new FileReader(solidFile));
String line = reader.readLine();
int numVertices = Integer.parseInt(line);
vertices = new Point3d[numVertices];
StringTokenizer tokens;
String token;
for(int i=0;i<numVertices;i++)
{
line = reader.readLine();
tokens = new StringTokenizer(line);
tokens.nextToken();
vertices[i]= new Point3d(Double.parseDouble(tokens.nextToken()), Double.parseDouble(tokens.nextToken()), Double.parseDouble(tokens.nextToken()));
}
reader.readLine();
line = reader.readLine();
int numTriangles = Integer.parseInt(line);
indices = new int[numTriangles*3];
for(int i=0,j=0;i<numTriangles*3;i=i+3,j++)
{
line = reader.readLine();
tokens = new StringTokenizer(line);
tokens.nextToken();
indices[i] = Integer.parseInt(tokens.nextToken());
indices[i+1] = Integer.parseInt(tokens.nextToken());
indices[i+2] = Integer.parseInt(tokens.nextToken());
}
colors = new Color3f[vertices.length];
Arrays.fill(colors, color);
defineGeometry();
}
catch(IOException e)
{
System.out.println("invalid file!");
e.printStackTrace();
}
}
/**
* Gets the solid mean
*
* @return point representing the mean
*/
protected Point3d getMean()
{
Point3d mean = new Point3d();
for(int i=0;i<vertices.length;i++)
{
mean.x += vertices[i].x;
mean.y += vertices[i].y;
mean.z += vertices[i].z;
}
mean.x /= vertices.length;
mean.y /= vertices.length;
mean.z /= vertices.length;
return mean;
}
}
4) at package jme3test/unbboolean add this file:
BoolMesh.java
package jme3test.unbboolean;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import jme3test.unbboolean.j3dbool.BooleanModeller;
import jme3test.unbboolean.j3dbool.Solid;
import com.jme3.math.Triangle;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.BufferUtils;
/**
* author: teique
*/
public class BoolMesh extends Mesh {
private Solid solid;
public enum eBoolMeshOperation {
Difference,
Intersection,
Union
}
public BoolMesh(){
}
public BoolMesh(Mesh initialMesh){
this(meshToSolid(initialMesh));
}
public BoolMesh(Solid initialSolid){
setInitialSolid(initialSolid);
}
public void setInitialSolid(Solid initialSolid){
this.solid = initialSolid;
solidToThisMesh(this.solid);
}
/**
* if initial solid is null, no matter the operation, solidToBool will be used to set initial solid...
* @param e
* @param solidToBool
* @return
*/
public BoolMesh applyBooleanOperation(eBoolMeshOperation e,Solid solidToBool){
Solid solidResult = null;
if(this.solid == null){
solidResult = solidToBool;
}else{
BooleanModeller bm = new BooleanModeller(this.solid,solidToBool);
switch(e){
case Difference: solidResult = bm.getDifference(); break;
case Intersection: solidResult = bm.getIntersection(); break;
case Union: solidResult = bm.getUnion(); break;
}
}
solidToThisMesh(solidResult);
this.solid = solidResult;
return this;
}
public Solid getSolid(){
return this.solid;
}
public static Vector3f p3dToV3f(Point3d p3d){
return new Vector3f((float)p3d.x,(float)p3d.y,(float)p3d.z);
}
public static Triangle createTriangle(Point3d p3dA,Point3d p3dB,Point3d p3dC){
return new Triangle(p3dToV3f(p3dA),p3dToV3f(p3dB),p3dToV3f(p3dC));
}
private void solidToThisMesh(Solid solid) {
solidToMesh(solid, this);
}
public static Mesh solidToMesh(Solid solid) {
return solidToMesh(solid, new Mesh());
}
private static Mesh solidToMesh(Solid solid, Mesh mesh) {
//vertices
{
Point3d[] vertices = solid.getVertices();
mesh.setVertexCount(vertices.length);
msg("this.v="+vertices.length);
float[] fb = new float[vertices.length*3];
for(int i=0; i<fb.length; i+=3){
Point3d p3d = vertices[i/3];
fb[i] = (float)p3d.x;
fb[i+1] = (float)p3d.y;
fb[i+2] = (float)p3d.z;
}
if(mesh.getBuffer(Type.Position) != null){
mesh.getBuffer(Type.Position).updateData(
BufferUtils.createFloatBuffer(fb));
}else{
mesh.setBuffer(Type.Position,3,fb);
}
mesh.updateBound();
}
//normals
{
Point3d[] vertices = solid.getVertices();
float[] fb = new float[vertices.length*3];
//Triangle[] triangles = new Triangle[vertices.length/3];
for(int i=0; i<vertices.length/3; i+=3){
Vector3f normal =
createTriangle(vertices[i],vertices[i+1],vertices[i+2])
.getNormal();
for(int j=0; j < 9; j+=3){
fb[j] = (float)normal.x;
fb[j+1] = (float)normal.y;
fb[j+2] = (float)normal.z;
}
}
if(mesh.getBuffer(Type.Normal) != null){
mesh.getBuffer(Type.Normal).updateData(
BufferUtils.createFloatBuffer(fb));
}else{
mesh.setBuffer(Type.Normal,3,fb);
}
}
//texture coordinates UV, faked on color3f at meshToSolid()
{
Color3f[] colors = solid.getColors();
msg("this.c="+colors.length);
float[] fb = new float[colors.length*2];
for(int i=0; i<fb.length; i+=2){
Color3f c3f = colors[i/2];
fb[i] = (float)c3f.x;
fb[i+1] = (float)c3f.y;
}
if(mesh.getBuffer(Type.TexCoord) != null){
mesh.getBuffer(Type.TexCoord).updateData(
BufferUtils.createFloatBuffer(fb));
}else{
mesh.setBuffer(Type.TexCoord,2,fb);
}
}
//index
msg("this.i="+solid.getIndices().length);
if(mesh.getBuffer(Type.Index) != null){
mesh.getBuffer(Type.Index).updateData(
BufferUtils.createIntBuffer(solid.getIndices()));
}else{
mesh.setBuffer(Type.Index,3,solid.getIndices());
}
//TODO: colors, whatchout here, using texturecoord data... needs better integration
{
Color3f[] colors = solid.getColors();
msg("this.c="+colors.length);
float[] fb = new float[colors.length*4];
for(int i=0; i<fb.length; i+=4){
Color3f c3f = colors[i/4];
fb[i] = (float)c3f.x;
fb[i+1] = (float)c3f.y;
fb[i+2] = (float)c3f.z;
fb[i+3] = 1.0f;
}
if(mesh.getBuffer(Type.Color) != null){
mesh.getBuffer(Type.Color).updateData(
BufferUtils.createFloatBuffer(fb));
}else{
mesh.setBuffer(Type.Color,3,fb);
}
}
//TangentBinormalGenerator.generate(mesh); //TODO enable this? any changes?
return mesh;
}
private static void msg(String str){
//System.out.println(str);
}
public static Solid meshToSolid(Mesh mesh){
FloatBuffer fbVertex = mesh.getFloatBuffer(Type.Position);
fbVertex.compact();
Point3d[] vertices = new Point3d[fbVertex.limit()/3];
msg("v="+vertices.length);
for(int i=0; i<fbVertex.limit(); i+=3){
vertices[i/3] = new Point3d(
fbVertex.get(i),fbVertex.get(i+1),fbVertex.get(i+2));
}
FloatBuffer fbTexCoord = mesh.getFloatBuffer(Type.TexCoord);
Color3f[] colors = new Color3f[vertices.length];
if(fbTexCoord != null){
//TODO improve (extend Solid) to allow color and texture coord?
// fake texture coords into colors to make compatible with Solid
// texturecoord has only UV, x,y two values; so the 3rd at color3f is ignored...
msg("t="+fbTexCoord.limit());
assert(colors.length*2 == fbTexCoord.limit());
for(int i=0; i<fbTexCoord.limit(); i+=2){
colors[i/2] = new Color3f(
fbTexCoord.get(i),fbTexCoord.get(i+1),0f);
}
}else{ // has texture!!!
FloatBuffer fbColor = mesh.getFloatBuffer(Type.Color);
if(fbColor == null){ //has nothing, so create something, useless?
colors = new Color3f[vertices.length];
for(int i=0; i<colors.length; i++){
colors[i] = new Color3f(0.3f,0.3f,0.6f); //arbitrary color
}
}else{
// has colors!
fbColor.compact();
colors = new Color3f[fbColor.limit()/3];
for(int i=0; i<fbColor.limit(); i+=3){
colors[i/3] = new Color3f(
fbColor.get(i),fbColor.get(i+1),fbColor.get(i+2));
}
}
msg("c="+colors.length);
}
IndexBuffer index = mesh.getIndexBuffer();
msg("i="+index.size());
//IntBuffer ib = BufferUtils.createIntBuffer(index.size());
IntBuffer ib = IntBuffer.wrap(new int[index.size()]);
for(int i=0; i<index.size(); i++){
ib.put(index.get(i));
}
int[] indices = ib.array();
return new Solid(vertices,indices,colors);
}
}
5) at package jme3test/unbboolean add this file:
TestBoolMesh.java
package jme3test.unbboolean;
import jme3test.unbboolean.BoolMesh.eBoolMeshOperation;
import jme3test.unbboolean.j3dbool.Solid;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.scene.shape.Sphere;
import com.jme3.system.AppSettings;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.TangentBinormalGenerator;
/**
* author: teique
*/
public class TestBoolMesh extends SimpleApplication {
public static void main(String[] args){
TestBoolMesh app = new TestBoolMesh();
//AppSettings as = new AppSettings(true);
//as.setResolution(800,600);
//app.setSettings(as);
//app.setShowSettings(false);
app.start();
}
@Override
public void simpleInitApp() {
flyCam.setMoveSpeed(10);
//inputManager.setCursorVisible(true);
Box b = new Box(Vector3f.ZERO, 0.5f,0.5f,0.5f);
BoolMesh bm = new BoolMesh(b);
Solid solidBox = bm.getSolid();
solidBox.rotateDegreesZ(45);
Sphere s = new Sphere(9,9,0.5f);
Solid solidSphere = BoolMesh.meshToSolid(s);
solidSphere.rotateDegreesY(90);
solidSphere.translate(0.25,0,0.25);
bm.applyBooleanOperation(eBoolMeshOperation.Difference,solidSphere);
Cylinder c = new Cylinder(6,6,0.25f,0.05f,2f,true,false);
Solid solidCylinder = BoolMesh.meshToSolid(c);
solidCylinder.rotateDegreesY(90);
solidCylinder.rotateDegreesZ(-30);
solidCylinder.rotateDegreesY(60);
solidCylinder.translate(0.1,0.2,0.1);
bm.applyBooleanOperation(eBoolMeshOperation.Union,solidCylinder);
Geometry geom = new Geometry("BoolMesh", bm);
//Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/WireColor.j3md");
//mat.setColor("m_Color", ColorRGBA.Blue.multLocal(0.75f));
//Material mat = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
//mat.setTexture("m_ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
//Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
Material mat = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md");
Texture tex = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");
tex.setWrap(WrapMode.Repeat);
mat.setTexture("m_ColorMap", tex);
geom.setMaterial(mat);
TangentBinormalGenerator.generate(geom.getMesh()); //any changes with this?
rootNode.attachChild(geom);
}
}
6) add these jars to the project
j3dcore.jar
j3dutils.jar
vecmath.jar
On Ubuntu they are from these packages:
libjava3d-java
libvecmath-java
EDIT: I would like to add that, if you make any improvement to speed or fix any bug, say so, and what you did, so I can test and update these code here, thanks!