/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.components.visibleComponents.shapes.mesh;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.media.opengl.GL;
import org.mt4j.components.bounds.BoundingSphere;
import org.mt4j.components.bounds.IBoundingShape;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.math.BezierVertex;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.ToolsBuffers;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLTexture;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.opengl.PGraphicsOpenGL;
/**
* A mesh class for drawing triangle meshes.
*
* @author Christopher Ruff
*/
public class MTTriangleMesh extends AbstractShape{
/** The triangles. */
private Triangle[] triangles;
/** The draw normals. */
private boolean drawNormals;
//FIXME EXPERIMENTAL
/** The outline contours. */
private List<Vertex[]> outlineContours;
/** The outline buffers. */
private List<FloatBuffer> outlineBuffers;
private boolean calculateDefaultNormals = true; //has to be initialized here, else not considered in first setGeomInfo()..
/**
* Creates a new triangle mesh.
*
* <p><strong>Important</strong>: This mesh expects triangles geometry!
* <br><li>An unindexed geometry's vertex array
* should contain pairs of three vertices.
* <br><li> An indexed geometry should contain pairs of three indices.
* <br>
*
* @param pApplet the applet
* @param geometryInfo the geometry info
*/
public MTTriangleMesh(PApplet pApplet, GeometryInfo geometryInfo) {
this(pApplet, geometryInfo, true);
}
/**
* Creates a new triangle mesh.
*
* <p><strong>Important</strong>: This mesh expects triangles geometry!
* <br><li>An unindexed geometry's vertex array
* should contain pairs of three vertices.
* <br><li> An indexed geometry should contain pairs of three indices.
* <br>
*
* @param pApplet the applet
* @param geometryInfo the geometry info
* @param calculateDefaultNormals sets if default normals for lightning should be
* calculated if the geometry doesent containe them yet - WE ARE ONLY REQUIRED TO CALCULATE NORMALS
* IF THE MESH IS TO BE USED WITH LIGHTNING!
*/
public MTTriangleMesh(PApplet pApplet, GeometryInfo geometryInfo, boolean calculateDefaultNormals) {
super(geometryInfo, pApplet);
this.calculateDefaultNormals = calculateDefaultNormals;
//FIXME EXPERIMENTAL
this.outlineContours = new ArrayList<Vertex[]>();
this.outlineBuffers = new ArrayList<FloatBuffer>();
//Some Settings
this.setFillDrawMode(GL.GL_TRIANGLES);
this.setName("unnamed triangle mesh");
this.drawNormals = false;
this.setNoStroke(true);
this.setBoundsBehaviour(AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK);
// this.setBoundsPickingBehaviour(AbstractShape.BOUNDS_ONLY_GEOMETRY_CHECK);
// this.setBoundsPickingBehaviour(AbstractShape.BOUNDS_ONLY_CHECK);
//FIXME we dont create triangles or normals here because we do so in the overriden method setGeometryInfo()
//which gets called in the super (abstractShape) constructor anyway
//-> else createTris() and createDefaultNormals would be called twice!!
//Create triangles from all the vertices (pairs of 3) or from the indices for internal use
// this.createTriangles(this.getGeometryInfo());
//Create normals for use with OpenGL lighting,
//but only if the geomInfo doesent have norms and createDefaultNormals = true
// this.createDefaultNormals(geometryInfo);
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setGeometryInfo(org.mt4j.components.visibleComponents.GeometryInfo)
*/
@Override
public void setGeometryInfo(GeometryInfo geometryInfo) {
//KEEP IN MIND THAT THIS IS CALLED IN ABSTRACTSHAPE CONSTRUCTOR!
//Create tris before setGeometryInfo, because in setGeometryInfo,
//defaultBounds are calced (may depend on triangles)
this.createTriangles(geometryInfo);
super.setGeometryInfo(geometryInfo);
this.createDefaultNormals(geometryInfo);
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setVertices(org.mt4j.util.math.Vertex[])
*/
@Override
public void setVertices(Vertex[] vertices) {
//create tris before setVertices, because in setVerts, defaultBounds are calced (depend on triangles)
this.createTriangles(new GeometryInfo(this.getRenderer(), vertices, this.getGeometryInfo().getNormals(), //TODO why not reconstruct() when geometryinfo already there?
this.getGeometryInfo().getIndices()));
super.setVertices(vertices);
this.createDefaultNormals(this.getGeometryInfo());
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#computeDefaultBounds()
*/
@Override
protected IBoundingShape computeDefaultBounds() {
return new BoundingSphere(this);
// return new OrientedBoundingBox(this));
}
/**
* Checks if default normals should be calculated if a
* new geometryInfo or new vertices are set on this mesh.
*
* @return true, if is creates the default normals
*/
public boolean isCalculateDefaultNormals() {
return calculateDefaultNormals;
}
/**
* Sets if default normals should be calculated if a
* new geometryInfo or new vertices are set on this mesh.
*
* @param calculateDefaultNormals the new creates the default normals
*/
public void setCalculateDefaultNormals(boolean calculateDefaultNormals) {
this.calculateDefaultNormals = calculateDefaultNormals;
}
/**
* Calculates default normals for this mesh if the mesh's geometryInfo doesent
* yet contain normals.
* Creates face or vertex normals - <br>
* Face normals if the geometry isnt indexed and Vertex normals are created if the geometry
* IS indexed.
*/
public void calculateDefaultNormals(){
boolean oldVal = this.isCalculateDefaultNormals();
this.setCalculateDefaultNormals(true);
this.createDefaultNormals(this.getGeometryInfo());
this.setCalculateDefaultNormals(oldVal);
}
/**
* Creates face or vertex normals if the geometryinfo doesent already contain normals and
* if isCalculateDefaultNormals() is true.
* Creates face normals if the geometry isnt indexed. Vertex normals are created if geometry
* IS indexed.
*
* @param geometryInfo the geometry info
*/
private void createDefaultNormals(GeometryInfo geometryInfo){
if (!this.getGeometryInfo().isContainsNormals() && this.isCalculateDefaultNormals()){
// System.out.println("MTTriangleMesh object: \"" + this + "\" -> Create default normals.");
//Create and set the face normals
if (!geometryInfo.isIndexed()){
Vector3D[] normals = this.getFaceOrVertexNormals();
this.getGeometryInfo().setNormals(normals, true, this.isUseVBOs());
}else{
//System.err.println("Triangle mesh geometry contains no normals and is indexed -> To create normals use the MeshNormalGenerator!");
Vector3D[] normals = this.getFaceOrVertexNormals();
this.getGeometryInfo().setNormals(normals, true, this.isUseVBOs());
}
}
}
/**
* Create triangles from the mesh information.
* <br>If the geometryInfo is indexed, one triangle is created per 3 indices.
* <br>If the geometry inst indexes, one triangle per 3 vertices is creates.
*
* @param geom the geom
*/
private void createTriangles(GeometryInfo geom){
Vertex[] vertices = geom.getVertices();
//TODO geometryInfo.getDrawMode()? bei tristrip m�sste ein triangle erzeugt werden f�r jedes neue vertex!?
ArrayList<Triangle> tris = new ArrayList<Triangle>();
if (geom.isIndexed()){
//System.out.println("MTTriangleMesh object: \"" + this.getName() + "\" Debug-> Supplied geometry is INDEXED");
int[] indices = geom.getIndices();
if (indices.length % 3 != 0){
System.err.println("WARNING: the indices of the indexed mesh geometry:\"" + this.getName() + "\" arent dividable by 3 => probably no TRIANGLES indices provided!");
}
for (int i = 0; i < indices.length/3; i++) {
int vertIndex0 = indices[i*3];
int vertIndex1 = indices[i*3+1];
int vertIndex2 = indices[i*3+2];
Vertex v0 = vertices[vertIndex0];
Vertex v1 = vertices[vertIndex1];
Vertex v2 = vertices[vertIndex2];
tris.add(new Triangle(v0, v1, v2, vertIndex0, vertIndex1, vertIndex2));
}
}else{
//System.out.println("MTTriangleMesh object: \"" + this.getName() + "\" Debug-> Supplied geometry is NOT INDEXED");
if (vertices.length % 3 != 0){
System.err.println("WARNING: the vertices of the mesh geometry:\"" + this.getName() + "\" arent dividable by 3 => probably no TRIANGLES array provided!");
}
//geht nur bei vertices/3 = ganze zahl (vertices sind dreiecke!)
for (int i = 0; i < vertices.length/3; i++) {
int vertIndex0 = i*3;
int vertIndex1 = i*3+1;
int vertIndex2 = i*3+2;
Vertex v0 = vertices[vertIndex0];
Vertex v1 = vertices[vertIndex1];
Vertex v2 = vertices[vertIndex2];
tris.add(new Triangle(v0, v1, v2, vertIndex0, vertIndex1, vertIndex2));
}
}
this.triangles = (Triangle[])tris.toArray(new Triangle[tris.size()]);
// System.out.println("MTTriangleMesh object: \"" + this + "\" Debug-> Triangles created: " + this.triangles.length);
}
/**
* Creates an array of normals, 1 for every vertex or index.
* , so every drawn vertex will have a normal information.
* <br>If the geometry is indexed, smooth interpolated normals
* are generated, else face normals are generated.
*
* @return the face or vertex normals
*
* the normals
*/
private Vector3D[] getFaceOrVertexNormals(){
Vector3D[] normals = new Vector3D[this.triangles.length*3];
GeometryInfo geom = this.getGeometryInfo();
if (geom.isIndexed()){
//Create smooth vertex normals, smoothed across all neighbors
int[] indices = geom.getIndices();
normals = new Vector3D[geom.getVertices().length];
for (int i = 0; i < indices.length/3; i++) {
if (normals[indices[i*3]] == null){
normals[indices[i*3]] = triangles[i].getNormalLocal().getCopy();
}else{
normals[indices[i*3]].addLocal(triangles[i].getNormalLocal());
}
if (normals[indices[i*3+1]] == null){
normals[indices[i*3+1]] = triangles[i].getNormalLocal().getCopy();
}else{
normals[indices[i*3+1]].addLocal(triangles[i].getNormalLocal());
}
if (normals[indices[i*3+2]] == null){
normals[indices[i*3+2]] = triangles[i].getNormalLocal().getCopy();
}else{
normals[indices[i*3+2]].addLocal(triangles[i].getNormalLocal());
}
}
for (int i = 0; i < normals.length; i++) {
Vector3D n = normals[i];
if (n == null){
n = new Vector3D(0,0,1);
}else{
n.normalizeLocal();
}
}
}else
{
//Create face normals for unindexed geometry
for (int i = 0; i < this.triangles.length; i++) {
Triangle tri = this.triangles[i];
normals[i*3] = tri.getNormalLocal();
normals[i*3+1] = tri.getNormalLocal();
normals[i*3+2] = tri.getNormalLocal();
}
}
// System.out.println("MTTriangleMesh Debug-> Normals created: " + normals.length);
return normals;
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#getCenterPointLocal()
*/
@Override
public Vector3D getCenterPointLocal() {
if (this.hasBounds()){
return this.getBounds().getCenterPointLocal();
}else{
BoundingSphere tempBounds = new BoundingSphere(this);
return tempBounds.getCenterPointLocal();
}
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#isGeometryContainsPointLocal(org.mt4j.util.math.Vector3D)
*/
@Override
public boolean isGeometryContainsPointLocal(Vector3D testPoint) {
// Point to local space - already done!
// testPoint.transform(this.getGlobalInverseMatrix());
//Send ray from the test point in x, y, and z direction
//and count the intersections (this is not really sufficient!)
Ray ray0 = new Ray(new Vector3D(testPoint), new Vector3D(1,0,0));
Ray ray1 = new Ray(new Vector3D(testPoint), new Vector3D(0,1,0));
Ray ray2 = new Ray(new Vector3D(testPoint), new Vector3D(0,0,1));
int i0 = this.getNumIntersections(ray0);
int i1 = this.getNumIntersections(ray1);
int i2 = this.getNumIntersections(ray2);
/*
System.out.println("I0:" + i0);
System.out.println("I1:" + i1);
System.out.println("I2:" + i2);
*/
//Check if intersection count is odd -> inside
return ((i0 & 1 ) != 0)
&& ((i1 & 1 ) != 0)
&& ((i2 & 1 ) != 0);
}
/**
* Check how often the ray intersects with the meshes triangles.
*
* @param ray the ray
*
* @return the number of intersecitons
*/
private int getNumIntersections(Ray ray){
//Evtl auch bbox pr�fen?
int intersectionsFound = 0;
//Save intersections to check duplicates because at an edge
//bewteeen 2 triangles both intersections are counted but
//we still can say we�re on the inside
boolean checkThoroughly = true;
ArrayList<Vector3D> intersections = new ArrayList<Vector3D>();
for (int i = 0; i < triangles.length; i++) {
Triangle tri = triangles[i];
Vector3D intersectionPoint = tri.getRayTriangleIntersection(ray);
boolean sameAlreadyEncountered = false;
if (intersectionPoint != null){
if (checkThoroughly){
for(Vector3D v: intersections){
if (v.equalsVectorWithTolerance(intersectionPoint, ToolsMath.ZERO_TOLERANCE)){
sameAlreadyEncountered = true;
}
}
}
if (!sameAlreadyEncountered){
intersections.add(intersectionPoint);
intersectionsFound++;
}
}
}
intersections = null; //Clean
return intersectionsFound;
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#getGeometryIntersectionLocal(org.mt4j.util.math.Ray)
*/
@Override
public Vector3D getGeometryIntersectionLocal(Ray ray){
float distance = Float.MAX_VALUE;
Vector3D returnVect = null;
for (int i = 0; i < triangles.length; i++) {
Triangle tri = triangles[i];
Vector3D intersectionPoint = tri.getRayTriangleIntersection(ray);
if (intersectionPoint != null){
float objDistance = intersectionPoint.getSubtracted(ray.getRayStartPoint()).length();
//It is accurate to go through all triangles and use the closest intersection
//but slower as just returning the first..
if (objDistance <= distance ){
distance = objDistance;
returnVect = intersectionPoint;
}
}
}
/*
if (returnVect != null){
System.out.println("Picked mesh: " + this.getName());
}
*/
return returnVect;
}
/**
* Gets the triangle count.
*
* @return the triangle count
*
* number of triangles in this mesh
*/
public int getTriangleCount(){
return triangles.length;
}
/**
* The triangles of this triangle mesh.
*
* @return the triangles
*/
public Triangle[] getTriangles(){
if (this.triangles == null){
this.createTriangles(this.getGeometryInfo());
return this.triangles;
}else{
return this.triangles;
}
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.AbstractVisibleComponent#drawComponent(processing.core.PGraphics)
*/
@Override
public void drawComponent(PGraphics g) {
PApplet pa = this.getRenderer();
if (this.isUseDirectGL()){
GL gl=((PGraphicsOpenGL)g).beginGL();
this.drawComponent(gl);
((PGraphicsOpenGL)this.getRenderer().g).endGL();
}else{ //Draw with pure proccessing...
pa.strokeWeight(this.getStrokeWeight());
// if (ConstantsAndSettings.getInstance().isOpenGlMode()){
if (this.isDrawSmooth())
pa.smooth();
else
pa.noSmooth();
// }
//NOTE: if noFill() and noStroke()->absolutely nothing will be drawn-even when texture is set
if (this.isNoFill())
pa.noFill();
else{
MTColor fillColor = this.getFillColor();
pa.fill(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
}
if (this.isNoStroke())
pa.noStroke();
else{
MTColor strokeColor = this.getStrokeColor();
pa.stroke(strokeColor.getR(), strokeColor.getG(), strokeColor.getB(), strokeColor.getAlpha());
}
//Set the tint values
MTColor fillColor = this.getFillColor();
pa.tint(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
//handles the drawing of the vertices with the texture coordinates
//try doing a smoothed poly outline with opengl
// if (
//// ConstantsAndSettings.getInstance().isOpenGlMode() &&
// this.isDrawSmooth() &&
// !this.isNoStroke()
// ){
// pa.noStroke();
// pa.noSmooth();
//
// //draw insided of polygon, without smooth or stroke
// this.drawWithProcessing(pa, this.getVerticesObjSpace(), PApplet.TRIANGLES, true);
//
// pa.smooth();
// pa.noFill();
// pa.stroke(this.getStrokeRed(), this.getStrokeGreen(), this.getStrokeBlue(), this.getStrokeAlpha());
//
// // DRAW SMOOTHED THE OUTLINE SHAPE OF THE POLYGON WIHTOUT FILL OR TEXTURE
//// drawWithProcessing(pa);
//
// for (Vertex[] outline : this.outlineContours){
// this.drawWithProcessing(pa, outline, PApplet.LINE, false);
// }
// }else{
if (!this.isNoStroke()){
pa.noFill();
MTColor strokeColor = this.getStrokeColor();
pa.stroke(strokeColor.getR(), strokeColor.getG(), strokeColor.getB(), strokeColor.getAlpha());
pa.strokeWeight(2);
if (this.isDrawSmooth())
pa.smooth();
for (Vertex[] outline : this.outlineContours){
this.drawWithProcessing(pa, outline, PApplet.POLYGON, false);
}
}
if (!this.isNoFill()){
pa.noStroke();
pa.noSmooth();
pa.fill(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
this.drawWithProcessing(pa, this.getVerticesLocal(), PApplet.TRIANGLES, true);
}
// }//end if gl and smooth
//reSet the tint values to defaults
pa.tint(255, 255, 255, 255);
if (/*ConstantsAndSettings.getInstance().isOpenGlMode() && */
this.isDrawSmooth())
pa.noSmooth(); //because of tesselation bug
}
if (drawNormals)
this.drawNormals();
}
/**
* Can be used to draw if the gl context has already been set up and is ready to use.
* (processingApplet.beginGL() has been called etc..)
*
* @param gl the gl
*/
public void drawComponent(GL gl) {
if (this.isUseDisplayList()){
int[] displayLists = this.getGeometryInfo().getDisplayListIDs();
if (!this.isNoFill()){
gl.glCallList(displayLists[0]);
}
if (!this.isNoStroke()){
if (this.outlineContours != null){
gl.glCallList(displayLists[1]);
}
}
}else{
if (this.isNoFill() && this.isNoStroke()){
return;
}else{
this.drawPureGl(gl);
}
}
}
/**
* loops through all the vertices of the polygon
* and uses processings "vertex()" command to set their position
* and texture.
*
* @param p the p
* @param vertices the vertices
* @param drawMode the draw mode
* @param useTexture the use texture
*/
private void drawWithProcessing(PApplet p, Vertex[] vertices, int drawMode, boolean useTexture){
p.beginShape(drawMode);
if (this.getTexture() != null && this.isTextureEnabled() && useTexture){
p.texture(this.getTexture());
p.textureMode(this.getTextureMode());
}
if (this.getGeometryInfo().isIndexed()){
int[] indices = this.getGeometryInfo().getIndices();
for (int i = 0; i < indices.length; i++) {
int index = indices[i];
drawP5Vertex(p, vertices[index], useTexture);
}
}
else{
for (int i = 0; i < vertices.length; i++) {
drawP5Vertex(p, vertices[i], useTexture);
}
}
p.endShape();
}
/**
* Draw p5 vertex.
*
* @param p the p
* @param v the v
* @param useTexture the use texture
*/
private void drawP5Vertex(PApplet p, Vertex v, boolean useTexture){
if (this.isTextureEnabled() && useTexture){
p.vertex(v.x, v.y, v.z, v.getTexCoordU(), v.getTexCoordV());
}else{
if (v.getType() == Vector3D.BEZIERVERTEX){
BezierVertex b = (BezierVertex)v;
p.bezierVertex(
b.getFirstCtrlPoint().x, b.getFirstCtrlPoint().y, b.getFirstCtrlPoint().z,
b.getSecondCtrlPoint().x, b.getSecondCtrlPoint().y, b.getSecondCtrlPoint().z,
b.x, b.y, b.z );
}
else{
p.vertex(v.x, v.y, v.z);
}
}
}
/**
* Draws the face normals of the mesh.
*/
private void drawNormals(){
PApplet r = this.getRenderer();
// Vector3D[] normals = this.getGeometryInfo().getNormals();
r.stroke(255, 0, 0);
r.strokeWeight(0.5f);
for (int i = 0; i < triangles.length; i++) {
Triangle t = triangles[i];
r.pushMatrix();
Vector3D centerPoint = t.getCenterPointLocal();
r.translate(centerPoint.x, centerPoint.y, centerPoint.z);
r.scale(-2,-2,-2);
r.line(0, 0, 0, t.getNormalLocal().x, t.getNormalLocal().y, t.getNormalLocal().z);
r.popMatrix();
}
// for (int i = 0; i < normals.length; i++) {
// Vector3D vector3D = normals[i];
// }
}
/**
* Draws the mesh with ogl functions.
*
* @param gl the gl
*/
private void drawPureGl(GL gl){
// /*
//Get display array/buffer pointers
FloatBuffer tbuff = this.getGeometryInfo().getTexBuff();
FloatBuffer vertBuff = this.getGeometryInfo().getVertBuff();
FloatBuffer colorBuff = this.getGeometryInfo().getColorBuff();
IntBuffer indexBuff = this.getGeometryInfo().getIndexBuff(); //null if not indexed
//Enable Pointers, set vertex array pointer
gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL.GL_COLOR_ARRAY);
if (this.isUseVBOs()){//Vertices
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOVerticesName());
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
}else{
gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertBuff);
}
//Default texture target
int textureTarget = GL.GL_TEXTURE_2D;
/////// DRAW SHAPE ///////
if (!this.isNoFill()){
boolean textureDrawn = false;
if (this.isTextureEnabled()
&& this.getTexture() != null
&& this.getTexture() instanceof GLTexture) //Bad for performance?
{
GLTexture tex = (GLTexture)this.getTexture();
textureTarget = tex.getTextureTarget();
//tells opengl which texture to reference in following calls from now on!
//the first parameter is eigher GL.GL_TEXTURE_2D or ..1D
gl.glEnable(textureTarget);
gl.glBindTexture(textureTarget, tex.getTextureID());
gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
if (this.isUseVBOs()){//Texture
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOTextureName());
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
}else{
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, tbuff);
}
textureDrawn = true;
}
// Normals
if (this.getGeometryInfo().isContainsNormals()){
gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBONormalsName());
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
}else{
gl.glNormalPointer(GL.GL_FLOAT, 0, this.getGeometryInfo().getNormalsBuff());
}
}
if (this.isUseVBOs()){//Color
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOColorName());
gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
}else{
gl.glColorPointer(4, GL.GL_FLOAT, 0, colorBuff);
}
//DRAW with drawElements if geometry is indexed, else draw with drawArrays!
if (this.getGeometryInfo().isIndexed()){
gl.glDrawElements(this.getFillDrawMode(), indexBuff.capacity(), GL.GL_UNSIGNED_INT, indexBuff); //limit() oder capacity()??
/*
int error = gl.glGetError();
if (error != GL.GL_NO_ERROR){
System.out.println("GL Error: " + error);
}else{
System.out.println("No gl error.");
}
*/
}else{
gl.glDrawArrays(this.getFillDrawMode(), 0, vertBuff.capacity()/3);
}
if (this.getGeometryInfo().isContainsNormals()){
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}
if (textureDrawn){
gl.glBindTexture(textureTarget, 0);//Unbind texture
gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(textureTarget); //weiter nach unten?
}
}
boolean outlineDrawn = false;
////////// DRAW OUTLINE ////////
if (!this.isNoStroke()
&& this.outlineBuffers != null //FIXME EXPERIMENT
&& this.outlineContours != null
){
outlineDrawn = true;
// FloatBuffer strokeColBuff = this.getStrokeColBuff();
// if (this.isUseVBOs()){
// gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getVBOStrokeColorName());
// gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
//
// gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);//Experimental
// }else{
// gl.glColorPointer(4, GL.GL_FLOAT, 0, strokeColBuff);
// }
//Turn on smooth outlines
// if (this.isDrawSmooth())
// gl.glEnable(GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, true);
// /*
//SET LINE STIPPLE
short lineStipple = this.getLineStipple();
if (lineStipple != 0){
gl.glLineStipple(1, lineStipple);
gl.glEnable(GL.GL_LINE_STIPPLE);
}
// */
if (this.getStrokeWeight() > 0)
gl.glLineWidth(this.getStrokeWeight());
//Dont use geometryinfo strokecolor buffer because its useless in a trianglemesh
//instead we use a single, simple stroke color and custom outlines, if provided
gl.glDisableClientState(GL.GL_COLOR_ARRAY); //disable color buffer use
gl.glColor4f(strokeR, strokeG, strokeB, strokeA);
//Always use just buffes and drawarrays instead of vbos..too complicated for a simple outline..
for(FloatBuffer outlineBuffer : this.outlineBuffers){ //FIXME EXPERIMENTAL
gl.glVertexPointer(3, GL.GL_FLOAT, 0, outlineBuffer);
// gl.glDrawArrays(GL.GL_LINES, 0, outlineBuffer.capacity()/3);
// gl.glDrawArrays(GL.GL_LINE_LOOP, 0, outlineBuffer.capacity()/3);
gl.glDrawArrays(GL.GL_LINE_STRIP, 0, outlineBuffer.capacity()/3);
}
/*
//DRAW mesh outline
//Draw with drawElements if geometry is indexed, else draw with drawArrays!
if (this.getGeometryInfo().isIndexed()){
gl.glDrawElements(GL.GL_LINES, indexBuff.limit(), GL.GL_UNSIGNED_INT, indexBuff);
// gl.glDrawElements(this.getFillDrawMode(), indexBuff.capacity(), GL.GL_UNSIGNED_INT, indexBuff);
}else{
gl.glDrawArrays(GL.GL_LINES, 0, vertBuff.capacity()/3);
}
*/
//RESET LINE STIPPLE
if (lineStipple != 0){
gl.glDisable(GL.GL_LINE_STIPPLE);
}
// if (this.isDrawSmooth())
// gl.glDisable(GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, false);
}
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
if (!outlineDrawn){
gl.glDisableClientState(GL.GL_COLOR_ARRAY); //If outline drawn we disabled color_array earlier
}
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
}
// */
}
/**
* Checks if is draw normals.
*
* @return true, if is draw normals
*/
public boolean isDrawNormals() {
return drawNormals;
}
/**
* Sets the draw normals.
*
* @param drawNormals the new draw normals
*/
public void setDrawNormals(boolean drawNormals) {
this.drawNormals = drawNormals;
}
@Override
protected void destroyComponent() {
this.triangles = null;
// if (!this.outlineBuffers.isEmpty()){
// outline = getGeometryInfo().getDisplayListIDs()[1];
// }
this.outlineBuffers.clear();
}
@Override
protected void destroyDisplayLists() {
super.destroyDisplayLists();
//System.out.println("Destroying mesh display lists: " + this);
}
/**
* Gets the outline contours.
*
* @return the outline contours
*/
public List<Vertex[]> getOutlineContours() {
return this.outlineContours;
}
/**
* Creates the outline buffers.
*/
private void createOutlineBuffers(){
outlineBuffers.clear();
for (Vertex[] outline : this.outlineContours){
outlineBuffers.add(ToolsBuffers.generateVertexBuffer(outline));
}
}
/** The stroke r. */
private float strokeR = 1;
/** The stroke g. */
private float strokeG = 1;
/** The stroke b. */
private float strokeB = 1;
/** The stroke a. */
private float strokeA = 1;
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#setStrokeColor(org.mt4j.util.MTColor)
*/
@Override
public void setStrokeColor(MTColor strokeColor) {
super.setStrokeColor(strokeColor);
this.strokeR = strokeColor.getR()/255f;
this.strokeG = strokeColor.getG()/255f;
this.strokeB = strokeColor.getB()/255f;
this.strokeA = strokeColor.getAlpha()/255f;
}
/**
* Sets the outline contours.
* <br><strong>NOTE: </strong> To draw the outlines,
* the default setting of noStroke has to be changed to false!
*
* @param contours the new outline contours
*/
public void setOutlineContours(List<Vertex[]> contours) {
this.outlineContours = contours;
this.createOutlineBuffers();
if (this.isUseDisplayList()){
int[] ids = this.getGeometryInfo().getDisplayListIDs();
//Delete default outline display list, not really usable in a mesh.
if (MT4jSettings.getInstance().isOpenGlMode()){
GL gl =((PGraphicsOpenGL)this.getRenderer().g).gl;
if (ids[1] != -1){
gl.glDeleteLists(ids[1], 1);
}
}
if (this.outlineContours != null){
ids[1] = generateContoursDisplayList();
this.getGeometryInfo().setDisplayListIDs(ids);
}
}
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.shapes.AbstractShape#generateDisplayLists()
*/
@Override
public void generateDisplayLists(){
super.generateDisplayLists();
int[] ids = this.getGeometryInfo().getDisplayListIDs();
//Delete default outline display list, not really usable in a mesh. To draw all triangles outlines..
//TODO nicht optimal, da erst eine displaylist erstellt wird, die gleich wieder gel�scht wird..
if (MT4jSettings.getInstance().isOpenGlMode()
&& this.outlineContours != null
){
if (ids[1] != -1){
GL gl =((PGraphicsOpenGL)this.getRenderer().g).gl;
gl.glDeleteLists(ids[1], 1);
}
//Create outline display list from manually set outline contours if available.
ids[1] = this.generateContoursDisplayList();
this.getGeometryInfo().setDisplayListIDs(ids);
}
}
/**
* Gens a displaylists of the outline contours (for svgs important for example, not so much for 3d objects..)
*
* @return the int
*/
private int generateContoursDisplayList(){
GL gl=((PGraphicsOpenGL)this.getRenderer().g).gl;
int listId = gl.glGenLists(1);
if (listId == 0){
System.err.println("Failed to create display list");
return 0;
}
gl.glNewList(listId, GL.GL_COMPILE);
// if (this.isDrawSmooth()){
// gl.glEnable(GL.GL_LINE_SMOOTH);
// }
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, true);
gl.glLineWidth(this.getStrokeWeight());
FloatBuffer strokeColBuff = this.getGeometryInfo().getStrokeColBuff();
gl.glColor4d (strokeColBuff.get(0), strokeColBuff.get(1), strokeColBuff.get(2), strokeColBuff.get(3));
for (Vertex[] varr : this.outlineContours){
gl.glBegin(GL.GL_LINE_STRIP);
for(Vertex v : varr){
gl.glVertex3f(v.x, v.y, v.z);
}
gl.glEnd();
}
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, false);
gl.glEndList();
return listId;
}
/*
private Vector3D[] generateNonIndexedNormals(){
GeometryInfo geom = this.getGeometryInfo();
NonIndexedNormalGenerator n = new NonIndexedNormalGenerator();
Vertex[] vertices = geom.getVerticesLocal();
float[] verts = new float[vertices.length*3];
for (int i = 0; i < verts.length/3; i++) {
verts[i*3] = vertices[i].x;
verts[i*3+1] = vertices[i].y;
verts[i*3+2] = vertices[i].z;
}
float[] normals = n.generateNormals(verts , geom.getIndices(), 90);
Vector3D[] normalVecs = new Vector3D[normals.length/3];
for (int i = 0; i < normalVecs.length; i++) {
normalVecs[i] = new Vector3D(normals[i*3], normals[i*3+1],normals[i*3+2]);
}
return normalVecs;
}
*/
}