I made this test app for the decal generator I’ve been working on. Still needs work and optimization but you can stick decals.
DecalTestApp.java:
[java]
/**
*
-
@author trblair
*/
import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.HashMap;
/**
- Example 9 - How to make walls and floors solid.
- This collision code uses Physics and a custom Action Listener.
-
@author normen, with edits by Zathras
*/
public class DecalTestApp extends SimpleApplication
implements ActionListener {
public static HashMap<String, Boolean> keysPressed;
private Spatial sceneModel;
private BulletAppState bulletAppState;
private RigidBodyControl landscape;
public DecalPlayer player;
private Vector3f walkDirection = new Vector3f();
public static AssetManager assetManager;
public static Node rootNode;
public static Camera cam;
public static Node hitNode;
public static boolean showPreview = false;
public static void main(String[] args) {
DecalTestApp app = new DecalTestApp();
app.start();
}
public void simpleInitApp() {
/** Set up Physics */
assetManager = this.getAssetManager();
rootNode = this.getRootNode();
cam = this.getCamera();
keysPressed = new HashMap();
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
// We re-use the flyby camera for rotation, while positioning is handled by physics
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
flyCam.setMoveSpeed(100);
setUpKeys();
setUpLight();
// We load the scene from the zip file and adjust its size.
// assetManager.registerLocator(“assets/”, FileLocator.class);
assetManager.registerLocator(“wildhouse.zip”, ZipLocator.class);
sceneModel = assetManager.loadModel(“main.scene”);
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass zero.
CollisionShape sceneShape =
CollisionShapeFactory.createMeshShape((Node) sceneModel);
landscape = new RigidBodyControl(sceneShape, 0);
sceneModel.addControl(landscape);
assetManager.registerLocator("assets/", FileLocator.class);
player = new DecalPlayer(new Vector3f(0,10,0));
player.init();
// We attach the scene and the player to the rootNode and the physics space,
// to make them appear in the game world.
hitNode = new Node();
hitNode.attachChild(sceneModel);
rootNode.attachChild(hitNode);
bulletAppState.getPhysicsSpace().add(landscape);
bulletAppState.getPhysicsSpace().add(player.player);
}
private void setUpLight() {
// We add light so we see the scene
AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(1.3f));
rootNode.addLight(al);
DirectionalLight dl = new DirectionalLight();
dl.setColor(ColorRGBA.White);
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
rootNode.addLight(dl);
}
/** We over-write some navigational key mappings here, so we can
- add physics-controlled walking and jumping: */
private void setUpKeys() {
inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping(“Up”, new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping(“Down”, new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping(“shoot”, new KeyTrigger(KeyInput.KEY_Z));
inputManager.addListener(this, “Left”);
inputManager.addListener(this, “Right”);
inputManager.addListener(this, “Up”);
inputManager.addListener(this, “Down”);
inputManager.addListener(this, “Jump”);
inputManager.addListener(this, “shoot”);
inputManager.addMapping("splat", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, "splat");
inputManager.addMapping("show", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addListener(this, "show");
}
/** These are our custom actions triggered by key presses.
-
We do not walk yet, we just keep track of the direction the user pressed. */
@Override
public void onAction(String binding, boolean value, float tpf) {if (value && binding.equals(“show”)) {
showPreview = (showPreview == true)? false : true;
}
keysPressed.put(binding, value);
}
/**
-
This is the main event loop–walking happens here.
-
We check in which direction the player is walking by interpreting
-
the camera direction forward (camDir) and to the side (camLeft).
-
The setWalkDirection() command is what lets a physics-controlled player walk.
-
We also make sure here that the camera moves with player.
*/
@Override
public void simpleUpdate(float tpf) {player.update();
}
}
[/java]
DecalPlayer.java;
[java]
/**
*
-
@author trblair
*/
public class DecalPlayer {public DecalProjector projector;
public Vector3f position;
public CharacterControl player;public Vector3f walkDirection;
public DecalPlayer(Vector3f position){
this.position = position;
}
public void init(){
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1); player = new CharacterControl(capsuleShape, 0.05f); player.setJumpSpeed(20); player.setFallSpeed(30); player.setGravity(30); player.setPhysicsLocation(new Vector3f(0, 10, 0)); Quaternion cam_rotation = DecalTestApp.cam.getRotation(); Quaternion projector_rotation = new Quaternion(cam_rotation.getX(),cam_rotation.getY(),cam_rotation.getZ(),cam_rotation.getW()); projector = new DecalProjector(position,projector_rotation,10); walkDirection = new Vector3f(0,0,0);
}
public void update(){
Vector3f camDir = DecalTestApp.cam.getDirection().clone().multLocal(0.6f); Vector3f camLeft = DecalTestApp.cam.getLeft().clone().multLocal(0.4f); walkDirection.set(0, 0, 0); Boolean keypress = DecalTestApp.keysPressed.get("Left"); if (keypress != null && keypress){ walkDirection.addLocal(camLeft); } keypress = DecalTestApp.keysPressed.get("Right"); if (keypress != null && keypress) { walkDirection.addLocal(camLeft.negate()); } keypress = DecalTestApp.keysPressed.get("Up"); if (keypress != null && keypress) { walkDirection.addLocal(camDir); } keypress = DecalTestApp.keysPressed.get("Down"); if (keypress != null && keypress) { walkDirection.addLocal(camDir.negate()); } player.setWalkDirection(walkDirection); DecalTestApp.cam.setLocation(player.getPhysicsLocation()); if(DecalTestApp.showPreview){ projector.updateSelectionPreview(); }else if(projector.decal_preview != null){ projector.decal_preview.setCullHint(Spatial.CullHint.Always); }
}
}
[/java]
DecalProjector.java:
[java]
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.Quaternion;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
*
-
@author trblair
*/
public class DecalProjector {public Geometry decal_preview = null;
public Geometry crossHair;
public DecalProjectionBox inner_projection;
public DecalProjectionBox outer_projection;
public float projection_size;
public Material preview_material;
public ArrayList triStrings;
public HashMap<String,Vector3f> vertMap;
public HashMap<String,Vector3f> normalMap;
public HashMap<String,Vector2f> textureMap;
public HashMap<String,Integer> vertKeys;
public Vector3f[] finalVerts;
public Vector3f[] finalNormals;
public Vector2f[] finalTexCoords;
public int[] finalIndices;
public long wait_time, current_time, last_time = 0;public DecalProjector(Vector3f position, Quaternion rotation, float size){
this.projection_size = size; inner_projection = new DecalProjectionBox( position, rotation, projection_size); outer_projection = new DecalProjectionBox( position, rotation, projection_size*2); preview_material = new Material(DecalTestApp.assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); preview_material.setTexture("ColorMap", DecalTestApp.assetManager.loadTexture("textures/transpBlue75.png")); preview_material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
}
public void updateSelectionPreview(){
CollisionResults results = new CollisionResults(); Ray ray = new Ray(DecalTestApp.cam.getLocation(), DecalTestApp.cam.getDirection()); DecalTestApp.rootNode.collideWith(ray, results); if(results.size()>0){ CollisionResult closest = results.getClosestCollision(); generateSelection(closest); displayPreview(); if(decal_preview != null){ decal_preview.setCullHint(Spatial.CullHint.Inherit); } Boolean rightClickDown = DecalTestApp.keysPressed.get("splat"); if((rightClickDown != null && rightClickDown && wait_time > 500)){ attachDecal(); last_time = System.currentTimeMillis(); } current_time = System.currentTimeMillis(); wait_time = current_time - last_time; }else if(decal_preview != null){ decal_preview.setCullHint(Spatial.CullHint.Always); }
}
public void generateSelection(CollisionResult collision){
Vector3f hit_position = collision.getContactPoint(); Vector3f hit_normal = collision.getContactNormal().normalize(); Vector3f cam_direction = DecalTestApp.cam.getDirection(); Vector3f direction = new Vector3f(cam_direction.x,cam_direction.y,cam_direction.z); Quaternion q = inner_projection.getRotation(); q.lookAt(hit_normal, direction); inner_projection.update( hit_position, q); outer_projection.update( hit_position, q); triStrings = new ArrayList(); vertMap = new HashMap(); normalMap = new HashMap(); textureMap = new HashMap(); vertKeys = new HashMap(); Geometry collisionGeometry = collision.getGeometry(); Mesh collisionMesh = collisionGeometry.getMesh(); clipSelectedMesh(collisionMesh); finalVerts = new Vector3f[vertMap.size()]; finalNormals = new Vector3f[vertMap.size()]; finalTexCoords = new Vector2f[vertMap.size()]; finalIndices = new int[triStrings.size()]; int vertCount = 0; for(Map.Entry<String,Vector3f> entry : vertMap.entrySet()){ String key = entry.getKey(); finalVerts[vertCount] = entry.getValue().add(normalMap.get(key).divide(10)); finalNormals[vertCount] = normalMap.get(key); finalTexCoords[vertCount] = textureMap.get(key); vertKeys.put(key, vertCount); vertCount++; } int indexCount = 0; for(int i = 0;i<triStrings.size();i+=3){ finalIndices[indexCount] = vertKeys.get((String)triStrings.get(i)); indexCount++; finalIndices[indexCount] = vertKeys.get((String)triStrings.get(i+1)); indexCount++; finalIndices[indexCount] = vertKeys.get((String)triStrings.get(i+2)); indexCount++; }
}
public void displayPreview(){
if(decal_preview == null){ Mesh newMesh = new Mesh(); newMesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(finalVerts)); newMesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(finalNormals)); newMesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(finalIndices)); newMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(finalTexCoords)); newMesh.updateBound(); decal_preview = new Geometry("decalPreview",newMesh); decal_preview.setMaterial(preview_material); decal_preview.setQueueBucket(RenderQueue.Bucket.Transparent); decal_preview.setUserData("debug",(new Boolean(true))); decal_preview.setLocalTranslation(0, 0, 0); DecalTestApp.rootNode.attachChild(decal_preview); }else{ decal_preview.getMesh().setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(finalVerts)); decal_preview.getMesh().setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(finalNormals)); decal_preview.getMesh().setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(finalIndices)); decal_preview.getMesh().setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(finalTexCoords)); decal_preview.getMesh().updateCounts(); decal_preview.getMesh().updateBound(); decal_preview.updateModelBound(); }
}
public void attachDecal(){
Texture decal_texture = DecalTestApp.assetManager.loadTexture("textures/Monkey.png"); Material decal_material = new Material(DecalTestApp.assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); decal_material.setTexture("ColorMap", decal_texture); decal_material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); Mesh newMesh = new Mesh(); newMesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(finalVerts)); newMesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(finalNormals)); newMesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(finalIndices)); newMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(finalTexCoords)); newMesh.updateBound(); Geometry decal = new Geometry("decal",newMesh); decal.getMesh().updateBound(); decal.setMaterial(decal_material); decal.setQueueBucket(RenderQueue.Bucket.Transparent); decal.setLocalTranslation(0, 0, 0); DecalTestApp.rootNode.attachChild(decal);
}
public void clipSelectedMesh(Mesh collisionMesh){
ArrayList <HashMap <String, Vector3f[]> > to_clip = new ArrayList(); FloatBuffer vertex_buffer = collisionMesh.getFloatBuffer(VertexBuffer.Type.Position); Vector3f[] vertex_array = BufferUtils.getVector3Array(vertex_buffer); FloatBuffer normal_buffer = collisionMesh.getFloatBuffer(VertexBuffer.Type.Normal); Vector3f[] normal_array = BufferUtils.getVector3Array(normal_buffer); IndexBuffer face_buffer = collisionMesh.getIndexBuffer(); int[] index_array; // Check instance type of index buffer and extract indexing data accordingly. if(face_buffer.getBuffer() instanceof ShortBuffer){ // ShortBuffer instace type is generally only encountered with JME primitive meshes. ShortBuffer index_buffer = (ShortBuffer)face_buffer.getBuffer(); index_buffer.rewind(); int length = index_buffer.limit(); index_array = new int[length]; int index_count = 0; // If we do encounter a ShortBuffer we must iterate the buffer // and extract each element individually as there is no BufferUtils.getShortArray method.. while(index_buffer.hasRemaining()){ // Copy each element of index buffer to index array. index_array[index_count] = (int)index_buffer.get(); index_count++; } }else{ // If index buffer isn't a ShortBuffer we can just // get the int array with BufferUtils. IntBuffer index_buffer = (IntBuffer)face_buffer.getBuffer(); index_array = BufferUtils.getIntArray(index_buffer); } for(int out_i = 0; out_i < index_array.length; out_i+=3){ int current_index = out_i; for(int in_i = 0; in_i < 3; in_i++){ Vector3f current_vertex = vertex_array[index_array[current_index + in_i]]; if(outer_projection.contains(current_vertex)){ Vector3f[] tri_verts = { vertex_array[index_array[current_index]], vertex_array[index_array[current_index+1]], vertex_array[index_array[current_index+2]]}; Vector3f[] tri_norms = { normal_array[index_array[current_index]], normal_array[index_array[current_index+1]], normal_array[index_array[current_index+2]]}; HashMap<String, Vector3f[]> tri_map = new HashMap(); tri_map.put("vertices", tri_verts); tri_map.put("normals", tri_norms); to_clip.add(tri_map); break; } } } float size = projection_size; float scale = size*2; ArrayList <HashMap <String, Vector3f[]> > clipped_inside = inner_projection.clipToProjection(to_clip, 0); for(int i = 0; i < clipped_inside.size(); i++){ HashMap <String, Vector3f[]> clipped_triangle = clipped_inside.get(i); Vector3f[] vertices = clipped_triangle.get("vertices"); Vector3f[] normals = clipped_triangle.get("normals"); for(int in_i = 0; in_i < vertices.length; in_i++){ Vector3f vert = vertices[in_i]; String vertKey = vert.toString(); vertMap.put(vertKey, vert ); normalMap.put(vertKey, normals[in_i]); Vector3f local = new Vector3f(vert); local = inner_projection.projection_geometry.worldToLocal(local, local); Vector2f texCoord = new Vector2f((local.x+size)/scale,(local.y+size)/scale); textureMap.put(vertKey, texCoord); triStrings.add(vertKey); } }
}
}
[/java]
DecalProjectionBox.java:
[java]
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
-
@author trblair
*/
public class DecalProjectionBox {public Quaternion rotation;
public Vector3f position;
public float size;
public int[] triangles;
public Vector3f[] local_vertices;
public Vector3f[] world_vertices;
public Vector3f[] projection_planes;
public Geometry projection_geometry;
public static final int COPLANAR = 0, FRONT = 1, BACK = 2, SPANNING = 3;
public static final double EPSILON = 1e-5;
public int bottomPlaneIndex;public DecalProjectionBox(Vector3f position, Quaternion rotation, float size){
this.position = position; this.rotation = rotation; this.size = size; local_vertices = new Vector3f[8]; world_vertices = new Vector3f[8]; projection_planes = new Vector3f[6]; triangles = new int[36]; init();
}
public void init() {
local_vertices[0] = new Vector3f((size*-1),(size*-1),(size*-1)); local_vertices[1] = new Vector3f((size*-1),size,(size*-1)); local_vertices[2] = new Vector3f(size,size,(size*-1)); local_vertices[3] = new Vector3f(size,(size*-1),(size*-1)); local_vertices[4] = new Vector3f(size,size,size); local_vertices[5] = new Vector3f((size*-1),size,size); local_vertices[6] = new Vector3f((size*-1),(size*-1),size);//0,1 2,3 4,7 5,6 local_vertices[7] = new Vector3f(size,(size*-1),size); //front plane triangles[0] = 3; triangles[1] = 0; triangles[2] = 1; projection_planes[0] = new Vector3f(3,0,1); triangles[3] = 1; triangles[4] = 2; triangles[5] = 3; //left plane triangles[6] = 7; triangles[7] = 3; triangles[8] = 2; projection_planes[1] = new Vector3f(7,3,2); triangles[9] = 2; triangles[10] = 4; triangles[11] = 7; //back plane triangles[12] = 6; triangles[13] = 7; triangles[14] = 4; projection_planes[2] = new Vector3f(6,7,4); triangles[15] = 4; triangles[16] = 5; triangles[17] = 6; //right plane triangles[18] = 0; triangles[19] = 6; triangles[20] = 5; projection_planes[3] = new Vector3f(0,6,5); triangles[21] = 5; triangles[22] = 1; triangles[23] = 0; //top plane triangles[24] = 2; triangles[25] = 1; triangles[26] = 5; projection_planes[4] = new Vector3f(2,1,5); triangles[27] = 5; triangles[28] = 4; triangles[29] = 2; //bottom plane triangles[30] = 7; triangles[31] = 6; triangles[32] = 0; projection_planes[5] = new Vector3f(7,6,0); triangles[33] = 0; triangles[34] = 3; triangles[35] = 7; bottomPlaneIndex = 5; Mesh box_mesh = new Mesh(); box_mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(local_vertices)); box_mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(triangles)); projection_geometry = new Geometry("see-through box", box_mesh); update(position, rotation);
}
// updates cube position, rotation and world_vertices array
public void update(Vector3f position, Quaternion rotation){this.position = position; projection_geometry.setLocalTranslation(position); this.rotation = rotation; projection_geometry.setLocalRotation(rotation); FloatBuffer vertBuff = projection_geometry.getMesh().getFloatBuffer(VertexBuffer.Type.Position); Vector3f[] verts = BufferUtils.getVector3Array(vertBuff); for(int i = 0; i < verts.length; i++){ Vector3f point = verts[i]; projection_geometry.localToWorld(point, point); world_vertices[i] = point; }
}
public Vector3f[] getWorldVertices(){
return world_vertices;
}
public Quaternion getRotation(){
return rotation;
}
public Vector3f getOrientationNormal(){
Vector3f planeIndices = projection_planes[bottomPlaneIndex]; Vector3f plane_normal = calculateNormal(world_vertices[(int)planeIndices.x], world_vertices[(int)planeIndices.y], world_vertices[(int)planeIndices.z]); return plane_normal.normalize().negate();
}
// return true if given point lies within the planar bounds of the cube
public boolean contains(Vector3f point){boolean contains = false; for(int i = 0 ;i < projection_planes.length; i++){ Vector3f planeIndices = projection_planes[i]; Vector3f plane_normal = calculateNormal(world_vertices[(int)planeIndices.x], world_vertices[(int)planeIndices.y], world_vertices[(int)planeIndices.z]); float plane_constant = plane_normal.dot(world_vertices[(int)planeIndices.x]); double t = plane_normal.dot(point) - plane_constant; if(t <= EPSILON){ contains = true; }else{ contains = false; return contains; } } return contains;
}
// recursively clips polygons to the cubes six planes. used in our decal projection algorithm
public ArrayList clipToProjection(ArrayList <HashMap <String, Vector3f[]> > unsplit_polygons, int plane_index){if(plane_index > (projection_planes.length-1) || unsplit_polygons.isEmpty()){ return unsplit_polygons; } Vector3f plane = projection_planes[plane_index]; Vector3f plane_normal = calculateNormal(world_vertices[(int)plane.x], world_vertices[(int)plane.y], world_vertices[(int)plane.z]); float plane_constant = plane_normal.dot(world_vertices[(int)plane.x]); ArrayList <HashMap <String, Vector3f[]> > split_polygons = new ArrayList(); for(int outer_i = 0; outer_i < unsplit_polygons.size(); outer_i++){ HashMap <String, Vector3f[]> unsplit_polygon_map = unsplit_polygons.get(outer_i); Vector3f[] unsplit_polygon_vertices = unsplit_polygon_map.get("vertices"); Vector3f[] unsplit_polygon_normals = unsplit_polygon_map.get("normals"); int[] types = new int[unsplit_polygon_vertices.length]; int polygon_type = 0; for(int middle_i = 0; middle_i < unsplit_polygon_vertices.length; middle_i++){ Vector3f current_vert = new Vector3f(unsplit_polygon_vertices[middle_i]); double t = plane_normal.dot(current_vert) - plane_constant; int type; if (t < EPSILON*-1) {type = BACK;} else if (t > EPSILON) { type = FRONT;} else { type = COPLANAR;} polygon_type |= type; types[middle_i] = type; } if(polygon_type == SPANNING){ ArrayList <Vector3f> clipped_back_vertices = new ArrayList(); ArrayList <Vector3f> clipped_back_normals = new ArrayList(); for(int inner_i = 0; inner_i < unsplit_polygon_vertices.length; inner_i++){ int j = (inner_i + 1) % unsplit_polygon_vertices.length; Vector3f vertI = new Vector3f(unsplit_polygon_vertices[inner_i]); Vector3f vertJ = new Vector3f(unsplit_polygon_vertices[j]); Vector3f normI = new Vector3f(unsplit_polygon_normals[inner_i]); Vector3f normJ = new Vector3f(unsplit_polygon_normals[j]); int typeI = types[inner_i]; int typeJ = types[j]; if(typeI != FRONT){ clipped_back_vertices.add(new Vector3f(vertI)); clipped_back_normals.add(new Vector3f(normI)); } if((typeI | typeJ) == SPANNING){ float t = (plane_constant - plane_normal.dot(vertI)) / plane_normal.dot(vertJ.subtract(vertI)); Vector3f v = vertI.interpolate(vertJ, t); clipped_back_vertices.add(v); Vector3f n = normI.interpolate(normJ, t).normalize(); clipped_back_normals.add(n); } } if(clipped_back_vertices.size() == 4){ HashMap <String,Vector3f[]> clipped_back_map_a = new HashMap(); HashMap <String,Vector3f[]> clipped_back_map_b = new HashMap(); Vector3f[] clipped_back_vertices_a = {clipped_back_vertices.get(0),clipped_back_vertices.get(1),clipped_back_vertices.get(2)}; Vector3f[] clipped_back_vertices_b = {clipped_back_vertices.get(2),clipped_back_vertices.get(3),clipped_back_vertices.get(0)}; Vector3f[] clipped_back_normals_a = {clipped_back_normals.get(0),clipped_back_normals.get(1),clipped_back_normals.get(2)}; Vector3f[] clipped_back_normals_b = {clipped_back_normals.get(2),clipped_back_normals.get(3),clipped_back_normals.get(0)}; clipped_back_map_a.put("vertices", clipped_back_vertices_a); clipped_back_map_a.put("normals", clipped_back_normals_a); clipped_back_map_b.put("vertices", clipped_back_vertices_b); clipped_back_map_b.put("normals", clipped_back_normals_b); split_polygons.add(clipped_back_map_a); split_polygons.add(clipped_back_map_b); }else if(clipped_back_vertices.size() == 3){ HashMap <String,Vector3f[]> clipped_back_map_a = new HashMap(); Vector3f[] clipped_back_vertices_a = {clipped_back_vertices.get(0),clipped_back_vertices.get(1),clipped_back_vertices.get(2)}; Vector3f[] clipped_back_normals_a = {clipped_back_normals.get(0),clipped_back_normals.get(1),clipped_back_normals.get(2)}; clipped_back_map_a.put("vertices", clipped_back_vertices_a); clipped_back_map_a.put("normals", clipped_back_normals_a); split_polygons.add(clipped_back_map_a); }else{ System.err.println("Clipping algorithm has encounter a degenerate case!! Output polygon is a line!!"); } }else if(polygon_type != FRONT){ split_polygons.add(unsplit_polygon_map); } } return clipToProjection(split_polygons, plane_index + 1);
}
public Vector3f calculateNormal(Vector3f vertA, Vector3f vertB, Vector3f vertC){
// the normal is equal to the normalized value of the cross product of B minus A and C minus A. Vector3f normal = vertB.subtract(vertA).cross((vertC.subtract(vertA))).normalize(); // return the computed normal. return normal;
}
}
[/java]