Removed sorting when triangulating
Properly determine the insertion edge when triangulation to avoid having to sort each vertex later Synchronize the plane viewer to prevent incorrect index saving when switching planes too quickly Use the shape instead of collision shape for summoned physics objects
This commit is contained in:
@@ -1141,10 +1141,22 @@ public class Mesh< T extends Region > {
|
|||||||
handlers.forEach( h -> h.onPartitionEvent( polygons ) );
|
handlers.forEach( h -> h.onPartitionEvent( polygons ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( mergeChains ) {
|
||||||
return polys.parallelStream()
|
return polys.parallelStream()
|
||||||
.map( Mesh::triangulate )
|
.map( Mesh::triangulate )
|
||||||
.flatMap( p -> p.parallelStream() )
|
.flatMap( p -> p.parallelStream() )
|
||||||
.collect( Collectors.toSet() );
|
.collect( Collectors.toSet() );
|
||||||
|
} else {
|
||||||
|
// If do not merge chains, then at least try to merge some adjacent collinear edges
|
||||||
|
// which may have resulted from partitioning into monotone polygons, since the triangles
|
||||||
|
// probably don't play too nicely with those(0 area triangles).
|
||||||
|
return polys.stream()
|
||||||
|
.peek( p -> mergeAdjacentCollinearEdges( p.getVertices(), p.getEdges() ).forEach( e -> p.getVertices().remove( e.getOrigin() ) ) )
|
||||||
|
.map( Mesh::triangulate )
|
||||||
|
.flatMap( p -> p.parallelStream() )
|
||||||
|
.collect( Collectors.toSet() );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2739,18 +2751,20 @@ public class Mesh< T extends Region > {
|
|||||||
// Go down each monotone chain and connect the vertices where possible.
|
// Go down each monotone chain and connect the vertices where possible.
|
||||||
// Implements the O(n) triangulation of a polygon as described in
|
// Implements the O(n) triangulation of a polygon as described in
|
||||||
// Computation Geometry Algorithms and Applications 3rd Ed.
|
// Computation Geometry Algorithms and Applications 3rd Ed.
|
||||||
final Collection< Vertex > toSort = new HashSet< Vertex >();
|
|
||||||
|
|
||||||
if ( polygon.getEdges().size() < 3 || polygon.getVertices().size() < 3 ) {
|
if ( polygon.getEdges().size() < 3 || polygon.getVertices().size() < 3 ) {
|
||||||
throw new IllegalStateException( "Invalid amount of verts/edges!" );
|
throw new IllegalStateException( "Invalid amount of verts/edges!" );
|
||||||
} else if ( polygon.getEdges().size() == 3 ) {
|
} else if ( polygon.getEdges().size() == 3 ) {
|
||||||
return Arrays.asList( new Polygon( polygon.getVertices().stream().map( v -> new Point( v.getPosition() ) ).toList() ) );
|
return Arrays.asList( new Polygon( polygon.getVertices().stream().map( v -> new Point( v.getPosition() ) ).collect( Collectors.toList() ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of which edge to insert new edges for the given vertex
|
||||||
|
final Map< Vertex, HalfEdge > insertEdges = new HashMap< Vertex, HalfEdge >();
|
||||||
final Queue< HalfEdge > edges = new PriorityQueue< HalfEdge >( ( a, b ) -> {
|
final Queue< HalfEdge > edges = new PriorityQueue< HalfEdge >( ( a, b ) -> {
|
||||||
return compare( a.getOrigin(), b.getOrigin() );
|
return compare( a.getOrigin(), b.getOrigin() );
|
||||||
} );
|
} );
|
||||||
edges.addAll( polygon.getEdges() );
|
edges.addAll( polygon.getEdges() );
|
||||||
|
edges.forEach( e -> insertEdges.put( e.getOrigin(), e ) );
|
||||||
|
|
||||||
final Stack< HalfEdge > stack = new Stack< HalfEdge >();
|
final Stack< HalfEdge > stack = new Stack< HalfEdge >();
|
||||||
// Add the first two vertices/edges
|
// Add the first two vertices/edges
|
||||||
@@ -2761,49 +2775,72 @@ public class Mesh< T extends Region > {
|
|||||||
while ( edges.size() > 1 ) {
|
while ( edges.size() > 1 ) {
|
||||||
final HalfEdge edge = edges.poll();
|
final HalfEdge edge = edges.poll();
|
||||||
|
|
||||||
if ( isPositive( edge ) ^ isPositive( stack.peek() ) ) {
|
final boolean isEdgePositive = isPositive( edge );
|
||||||
|
if ( isEdgePositive ^ isPositive( stack.peek() ) ) {
|
||||||
// Insert an edge from the current event to each vertex in the stack
|
// Insert an edge from the current event to each vertex in the stack
|
||||||
HalfEdge current = stack.pop();
|
HalfEdge current = stack.pop();
|
||||||
|
// Keep track of the latest edge that we can splice to
|
||||||
|
HalfEdge insertEdge = edge;
|
||||||
while ( !stack.isEmpty() ) {
|
while ( !stack.isEmpty() ) {
|
||||||
final HalfEdge newEdge = new HalfEdge();
|
final HalfEdge newEdge = new HalfEdge();
|
||||||
|
|
||||||
newEdge.setOrigin( edge.getOrigin() );
|
newEdge.setOrigin( edge.getOrigin() );
|
||||||
newEdge.getSym().setOrigin( current.getOrigin() );
|
newEdge.getSym().setOrigin( current.getOrigin() );
|
||||||
|
|
||||||
|
// Find the correct edge to insert onto for the edge in the stack
|
||||||
|
// If the current edge is positive, then the stack edge must be negative,
|
||||||
|
// and vice versa. In addition, the edge in the stack may already have
|
||||||
|
// some edges added to it previously.
|
||||||
|
if ( isEdgePositive ) {
|
||||||
|
HalfEdge.splice( newEdge, insertEdge );
|
||||||
|
HalfEdge.splice( newEdge.getSym(), insertEdges.get( current.getOrigin() ) );
|
||||||
|
|
||||||
|
insertEdges.put( current.getOrigin(), newEdge.getSym() );
|
||||||
|
} else {
|
||||||
HalfEdge.splice( newEdge, edge );
|
HalfEdge.splice( newEdge, edge );
|
||||||
HalfEdge.splice( newEdge.getSym(), current );
|
HalfEdge.splice( newEdge.getSym(), insertEdges.get( current.getOrigin() ) );
|
||||||
|
}
|
||||||
|
insertEdge = newEdge;
|
||||||
|
|
||||||
polygon.addEdge( newEdge );
|
polygon.addEdge( newEdge );
|
||||||
|
|
||||||
toSort.add( edge.getOrigin() );
|
|
||||||
toSort.add( current.getOrigin() );
|
|
||||||
|
|
||||||
current = stack.pop();
|
current = stack.pop();
|
||||||
}
|
}
|
||||||
|
if ( !isEdgePositive ) {
|
||||||
|
insertEdges.put( edge.getOrigin(), edge.getSym().getNext().getSym().getNext() );
|
||||||
|
}
|
||||||
|
|
||||||
stack.push( prev );
|
stack.push( prev );
|
||||||
} else {
|
} else {
|
||||||
HalfEdge current = stack.pop();
|
HalfEdge current = stack.pop();
|
||||||
final boolean isPositive = isPositive( edge );
|
HalfEdge leftEdge = isEdgePositive ? edge.getPrev() : edge;
|
||||||
HalfEdge leftEdge = isPositive ? edge.getPrev() : edge;
|
HalfEdge insertEdge = edge;
|
||||||
while ( !stack.isEmpty() ) {
|
while ( !stack.isEmpty() ) {
|
||||||
final HalfEdge next = stack.peek();
|
final HalfEdge next = stack.peek();
|
||||||
|
|
||||||
final Vector2d diagonal = next.getOrigin().getPosition().subtracted( edge.getOrigin().getPosition() );
|
final Vector2d diagonal = next.getOrigin().getPosition().subtracted( edge.getOrigin().getPosition() ).normalize();
|
||||||
final double cross = diagonal.cross( leftEdge.toVector2d() );
|
final double cross = diagonal.cross( leftEdge.toVector2d().normalize() );
|
||||||
|
|
||||||
// Check if the diagonal is inside the polygon
|
// Check if the diagonal is inside the polygon... Use some tolerance
|
||||||
final boolean isInside = isPositive ? cross > 0 : cross < 0;
|
final boolean isInside = isEdgePositive ? cross > CROSS_TOLERANCE : cross < -CROSS_TOLERANCE;
|
||||||
if ( isInside ) {
|
if ( isInside ) {
|
||||||
final HalfEdge newEdge = new HalfEdge();
|
final HalfEdge newEdge = new HalfEdge();
|
||||||
|
|
||||||
newEdge.setOrigin( edge.getOrigin() );
|
newEdge.setOrigin( edge.getOrigin() );
|
||||||
newEdge.getSym().setOrigin( next.getOrigin() );
|
newEdge.getSym().setOrigin( next.getOrigin() );
|
||||||
|
|
||||||
|
if ( isEdgePositive ) {
|
||||||
HalfEdge.splice( newEdge, edge );
|
HalfEdge.splice( newEdge, edge );
|
||||||
HalfEdge.splice( newEdge.getSym(), next );
|
HalfEdge.splice( newEdge.getSym(), insertEdges.get( next.getOrigin() ) );
|
||||||
|
|
||||||
toSort.add( edge.getOrigin() );
|
insertEdges.put( next.getOrigin(), newEdge.getSym() );
|
||||||
toSort.add( next.getOrigin() );
|
|
||||||
|
polygon.addEdge( newEdge );
|
||||||
|
} else {
|
||||||
|
HalfEdge.splice( newEdge, insertEdge );
|
||||||
|
HalfEdge.splice( newEdge.getSym(), insertEdges.get( next.getOrigin() ) );
|
||||||
|
}
|
||||||
|
insertEdge = newEdge;
|
||||||
|
|
||||||
polygon.addEdge( newEdge );
|
polygon.addEdge( newEdge );
|
||||||
|
|
||||||
@@ -2814,6 +2851,10 @@ public class Mesh< T extends Region > {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( !isEdgePositive ) {
|
||||||
|
insertEdges.put( edge.getOrigin(), insertEdge );
|
||||||
|
}
|
||||||
|
|
||||||
stack.push( current );
|
stack.push( current );
|
||||||
}
|
}
|
||||||
stack.push( edge );
|
stack.push( edge );
|
||||||
@@ -2821,8 +2862,13 @@ public class Mesh< T extends Region > {
|
|||||||
prev = edge;
|
prev = edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The last edge must be a negative edge, since
|
||||||
|
// the last vertex must terminate the polygon and
|
||||||
|
// therefore have no right-going edges.
|
||||||
final HalfEdge last = edges.poll();
|
final HalfEdge last = edges.poll();
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
final boolean isPositive = isPositive( stack.peek() );
|
||||||
|
HalfEdge insertEdge = last;
|
||||||
while ( stack.size() > 1 ) {
|
while ( stack.size() > 1 ) {
|
||||||
final HalfEdge popped = stack.pop();
|
final HalfEdge popped = stack.pop();
|
||||||
final HalfEdge newEdge = new HalfEdge();
|
final HalfEdge newEdge = new HalfEdge();
|
||||||
@@ -2830,20 +2876,18 @@ public class Mesh< T extends Region > {
|
|||||||
newEdge.setOrigin( last.getOrigin() );
|
newEdge.setOrigin( last.getOrigin() );
|
||||||
newEdge.getSym().setOrigin( popped.getOrigin() );
|
newEdge.getSym().setOrigin( popped.getOrigin() );
|
||||||
|
|
||||||
|
if ( isPositive ) {
|
||||||
HalfEdge.splice( newEdge, last );
|
HalfEdge.splice( newEdge, last );
|
||||||
HalfEdge.splice( newEdge.getSym(), popped );
|
} else {
|
||||||
|
HalfEdge.splice( newEdge, insertEdge );
|
||||||
|
}
|
||||||
|
HalfEdge.splice( newEdge.getSym(), insertEdges.get( popped.getOrigin() ) );
|
||||||
|
|
||||||
|
insertEdge = newEdge;
|
||||||
|
|
||||||
polygon.addEdge( newEdge );
|
polygon.addEdge( newEdge );
|
||||||
|
|
||||||
toSort.add( last.getOrigin() );
|
|
||||||
toSort.add( popped.getOrigin() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lazy solution, just sort the vertices
|
|
||||||
// Eventually we will remove this
|
|
||||||
// TODO Remove this NOW
|
|
||||||
toSort.parallelStream().forEach( v -> sort( v ) );
|
|
||||||
|
|
||||||
// Convert each vertex to a point
|
// Convert each vertex to a point
|
||||||
final Map< Vertex, Point > pointMap = polygon.vertices.parallelStream().collect( Collectors.toMap( Function.identity(), v -> new Point( v.getPosition() ) ) );
|
final Map< Vertex, Point > pointMap = polygon.vertices.parallelStream().collect( Collectors.toMap( Function.identity(), v -> new Point( v.getPosition() ) ) );
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
@@ -83,6 +84,8 @@ public class MeshingTest2 extends JPanel {
|
|||||||
private JLabel statusBar;
|
private JLabel statusBar;
|
||||||
private JComboBox< String > selectionBox;
|
private JComboBox< String > selectionBox;
|
||||||
|
|
||||||
|
private ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||||
@@ -129,7 +132,7 @@ public class MeshingTest2 extends JPanel {
|
|||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
List< Plane > masterPlanes = new ArrayList< Plane >();
|
final List< Plane > masterPlanes = new ArrayList< Plane >();
|
||||||
System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
||||||
int limit = 1;
|
int limit = 1;
|
||||||
for ( File file : CHUNK_DIR.listFiles() ) {
|
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||||
@@ -140,8 +143,9 @@ public class MeshingTest2 extends JPanel {
|
|||||||
System.out.println( "Remaining: " + limit );
|
System.out.println( "Remaining: " + limit );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) );
|
|
||||||
// masterPlanes.add( planes.get( 177 ) );
|
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-1,-1" ) );
|
||||||
|
// masterPlanes.add( planes.get( 20 ) );
|
||||||
// masterPlanes.addAll( planes );
|
// masterPlanes.addAll( planes );
|
||||||
|
|
||||||
// masterPlanes.add( getTestPlane() );
|
// masterPlanes.add( getTestPlane() );
|
||||||
@@ -679,23 +683,24 @@ public class MeshingTest2 extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MeshingTest2 setIndex( final int index ) {
|
private MeshingTest2 setIndex( final int index ) {
|
||||||
// TODO Synchronize this
|
|
||||||
final int planeCount = planes.size();
|
final int planeCount = planes.size();
|
||||||
final int newIndex = ( ( ( index % planeCount ) + planeCount ) % planeCount );
|
final int newIndex = ( ( ( index % planeCount ) + planeCount ) % planeCount );
|
||||||
|
lock.lock();
|
||||||
if ( this.index != newIndex ) {
|
if ( this.index != newIndex ) {
|
||||||
this.index = newIndex;
|
this.index = newIndex;
|
||||||
|
|
||||||
new Thread( () -> { getData( this::repaint ); } ).start();
|
|
||||||
|
|
||||||
if ( selectionBox != null ) {
|
if ( selectionBox != null ) {
|
||||||
selectionBox.setSelectedIndex( newIndex );
|
selectionBox.setSelectedIndex( newIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new Thread( () -> { getData( this.index, this::repaint ); } ).start();
|
||||||
}
|
}
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlaneData getData( final Runnable callback ) {
|
private PlaneData getData( final int index, final Runnable callback ) {
|
||||||
if ( data[ index ] != null ) {
|
if ( data[ index ] != null ) {
|
||||||
callback.run();
|
callback.run();
|
||||||
return data[ index ];
|
return data[ index ];
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import java.util.List;
|
|||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
||||||
@@ -31,7 +30,8 @@ public class MeshingTest3 {
|
|||||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||||
for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
||||||
final File file = CHUNK_DIR.listFiles()[ i ];
|
final File file = CHUNK_DIR.listFiles()[ i ];
|
||||||
System.out.println( "Meshing " + file + "\t" + ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length );
|
System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file );
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
final List< Plane > planes = mesh( file );
|
final List< Plane > planes = mesh( file );
|
||||||
planes.parallelStream().forEach( p -> {
|
planes.parallelStream().forEach( p -> {
|
||||||
try {
|
try {
|
||||||
@@ -42,15 +42,19 @@ public class MeshingTest3 {
|
|||||||
System.exit( 1 );
|
System.exit( 1 );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
long time = System.currentTimeMillis() - start;
|
||||||
|
|
||||||
|
System.out.println( "\tTook " + time + "ms" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) ); // 123
|
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-1,-1" ) ); // 123
|
||||||
// for ( int i = 0; i < planes.size(); ++i ) {
|
// for ( int i = 0; i < planes.size(); ++i ) {
|
||||||
// System.out.println( "Meshing plane " + i );
|
// System.out.println( "Meshing plane " + i );
|
||||||
// process( planes.get( i ) );
|
// process( planes.get( i ) );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// process( planes.get( 177 ) );
|
// process( planes.get( 20 ) );
|
||||||
} else {
|
} else {
|
||||||
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||||
}
|
}
|
||||||
@@ -74,7 +78,7 @@ public class MeshingTest3 {
|
|||||||
String[] split = name.split( "," );
|
String[] split = name.split( "," );
|
||||||
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||||
|
|
||||||
System.out.println( "Parsing chunk " + location );
|
System.out.println( "\tParsing chunk " + location );
|
||||||
|
|
||||||
// First parse the file to get a list of all bounding boxes that we can use
|
// First parse the file to get a list of all bounding boxes that we can use
|
||||||
List< AABB > boxes = new ArrayList< AABB >();
|
List< AABB > boxes = new ArrayList< AABB >();
|
||||||
@@ -100,7 +104,7 @@ public class MeshingTest3 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println( "Found " + boxes.size() + " boxes" );
|
System.out.println( "\tFound " + boxes.size() + " boxes" );
|
||||||
// Now that we have a bunch of bounding boxes, do whatever
|
// Now that we have a bunch of bounding boxes, do whatever
|
||||||
|
|
||||||
MeshBuilder builder = new MeshBuilder();
|
MeshBuilder builder = new MeshBuilder();
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ public class MiniePlugin extends JavaPlugin {
|
|||||||
displayData = Material.TNT.createBlockData();
|
displayData = Material.TNT.createBlockData();
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox[] boxes = convertFrom( getShape( displayData, null, BlockShapeType.VISUAL_SHAPE ) );
|
BoundingBox[] boxes = convertFrom( getShape( displayData, null, BlockShapeType.SHAPE ) );
|
||||||
Vector blockCenter = calculateCenter( boxes );
|
Vector blockCenter = calculateCenter( boxes );
|
||||||
|
|
||||||
CollisionShape box;
|
CollisionShape box;
|
||||||
|
|||||||
Reference in New Issue
Block a user