Improve tessellation algorithm
Added new mesh class Rewrite mesh algorithms to be less error prone Make code more concise Tried to add some documentation Added half edge set
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.EdgeSet;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.HalfEdge;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.EdgePolygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vertex;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple.GluWindingRule;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
|
||||
|
||||
public class MeshingTest2 extends JPanel {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
||||
|
||||
JFrame f;
|
||||
|
||||
private int windowWidth = 1200;
|
||||
private int windowHeight = 1000;
|
||||
|
||||
// private int centerX = 600;
|
||||
// private int centerY = 400;
|
||||
|
||||
private int centerX = 400;
|
||||
private int centerY = 1200;
|
||||
|
||||
private Graphics g;
|
||||
|
||||
private int scale = 8;
|
||||
|
||||
private Collection< Vertex > data;
|
||||
private Collection< EdgePolygon > polygons;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||
Collection< Plane > planes = mesh( file );
|
||||
|
||||
Plane draw = null;
|
||||
|
||||
System.out.println( "Planes: " + planes.size() );
|
||||
for ( Plane plane : planes ) {
|
||||
if ( plane.polygons.size() > 100 ) {
|
||||
draw = plane;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// draw = planes.get( new Random().nextInt( planes.size() ) );
|
||||
|
||||
// test( planes );
|
||||
|
||||
final Collection< Vertex > data = process( draw );
|
||||
final Collection< EdgePolygon > polys = triangulate( draw );
|
||||
// final Collection< EdgePolygon > polys = test();
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new MeshingTest2( data, polys );
|
||||
}
|
||||
} );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< Plane > mesh( File file ) {
|
||||
String name = file.getName();
|
||||
String[] split = name.split( "," );
|
||||
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||
|
||||
System.out.println( "Parsing chunk " + location );
|
||||
|
||||
// First parse the file to get a list of all bounding boxes that we can use
|
||||
List< AABB > boxes = new ArrayList< AABB >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( "," );
|
||||
double minX = Double.parseDouble( values[ 0 ] );
|
||||
double minY = Double.parseDouble( values[ 1 ] );
|
||||
double minZ = Double.parseDouble( values[ 2 ] );
|
||||
double maxX = Double.parseDouble( values[ 3 ] );
|
||||
double maxY = Double.parseDouble( values[ 4 ] );
|
||||
double maxZ = Double.parseDouble( values[ 5 ] );
|
||||
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
System.out.println( "Found " + boxes.size() + " boxes" );
|
||||
// Now that we have a bunch of bounding boxes, do whatever
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
Plane draw = null;
|
||||
for ( AABB box : boxes ) {
|
||||
for ( Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
}
|
||||
|
||||
return builder.planes;
|
||||
}
|
||||
|
||||
private static void test( final Collection< Plane > planes ) {
|
||||
final long processAllStart = System.currentTimeMillis();
|
||||
planes.parallelStream().forEach( plane -> {
|
||||
try {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
mesh.generateRegions();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println( plane.polygons.size() + ":\t " + ( end - start ) + "ms" );
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} );
|
||||
final long processAllEnd = System.currentTimeMillis();
|
||||
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
|
||||
}
|
||||
|
||||
private static Collection< Vertex > process( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
System.out.println( "Norm:\t" + plane.normal );
|
||||
System.out.println( "Ref:\t" + plane.point );
|
||||
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
|
||||
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
|
||||
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
mesh.generateRegions();
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
|
||||
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||
return mesh.getVertices();
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > triangulate( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
System.out.println( "Norm:\t" + plane.normal );
|
||||
System.out.println( "Ref:\t" + plane.point );
|
||||
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
|
||||
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
|
||||
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
mesh.generateRegions();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
|
||||
final Collection< EdgePolygon > polys = mesh.mesh();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||
|
||||
return polys;
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > test() {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
mesh.simplify();
|
||||
|
||||
mesh.generateRegions();
|
||||
|
||||
return mesh.mesh();
|
||||
}
|
||||
|
||||
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||
List< Facet > facets = new ArrayList< Facet >();
|
||||
|
||||
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
||||
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
||||
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
||||
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
||||
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
||||
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
||||
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
||||
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p3 );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p5 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p6 );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
return facets;
|
||||
}
|
||||
|
||||
static class AABB {
|
||||
double xmin, ymin, zmin;
|
||||
double xmax, ymax, zmax;
|
||||
|
||||
AABB( double xmin, double ymin, double zmin, double xmax, double ymax, double zmax ) {
|
||||
this.xmin = xmin;
|
||||
this.ymin = ymin;
|
||||
this.zmin = zmin;
|
||||
|
||||
this.xmax = xmax;
|
||||
this.ymax = ymax;
|
||||
this.zmax = zmax;
|
||||
}
|
||||
|
||||
double getVolume() {
|
||||
return ( xmax - xmin ) * ( ymax - ymin ) * ( zmax - zmin );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format( "(%f, %f, %f) -> (%f, %f, %f)", xmin, ymin, zmin, xmax, ymax, zmax );
|
||||
}
|
||||
}
|
||||
|
||||
public MeshingTest2( Collection< Vertex > polys, Collection< EdgePolygon > polygons ) {
|
||||
data = polys;
|
||||
this.polygons = polygons;
|
||||
|
||||
f = new JFrame( "Drawing Board" );
|
||||
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
|
||||
f.add( this );
|
||||
|
||||
f.setSize( windowWidth, windowHeight );
|
||||
f.setVisible( true );
|
||||
f.setResizable( true );
|
||||
|
||||
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
}
|
||||
|
||||
public void drawPoint( double x, double y ) {
|
||||
g.fillRect( ( int ) x * scale, ( int ) y * scale, scale, scale );
|
||||
}
|
||||
|
||||
private static Vector2d rotate( Vector2d a, double angle ) {
|
||||
double cos = Math.cos( angle );
|
||||
double sin = Math.sin( angle );
|
||||
double ax = a.getX() * cos - a.getY() * sin;
|
||||
double ay = a.getX() * sin + a.getY() * cos;
|
||||
return new Vector2d( ax, ay );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent( Graphics g ) {
|
||||
super.paintComponent( g );
|
||||
this.g = g;
|
||||
|
||||
g.setColor( new Color( 200, 200, 200 ) );
|
||||
g.drawLine( centerX, 0, centerX, 2000 );
|
||||
g.drawLine( 0, centerY, 2000, centerY );
|
||||
g.setColor( Color.BLACK );
|
||||
|
||||
if ( data != null ) {
|
||||
Collection< Vertex > polygons = data;
|
||||
|
||||
EdgeSet edges = new EdgeSet();
|
||||
for ( Vertex vert : polygons ) {
|
||||
for ( HalfEdge edge : vert ) {
|
||||
if ( edges.add( edge ) ) {
|
||||
Point p1 = edge.getOrigin().getPosition();
|
||||
Point p2 = edge.getDest().getPosition();
|
||||
g.drawLine(
|
||||
( int ) ( p1.getX() * scale ) + centerX,
|
||||
( int ) ( p1.getY() * scale ) + centerY,
|
||||
( int ) ( p2.getX() * scale ) + centerX,
|
||||
( int ) ( p2.getY() * scale ) + centerY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final double diff = scale * 0.3;
|
||||
final Point point = vert.getPosition();
|
||||
g.drawRect( ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( polygons != null ) {
|
||||
System.out.println( "Polygon count: " + polygons.size() );
|
||||
g.setColor( Color.BLACK );
|
||||
for ( final EdgePolygon p : polygons ) {
|
||||
final Collection< HalfEdge > edges = p.getEdges();
|
||||
|
||||
for ( HalfEdge edge : edges ) {
|
||||
Point p1 = edge.getOrigin().getPosition();
|
||||
Point p2 = edge.getDest().getPosition();
|
||||
g.drawLine(
|
||||
( int ) ( p1.getX() * scale ) + centerX + 400,
|
||||
( int ) ( p1.getY() * scale ) + centerY,
|
||||
( int ) ( p2.getX() * scale ) + centerX + 400,
|
||||
( int ) ( p2.getY() * scale ) + centerY
|
||||
);
|
||||
}
|
||||
|
||||
// int size = edges.size();
|
||||
// int[] xPoints = new int[ size ];
|
||||
// int[] yPoints = new int[ size ];
|
||||
//
|
||||
// HalfEdge edge = edges.iterator().next();
|
||||
// for ( int i = 0; i < edges.size(); ++i ) {
|
||||
// final Point point = edge.getOrigin().getPosition();
|
||||
// xPoints[ i ] = ( int ) ( point.getX() * scale ) + centerX;
|
||||
// yPoints[ i ] = ( int ) ( point.getY() * scale ) + centerY;
|
||||
// }
|
||||
//
|
||||
// g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
|
||||
// g.fillPolygon( xPoints, yPoints, size );
|
||||
}
|
||||
|
||||
g.setColor( Color.BLACK );
|
||||
for ( final EdgePolygon p : polygons ) {
|
||||
for ( final Vertex v : p.getVertices() ) {
|
||||
final Point point = v.getPosition();
|
||||
|
||||
double diff = scale * .3;
|
||||
g.drawRect( 400 + ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Point[] points = {
|
||||
// new Point( 0, -2 ),
|
||||
// new Point( 1.5, -3.0 ),
|
||||
// new Point( 0.0, -2.0 ),
|
||||
// new Point( 0.9545454545454546, -1.3636363636363638 ),
|
||||
// new Point( -0.8333333333333334, -1.0 ),
|
||||
// new Point( 0.8333333333333334, -1.0 ),
|
||||
// new Point( -0.5, 0.0 ),
|
||||
// new Point( 0.5, 0.0 ),
|
||||
// new Point( -0.16666666666666669, 1.0 ),
|
||||
// new Point( 0.16666666666666669, 1.0 ),
|
||||
// new Point( -0.16666666666666669, 1.0 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( -0.16666666666666669, 1.0 ),
|
||||
// new Point( 0, 1.5 ),
|
||||
// new Point( 0, 1.5 ),
|
||||
// new Point( 0.16666666666666669, 1.0 ),
|
||||
// new Point( -1, 2 ),
|
||||
// new Point( 1, 2 ),
|
||||
// };
|
||||
//
|
||||
// int size = points.length >> 1;
|
||||
// for ( int i = 0; i < size;i++ ) {
|
||||
// Point a = points[ i << 1 ];
|
||||
// Point b = points[ ( i << 1 ) + 1 ];
|
||||
//
|
||||
// g.drawLine( ( int ) ( a.x * scale ) + centerX, ( int ) ( a.y * scale ) + centerY, ( int ) ( b.x * scale ) + centerX, ( int ) ( b.y * scale ) + centerY );
|
||||
// }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user