Change to use facet format for saving and processing chunks
Fixed bug where merging and splitting connected regions may result in a chain desync Added spawn size to minie spawn command Added live chunk generation Use a smarter more conservative facet generation method for each chunk to reduce initial mesh simplification costs
This commit is contained in:
@@ -1248,13 +1248,6 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reference to a collection of links
|
||||
*/
|
||||
class ChainReference {
|
||||
Collection< Link > links = new LinkedHashSet< Link >();
|
||||
}
|
||||
|
||||
/**
|
||||
* A partition represents a monotone section of a polygon at the current
|
||||
* scan line. At any given time, a vertical scan line which passes through
|
||||
@@ -1288,7 +1281,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
// Keep track of all chains that were formed in this current partition.
|
||||
// For all new chains, see what previous chains it may intersect with
|
||||
ChainReference chains = new ChainReference();
|
||||
Collection< Link > chains = new LinkedHashSet< Link >();
|
||||
|
||||
Partition( final HalfEdge lower, final HalfEdge upper ) {
|
||||
this.lower = lower;
|
||||
@@ -1568,7 +1561,7 @@ public class Mesh< T extends Region > {
|
||||
final Vertex chainMidpoint = chain.getMidpoint();
|
||||
final Vertex chainDestination = chain.getDest();
|
||||
|
||||
for ( final Link other : chains.links ) {
|
||||
for ( final Link other : chains ) {
|
||||
if ( other.getOrigin() == chainOrigin && other.getDest() == chainDestination ) {
|
||||
// Already added this chain, so break out early
|
||||
return;
|
||||
@@ -1595,7 +1588,7 @@ public class Mesh< T extends Region > {
|
||||
* This is because it depends on the new link's destination being less than
|
||||
* any other vertex in all the existing links, which should never be possible.
|
||||
*/
|
||||
chains.links.parallelStream().forEach( other -> {
|
||||
chains.parallelStream().forEach( other -> {
|
||||
if ( other.getMidpoint() == chainOrigin && other.getDest() == chainMidpoint ) {
|
||||
// The new chain is a continuation of this chain
|
||||
other.next = chain;
|
||||
@@ -1709,7 +1702,7 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
} );
|
||||
chains.links.add( chain );
|
||||
chains.add( chain );
|
||||
}
|
||||
|
||||
void add( final Vertex vertex, final VisibilityRegion region ) {
|
||||
@@ -1829,7 +1822,7 @@ public class Mesh< T extends Region > {
|
||||
partition.incrementUpper( partition.lower.getSym() );
|
||||
|
||||
// Gather all chains from the partition
|
||||
chainLinks.add( partition.chains.links );
|
||||
chainLinks.add( partition.chains );
|
||||
|
||||
// Remove this partition from the lower edge map as well
|
||||
lowerEdgeMap.remove( partition.lower );
|
||||
@@ -2017,9 +2010,17 @@ public class Mesh< T extends Region > {
|
||||
bottom.incrementUpper( bottomLeft );
|
||||
|
||||
// Merge the refs
|
||||
if ( top.chains.links != bottom.chains.links ) {
|
||||
top.chains.links.addAll( bottom.chains.links );
|
||||
bottom.chains.links = top.chains.links;
|
||||
if ( top.chains != bottom.chains ) {
|
||||
top.chains.addAll( bottom.chains );
|
||||
|
||||
// Find all other partitions which share the same chain reference
|
||||
// and set those to the new total collection
|
||||
for ( final Partition partition : lowerEdgeMap.values() ) {
|
||||
if ( partition.chains == bottom.chains ) {
|
||||
partition.chains = top.chains;
|
||||
}
|
||||
}
|
||||
bottom.chains = top.chains;
|
||||
}
|
||||
|
||||
// Merge the two partitions
|
||||
|
||||
@@ -6,7 +6,10 @@ 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.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -28,11 +31,18 @@ public class MeshingTest3 {
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
||||
final File file = CHUNK_DIR.listFiles()[ i ];
|
||||
System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file );
|
||||
long start = System.currentTimeMillis();
|
||||
final List< Plane > planes = mesh( file );
|
||||
|
||||
final long allStart = System.currentTimeMillis();
|
||||
final AtomicInteger index = new AtomicInteger( 0 );
|
||||
final Collection< File > files = Arrays.asList( CHUNK_DIR.listFiles() );
|
||||
files.parallelStream().forEach( f -> {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( "Meshing file: " + f + "\n" );
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
final List< Plane > planes = mesh( f, builder );
|
||||
|
||||
planes.parallelStream().forEach( p -> {
|
||||
try {
|
||||
process( p );
|
||||
@@ -45,8 +55,32 @@ public class MeshingTest3 {
|
||||
|
||||
long time = System.currentTimeMillis() - start;
|
||||
|
||||
System.out.println( "\tTook " + time + "ms" );
|
||||
}
|
||||
builder.append( "\tTook " + time + "ms\n" );
|
||||
builder.append( "\tCompleted " + index.incrementAndGet() + "/" + files.size() );
|
||||
|
||||
System.out.println( builder.toString() );
|
||||
} );
|
||||
System.out.println( "Took a total of " + ( System.currentTimeMillis() - allStart ) + "ms" );
|
||||
|
||||
// for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
||||
// final File file = CHUNK_DIR.listFiles()[ i ];
|
||||
// System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file );
|
||||
// long start = System.currentTimeMillis();
|
||||
// final List< Plane > planes = mesh( file, null );
|
||||
// planes.parallelStream().forEach( p -> {
|
||||
// try {
|
||||
// process( p );
|
||||
// } catch ( IllegalStateException e ) {
|
||||
// e.printStackTrace();
|
||||
//
|
||||
// System.exit( 1 );
|
||||
// }
|
||||
// } );
|
||||
//
|
||||
// long time = System.currentTimeMillis() - start;
|
||||
//
|
||||
// System.out.println( "\tTook " + time + "ms" );
|
||||
// }
|
||||
|
||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-10,-10" ) ); // 25
|
||||
// for ( int i = 0; i < planes.size(); ++i ) {
|
||||
@@ -73,13 +107,11 @@ public class MeshingTest3 {
|
||||
}
|
||||
}
|
||||
|
||||
private static List< Plane > mesh( File file ) {
|
||||
private static List< Plane > mesh( File file, final StringBuilder stringBuilder ) {
|
||||
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( "\tParsing 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 ) ) ) {
|
||||
@@ -104,7 +136,13 @@ public class MeshingTest3 {
|
||||
return null;
|
||||
}
|
||||
|
||||
System.out.println( "\tFound " + boxes.size() + " boxes" );
|
||||
if ( stringBuilder != null ) {
|
||||
stringBuilder.append( "\tParsing chunk " + location + "\n" );
|
||||
stringBuilder.append( "\tFound " + boxes.size() + " boxes\n" );
|
||||
} else {
|
||||
System.out.println( "\tParsing chunk " + location );
|
||||
System.out.println( "\tFound " + boxes.size() + " boxes" );
|
||||
}
|
||||
// Now that we have a bunch of bounding boxes, do whatever
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -14,7 +15,9 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -37,16 +40,41 @@ public class MeshingTest4 {
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
||||
final File file = CHUNK_DIR.listFiles()[ i ];
|
||||
System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file );
|
||||
long start = System.currentTimeMillis();
|
||||
final List< Plane > planes = mesh( file );
|
||||
savePly2( new File( MODEL_DIR, file.getName() + ".ply" ), planes.parallelStream().flatMap( p -> process( p ).parallelStream().map( p::convert ) ).collect( Collectors.toSet() ) );
|
||||
long time = System.currentTimeMillis() - start;
|
||||
|
||||
// Omega mesh time
|
||||
// omegaMesh( CHUNK_DIR, new File( MODEL_DIR, "omega.ply" ) );
|
||||
|
||||
final long allStart = System.currentTimeMillis();
|
||||
final AtomicInteger index = new AtomicInteger( 0 );
|
||||
final Collection< File > files = Arrays.asList( CHUNK_DIR.listFiles() );
|
||||
files.parallelStream().forEach( f -> {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( "Meshing file: " + f + "\n" );
|
||||
|
||||
System.out.println( "\tTook " + time + "ms" );
|
||||
}
|
||||
final long start = System.currentTimeMillis();
|
||||
final List< Plane > planes = mesh( f, builder );
|
||||
savePly2( new File( MODEL_DIR, f.getName() + ".ply" ), planes.parallelStream().flatMap( p -> process( p ).parallelStream().map( p::convert ) ).collect( Collectors.toSet() ) );
|
||||
|
||||
final long time = System.currentTimeMillis() - start;
|
||||
|
||||
builder.append( "\tTook " + time + "ms\n" );
|
||||
builder.append( "\tCompleted " + index.incrementAndGet() + "/" + files.size() );
|
||||
|
||||
System.out.println( builder.toString() );
|
||||
} );
|
||||
System.out.println( "Took a total of " + ( System.currentTimeMillis() - allStart ) );
|
||||
|
||||
// for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) {
|
||||
// final File file = CHUNK_DIR.listFiles()[ i ];
|
||||
// System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file );
|
||||
// final long start = System.currentTimeMillis();
|
||||
// final List< Plane > planes = mesh( file );
|
||||
// savePly2( new File( MODEL_DIR, file.getName() + ".ply" ), planes.parallelStream().flatMap( p -> process( p ).parallelStream().map( p::convert ) ).collect( Collectors.toSet() ) );
|
||||
// long time = System.currentTimeMillis() - start;
|
||||
//
|
||||
// System.out.println( "\tTook " + time + "ms" );
|
||||
// }
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||
}
|
||||
@@ -156,6 +184,35 @@ public class MeshingTest4 {
|
||||
}
|
||||
}
|
||||
|
||||
private static void omegaMesh( final File directory, final File output ) {
|
||||
final List< AABB > allBoxes = new ArrayList< AABB >();
|
||||
for ( int i = 0; i < directory.listFiles().length; ++i ) {
|
||||
final File file = directory.listFiles()[ i ];
|
||||
System.out.println( ( i + 1 ) + "/" + directory.listFiles().length + ": Getting meshes for: " + file );
|
||||
allBoxes.addAll( getBoxes( file ) );
|
||||
}
|
||||
System.out.println( "Gathered " + allBoxes.size() + " boxes" );
|
||||
final MeshBuilder builder = new MeshBuilder();
|
||||
int boxCount = 0;
|
||||
for ( final AABB box : allBoxes ) {
|
||||
for ( final Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
if ( ++boxCount % 100_000 == 0 ) {
|
||||
System.out.println( "Added box " + boxCount + "/" + allBoxes.size() + "\t" + ( boxCount / ( double ) allBoxes.size() ) + "%" );
|
||||
}
|
||||
}
|
||||
System.out.println( "Starting to mesh " + builder.planes.size() + " planes" );
|
||||
final long start = System.currentTimeMillis();
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
savePly2( output, builder.planes.parallelStream().flatMap( p -> {
|
||||
final Stream< Facet > facets = process( p ).parallelStream().map( p::convert );
|
||||
System.out.println( "Finished " + counter.incrementAndGet() + "/" + builder.planes.size() );
|
||||
return facets;
|
||||
} ).collect( Collectors.toSet() ) );
|
||||
System.out.println( "\tTook " + ( System.currentTimeMillis() - start ) + "ms" );
|
||||
}
|
||||
|
||||
private static Collection< Polygon > process( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
@@ -170,13 +227,11 @@ public class MeshingTest4 {
|
||||
}
|
||||
}
|
||||
|
||||
private static List< Plane > mesh( File file ) {
|
||||
private static List< Plane > mesh( File file, final StringBuilder stringBuilder ) {
|
||||
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( "\tParsing 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 ) ) ) {
|
||||
@@ -209,12 +264,53 @@ public class MeshingTest4 {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println( "\tFound " + boxes.size() + " boxes and " + builder.planes.size() + " planes" );
|
||||
|
||||
if ( stringBuilder != null ) {
|
||||
stringBuilder.append( "\tParsing chunk " + location + "\n" );
|
||||
stringBuilder.append( "\tFound " + boxes.size() + " boxes and " + builder.planes.size() + " planes\n" );
|
||||
} else {
|
||||
System.out.println( "\tParsing chunk " + location );
|
||||
System.out.println( "\tFound " + boxes.size() + " boxes and " + builder.planes.size() + " planes" );
|
||||
}
|
||||
|
||||
return builder.planes;
|
||||
}
|
||||
|
||||
private static List< AABB > getBoxes( final File file ) {
|
||||
final String name = file.getName();
|
||||
final String[] split = name.split( "," );
|
||||
final int chunkX = Integer.parseInt( split[ 1 ] );
|
||||
final int chunkZ = Integer.parseInt( split[ 2 ] );
|
||||
final int blockX = chunkX << 4;
|
||||
final int blockZ = chunkZ << 4;
|
||||
|
||||
// First parse the file to get a list of all bounding boxes that we can use
|
||||
final 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 + blockX, minY, minZ + blockZ, maxX + blockX, maxY, maxZ + blockZ ) );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return Collections.emptyList();
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return boxes;
|
||||
}
|
||||
|
||||
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||
List< Facet > facets = new ArrayList< Facet >();
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
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.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
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 MeshingTest5 {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File FACET_DIR = new File( BASE, "facets" );
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( FACET_DIR.exists() && FACET_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + FACET_DIR.list().length + " files" );
|
||||
|
||||
final long allStart = System.currentTimeMillis();
|
||||
final AtomicInteger index = new AtomicInteger( 0 );
|
||||
final Collection< File > files = Arrays.asList( FACET_DIR.listFiles() );
|
||||
files.parallelStream().forEach( f -> {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( "Meshing file: " + f + "\n" );
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
final List< Plane > planes = mesh( f, builder );
|
||||
|
||||
planes.parallelStream().forEach( p -> {
|
||||
try {
|
||||
process( p );
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
|
||||
System.exit( 1 );
|
||||
}
|
||||
} );
|
||||
|
||||
long time = System.currentTimeMillis() - start;
|
||||
|
||||
builder.append( "\tTook " + time + "ms\n" );
|
||||
builder.append( "\tCompleted " + index.incrementAndGet() + "/" + files.size() );
|
||||
|
||||
System.out.println( builder.toString() );
|
||||
} );
|
||||
System.out.println( "Took a total of " + ( System.currentTimeMillis() - allStart ) + "ms" );
|
||||
|
||||
// for ( int i = 0; i < FACET_DIR.listFiles().length; ++i ) {
|
||||
// final File file = FACET_DIR.listFiles()[ i ];
|
||||
// System.out.println( ( i + 1 ) + "/" + FACET_DIR.listFiles().length + ":\tMeshing " + file );
|
||||
// long start = System.currentTimeMillis();
|
||||
// final List< Plane > planes = mesh( file, null );
|
||||
// planes.parallelStream().forEach( p -> {
|
||||
// try {
|
||||
// process( p );
|
||||
// } catch ( IllegalStateException e ) {
|
||||
// e.printStackTrace();
|
||||
//
|
||||
// System.exit( 1 );
|
||||
// }
|
||||
// } );
|
||||
//
|
||||
// long time = System.currentTimeMillis() - start;
|
||||
//
|
||||
// System.out.println( "\tTook " + time + "ms" );
|
||||
// }
|
||||
|
||||
// final List< Plane > planes = mesh( new File( FACET_DIR, "world,-1,2.facet" ), null ); // 25
|
||||
// for ( int i = 0; i < planes.size(); ++i ) {
|
||||
// System.out.println( "Meshing plane " + i );
|
||||
// process( planes.get( i ) );
|
||||
// }
|
||||
|
||||
// process( planes.get( 73 ) );
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + FACET_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
private static void process( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
|
||||
mesh.meshify();
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
}
|
||||
}
|
||||
|
||||
private static List< Plane > mesh( File file, final StringBuilder stringBuilder ) {
|
||||
final String name = file.getName().substring( 0, file.getName().indexOf( '.' ) );
|
||||
final String[] split = name.split( "," );
|
||||
final ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||
|
||||
// First parse the file to get a list of all facets
|
||||
final List< Facet > facets = new ArrayList< Facet >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( " " );
|
||||
final int facetCount = Integer.parseInt( values[ 0 ] );
|
||||
final double nx = Double.parseDouble( values[ 1 ] );
|
||||
final double ny = Double.parseDouble( values[ 2 ] );
|
||||
final double nz = Double.parseDouble( values[ 3 ] );
|
||||
|
||||
final Facet facet = new Facet();
|
||||
facet.normal = new Vector( nx, ny, nz );
|
||||
|
||||
int point = 0;
|
||||
while ( point < facetCount && ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values2 = line.split( " " );
|
||||
final double px = Double.parseDouble( values2[ 0 ] );
|
||||
final double py = Double.parseDouble( values2[ 1 ] );
|
||||
final double pz = Double.parseDouble( values2[ 2 ] );
|
||||
facet.points.add( new Vector( px, py, pz ) );
|
||||
++point;
|
||||
}
|
||||
}
|
||||
facets.add( facet );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( stringBuilder != null ) {
|
||||
stringBuilder.append( "\tParsing chunk " + location + "\n" );
|
||||
stringBuilder.append( "\tFound " + facets.size() + " facets\n" );
|
||||
} else {
|
||||
System.out.println( "\tParsing chunk " + location );
|
||||
System.out.println( "\tFound " + facets.size() + " facets" );
|
||||
}
|
||||
// Now that we have a bunch of bounding boxes, do whatever
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
for ( Facet facet : facets ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
|
||||
return builder.planes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,985 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Chain;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.MeshEventHandler;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Segment;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||
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 MeshingTest6 extends JPanel {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File FACET_DIR = new File( BASE, "facets" );
|
||||
|
||||
JFrame f;
|
||||
|
||||
// private int windowWidth = 1200;
|
||||
// private int windowHeight = 1000;
|
||||
|
||||
private int windowWidth = 1200;
|
||||
private int windowHeight = 800;
|
||||
|
||||
// private int centerX = 600;
|
||||
// private int centerY = 400;
|
||||
|
||||
private int offsetX = windowWidth >> 1;
|
||||
private int offsetY = windowHeight >> 1;
|
||||
|
||||
private double centerX = 0;
|
||||
private double centerY = 0;
|
||||
|
||||
private double scale = 8;
|
||||
|
||||
private int stringBoardX = -100;
|
||||
private int stringBoardY = 20;
|
||||
|
||||
final private List< Plane > planes;
|
||||
final private PlaneData[] data;
|
||||
int index = -1;
|
||||
|
||||
private JLabel statusBar;
|
||||
private JComboBox< String > selectionBox;
|
||||
|
||||
private ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( FACET_DIR.exists() && FACET_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + FACET_DIR.list().length + " files" );
|
||||
// for ( File file : FACET_DIR.listFiles() ) {
|
||||
// List< Plane > planes = mesh( file );
|
||||
|
||||
// Plane draw = null;
|
||||
|
||||
// System.out.println( "Planes: " + planes.size() );
|
||||
// int planeIndex = 0;
|
||||
// for ( planeIndex = 0; planeIndex < planes.size(); ++planeIndex ) {
|
||||
// if ( planes.get( planeIndex ).polygons.size() > 100 ) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Collections.sort( planes, ( a, b ) -> Integer.compare( b.polygons.size(), a.polygons.size() ) );
|
||||
// draw = planes.get( 4 );
|
||||
|
||||
// Select a random plane to draw
|
||||
// draw = planes.get( new Random().nextInt( planes.size() ) );
|
||||
|
||||
// System.out.println( "Draw is " + draw );
|
||||
// Attempt to mesh all planes
|
||||
// try {
|
||||
// test( planes );
|
||||
// } catch ( PolygonException e ) {
|
||||
// draw = e.getPlane();
|
||||
// System.out.println( "Draw is now " + draw );
|
||||
// }
|
||||
|
||||
// final PlaneData data = process( draw );
|
||||
// final Collection< Polygon > polys = triangulate( draw );
|
||||
// final Collection< Polygon > polys = test();
|
||||
// System.out.println( "Polygon count: " + data.polygons.size() );
|
||||
|
||||
// final int startingIndex = planeIndex;
|
||||
// SwingUtilities.invokeLater( new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// new MeshingTest2( planes ).setIndex( startingIndex ).start();;
|
||||
// }
|
||||
// } );
|
||||
// break;
|
||||
// }
|
||||
|
||||
final List< Plane > masterPlanes = new ArrayList< Plane >();
|
||||
System.out.println( "Facet files: " + FACET_DIR.listFiles().length );
|
||||
int limit = 1;
|
||||
for ( File file : FACET_DIR.listFiles() ) {
|
||||
masterPlanes.addAll( mesh( file ) );
|
||||
if ( --limit <= 0 ) {
|
||||
break;
|
||||
} else {
|
||||
System.out.println( "Remaining: " + limit );
|
||||
}
|
||||
}
|
||||
|
||||
// final List< Plane > planes = mesh( new File( FACET_DIR, "world,-1,2.facet" ) );
|
||||
// masterPlanes.add( planes.get( 73 ) );
|
||||
// masterPlanes.addAll( planes );
|
||||
|
||||
System.out.println( "Adding test plane" );
|
||||
masterPlanes.add( getTestPlane() );
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new MeshingTest6( masterPlanes ).start();
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + FACET_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
private static List< Plane > mesh( File file ) {
|
||||
final String name = file.getName().substring( 0, file.getName().indexOf( '.' ) );
|
||||
final String[] split = name.split( "," );
|
||||
final 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 facets
|
||||
final List< Facet > facets = new ArrayList< Facet >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( " " );
|
||||
final int facetCount = Integer.parseInt( values[ 0 ] );
|
||||
final double nx = Double.parseDouble( values[ 1 ] );
|
||||
final double ny = Double.parseDouble( values[ 2 ] );
|
||||
final double nz = Double.parseDouble( values[ 3 ] );
|
||||
|
||||
final Facet facet = new Facet();
|
||||
facet.normal = new Vector( nx, ny, nz );
|
||||
|
||||
int point = 0;
|
||||
while ( point < facetCount && ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values2 = line.split( " " );
|
||||
final double px = Double.parseDouble( values2[ 0 ] );
|
||||
final double py = Double.parseDouble( values2[ 1 ] );
|
||||
final double pz = Double.parseDouble( values2[ 2 ] );
|
||||
facet.points.add( new Vector( px, py, pz ) );
|
||||
++point;
|
||||
}
|
||||
}
|
||||
facets.add( facet );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now that we have a bunch of facets, do whatever
|
||||
System.out.println( "Found " + facets.size() + " facets" );
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
for ( Facet facet : facets ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
|
||||
return builder.planes;
|
||||
}
|
||||
|
||||
public static class PolygonException extends RuntimeException {
|
||||
Plane plane;
|
||||
|
||||
PolygonException( Plane plane ) {
|
||||
this.plane = plane;
|
||||
}
|
||||
|
||||
Plane getPlane() {
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
final Collection< Polygon > polys = mesh.copyOf().mesh();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println( plane.polygons.size() + " to " + polys.size() + ":\t " + ( end - start ) + "ms" );
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
throw new PolygonException( plane );
|
||||
}
|
||||
} );
|
||||
final long processAllEnd = System.currentTimeMillis();
|
||||
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
|
||||
}
|
||||
|
||||
private static PlaneData process( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
final PlaneData data = new PlaneData();
|
||||
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.getEdgeCount() / 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.getEdgeCount() / 2 ) );
|
||||
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
|
||||
try {
|
||||
mesh.generateRegions();
|
||||
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
} catch ( Exception e ) {
|
||||
e.printStackTrace();
|
||||
|
||||
return data;
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
mesh.addHandler( new MeshEventHandler() {
|
||||
@Override
|
||||
public void onChainGenerationEvent( Collection< Chain > chains ) {
|
||||
data.chains = chains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreChainMergeEvent( Collection< Chain > chains ) {
|
||||
data.selectedChains = chains;
|
||||
|
||||
final List< Chain > sorted = new ArrayList< Chain >( chains );
|
||||
Collections.sort( sorted, ( a, b ) -> {
|
||||
final double diff = a.getStart().getX() - b.getStart().getX();
|
||||
if ( Math.abs( diff ) < 1e-7 ) {
|
||||
return Double.compare( a.getStart().getY(), b.getStart().getY() );
|
||||
}
|
||||
return Double.compare( diff, 0 );
|
||||
} );
|
||||
for ( Chain chain : sorted ) {
|
||||
System.out.println( "c: " + chain.getPoints() );
|
||||
}
|
||||
|
||||
final Collection< Point > points = new HashSet< Point >();
|
||||
for ( Chain chain : chains ) {
|
||||
for ( int i = 1; i < chain.getPoints().size() - 1; ++i ) {
|
||||
if ( !points.add( chain.getPoints().get( i ) ) ) {
|
||||
System.out.println( "Intersection at " + chain.getPoints().get( i ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartitionEvent( Collection< Polygon > polygons ) {
|
||||
data.polygons = polygons;
|
||||
}
|
||||
} );
|
||||
|
||||
try {
|
||||
// If the chains are the issue, set this to false and see if it still meshes
|
||||
mesh.setMergeChains( true );
|
||||
|
||||
final Collection< Polygon > triangles = mesh.mesh();
|
||||
if ( !triangles.isEmpty() ) {
|
||||
data.polygons = triangles;
|
||||
}
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// final List< Point > points = new ArrayList< Point >( data.vertices );
|
||||
// Collections.sort( points, ( a, b ) -> {
|
||||
// final int x = Double.compare( a.getX(), b.getX() );
|
||||
// if ( x == 0 ) {
|
||||
// return Double.compare( a.getY(), b.getY() );
|
||||
// }
|
||||
// return x;
|
||||
// } );
|
||||
// for ( Point v : points ) {
|
||||
// System.out.println( v.getX() + ", " + v.getY() );
|
||||
// }
|
||||
|
||||
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||
return data;
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< Polygon > 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.getEdgeCount() / 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.getEdgeCount() / 2 ) );
|
||||
|
||||
mesh.generateRegions();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
// Make sure this works
|
||||
mesh = mesh.copyOf();
|
||||
|
||||
Collection< Polygon > polys = new HashSet< Polygon >();
|
||||
|
||||
try {
|
||||
polys = mesh.mesh();
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||
|
||||
return polys;
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private static PlaneData test() {
|
||||
final PlaneData data = new PlaneData();
|
||||
final Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
mesh.addHandler( new MeshEventHandler() {
|
||||
@Override
|
||||
public void onChainGenerationEvent( Collection< Chain > chains ) {
|
||||
data.chains = chains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreChainMergeEvent( Collection< Chain > chains ) {
|
||||
data.selectedChains = chains;
|
||||
|
||||
for ( Chain chain : chains ) {
|
||||
System.out.println( "c: " + chain.getPoints() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -1, 0 ),
|
||||
// new Point( 1, -2 ),
|
||||
// new Point( 1, -1 ),
|
||||
// new Point( 2, -1 ),
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 1, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 5, 0 ),
|
||||
// new Point( 5, 1 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 4, 2 ),
|
||||
// new Point( 3, 2 ),
|
||||
// new Point( 3, 3 ),
|
||||
// new Point( 2, 3 ),
|
||||
// new Point( 2, 4 ),
|
||||
// new Point( 0, 4 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 2, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 2, 2 ),
|
||||
// new Point( 2, 4 ),
|
||||
// new Point( 1, 4 ),
|
||||
// new Point( 1, 3 ),
|
||||
// new Point( 0, 3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( 3, 1 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 4, 2 ),
|
||||
// new Point( 2, 2 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 2, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 3, 1 ),
|
||||
// new Point( 3, 2 ),
|
||||
// new Point( 2, 2 ),
|
||||
// new Point( 2, 5 ),
|
||||
// new Point( 1, 5 ),
|
||||
// new Point( 1, 7 ),
|
||||
// new Point( 0, 7 ),
|
||||
// new Point( 0, 4 ),
|
||||
// new Point( 1, 4 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 6, 0 ),
|
||||
// new Point( 6, 1 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 4, 2 ),
|
||||
// new Point( 2, 2 ),
|
||||
// new Point( 2, 3 ),
|
||||
// new Point( 0, 3 ),
|
||||
// new Point( 0, 1 ),
|
||||
// new Point( 1, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 3, 1 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( 10, 0 ),
|
||||
// new Point( 10, 6 ),
|
||||
// new Point( 9, 6 ),
|
||||
// new Point( 9, 4 ),
|
||||
// new Point( 8, 4 ),
|
||||
// new Point( 8, 3 ),
|
||||
// new Point( 5, 3 ),
|
||||
// new Point( 5, 2 ),
|
||||
// new Point( 0, 2 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 7, 2 ),
|
||||
// new Point( 9, 2 ),
|
||||
// new Point( 9, 3 ),
|
||||
// new Point( 7, 3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 6, 1 ),
|
||||
// new Point( 7, 2 ),
|
||||
// new Point( 1, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 2, 2 ),
|
||||
// new Point( 8, 3 ),
|
||||
// new Point( 9, 4 ),
|
||||
// new Point( 3, 3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 2.816104467407629, -3.7472939407853763 ),
|
||||
// new Point( 2.815383313894849, -2.9972942874937156 ),
|
||||
// new Point( 2.0028836894955497, -2.9980755371325607 ),
|
||||
// new Point( 2.0036048430083304, -3.7480751904242213 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 2.8144217758778085, -1.9972947497715015 ),
|
||||
// new Point( 2.0019221514785093, -1.998075999410347 ),
|
||||
// new Point( 2.00264330499129, -2.748075652702007 ),
|
||||
// new Point( 2.8151429293905887, -2.747294403063162 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 0 ),
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 0, 2 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 1, 3 ),
|
||||
// new Point( 0, 3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -1, -1 ),
|
||||
// new Point( 2, -1 ),
|
||||
// new Point( 2, 4 ),
|
||||
// new Point( -1, 4 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
data.polygons = mesh.mesh();
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static Plane getTestPlane() {
|
||||
final Plane plane = new Plane();
|
||||
|
||||
plane.normal = new Vector( 0, 0, 1 );
|
||||
plane.point = new Vector( 1, 0, 0 );
|
||||
|
||||
// plane.polygons.add( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 3, 2 ),
|
||||
// new Point( 4, 3 ),
|
||||
// new Point( 3, 4 ),
|
||||
// new Point( 2, 3 ),
|
||||
// new Point( 1, 4 ),
|
||||
// new Point( 0, 3 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 0, 1 )
|
||||
// plane.polygons.add( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 3, 2 ),
|
||||
// new Point( 4, 3 ),
|
||||
// new Point( 3, 4 ),
|
||||
// new Point( 2, 3 ),
|
||||
// new Point( 1, 4 ),
|
||||
// new Point( 0, 3 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ) );
|
||||
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, 1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( 1, 3 ),
|
||||
new Point( -1, 3 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -2, -1 ),
|
||||
new Point( -1, -1 ),
|
||||
new Point( -1, 2 ),
|
||||
new Point( -2, 2 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -4, -2 ),
|
||||
new Point( -1, -2 ),
|
||||
new Point( -1, 1.5 ),
|
||||
new Point( -4, 1.5 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -3, 0 ),
|
||||
new Point( 3, 0 ),
|
||||
new Point( -1.5, -3 ),
|
||||
new Point( 0, 1.5 ),
|
||||
new Point( 1.5, -3 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, 0 ),
|
||||
new Point( 0, 1.5 ),
|
||||
new Point( 1, 0 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( 1, 0 ),
|
||||
new Point( 0, -2 ),
|
||||
new Point( -1, 0 )
|
||||
) ) );
|
||||
|
||||
return plane;
|
||||
}
|
||||
|
||||
public MeshingTest6( List< Plane > data ) {
|
||||
this.planes = data;
|
||||
this.data = new PlaneData[ data.size() ];
|
||||
}
|
||||
|
||||
private MeshingTest6 setIndex( final int index ) {
|
||||
final int planeCount = planes.size();
|
||||
final int newIndex = ( ( ( index % planeCount ) + planeCount ) % planeCount );
|
||||
lock.lock();
|
||||
if ( this.index != newIndex ) {
|
||||
this.index = newIndex;
|
||||
|
||||
if ( selectionBox != null ) {
|
||||
selectionBox.setSelectedIndex( newIndex );
|
||||
}
|
||||
|
||||
new Thread( () -> { getData( this.index, this::repaint ); } ).start();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private PlaneData getData( final int index, final Runnable callback ) {
|
||||
if ( data[ index ] != null ) {
|
||||
callback.run();
|
||||
return data[ index ];
|
||||
} else {
|
||||
setStatus( "Loading..." );
|
||||
final PlaneData data = process( planes.set( index, null ) );
|
||||
setStatus( "Done!" );
|
||||
this.data[ index ] = data;
|
||||
callback.run();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private void setStatus( final String status ) {
|
||||
if ( statusBar != null ) {
|
||||
statusBar.setText( status );
|
||||
}
|
||||
}
|
||||
|
||||
private MeshingTest6 preMesh() {
|
||||
for ( int i = 0; i < planes.size(); ++i ) {
|
||||
this.data[ i ] = process( planes.set( i, null ) );
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void start() {
|
||||
if ( index == -1 ) {
|
||||
setIndex( 0 );
|
||||
}
|
||||
|
||||
f = new JFrame( "Drawing Board" );
|
||||
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
|
||||
final MouseAdapter adapter = new MouseAdapter() {
|
||||
private boolean dragging = false;
|
||||
private double initX, initY;
|
||||
private int dragX, dragY;
|
||||
private double scroll = Math.log( scale );
|
||||
|
||||
@Override
|
||||
public void mousePressed( MouseEvent e ) {
|
||||
dragging = true;
|
||||
|
||||
initX = centerX;
|
||||
initY = centerY;
|
||||
|
||||
dragX = e.getXOnScreen();
|
||||
dragY = e.getYOnScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged( MouseEvent e ) {
|
||||
if ( dragging ) {
|
||||
centerX = initX + ( ( e.getXOnScreen() - dragX ) / scale );
|
||||
centerY = initY + ( ( e.getYOnScreen() - dragY ) / scale );
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||
if ( e.getWheelRotation() < 0 ) {
|
||||
scroll += e.isControlDown() ? 0.1 : 0.5;
|
||||
} else {
|
||||
scroll -= e.isControlDown() ? 0.1 : 0.5;
|
||||
}
|
||||
scale = Math.exp( scroll );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved( MouseEvent e ) {
|
||||
super.mouseMoved( e );
|
||||
|
||||
repaint();
|
||||
}
|
||||
};
|
||||
|
||||
addMouseListener( adapter );
|
||||
addMouseMotionListener( adapter );
|
||||
addMouseWheelListener( adapter );
|
||||
|
||||
f.addComponentListener( new ComponentAdapter() {
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
offsetX = getWidth() >> 1;
|
||||
offsetY = getHeight() >> 1;
|
||||
};
|
||||
} );
|
||||
|
||||
final JPanel taskbar = new JPanel();
|
||||
taskbar.setLayout( new FlowLayout() );
|
||||
|
||||
{
|
||||
final JComboBox< String > data = new JComboBox< String >();
|
||||
for ( int i = 0; i < planes.size(); ++i ) {
|
||||
data.addItem( "Plane " + i );
|
||||
}
|
||||
data.addActionListener( e -> {
|
||||
setIndex( data.getSelectedIndex() );
|
||||
} );
|
||||
taskbar.add( data );
|
||||
|
||||
selectionBox = data;
|
||||
}
|
||||
|
||||
{
|
||||
final JLabel label = new JLabel();
|
||||
label.setPreferredSize( new Dimension( 100, 16 ) );
|
||||
label.setHorizontalAlignment( SwingConstants.LEFT );
|
||||
taskbar.add( label );
|
||||
|
||||
statusBar = label;
|
||||
}
|
||||
|
||||
{
|
||||
final JButton prev = new JButton( "Previous" );
|
||||
prev.addActionListener( e -> { setIndex( index - 1 ); } );
|
||||
taskbar.add( prev );
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
final JButton prev = new JButton( "Next" );
|
||||
prev.addActionListener( e -> { setIndex( index + 1 ); } );
|
||||
taskbar.add( prev );
|
||||
}
|
||||
|
||||
f.add( taskbar, BorderLayout.NORTH );
|
||||
|
||||
f.add( this );
|
||||
|
||||
f.setSize( windowWidth, windowHeight );
|
||||
f.setVisible( true );
|
||||
f.setResizable( true );
|
||||
|
||||
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
private void drawOnBoard( Graphics g, String str, int x, int y ) {
|
||||
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, ( int ) scale * 5 ) );
|
||||
g.drawString( str, ( int ) ( ( centerX + x + stringBoardX ) * scale ) + offsetX, ( int ) ( ( centerY - y + stringBoardY ) * scale ) + offsetY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent( Graphics g ) {
|
||||
super.paintComponent( g );
|
||||
|
||||
final PlaneData data = this.data[ index ];
|
||||
|
||||
g.setColor( new Color( 200, 200, 200 ) );
|
||||
g.drawLine( ( int ) ( scale * centerX ) + offsetX, 0, ( int ) ( scale * centerX ) + offsetX, getHeight() );
|
||||
g.drawLine( 0, ( int ) ( scale * centerY ) + offsetY, getWidth(), ( int ) ( scale * centerY ) + offsetY );
|
||||
g.setColor( Color.BLACK );
|
||||
|
||||
if ( data != null ) {
|
||||
int highestX = 40;
|
||||
if ( !data.edges.isEmpty() ) {
|
||||
Collection< Point > points = data.vertices;
|
||||
|
||||
if ( !data.polygons.isEmpty() ) {
|
||||
highestX = ( int ) ( points.parallelStream()
|
||||
.max( ( a, b ) -> { return Double.compare( a.getX(), b.getX() ); } ).get().getX() + 40.5 );
|
||||
} else {
|
||||
highestX = 0;
|
||||
}
|
||||
|
||||
g.setColor( Color.BLACK );
|
||||
for ( final Segment segment : data.edges ) {
|
||||
Point p1 = segment.getStart();
|
||||
Point p2 = segment.getEnd();
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
|
||||
for ( final Point point : points ) {
|
||||
final double diff = scale * 0.15;
|
||||
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int count = 2;
|
||||
Random random = new Random( hashCode() );
|
||||
|
||||
final int spacing = 25;
|
||||
|
||||
for ( final Polygon p : data.polygons ) {
|
||||
final List< Point > points = p.getPoints();
|
||||
final int[] x = new int[ points.size() ];
|
||||
final int[] y = new int[ points.size() ];
|
||||
final int[] xOffset = new int[ points.size() ];
|
||||
final int[] yOffset = new int[ points.size() ];
|
||||
final int[] filledX = new int[ points.size() ];
|
||||
final int[] filledY = new int[ points.size() ];
|
||||
for ( int i = 0; i < points.size(); ++i ) {
|
||||
final Point point = points.get( i );
|
||||
x[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
||||
y[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
||||
xOffset[ i ] = ( int ) ( ( centerX + point.getX() + count * spacing ) * scale ) + offsetX;
|
||||
yOffset[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
||||
filledX[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
||||
filledY[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
|
||||
}
|
||||
|
||||
g.setColor( Color.BLACK );
|
||||
g.drawPolygon( x, y, points.size() );
|
||||
g.drawPolygon( xOffset, yOffset, points.size() );
|
||||
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
|
||||
g.fillPolygon( filledX, filledY, points.size() );
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
g.setColor( Color.RED );
|
||||
for ( final Chain chain : data.chains ) {
|
||||
final Point p1 = chain.getStart();
|
||||
final Point p2 = chain.getEnd();
|
||||
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
g.setColor( Color.BLUE );
|
||||
for ( final Chain chain : data.selectedChains ) {
|
||||
final Point p1 = chain.getStart();
|
||||
final Point p2 = chain.getEnd();
|
||||
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor( Color.BLACK );
|
||||
drawOnBoard( g, "Plane index: " + index, 0, 10 );
|
||||
g.setColor( Color.CYAN );
|
||||
drawOnBoard( g, "Edge count: " + data.edges.size(), 0, 0 );
|
||||
g.setColor( Color.GREEN );
|
||||
drawOnBoard( g, "Vertex count: " + data.vertices.size(), 0, -5 );
|
||||
g.setColor( Color.MAGENTA );
|
||||
drawOnBoard( g, "Triangle count: " + data.polygons.size(), 0, -10 );
|
||||
g.setColor( Color.RED );
|
||||
drawOnBoard( g, "Total chain count: " + data.chains.size(), 0, -15 );
|
||||
g.setColor( Color.BLUE );
|
||||
drawOnBoard( g, "Optimal chain count: " + data.selectedChains.size(), 0, -20 );
|
||||
}
|
||||
|
||||
java.awt.Point p = MouseInfo.getPointerInfo().getLocation();
|
||||
java.awt.Point componentLocation = getLocationOnScreen();
|
||||
p = new java.awt.Point( p.x - componentLocation.x, p.y - componentLocation.y );
|
||||
g.setColor( Color.RED );
|
||||
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, 30 ) );
|
||||
g.drawString( "X: " + ( ( p.x - offsetX ) / scale - centerX ), 10, 30 );
|
||||
g.drawString( "Y: " + ( - ( ( p.y - offsetY ) / scale - centerY ) ), 10, 60 );
|
||||
}
|
||||
|
||||
private static class PlaneData {
|
||||
Collection< Point > vertices = new HashSet< Point >();
|
||||
Collection< Segment > edges = new HashSet< Segment >();
|
||||
Collection< Chain > chains = new HashSet< Chain >();
|
||||
Collection< Chain > selectedChains = new HashSet< Chain >();
|
||||
Collection< Polygon > polygons = new HashSet< Polygon >();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData;
|
||||
import org.bukkit.entity.BlockDisplay;
|
||||
@@ -47,8 +48,11 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
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.MeshingTest2.AABB;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.CommandParameters;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.SubCommand;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor.CommandExecutableMessage;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.InputValidatorDouble;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.InputValidatorInt;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender.SenderValidatorPlayer;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
@@ -158,9 +162,9 @@ public class MiniePlugin extends JavaPlugin {
|
||||
@EventHandler
|
||||
private void onChunkLoad( ChunkLoadEvent event ) {
|
||||
if ( event.getWorld().getName().equalsIgnoreCase( "world" ) ) {
|
||||
// getLogger().info( "Loading chunk..." );
|
||||
// scanAndGenerate( event.getChunk() );
|
||||
// saveChunk( event.getChunk() );
|
||||
getLogger().info( "Loading chunk " + event.getChunk().getX() + ", " + event.getChunk().getZ() );
|
||||
scanAndGenerate( event.getChunk() );
|
||||
saveFacets( event.getChunk() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +181,12 @@ public class MiniePlugin extends JavaPlugin {
|
||||
}, this );
|
||||
|
||||
|
||||
// getLogger().info( "There are " + Bukkit.getWorld( "world" ).getLoadedChunks().length + " chunks" );
|
||||
getLogger().info( "There are " + Bukkit.getWorld( "world" ).getLoadedChunks().length + " chunks" );
|
||||
// getLogger().info( "Converted " + saveAllChunks( Bukkit.getWorld( "world" ) ) );
|
||||
|
||||
scanAndGenerate( Bukkit.getWorld( "world" ) );
|
||||
saveAllFacets( Bukkit.getWorld( "world" ) );
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer( this, () -> {
|
||||
if ( !paused ) {
|
||||
// Update the real world with the physics space objects
|
||||
@@ -284,69 +291,9 @@ public class MiniePlugin extends JavaPlugin {
|
||||
new SubCommand( "minie" )
|
||||
.addSenderValidator( new SenderValidatorPlayer() )
|
||||
.add( new SubCommand( "spawn" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
loc.setPitch( 0 );
|
||||
loc.setDirection( new Vector( 0, 0, 0 ) );
|
||||
|
||||
BlockData displayData;
|
||||
ItemStack item = player.getInventory().getItemInMainHand();
|
||||
if ( item != null && item.getType() != Material.AIR && item.getType().isBlock() ) {
|
||||
displayData = item.getType().createBlockData();
|
||||
} else {
|
||||
displayData = Material.TNT.createBlockData();
|
||||
}
|
||||
|
||||
BoundingBox[] boxes = convertFrom( getShape( displayData, null, BlockShapeType.SHAPE ) );
|
||||
Vector blockCenter = calculateCenter( boxes );
|
||||
|
||||
CollisionShape box;
|
||||
if ( boxes.length == 0 ) {
|
||||
player.sendMessage( "No bounding box detected! Wrong method?" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
} else {
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
center.subtract( blockCenter );
|
||||
CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
}
|
||||
box = compound;
|
||||
}
|
||||
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
|
||||
// Since the center of the rigid body and the box shape are different, we need to offset the location
|
||||
rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
|
||||
|
||||
// Convert the display direction vector to a quaternion
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
// Create the display
|
||||
BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
|
||||
d.setBlock( displayData );
|
||||
} );
|
||||
|
||||
linkedDisplays.put( rigid, new AuxiliaryDisplayStruct( display, blockCenter ) );
|
||||
|
||||
if ( displayData.getMaterial() == Material.TNT ) {
|
||||
isTnt.add( rigid );
|
||||
}
|
||||
|
||||
player.sendMessage( "Created display" );
|
||||
} ) )
|
||||
.add( new SubCommand( new InputValidatorDouble( 0.01, 100 ) )
|
||||
.defaultTo( this::spawnBlock ) )
|
||||
.defaultTo( this::spawnBlock ) )
|
||||
.add( new SubCommand( "constraint" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
@@ -436,6 +383,24 @@ public class MiniePlugin extends JavaPlugin {
|
||||
player.sendMessage( "Registering chunk..." );
|
||||
scanAndGenerate( player.getLocation().getChunk() );
|
||||
} ) )
|
||||
.add( new SubCommand( "area" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
|
||||
player.sendMessage( "Registering chunk..." );
|
||||
final World world = player.getWorld();
|
||||
final Chunk chunk = player.getLocation().getChunk();
|
||||
final int x = chunk.getX();
|
||||
final int z = chunk.getZ();
|
||||
|
||||
int radius = 5;
|
||||
for ( int i = -radius; i < radius + 1; ++i ) {
|
||||
for ( int j = -radius; j < radius + 1; ++j ) {
|
||||
getLogger().info( "Queueing chunk scan at (" + ( x + i ) + ", " + ( z + j ) + ")" );
|
||||
scanAndGenerate( world.getChunkAt( x + i, z + j ) );
|
||||
}
|
||||
}
|
||||
} ) )
|
||||
.add( new SubCommand( "plane" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
@@ -511,6 +476,86 @@ public class MiniePlugin extends JavaPlugin {
|
||||
} );
|
||||
}
|
||||
|
||||
private void spawnBlock( final CommandSender sender, final String[] args, final CommandParameters params ) {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
// Optional scale
|
||||
double scale = 1;
|
||||
if ( params.size() > 2 ) {
|
||||
scale = params.getFirst( double.class );
|
||||
}
|
||||
|
||||
loc.setPitch( 0 );
|
||||
loc.setDirection( new Vector( 0, 0, 0 ) );
|
||||
|
||||
BlockData displayData;
|
||||
ItemStack item = player.getInventory().getItemInMainHand();
|
||||
if ( item != null && item.getType() != Material.AIR && item.getType().isBlock() ) {
|
||||
displayData = item.getType().createBlockData();
|
||||
} else {
|
||||
displayData = Material.TNT.createBlockData();
|
||||
}
|
||||
|
||||
BoundingBox[] boxes = convertFrom( getShape( displayData, null, BlockShapeType.SHAPE ) );
|
||||
Vector blockCenter = calculateCenter( boxes );
|
||||
|
||||
CollisionShape box;
|
||||
if ( boxes.length == 0 ) {
|
||||
player.sendMessage( "No bounding box detected! Wrong method?" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
} else {
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
center.subtract( blockCenter );
|
||||
CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
}
|
||||
box = compound;
|
||||
}
|
||||
|
||||
box.setScale( ( float ) scale );
|
||||
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
|
||||
// Since the center of the rigid body and the box shape are different, we need to offset the location
|
||||
rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
|
||||
|
||||
// Convert the display direction vector to a quaternion
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
rigid.setMass( rigid.getMass() * ( float ) ( scale * scale ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
// Create the display
|
||||
BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
|
||||
d.setBlock( displayData );
|
||||
d.setViewRange( 1000 );
|
||||
} );
|
||||
|
||||
linkedDisplays.put( rigid, new AuxiliaryDisplayStruct( display, blockCenter.multiply( scale ) ).setScale( new Vector( scale, scale, scale ) ) );
|
||||
|
||||
if ( displayData.getMaterial() == Material.TNT ) {
|
||||
isTnt.add( rigid );
|
||||
}
|
||||
|
||||
player.sendMessage( "Created display" );
|
||||
}
|
||||
|
||||
/*
|
||||
* Replaced with saveAllFacets
|
||||
*/
|
||||
@Deprecated
|
||||
private int saveAllChunks( World world ) {
|
||||
int total = 0;
|
||||
for ( Chunk chunk : world.getLoadedChunks() ) {
|
||||
@@ -519,6 +564,10 @@ public class MiniePlugin extends JavaPlugin {
|
||||
return total;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replaced with saveFacets
|
||||
*/
|
||||
@Deprecated
|
||||
private int saveChunk( Chunk chunk ) {
|
||||
File saveFile = new File( getDataFolder() + "/chunks", chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ() );
|
||||
saveFile.getParentFile().mkdirs();
|
||||
@@ -560,6 +609,140 @@ public class MiniePlugin extends JavaPlugin {
|
||||
return count;
|
||||
}
|
||||
|
||||
private void saveAllFacets( final World world ) {
|
||||
for ( final Chunk chunk : world.getLoadedChunks() ) {
|
||||
saveFacets( chunk );
|
||||
}
|
||||
}
|
||||
|
||||
private void saveFacets( final Chunk chunk ) {
|
||||
final Collection< Facet > facets = getFacetsConservative( chunk );
|
||||
saveFacets( new File( getDataFolder() + "/facets", chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ() + ".facet" ), facets );
|
||||
}
|
||||
|
||||
private Collection< Facet > getFacetsConservative( Chunk chunk ) {
|
||||
final World world = chunk.getWorld();
|
||||
final int worldMinHeight = world.getMinHeight();
|
||||
final int worldMaxHeight = world.getMaxHeight();
|
||||
final int worldHeight = worldMaxHeight - worldMinHeight;
|
||||
|
||||
final Collection< Facet > facets = new HashSet< Facet >();
|
||||
|
||||
BlockVoxelType[] types = new BlockVoxelType[ worldHeight << 8 ];
|
||||
|
||||
for ( int y = worldMinHeight; y < worldMaxHeight; ++y ) {
|
||||
final int yIndex = ( y - worldMinHeight ) << 8;
|
||||
for ( int z = 0; z < 16; ++z ) {
|
||||
final int zIndex = yIndex + ( z << 4 );
|
||||
for ( int x = 0; x < 16; ++x ) {
|
||||
final int index = zIndex + x;
|
||||
final Block block = chunk.getBlock( x, y, z );
|
||||
if ( !( block.isEmpty() && block.isLiquid() ) ) {
|
||||
|
||||
final BlockData data = block.getBlockData();
|
||||
final Location loc = block.getLocation();
|
||||
|
||||
// Start the box at 0, 0, 0
|
||||
final BoundingBox[] boundingBoxes = convertFrom( getShape( data, loc, BlockShapeType.COLLISION_SHAPE ), new Vector( x, y - worldMinHeight, z ) );
|
||||
|
||||
if ( boundingBoxes.length == 0 ) {
|
||||
// No bounding box
|
||||
types[ index ] = BlockVoxelType.NONE;
|
||||
} else if ( boundingBoxes.length == 1 && boundingBoxes[ 0 ].getVolume() == 1 ) {
|
||||
// Solid
|
||||
types[ index ] = BlockVoxelType.SOLID;
|
||||
} else {
|
||||
types[ index ] = BlockVoxelType.TRANSPARENT;
|
||||
// Complex bounding shape, and transparent
|
||||
|
||||
// Convert the bounding box to facets
|
||||
for ( final BoundingBox box : boundingBoxes ) {
|
||||
facets.addAll( generateFacetsFor( box ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
types[ index ] = BlockVoxelType.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now generate facets for each solid block based on if the side they are on is visible
|
||||
for ( int y = 0; y < worldHeight; ++y ) {
|
||||
final int yIndex = y << 8;
|
||||
for ( int z = 0; z < 16; ++z ) {
|
||||
final int zIndex = z << 4;
|
||||
for ( int x = 0; x < 16; ++x ) {
|
||||
final int index = yIndex + zIndex + x;
|
||||
if ( types[ index ] == BlockVoxelType.SOLID ) {
|
||||
// Check each face
|
||||
if ( y == 0 || types[ index - 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( y == worldHeight - 1 || types[ index + 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 0 || types[ index - 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 15 || types[ index + 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 0 || types[ index - 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 15 || types[ index + 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return facets;
|
||||
}
|
||||
|
||||
private void scanAndGenerate( World world ) {
|
||||
for ( Chunk chunk : world.getLoadedChunks() ) {
|
||||
scanAndGenerate( chunk );
|
||||
@@ -569,40 +752,19 @@ public class MiniePlugin extends JavaPlugin {
|
||||
private void scanAndGenerate( final Chunk chunk ) {
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
// getLogger().info( "Gathering bounding boxes..." );
|
||||
final World world = chunk.getWorld();
|
||||
final int worldMinHeight = world.getMinHeight();
|
||||
final int worldMaxHeight = world.getMaxHeight();
|
||||
final Collection< BoundingBox > boxes = new ArrayDeque< BoundingBox >();
|
||||
getLogger().info( "Gathering bounding boxes..." );
|
||||
for ( int y = worldMinHeight; y < worldMaxHeight; ++y ) {
|
||||
for ( int z = 0; z < 16; ++z ) {
|
||||
for ( int x = 0; x < 16; ++x ) {
|
||||
Block block = chunk.getBlock( x, y, z );
|
||||
if ( !( block.isEmpty() && block.isLiquid() ) ) {
|
||||
final BlockData data = block.getBlockData();
|
||||
final Location loc = block.getLocation();
|
||||
|
||||
// Start the box at 0, 0, 0
|
||||
final BoundingBox[] boundingBoxes = convertFrom( getShape( data, loc, BlockShapeType.COLLISION_SHAPE ), new Vector( x, y - worldMinHeight, z ) );
|
||||
|
||||
for ( BoundingBox box : boundingBoxes ) {
|
||||
boxes.add( box );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Collection< Facet > originalFacets = getFacetsConservative( chunk );
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously( this, () -> {
|
||||
getLogger().info( "Building planes..." );
|
||||
// getLogger().info( "Building planes..." );
|
||||
final MeshBuilder builder = new MeshBuilder();
|
||||
for ( final BoundingBox box : boxes ) {
|
||||
for ( final Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
for ( final Facet facet : originalFacets ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
|
||||
getLogger().info( "Meshing planes..." );
|
||||
// getLogger().info( "Meshing planes..." );
|
||||
Collection< Facet > facets = builder.planes.parallelStream().flatMap( p -> process( p ).parallelStream().map( p::convert ) ).collect( Collectors.toSet() );
|
||||
|
||||
final class VectorRef {
|
||||
@@ -616,7 +778,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
final Map< Vector, VectorRef > vertices = new HashMap< Vector, VectorRef >();
|
||||
final List< Triangle > triangles = new ArrayList< Triangle >();
|
||||
|
||||
getLogger().info( "Generating triangles..." );
|
||||
// getLogger().info( "Generating triangles..." );
|
||||
// Convert each facet to a triangle
|
||||
for ( final Facet facet : facets ) {
|
||||
if ( facet.points.size() != 3 ) {
|
||||
@@ -664,7 +826,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
}
|
||||
} );
|
||||
|
||||
getLogger().info( "Creating buffers..." );
|
||||
// getLogger().info( "Creating buffers..." );
|
||||
final Vector3f[] positionArray = new Vector3f[ sorted.size() ];
|
||||
for ( int i = 0; i < sorted.size(); ++i ) {
|
||||
final Entry< Vector, VectorRef > entry = sorted.get( i );
|
||||
@@ -688,12 +850,12 @@ public class MiniePlugin extends JavaPlugin {
|
||||
indexArray[ indexArrayStart + 2 ] = triangle.refs.get( 2 ).index;
|
||||
}
|
||||
|
||||
getLogger().info( "Adding mesh collision shape..." );
|
||||
// getLogger().info( "Adding mesh collision shape..." );
|
||||
final IndexedMesh nativeMesh = new IndexedMesh( positionArray, indexArray );
|
||||
// Enable quantized AABB compression... hopefully that doesn't mess with the accuracy or anything...
|
||||
final MeshCollisionShape mesh = new MeshCollisionShape( true, nativeMesh );
|
||||
|
||||
getLogger().info( "Mesh has " + mesh.countMeshVertices() + " vertices and " + mesh.countMeshTriangles() + " triangles" );
|
||||
// getLogger().info( "Mesh has " + mesh.countMeshVertices() + " vertices and " + mesh.countMeshTriangles() + " triangles" );
|
||||
|
||||
// Create a rigid body
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( mesh, PhysicsBody.massForStatic );
|
||||
@@ -709,13 +871,31 @@ public class MiniePlugin extends JavaPlugin {
|
||||
getLogger().warning( "Old rigid body found!" );
|
||||
space.remove( old );
|
||||
}
|
||||
getLogger().info( "Done!" );
|
||||
// getLogger().info( "Done!" );
|
||||
final long time = System.currentTimeMillis() - start;
|
||||
getLogger().info( "Took " + time + "ms" );
|
||||
getLogger().info( "Took " + time + "ms to mesh chunk " + chunk.getX() + ", " + chunk.getZ() + " into " + mesh.countMeshVertices() + " vertices and " + mesh.countMeshTriangles() + " triangles from " + facets.size() + " facets" );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
private void saveFacets( final File file, final Collection< Facet > facets ) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.delete();
|
||||
|
||||
try ( FileWriter writer = new FileWriter( file ) ) {
|
||||
for ( final Facet facet : facets ) {
|
||||
final Vector normal = facet.normal;
|
||||
|
||||
writer.write( facet.points.size() + " " + normal.getX() + " " + normal.getY() + " " + normal.getZ() + "\n" );
|
||||
for ( final Vector point : facet.points ) {
|
||||
writer.write( point.getX() + " " + point.getY() + " " + point.getZ() + "\n" );
|
||||
}
|
||||
}
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean convert( Block block ) {
|
||||
BlockState state = block.getState();
|
||||
BlockData data = state.getBlockData();
|
||||
@@ -960,6 +1140,11 @@ public class MiniePlugin extends JavaPlugin {
|
||||
VISUAL_SHAPE,
|
||||
INTERACTION_SHAPE,
|
||||
BLOCK_SUPPORT_SHAPE
|
||||
|
||||
}
|
||||
|
||||
private enum BlockVoxelType {
|
||||
NONE,
|
||||
TRANSPARENT,
|
||||
SOLID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorDouble implements InputValidator< Double > {
|
||||
protected double min = Integer.MIN_VALUE;
|
||||
protected double max = Integer.MAX_VALUE;
|
||||
|
||||
public InputValidatorDouble() {
|
||||
}
|
||||
|
||||
public InputValidatorDouble( double min, double max ) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String[] input, String[] args ) {
|
||||
try {
|
||||
double i = Double.valueOf( input[ 0 ] );
|
||||
return i >= min && i <= max;
|
||||
} catch ( Exception exception ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get( CommandSender sender, String[] input ) {
|
||||
return Double.parseDouble( input[ 0 ] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,11 +10,11 @@ public class MeshBuilder {
|
||||
public List< Plane > planes = new ArrayList< Plane >();
|
||||
|
||||
public Plane addFacet( Facet facet ) {
|
||||
for ( Plane plane : planes ) {
|
||||
if ( plane.matches( facet.normal, facet.points.get( 0 ) ) ) {
|
||||
plane.addShape( facet );
|
||||
return plane;
|
||||
}
|
||||
final Optional< Plane > opt = planes.stream().filter( p -> p.matches( facet.normal, facet.points.get( 0 ) ) ).findAny();
|
||||
if ( opt.isPresent() ) {
|
||||
final Plane plane = opt.get();
|
||||
plane.addShape( facet );
|
||||
return plane;
|
||||
}
|
||||
|
||||
Plane plane = new Plane();
|
||||
|
||||
Reference in New Issue
Block a user