Initial cleanup and refactor
This commit is contained in:
253
plugin/dependency-reduced-pom.xml
Normal file
253
plugin/dependency-reduced-pom.xml
Normal file
@@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>mc-mesh-master</artifactId>
|
||||
<groupId>com.aaaaahhhhhhh.bananapuncher714</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>mc-mesh-plugin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.1</version>
|
||||
<configuration>
|
||||
<release>21</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>com.aaaaahhhhhhh.bananapuncher714.minietest.MeshingTest6</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.21.10-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jline</artifactId>
|
||||
<groupId>org.jline</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j-iostreams</artifactId>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<groupId>com.github.stephenc.jcip</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>msal4j</artifactId>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>authlib</artifactId>
|
||||
<groupId>com.mojang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>brigadier</artifactId>
|
||||
<groupId>com.mojang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>datafixerupper</artifactId>
|
||||
<groupId>com.mojang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jtracy</artifactId>
|
||||
<groupId>com.mojang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>com.mojang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>content-type</artifactId>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>lang-tag</artifactId>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>oauth2-oidc-sdk</artifactId>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<groupId>commons-io</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-resolver</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-transport-classes-epoll</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>fastutil</artifactId>
|
||||
<groupId>it.unimi.dsi</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jna</artifactId>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>accessors-smart</artifactId>
|
||||
<groupId>net.minidev</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>json-smart</artifactId>
|
||||
<groupId>net.minidev</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>lz4-java</artifactId>
|
||||
<groupId>org.lz4</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<groupId>commons-lang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<groupId>org.xerial</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>maven-resolver-provider</artifactId>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>maven-resolver-transport-http</artifactId>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
78
plugin/pom.xml
Normal file
78
plugin/pom.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.aaaaahhhhhhh.bananapuncher714</groupId>
|
||||
<artifactId>mc-mesh-master</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>mc-mesh-plugin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.21.10-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.stephengold</groupId>
|
||||
<artifactId>Minie</artifactId>
|
||||
<version>9.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scijava</groupId>
|
||||
<artifactId>native-lib-loader</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aaaaahhhhhhh.bananapuncher714</groupId>
|
||||
<artifactId>mc-mesh-core</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.1</version>
|
||||
<configuration>
|
||||
<release>21</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>com.aaaaahhhhhhh.bananapuncher714.minietest.MeshingTest6</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh;
|
||||
|
||||
public class ChunkMeshCache {
|
||||
final File directory;
|
||||
Map< ChunkLocation, ChunkMesh > meshes = new TreeMap< ChunkLocation, ChunkMesh >();
|
||||
Collection< ChunkLocation > shouldRemove = new HashSet< ChunkLocation >();
|
||||
ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public ChunkMeshCache( final File directory ) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public void onChunkLoad( final Chunk chunk ) {
|
||||
final ChunkLocation location = new ChunkLocation( chunk );
|
||||
|
||||
// Unqueue this chunk for removal if it's queued
|
||||
shouldRemove.remove( location );
|
||||
|
||||
// TODO Add a ticket
|
||||
chunk.addPluginChunkTicket( null );
|
||||
|
||||
// Does the mesh map contain the current location?
|
||||
{
|
||||
// If not, then load or mesh the chunk
|
||||
// Emit a chunk mesh complete event
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void saveAndRemove( final ChunkLocation location ) {
|
||||
lock.lock();
|
||||
meshes.remove( location );
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void saveAndRemoveAll() {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
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.List;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
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 MeshTest {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||
doStuff( file );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
private static void doStuff( File file ) {
|
||||
String name = file.getName();
|
||||
String[] split = name.split( "," );
|
||||
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||
|
||||
System.out.println( "Parsing chunk " + location );
|
||||
|
||||
// First parse the file to get a list of all bounding boxes that we can use
|
||||
List< AABB > boxes = new ArrayList< AABB >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( "," );
|
||||
double minX = Double.parseDouble( values[ 0 ] );
|
||||
double minY = Double.parseDouble( values[ 1 ] );
|
||||
double minZ = Double.parseDouble( values[ 2 ] );
|
||||
double maxX = Double.parseDouble( values[ 3 ] );
|
||||
double maxY = Double.parseDouble( values[ 4 ] );
|
||||
double maxZ = Double.parseDouble( values[ 5 ] );
|
||||
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println( "Found " + boxes.size() + " boxes" );
|
||||
// Now that we have a bunch of bounding boxes, do whatever
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
int i = 0;
|
||||
for ( AABB box : boxes ) {
|
||||
for ( Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println( "Planes: " + builder.planes.size() );
|
||||
for ( Plane plane : builder.planes ) {
|
||||
System.out.println( "Plane" );
|
||||
System.out.println( "Norm:\t" + plane.normal );
|
||||
System.out.println( "Ref:\t" + plane.point );
|
||||
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||
}
|
||||
|
||||
/*
|
||||
* The general plan is as follows:
|
||||
* - Create a new mesh object
|
||||
* - Insert faces one by one until we have all the shapes we want
|
||||
* - In this case, it happens to be perfect AABB
|
||||
* - Process the faces
|
||||
* - Sort them into their correct plane of existence, along with their normal
|
||||
* - In this case, their unique plane can be defined by their normal, and a point
|
||||
* - Use the first point of the polygon to compare
|
||||
* - Use the smallest point we can find as the reference point
|
||||
* - O(1) operation per face, I suppose
|
||||
* - Need to check point and normal with tolerance which is why
|
||||
* - Now that we have faces each in their assorted group, we can start to tessellate each one
|
||||
* - Generate even/odd regions and remove any colinear edges
|
||||
* - Save this as the pre-mesh
|
||||
* - Now trianglate each group and put that into a mesh collision shape or gimpact shape or whatnot
|
||||
* - Use an IndexedMesh with the two array constructor
|
||||
* - Put into game and test
|
||||
*/
|
||||
}
|
||||
|
||||
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||
List< Facet > facets = new ArrayList< Facet >();
|
||||
|
||||
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
||||
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
||||
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
||||
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
||||
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
||||
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
||||
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
||||
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p3 );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p5 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p8 );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p6 );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
return facets;
|
||||
}
|
||||
|
||||
static class AABB {
|
||||
double xmin, ymin, zmin;
|
||||
double xmax, ymax, zmax;
|
||||
|
||||
AABB( double xmin, double ymin, double zmin, double xmax, double ymax, double zmax ) {
|
||||
this.xmin = xmin;
|
||||
this.ymax = ymin;
|
||||
this.zmin = zmin;
|
||||
|
||||
this.xmax = xmax;
|
||||
this.ymax = ymax;
|
||||
this.zmax = zmax;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,235 @@
|
||||
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.MeshingTest2.AABB;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Mesh speed test
|
||||
*/
|
||||
public class MeshingTest3 {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
|
||||
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 );
|
||||
} 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 < 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 ) {
|
||||
// System.out.println( "Meshing plane " + i );
|
||||
// process( planes.get( i ) );
|
||||
// }
|
||||
|
||||
// process( planes.get( 27 ) );
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + CHUNK_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 ) {
|
||||
String name = file.getName();
|
||||
String[] split = name.split( "," );
|
||||
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||
|
||||
// First parse the file to get a list of all bounding boxes that we can use
|
||||
List< AABB > boxes = new ArrayList< AABB >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( "," );
|
||||
double minX = Double.parseDouble( values[ 0 ] );
|
||||
double minY = Double.parseDouble( values[ 1 ] );
|
||||
double minZ = Double.parseDouble( values[ 2 ] );
|
||||
double maxX = Double.parseDouble( values[ 3 ] );
|
||||
double maxY = Double.parseDouble( values[ 4 ] );
|
||||
double maxZ = Double.parseDouble( values[ 5 ] );
|
||||
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
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();
|
||||
for ( AABB box : boxes ) {
|
||||
for ( Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
}
|
||||
|
||||
return builder.planes;
|
||||
}
|
||||
|
||||
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||
List< Facet > facets = new ArrayList< Facet >();
|
||||
|
||||
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
||||
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
||||
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
||||
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
||||
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
||||
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
||||
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
||||
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p3 );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p5 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p6 );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
return facets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
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.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
|
||||
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.MeshingTest2.AABB;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Mesh chunk data to ply2 format
|
||||
*/
|
||||
public class MeshingTest4 {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
||||
private static final File MODEL_DIR = new File( BASE, "chunk_models" );
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||
|
||||
// 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" );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
private static void savePly2( final File file, Collection< Facet > facets ) {
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
if ( file.exists() ) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
class VectorRef {
|
||||
int index;
|
||||
}
|
||||
|
||||
class Triangle {
|
||||
List< VectorRef > refs = new ArrayList< VectorRef >();
|
||||
}
|
||||
|
||||
Map< Vector, VectorRef > vertices = new HashMap< Vector, VectorRef >();
|
||||
Collection< Triangle > triangles = new HashSet< Triangle >();
|
||||
|
||||
// Convert each facet to a triangle
|
||||
for ( final Facet facet : facets ) {
|
||||
if ( facet.points.size() != 3 ) {
|
||||
System.out.println( "Warning: Facet is not a triangle!" );
|
||||
}
|
||||
|
||||
final Triangle triangle = new Triangle();
|
||||
for ( final Vector vec : facet.points ) {
|
||||
VectorRef ref = null;
|
||||
for ( final Entry< Vector, VectorRef > entry : vertices.entrySet() ) {
|
||||
final Vector existingVec = entry.getKey();
|
||||
|
||||
if ( existingVec.distanceSquared( vec ) < 1e-8 ) {
|
||||
ref = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ref == null ) {
|
||||
ref = new VectorRef();
|
||||
vertices.put( vec, ref );
|
||||
}
|
||||
|
||||
triangle.refs.add( ref );
|
||||
}
|
||||
triangles.add( triangle );
|
||||
}
|
||||
|
||||
// Sort the vertices
|
||||
final List< Entry< Vector, VectorRef > > sorted = new ArrayList< Entry< Vector, VectorRef > >( vertices.entrySet() );
|
||||
Collections.sort( sorted, ( aEntry, bEntry ) -> {
|
||||
final Vector a = aEntry.getKey();
|
||||
final Vector b = bEntry.getKey();
|
||||
|
||||
final double xDiff = a.getX() - b.getX();
|
||||
if ( xDiff == 0 ) {
|
||||
final double yDiff = a.getY() - b.getY();
|
||||
if ( yDiff == 0 ) {
|
||||
return Double.compare( a.getZ(), b.getZ() );
|
||||
} else {
|
||||
return Double.compare( yDiff, 0 );
|
||||
}
|
||||
} else {
|
||||
return Double.compare( xDiff, 0 );
|
||||
}
|
||||
} );
|
||||
|
||||
try ( PrintWriter writer = new PrintWriter( file ) ) {
|
||||
writer.println( "ply" );
|
||||
writer.println( "format ascii 1.0" );
|
||||
writer.println( "comment Created by java tess" );
|
||||
writer.println( "element vertex " + sorted.size() );
|
||||
writer.println( "property float x" );
|
||||
writer.println( "property float y" );
|
||||
writer.println( "property float z" );
|
||||
writer.println( "element face " + triangles.size() );
|
||||
writer.println( "element property list uchar uint vertex_indices" );
|
||||
writer.println( "end_header" );
|
||||
|
||||
// Write vertices
|
||||
for ( int i = 0; i < sorted.size(); ++i ) {
|
||||
final Entry< Vector, VectorRef > entry = sorted.get( i );
|
||||
entry.getValue().index = i;
|
||||
|
||||
final Vector vector = entry.getKey();
|
||||
writer.println( String.format( "%f %f %f", vector.getX(), vector.getY(), vector.getZ() ) );
|
||||
}
|
||||
|
||||
for ( final Triangle triangle : triangles ) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( triangle.refs.size() );
|
||||
for ( final VectorRef ref : triangle.refs ) {
|
||||
builder.append( " " );
|
||||
builder.append( ref.index );
|
||||
}
|
||||
|
||||
writer.println( builder.toString() );
|
||||
}
|
||||
|
||||
System.out.println( "\tWriting " + triangles.size() + " triangles and " + sorted.size() + " vertices to " + file );
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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 ); } );
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
|
||||
return mesh.meshify();
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
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 ] ) );
|
||||
|
||||
// First parse the file to get a list of all bounding boxes that we can use
|
||||
List< AABB > boxes = new ArrayList< AABB >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
String[] values = line.split( "," );
|
||||
double minX = Double.parseDouble( values[ 0 ] );
|
||||
double minY = Double.parseDouble( values[ 1 ] );
|
||||
double minZ = Double.parseDouble( values[ 2 ] );
|
||||
double maxX = Double.parseDouble( values[ 3 ] );
|
||||
double maxY = Double.parseDouble( values[ 4 ] );
|
||||
double maxZ = Double.parseDouble( values[ 5 ] );
|
||||
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now that we have a bunch of bounding boxes, do whatever
|
||||
|
||||
MeshBuilder builder = new MeshBuilder();
|
||||
for ( AABB box : boxes ) {
|
||||
for ( Facet facet : generateFacetsFor( box ) ) {
|
||||
builder.addFacet( facet );
|
||||
}
|
||||
}
|
||||
|
||||
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 >();
|
||||
|
||||
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
||||
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
||||
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
||||
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
||||
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
||||
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
||||
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
||||
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p3 );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p5 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p1 );
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p6 );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
return facets;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
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.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Mesh speed test for facets
|
||||
*/
|
||||
public class MeshingTest5 {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File FACET_DIR = new File( BASE, "facets" );
|
||||
|
||||
private static Collection< Facet > facets = Collections.synchronizedCollection( new LinkedHashSet< Facet >() );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
synchronized( facets ) {
|
||||
facets.addAll( plane.convert( 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,991 @@
|
||||
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.MeshChainEventHandler;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.MeshPartitionEventHandler;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Facet meshing visualizer
|
||||
*/
|
||||
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.addChainHandler( new MeshChainEventHandler() {
|
||||
@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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
mesh.addPartitionHandler( new MeshPartitionEventHandler() {
|
||||
@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.addChainHandler( new MeshChainEventHandler() {
|
||||
@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 >();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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 com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Mesh speed test for polygons
|
||||
*/
|
||||
public class MeshingTest7 {
|
||||
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||
private static final File POLYGON_DIR = new File( BASE, "polygons" );
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( POLYGON_DIR.exists() && POLYGON_DIR.isDirectory() ) {
|
||||
System.out.println( "Found " + POLYGON_DIR.list().length + " files" );
|
||||
|
||||
final long allStart = System.currentTimeMillis();
|
||||
final AtomicInteger index = new AtomicInteger( 0 );
|
||||
final Collection< File > files = Arrays.asList( POLYGON_DIR.listFiles() );
|
||||
files.parallelStream().forEach( f -> {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append( "Meshing file: " + f + "\n" );
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
final List< Polygon > polygons = mesh( f, builder );
|
||||
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
for ( Polygon poly : polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
|
||||
mesh.meshify();
|
||||
|
||||
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" );
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + POLYGON_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
private static List< Polygon > mesh( File file, final StringBuilder stringBuilder ) {
|
||||
// First parse the file to get a list of all polygons
|
||||
final List< Polygon > polys = new ArrayList< Polygon >();
|
||||
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line;
|
||||
while ( ( line = reader.readLine() ) != null ) {
|
||||
if ( !line.isEmpty() ) {
|
||||
final int pointCount = Integer.parseInt( line );
|
||||
|
||||
List< Point > points = new ArrayList< Point >( pointCount );
|
||||
|
||||
int point = 0;
|
||||
while ( point < pointCount && ( 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 ] );
|
||||
points.add( new Point( px, py ) );
|
||||
++point;
|
||||
}
|
||||
}
|
||||
|
||||
polys.add( new Polygon( points ) ) ;
|
||||
}
|
||||
}
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return polys;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
|
||||
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
|
||||
import com.jme3.bullet.objects.PhysicsBody;
|
||||
import com.jme3.bullet.objects.PhysicsRigidBody;
|
||||
import com.jme3.math.Plane;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
public class MinieTest {
|
||||
|
||||
public static void main( String[] args ) {
|
||||
System.loadLibrary( "bulletjme" );
|
||||
|
||||
System.out.println( "Testing a simple PhysicsSpace" );
|
||||
PhysicsSpace space = new PhysicsSpace( BroadphaseType.DBVT );
|
||||
|
||||
PlaneCollisionShape plane = new PlaneCollisionShape( new Plane( new Vector3f( 0, 1, 0 ), 0 ) );
|
||||
PhysicsRigidBody planeBody = new PhysicsRigidBody( plane, PhysicsBody.massForStatic );
|
||||
planeBody.setPhysicsLocation( new Vector3f( 0, 0, 0 ) );
|
||||
|
||||
BoxCollisionShape box = new BoxCollisionShape( 0.5f );
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
rigid.setPhysicsLocation( new Vector3f( 0f, 100f, 0f ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
space.addCollisionObject( planeBody );
|
||||
|
||||
for ( float i = 0; i < 9.6; i += 1 / 256.0 ) {
|
||||
space.update( i );
|
||||
Vector3f loc = rigid.getPhysicsLocation();
|
||||
System.out.println( i + "\t: " + loc.getX() + ", " + loc.getY() + ", " + loc.getZ() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BukkitUtil;
|
||||
|
||||
public class CommandBase {
|
||||
protected PluginCommand command;
|
||||
|
||||
public CommandBase( String command ) {
|
||||
this.command = BukkitUtil.constructCommand( command );
|
||||
}
|
||||
|
||||
public CommandBase setPermission( String permission ) {
|
||||
command.setPermission( permission );
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBase setDescription( String description ) {
|
||||
command.setDescription( description );
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBase addAliases( String... aliases ) {
|
||||
Set< String > aliasSet = new HashSet< String >( command.getAliases() );
|
||||
for ( String alias : aliases ) {
|
||||
aliasSet.add( alias );
|
||||
}
|
||||
command.setAliases( new ArrayList< String >( aliasSet ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBase addAliases( Collection< String > aliases ) {
|
||||
Set< String > aliasSet = new HashSet< String >( command.getAliases() );
|
||||
aliasSet.addAll( aliases );
|
||||
command.setAliases( new ArrayList< String >( aliasSet ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBase setPermission( Permission permission ) {
|
||||
return setPermission( permission.toString() );
|
||||
}
|
||||
|
||||
public CommandBase setSubCommand( SubCommand subCommand ) {
|
||||
subCommand.applyTo( command );
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginCommand build() {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor.CommandExecutable;
|
||||
|
||||
public class CommandOption {
|
||||
protected CommandExecutable exe;
|
||||
protected String[] args;
|
||||
protected CommandParameters parameter;
|
||||
|
||||
public CommandOption( CommandExecutable exe, String[] args, CommandParameters parameter ) {
|
||||
this.exe = exe;
|
||||
this.args = args;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
public int getArgumentSize() {
|
||||
return args.length;
|
||||
}
|
||||
|
||||
public void execute( CommandSender sender ) {
|
||||
exe.execute( sender, args, parameter );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
public class CommandParameters {
|
||||
protected List< Object > parameters = new ArrayList< Object >();
|
||||
|
||||
public CommandParameters() {
|
||||
}
|
||||
|
||||
public CommandParameters( CommandParameters copy ) {
|
||||
parameters.addAll( copy.parameters );
|
||||
}
|
||||
|
||||
public void add( Object object ) {
|
||||
parameters.add( object );
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return parameters.size();
|
||||
}
|
||||
|
||||
public < T > T get( Class < T > clazz, int index ) {
|
||||
clazz = Primitives.wrap( clazz );
|
||||
if ( index < 0 || index >= parameters.size() ) {
|
||||
return null;
|
||||
}
|
||||
Object obj = parameters.get( index );
|
||||
if ( obj != null && clazz.isInstance( obj ) ) {
|
||||
return clazz.cast( obj );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public < T > T getLast( Class< T > clazz ) {
|
||||
clazz = Primitives.wrap( clazz );
|
||||
for ( int i = parameters.size() - 1; i >= 0; i-- ) {
|
||||
Object obj = parameters.get( i );
|
||||
if ( obj != null && clazz.isInstance( obj ) ) {
|
||||
return clazz.cast( obj );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public < T > T getFirst( Class< T > clazz ) {
|
||||
clazz = Primitives.wrap( clazz );
|
||||
for ( int i = 0; i < parameters.size(); i++ ) {
|
||||
Object obj = parameters.get( i );
|
||||
if ( obj != null && clazz.isInstance( obj ) ) {
|
||||
return clazz.cast( obj );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandParameters[" + parameters.stream().map( Object::toString ).collect( Collectors.joining( ", " ) ) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class CommandResult {
|
||||
protected List< CommandOption > options = new ArrayList< CommandOption >();
|
||||
|
||||
public void add( CommandOption option ) {
|
||||
options.add( option );
|
||||
}
|
||||
|
||||
public void addAll( Collection< CommandOption > options ) {
|
||||
this.options.addAll( options );
|
||||
}
|
||||
|
||||
public void add( CommandResult result ) {
|
||||
this.options.addAll( result.getOptions() );
|
||||
}
|
||||
|
||||
public List< CommandOption > getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void execute( CommandSender sender ) {
|
||||
CommandOption lowest = null;
|
||||
for ( CommandOption option : options ) {
|
||||
if ( lowest == null || option.getArgumentSize() < lowest.getArgumentSize() ) {
|
||||
lowest = option;
|
||||
}
|
||||
}
|
||||
if ( lowest != null ) {
|
||||
lowest.execute( sender );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
public class SplitCommand {
|
||||
protected String[] input;
|
||||
protected String[] arguments;
|
||||
|
||||
public SplitCommand( String[] input, String[] arguments ) {
|
||||
this.input = input;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public String[] getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput( String[] input ) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public String[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public void setArguments( String[] arguments ) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
|
||||
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.Set;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.util.StringUtil;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor.CommandExecutable;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.InputValidator;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.InputValidatorString;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender.SenderValidator;
|
||||
|
||||
/**
|
||||
* A build and run command framework for automatic tab completions and easy branching.
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
public class SubCommand {
|
||||
protected List< SubCommand > subCommands = new ArrayList< SubCommand >();
|
||||
|
||||
protected InputValidator< ? > validator;
|
||||
protected Set< SenderValidator > senderValidators = new HashSet< SenderValidator >();
|
||||
protected CommandExecutable whenUnknown;
|
||||
protected CommandExecutable whenNone;
|
||||
|
||||
/**
|
||||
* Accept anything as a valid input
|
||||
*/
|
||||
public SubCommand() {
|
||||
}
|
||||
|
||||
// Helped constructor, really belongs in a factory.
|
||||
/**
|
||||
* Create a SubCommand with the string as the input validator.
|
||||
*
|
||||
* @param command
|
||||
* The subcommand value.
|
||||
*/
|
||||
public SubCommand( String command ) {
|
||||
this( new InputValidatorString( command ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SubCommand with the input validator provided.
|
||||
*
|
||||
* @param validator
|
||||
* Can be null.
|
||||
*/
|
||||
public SubCommand( InputValidator< ? > validator ) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public SubCommand add( SubCommand builder ) {
|
||||
subCommands.add( builder );
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubCommand addSenderValidator( SenderValidator validator ) {
|
||||
senderValidators.add( validator );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ran when the arguments provided don't match any SubCommands registered.
|
||||
*
|
||||
* @param executable
|
||||
* An executable where the arguments will start with the unknown subcommand.
|
||||
* @return
|
||||
* Builder pattern return.
|
||||
*/
|
||||
public SubCommand whenUnknown( CommandExecutable executable ) {
|
||||
this.whenUnknown = executable;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Naming by jetp250
|
||||
/**
|
||||
* Ran when there are no arguments provided, or if the executable for when unknown is not set.
|
||||
*
|
||||
* @param executable
|
||||
* If null, nothing will happen.
|
||||
* @return
|
||||
* Builder pattern return.
|
||||
*/
|
||||
public SubCommand defaultTo( CommandExecutable executable ) {
|
||||
this.whenNone = executable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean matches( CommandSender sender ) {
|
||||
for ( SenderValidator validator : senderValidators ) {
|
||||
if ( !validator.isValid( sender ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean matches( CommandSender sender, String input[], String[] args ) {
|
||||
return ( validator == null ? true : validator.isValid( sender, input, args ) ) && matches( sender );
|
||||
}
|
||||
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
return validator == null ? null : validator.getTabCompletes( sender, input );
|
||||
}
|
||||
|
||||
public List< SubCommand > getSubCommands() {
|
||||
return subCommands;
|
||||
}
|
||||
|
||||
public InputValidator< ? > getInputValidator() {
|
||||
return validator;
|
||||
}
|
||||
|
||||
public Set< SenderValidator > getSenderValidators() {
|
||||
return senderValidators;
|
||||
}
|
||||
|
||||
public CommandResult submit( CommandSender sender, String[] command, String[] args, CommandParameters parameter ) {
|
||||
CommandResult result = new CommandResult();
|
||||
parameter = new CommandParameters( parameter );
|
||||
if ( validator != null ) {
|
||||
parameter.add( validator.get( sender, command ) );
|
||||
} else {
|
||||
parameter.add( null );
|
||||
}
|
||||
if ( args.length > 0 ) {
|
||||
boolean found = false;
|
||||
for ( SubCommand subCommand : subCommands ) {
|
||||
SplitCommand split = split( args, subCommand.getInputValidator().getArgumentCount() );
|
||||
String[] input = split.getInput();
|
||||
String[] newArgs = split.getArguments();
|
||||
if ( subCommand.matches( sender, input, newArgs ) ) {
|
||||
found = true;
|
||||
|
||||
result.add( subCommand.submit( sender, input, newArgs, parameter ) );
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
if ( whenUnknown != null ) {
|
||||
result.add( new CommandOption( whenUnknown, args, parameter ) );
|
||||
} else if ( whenNone != null ) {
|
||||
result.add( new CommandOption( whenNone, args, parameter ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( whenNone != null ) {
|
||||
result.add( new CommandOption( whenNone, args, parameter ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection< String > getTabCompletions( CommandSender sender, String[] args ) {
|
||||
Set< String > tabs = new HashSet< String >();
|
||||
if ( args.length > 0 ) {
|
||||
for ( SubCommand subCommand : subCommands ) {
|
||||
if ( subCommand.matches( sender ) ) {
|
||||
SplitCommand split = split( args, subCommand.getInputValidator().getArgumentCount() );
|
||||
String[] input = split.getInput();
|
||||
String[] newArgs = split.getArguments();
|
||||
// Check if the subcommand matches the argument, and if it has subcommands of its own
|
||||
if ( subCommand.matches( sender, input, newArgs ) && !subCommand.getSubCommands().isEmpty() ) {
|
||||
tabs.addAll( subCommand.getTabCompletions( sender, newArgs ) );
|
||||
} else if ( newArgs.length == 0 ) {
|
||||
// If not, and this is the last argument, add all possible tab completes
|
||||
// This allows for "recommendations"
|
||||
Collection< String > completions = subCommand.getTabCompletes( sender, input );
|
||||
if ( completions != null ) {
|
||||
tabs.addAll( completions );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
public SubCommand applyTo( PluginCommand command ) {
|
||||
command.setExecutor( this::onCommand );
|
||||
command.setTabCompleter( this::onTabComplete );
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean onCommand( CommandSender sender, Command command, String arg2, String[] args ) {
|
||||
CommandParameters parameters = new CommandParameters();
|
||||
if ( matches( sender ) ) {
|
||||
String[] commandArr = new String[ args.length + 1 ];
|
||||
|
||||
commandArr[ 0 ] = command.getName();
|
||||
for ( int i = 1; i < commandArr.length; i++ ) {
|
||||
commandArr[ i ] = args[ i - 1 ];
|
||||
}
|
||||
|
||||
SplitCommand split = split( commandArr, validator.getArgumentCount() );
|
||||
submit( sender, split.getInput(), split.getArguments(), parameters ).execute( sender );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List< String > onTabComplete( CommandSender sender, Command command, String arg2, String[] args ) {
|
||||
List< String > completions = new ArrayList< String >();
|
||||
String[] commandArr = new String[ args.length + 1 ];
|
||||
|
||||
commandArr[ 0 ] = command.getName();
|
||||
for ( int i = 1; i < commandArr.length; i++ ) {
|
||||
commandArr[ i ] = args[ i - 1 ];
|
||||
}
|
||||
|
||||
SplitCommand split = split( commandArr, validator.getArgumentCount() );
|
||||
StringUtil.copyPartialMatches( args[ args.length - 1 ], getTabCompletions( sender, split.getArguments() ), completions );
|
||||
Collections.sort( completions );
|
||||
return completions;
|
||||
}
|
||||
|
||||
protected static SplitCommand split( String[] command, int inputSize ) {
|
||||
String[] input = command.length > 0 ? Arrays.copyOfRange( command, 0, Math.min( inputSize, command.length ) ) : new String[ 0 ];
|
||||
String[] args = command.length > inputSize ? Arrays.copyOfRange( command, inputSize, command.length ) : new String[ 0 ];
|
||||
|
||||
return new SplitCommand( input, args );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.CommandParameters;
|
||||
|
||||
public interface CommandExecutable {
|
||||
void execute( CommandSender sender, String[] args, CommandParameters params );
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.CommandParameters;
|
||||
|
||||
public class CommandExecutableMessage implements CommandExecutable {
|
||||
protected String message;
|
||||
|
||||
public CommandExecutableMessage( String message ) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( CommandSender sender, String[] args, CommandParameters params ) {
|
||||
sender.sendMessage( message );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor;
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Classes for building complex command trees.
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command;
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public interface InputValidator< T > {
|
||||
Collection< String > getTabCompletes( CommandSender sender, String[] input );
|
||||
boolean isValid( CommandSender sender, String[] input, String[] args );
|
||||
T get( CommandSender sender, String[] input );
|
||||
default int getArgumentCount() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorArguments implements InputValidator< Void > {
|
||||
protected int min;
|
||||
protected int max;
|
||||
|
||||
public InputValidatorArguments( int amount ) {
|
||||
this.min = amount;
|
||||
this.max = amount;
|
||||
}
|
||||
|
||||
public InputValidatorArguments( int min, int 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 ) {
|
||||
int len = args.length;
|
||||
return len >= min && len <= max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void get( CommandSender sender, String input[] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorBoolean implements InputValidator< Boolean > {
|
||||
protected Set< String > trueVals = new HashSet< String >();
|
||||
protected Set< String > falseVals = new HashSet< String >();
|
||||
|
||||
public InputValidatorBoolean( String[] trueVal, String[] falseVal ) {
|
||||
for ( String str : trueVal ) {
|
||||
trueVals.add( str.toLowerCase() );
|
||||
}
|
||||
|
||||
for ( String str : falseVal ) {
|
||||
falseVals.add( str.toLowerCase() );
|
||||
}
|
||||
}
|
||||
|
||||
public InputValidatorBoolean() {
|
||||
trueVals.add( "true" );
|
||||
falseVals.add( "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
Set< String > combined = new HashSet< String >( trueVals );
|
||||
combined.addAll( falseVals );
|
||||
return combined;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String input[], String[] args ) {
|
||||
String lowercase = input[ 0 ].toLowerCase();
|
||||
return trueVals.contains( lowercase ) || falseVals.contains( lowercase );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get( CommandSender sender, String input[] ) {
|
||||
String lowercase = input[ 0 ].toLowerCase();
|
||||
return trueVals.contains( lowercase );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorChain< T > implements InputValidator< T > {
|
||||
protected InputValidator< T > primaryValidator;
|
||||
protected List< InputValidator< ? > > validators = new ArrayList< InputValidator< ? > >();
|
||||
|
||||
public InputValidatorChain( InputValidator< T > primaryValidator ) {
|
||||
this.primaryValidator = primaryValidator;
|
||||
}
|
||||
|
||||
public InputValidatorChain< T > addValidator( InputValidator< ? > validator ) {
|
||||
validators.add( validator );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
return primaryValidator.getTabCompletes( sender, input );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String input[], String[] args ) {
|
||||
if ( !primaryValidator.isValid( sender, input, args ) ) {
|
||||
return false;
|
||||
}
|
||||
for ( InputValidator< ? > validator : validators ) {
|
||||
if ( !validator.isValid( sender, input, args ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get( CommandSender sender, String input[] ) {
|
||||
return primaryValidator.get( sender, input );
|
||||
}
|
||||
}
|
||||
@@ -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 ] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorInt implements InputValidator< Integer > {
|
||||
protected int min = Integer.MIN_VALUE;
|
||||
protected int max = Integer.MAX_VALUE;
|
||||
|
||||
public InputValidatorInt() {
|
||||
}
|
||||
|
||||
public InputValidatorInt( int min, int 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 {
|
||||
int i = Integer.valueOf( input[ 0 ] );
|
||||
return i >= min && i <= max;
|
||||
} catch ( Exception exception ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get( CommandSender sender, String input[] ) {
|
||||
return Integer.parseInt( input[ 0 ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorPattern implements InputValidator< String > {
|
||||
protected String pattern;
|
||||
|
||||
public InputValidatorPattern( String pattern ) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String input[], String[] args ) {
|
||||
return input[ 0 ].matches( pattern );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get( CommandSender sender, String input[] ) {
|
||||
return input[ 0 ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class InputValidatorPlayer implements InputValidator< Player > {
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
Set< String > playerNames = new HashSet< String >();
|
||||
for ( Player player : Bukkit.getOnlinePlayers() ) {
|
||||
playerNames.add( player.getName() );
|
||||
}
|
||||
return playerNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String input[], String[] args ) {
|
||||
return Bukkit.getPlayerExact( input[ 0 ] ) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player get( CommandSender sender, String input[] ) {
|
||||
return Bukkit.getPlayerExact( input[ 0 ] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class InputValidatorString implements InputValidator< String > {
|
||||
protected Set< String > values = new HashSet< String >();
|
||||
|
||||
public InputValidatorString( String value ) {
|
||||
values.add( value.toLowerCase() );
|
||||
}
|
||||
|
||||
public InputValidatorString( String... values ) {
|
||||
Validate.isTrue( values.length > 0, "Must provide at least 1 argument!" );
|
||||
for ( String str : values ) {
|
||||
this.values.add( str.toLowerCase() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< String > getTabCompletes( CommandSender sender, String[] input ) {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender, String input[], String[] args ) {
|
||||
return values.contains( input[ 0 ].toLowerCase() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get( CommandSender sender, String input[] ) {
|
||||
return input[ 0 ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator;
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public interface SenderValidator {
|
||||
boolean isValid( CommandSender sender );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class SenderValidatorNotPlayer implements SenderValidator {
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender ) {
|
||||
return !( sender instanceof Player );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class SenderValidatorPermission implements SenderValidator {
|
||||
protected Set< String > permissions = new HashSet< String >();
|
||||
|
||||
public SenderValidatorPermission( String... permissions ) {
|
||||
for ( String permission : permissions ) {
|
||||
this.permissions.add( permission );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender ) {
|
||||
for ( String permission : permissions ) {
|
||||
if ( sender.hasPermission( permission ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class SenderValidatorPlayer implements SenderValidator {
|
||||
@Override
|
||||
public boolean isValid( CommandSender sender ) {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender;
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
||||
public class Aabb implements Serializable {
|
||||
private static final long serialVersionUID = 6518512405568765913L;
|
||||
|
||||
public final int minX;
|
||||
public final int minY;
|
||||
public final int minZ;
|
||||
public final int maxX;
|
||||
public final int maxY;
|
||||
public final int maxZ;
|
||||
|
||||
public final int width;
|
||||
public final int depth;
|
||||
public final int height;
|
||||
|
||||
public Aabb( final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ ) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.maxZ = maxZ;
|
||||
|
||||
this.width = maxX - minX;
|
||||
this.depth = maxZ - minZ;
|
||||
this.height = maxY - minY;
|
||||
}
|
||||
|
||||
public Aabb( final BoundingBox box ) {
|
||||
this.minX = ( int ) box.getMinX();
|
||||
this.maxX = ( int ) box.getMaxX();
|
||||
this.minY = ( int ) box.getMinY();
|
||||
this.maxY = ( int ) box.getMaxY();
|
||||
this.minZ = ( int ) box.getMinZ();
|
||||
this.maxZ = ( int ) box.getMaxZ();
|
||||
|
||||
this.width = maxX - minX;
|
||||
this.depth = maxZ - minZ;
|
||||
this.height = maxY - minY;
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return width * depth * height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
public enum BlockDataType {
|
||||
NONE, COMPLEX, SOLID
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BinaryWriter;
|
||||
|
||||
public class BlockDataTypeSet {
|
||||
private static final int BLOCK_SIZE;
|
||||
private static final BitSet[] SETS;
|
||||
|
||||
static {
|
||||
final int length = BlockDataType.values().length;
|
||||
BLOCK_SIZE = 32 - Integer.numberOfLeadingZeros( length );
|
||||
SETS = new BitSet[ length ];
|
||||
|
||||
// Perhaps not the fastest, since it sets each bit in N time
|
||||
for ( int i = 0; i < length; ++i ) {
|
||||
final BitSet set = new BitSet();
|
||||
for ( int j = 0; j < BLOCK_SIZE; ++j ) {
|
||||
set.set( j, ( ( i >> j ) & 1 ) == 1 );
|
||||
}
|
||||
SETS[ i ] = set;
|
||||
}
|
||||
}
|
||||
|
||||
protected BitSet buffer;
|
||||
|
||||
public BlockDataTypeSet( final byte[] arr ) {
|
||||
buffer = BitSet.valueOf( arr );
|
||||
}
|
||||
|
||||
public BlockDataTypeSet( final int size ) {
|
||||
buffer = new BitSet( size * BLOCK_SIZE );
|
||||
}
|
||||
|
||||
public void set( final int index, final BlockDataType type ) {
|
||||
final BitSet set = SETS[ type.ordinal() ];
|
||||
for ( int i = 0; i < BLOCK_SIZE; ++i ) {
|
||||
buffer.set( ( index * BLOCK_SIZE ) + i, set.get( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
public BlockDataType get( final int index ) {
|
||||
final int bufferIndex = index * BLOCK_SIZE;
|
||||
int ord = 0;
|
||||
for ( int i = buffer.nextSetBit( bufferIndex ); i >= bufferIndex && i < bufferIndex + BLOCK_SIZE; i = buffer.nextSetBit( i + 1 ) ) {
|
||||
ord |= 1 << ( i - bufferIndex );
|
||||
}
|
||||
return BlockDataType.values()[ ord ];
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
/**
|
||||
* Represents a Bukkit chunk's location.
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
public class ChunkLocation implements Comparable< ChunkLocation > {
|
||||
private int x, z;
|
||||
private String worldName;
|
||||
private World world;
|
||||
|
||||
/**
|
||||
* Construct a ChunkLocation from a Location.
|
||||
*
|
||||
* @param location
|
||||
* Cannot be null.
|
||||
*/
|
||||
public ChunkLocation( Location location ) {
|
||||
Validate.notNull( location );
|
||||
world = location.getWorld();
|
||||
worldName = world.getName();
|
||||
x = location.getBlockX() >> 4;
|
||||
z = location.getBlockZ() >> 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a ChunkLocation.
|
||||
*
|
||||
* @param location
|
||||
* Cannot be null.
|
||||
*/
|
||||
public ChunkLocation( ChunkLocation location ) {
|
||||
Validate.notNull( location );
|
||||
x = location.x;
|
||||
z = location.z;
|
||||
worldName = location.worldName;
|
||||
world = location.world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ChunkLocation from chunk coordinates.
|
||||
*
|
||||
* @param world
|
||||
* Cannot be null.
|
||||
* @param x
|
||||
* Chunk coordinate x value.
|
||||
* @param z
|
||||
* Chunk coordinate z value.
|
||||
*/
|
||||
public ChunkLocation( World world, int x, int z ) {
|
||||
Validate.notNull( world );
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.worldName = world.getName();
|
||||
this.world = world;
|
||||
|
||||
}
|
||||
|
||||
public ChunkLocation( String world, int x, int z ) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.worldName = world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ChunkLocation from a Bukkit chunk.
|
||||
*
|
||||
* @param chunk
|
||||
* Cannot be null.
|
||||
*/
|
||||
public ChunkLocation( Chunk chunk ) {
|
||||
Validate.notNull( chunk );
|
||||
world = chunk.getWorld();
|
||||
worldName = world.getName();
|
||||
x = chunk.getX();
|
||||
z = chunk.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ChunkLocation from a ChunkSnapshot.
|
||||
*
|
||||
* @param snapshot
|
||||
* Cannot be null. The world will not be cached, only the world name.
|
||||
*/
|
||||
public ChunkLocation( ChunkSnapshot snapshot ) {
|
||||
Validate.notNull( snapshot );
|
||||
worldName = snapshot.getWorldName();
|
||||
x = snapshot.getX();
|
||||
z = snapshot.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate.
|
||||
*
|
||||
* @return
|
||||
* Chunk coordinate x value.
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x coordinate.
|
||||
*
|
||||
* @param x
|
||||
* Chunk coordinate x value.
|
||||
*/
|
||||
public ChunkLocation setX( int x ) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the z coordinate.
|
||||
*
|
||||
* @return
|
||||
* Chunk coordinate z value.
|
||||
*/
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the z coordinate.
|
||||
*
|
||||
* @param z
|
||||
* Chunk coordinate z value.
|
||||
*/
|
||||
public ChunkLocation setZ( int z ) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add coordinates from current coordinates.
|
||||
*
|
||||
* @param x
|
||||
* Chunk x coordinate value.
|
||||
* @param z
|
||||
* Chunk z coordinate value.
|
||||
* @return
|
||||
* This location.
|
||||
*/
|
||||
public ChunkLocation add( int x, int z ) {
|
||||
this.x += x;
|
||||
this.z += z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract coordinates from current coordinates.
|
||||
*
|
||||
* @param x
|
||||
* Chunk x coordinate value.
|
||||
* @param z
|
||||
* Chunk z coordinate value.
|
||||
* @return
|
||||
* This location.
|
||||
*/
|
||||
public ChunkLocation subtract( int x, int z ) {
|
||||
this.x -= x;
|
||||
this.z -= z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getWorldName() {
|
||||
return worldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world.
|
||||
*
|
||||
* @return
|
||||
* A non-null world, will fetch and cache the world if not cached already.
|
||||
*/
|
||||
public World getWorld() {
|
||||
if ( world == null ) {
|
||||
world = Bukkit.getWorld( worldName );
|
||||
}
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the world.
|
||||
*
|
||||
* @param world
|
||||
* Cannot be null.
|
||||
*/
|
||||
public void setWorld( World world ) {
|
||||
Validate.notNull( world );
|
||||
this.world = world;
|
||||
this.worldName = world.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chunk this location represents.
|
||||
*
|
||||
* @return
|
||||
* May load the chunk.
|
||||
*/
|
||||
public Chunk getChunk() {
|
||||
return world.getChunkAt( x, z );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the chunk that this location represents is loaded.
|
||||
*
|
||||
* @return
|
||||
* If the chunk is loaded. Does not load the chunk.
|
||||
*/
|
||||
public boolean isLoaded() {
|
||||
return getWorld().isChunkLoaded( x, z );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the chunk that this location represents.
|
||||
*/
|
||||
public void load() {
|
||||
getWorld().loadChunk( x, z );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this chunk exists.
|
||||
*
|
||||
* @return
|
||||
* Checks if the chunk exists, does not have to be loaded.
|
||||
*/
|
||||
public boolean exists() {
|
||||
return getWorld().loadChunk( x, z, false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( worldName == null ) ? 0 : worldName.hashCode() );
|
||||
result = prime * result + x;
|
||||
result = prime * result + z;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChunkLocation other = ( ChunkLocation ) obj;
|
||||
|
||||
if ( worldName == null ) {
|
||||
if ( other.worldName != null ) {
|
||||
return false;
|
||||
}
|
||||
} else if ( !worldName.equals( other.worldName ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( x != other.x ) {
|
||||
return false;
|
||||
}
|
||||
if ( z != other.z ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ChunkLocation{x:" + x + ",z:" + z + ",world:" + worldName + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo( ChunkLocation o ) {
|
||||
final int worldCompare = worldName.compareTo( o.worldName );
|
||||
if ( worldCompare == 0 ) {
|
||||
if ( x == o.x ) {
|
||||
return Integer.compare( z, o.z );
|
||||
}
|
||||
return Integer.compare( x, o.x );
|
||||
}
|
||||
return worldCompare;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.MinecraftPlane.PlaneAxis;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BinaryWriter;
|
||||
import com.jme3.bullet.collision.shapes.infos.IndexedMesh;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
public class ChunkMesh {
|
||||
// TODO Public for now while I figure out how to organize the stuff properly
|
||||
|
||||
// Hashcode of the current block's blockdata so we can check if it changed
|
||||
private int[] data;
|
||||
// The bounding boxes for each complex block, null if air or a cube
|
||||
private BoundingBox[][] boxes;
|
||||
// Stores which blocks are complex/simple/empty
|
||||
private BlockDataTypeSet blockTypes;
|
||||
// References to every point used by at least one planemesh
|
||||
public TreeSet< VectorReference > pointReferences = new TreeSet< VectorReference >();
|
||||
// Map of meshes to each plane
|
||||
// TODO Allow for non axis-aligned planes
|
||||
public Map< MinecraftPlane, PlaneMesh > planes = new TreeMap< MinecraftPlane, PlaneMesh >();
|
||||
// Bounding box of this mesh
|
||||
private Aabb box;
|
||||
|
||||
public ChunkMesh( BoundingBox box ) {
|
||||
this.box = new Aabb( box );
|
||||
|
||||
final int volume = this.box.getVolume();
|
||||
data = new int[ volume ];
|
||||
boxes = new BoundingBox[ volume ][];
|
||||
blockTypes = new BlockDataTypeSet( volume );
|
||||
}
|
||||
|
||||
public ChunkMesh( Aabb box ) {
|
||||
this.box = box;
|
||||
|
||||
final int volume = this.box.getVolume();
|
||||
data = new int[ volume ];
|
||||
boxes = new BoundingBox[ volume ][];
|
||||
blockTypes = new BlockDataTypeSet( volume );
|
||||
}
|
||||
|
||||
public void setType( final int index, final BlockDataType type ) {
|
||||
blockTypes.set( index, type );
|
||||
}
|
||||
|
||||
public BlockDataType getType( final int index ) {
|
||||
return blockTypes.get( index );
|
||||
}
|
||||
|
||||
public void setData( final int index, final BlockData data ) {
|
||||
this.data[ index ] = data.getAsString().hashCode();
|
||||
}
|
||||
|
||||
public void setBoxes( final int index, final BoundingBox[] boxes ) {
|
||||
this.boxes[ index ] = boxes;
|
||||
}
|
||||
|
||||
public boolean isBlockAt( final int index, final BlockData data ) {
|
||||
return blockTypes.get( index ) == BlockDataType.COMPLEX && this.data[ index ] == data.getAsString().hashCode();
|
||||
}
|
||||
|
||||
public BoundingBox[] getBoxesAt( final int index ) {
|
||||
return boxes[ index ];
|
||||
}
|
||||
|
||||
public PlaneMesh getOrAdd( final MinecraftPlane plane ) {
|
||||
PlaneMesh results = planes.get( plane );
|
||||
if ( results == null ) {
|
||||
results = new PlaneMesh();
|
||||
planes.put( plane, results );
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public IndexedMesh toIndexedMesh() {
|
||||
final Vector3f[] positionArray = new Vector3f[ pointReferences.size() ];
|
||||
int vertexIndex = 0;
|
||||
for ( final VectorReference ref : pointReferences ) {
|
||||
final Vector vector = ref.vector;
|
||||
|
||||
positionArray[ vertexIndex ] = new Vector3f( ( float ) vector.getX(), ( float ) vector.getY(), ( float ) vector.getZ() );
|
||||
ref.index = vertexIndex;
|
||||
|
||||
++vertexIndex;
|
||||
}
|
||||
|
||||
final int totalTriangleCount = planes.values().stream().mapToInt( m -> m.completedPolygons.size() ).sum();
|
||||
|
||||
int triangleIndex = 0;
|
||||
final int[] indexArray = new int[ totalTriangleCount * 3 ];
|
||||
for ( final PlaneMesh mesh : planes.values() ) {
|
||||
for ( final IndexedPolygon triangle : mesh.completedPolygons ) {
|
||||
if ( triangle.points.size() != 3 ) {
|
||||
throw new IllegalStateException( "Triangle does not have exactly 3 vertices!" );
|
||||
}
|
||||
|
||||
final int indexArrayStart = triangleIndex * 3;
|
||||
indexArray[ indexArrayStart ] = triangle.points.get( 0 ).index;
|
||||
indexArray[ indexArrayStart + 1 ] = triangle.points.get( 1 ).index;
|
||||
indexArray[ indexArrayStart + 2 ] = triangle.points.get( 2 ).index;
|
||||
|
||||
++triangleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexedMesh( positionArray, indexArray );
|
||||
}
|
||||
|
||||
public void writeTo( final BinaryWriter writer ) {
|
||||
// Gather all vectors and map them to indices
|
||||
|
||||
|
||||
|
||||
// Write the bounding box
|
||||
writer.write( box.minX );
|
||||
writer.write( box.minY );
|
||||
writer.write( box.minZ );
|
||||
writer.write( box.maxX );
|
||||
writer.write( box.maxY );
|
||||
writer.write( box.maxZ );
|
||||
|
||||
// Write our data blocks
|
||||
for ( final int hash : data ) {
|
||||
writer.write( hash );
|
||||
}
|
||||
|
||||
// Write our bounding boxes
|
||||
for ( final BoundingBox[] boxArr : boxes ) {
|
||||
// Write how many boxes
|
||||
if ( boxArr == null ) {
|
||||
writer.write( ( int ) 0 );
|
||||
} else {
|
||||
writer.write( boxArr.length );
|
||||
|
||||
for ( final BoundingBox box : boxArr ) {
|
||||
writer.write( box.getMinX() );
|
||||
writer.write( box.getMinY() );
|
||||
writer.write( box.getMinZ() );
|
||||
writer.write( box.getMaxX() );
|
||||
writer.write( box.getMaxY() );
|
||||
writer.write( box.getMaxZ() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write our block types
|
||||
final byte[] blockTypeBuf = blockTypes.toByteArray();
|
||||
writer.write( blockTypeBuf.length );
|
||||
writer.write( blockTypeBuf );
|
||||
|
||||
// Write our point references
|
||||
writer.write( pointReferences.size() );
|
||||
for ( final VectorReference ref : pointReferences ) {
|
||||
writer.write( ref.vector.getX() );
|
||||
writer.write( ref.vector.getY() );
|
||||
writer.write( ref.vector.getZ() );
|
||||
writer.write( ref.index );
|
||||
writer.write( ref.referenceCount );
|
||||
}
|
||||
|
||||
// Write our planes
|
||||
writer.write( planes.size() );
|
||||
for ( final Entry< MinecraftPlane, PlaneMesh > entry : planes.entrySet() ) {
|
||||
final MinecraftPlane plane = entry.getKey();
|
||||
writer.write( plane.normal.ordinal() );
|
||||
writer.write( plane.offset );
|
||||
|
||||
final PlaneMesh mesh = entry.getValue();
|
||||
|
||||
// Write each region
|
||||
writer.write( mesh.generatedRegions.size() );
|
||||
for ( final Polygon poly : mesh.generatedRegions ) {
|
||||
writer.write( poly.getPoints().size() );
|
||||
for ( final Point p : poly.getPoints() ) {
|
||||
writer.write( p.getX() );
|
||||
writer.write( p.getY() );
|
||||
}
|
||||
}
|
||||
|
||||
// Write each triangle
|
||||
writer.write( mesh.completedPolygons.size() );
|
||||
for ( final IndexedPolygon poly : mesh.completedPolygons ) {
|
||||
writer.write( poly.points.size() );
|
||||
for ( final VectorReference ref : poly.points ) {
|
||||
writer.write( ref.index );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ChunkMesh readFrom( final ByteBuffer buffer ) {
|
||||
final Aabb box = new Aabb( buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt() );
|
||||
|
||||
final ChunkMesh mesh = new ChunkMesh( box );
|
||||
|
||||
final int size = box.getVolume();
|
||||
|
||||
buffer.asIntBuffer().get( mesh.data );
|
||||
buffer.position( buffer.position() + ( mesh.data.length << 2 ) );
|
||||
|
||||
// Read our boxes
|
||||
for ( int i = 0; i < size; ++i ) {
|
||||
final int boxCount = buffer.getInt();
|
||||
if ( boxCount > 0 ) {
|
||||
final BoundingBox[] boxes = new BoundingBox[ boxCount ];
|
||||
for ( int j = 0; j < boxCount; ++j ) {
|
||||
boxes[ j ] = new BoundingBox( buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble() );
|
||||
}
|
||||
mesh.boxes[ i ] = boxes;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the block types
|
||||
final int blockTypeBufSize = buffer.getInt();
|
||||
final byte[] bitSet = new byte[ blockTypeBufSize ];
|
||||
buffer.get( bitSet );
|
||||
mesh.blockTypes = new BlockDataTypeSet( bitSet );
|
||||
|
||||
// Read the plane references
|
||||
final int pointCount = buffer.getInt();
|
||||
final VectorReference[] references = new VectorReference[ pointCount ];
|
||||
for ( int i = 0; i < pointCount; ++i ) {
|
||||
final VectorReference reference = new VectorReference( new Vector( buffer.getDouble(), buffer.getDouble(), buffer.getDouble() ) );
|
||||
reference.index = buffer.getInt();
|
||||
reference.referenceCount = buffer.getInt();
|
||||
references[ reference.index ] = reference;
|
||||
mesh.pointReferences.add( reference );
|
||||
}
|
||||
|
||||
// Get the planes
|
||||
final int planeCount = buffer.getInt();
|
||||
for ( int i = 0; i < planeCount; ++i ) {
|
||||
final MinecraftPlane plane = new MinecraftPlane( PlaneAxis.values()[ buffer.getInt() ], buffer.getDouble() );
|
||||
|
||||
final PlaneMesh planeMesh = new PlaneMesh();
|
||||
planeMesh.generatedRegions = new ArrayDeque< Polygon >();
|
||||
planeMesh.completedPolygons = new ArrayDeque< IndexedPolygon >();
|
||||
final int regionCount = buffer.getInt();
|
||||
for ( int j = 0; j < regionCount; ++j ) {
|
||||
final int vertexCount = buffer.getInt();
|
||||
|
||||
final List< Point > points = new ArrayList< Point >();
|
||||
for ( int k = 0; k < vertexCount; ++k ) {
|
||||
points.add( new Point( buffer.getDouble(), buffer.getDouble() ) );
|
||||
}
|
||||
|
||||
planeMesh.generatedRegions.add( new Polygon( points ) );
|
||||
}
|
||||
|
||||
final int triangleCount = buffer.getInt();
|
||||
for ( int j = 0; j < triangleCount; ++j ) {
|
||||
final int vertexCount = buffer.getInt();
|
||||
|
||||
final IndexedPolygon triangle = new IndexedPolygon();
|
||||
for ( int k = 0; k < vertexCount; ++k ) {
|
||||
triangle.points.add( references[ buffer.getInt() ] );
|
||||
}
|
||||
|
||||
planeMesh.completedPolygons.add( triangle );
|
||||
}
|
||||
|
||||
mesh.planes.put( plane, planeMesh );
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IndexedPolygon {
|
||||
public List< VectorReference > points = new ArrayList< VectorReference >();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
|
||||
public class MinecraftPlane implements Comparable< MinecraftPlane > {
|
||||
public final PlaneAxis normal;
|
||||
public final double offset;
|
||||
|
||||
public MinecraftPlane( final PlaneAxis axis, final double offset ) {
|
||||
this.normal = axis;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public Vector convert( final Point point ) {
|
||||
switch ( normal ) {
|
||||
case X:
|
||||
return new Vector( offset, point.getX(), point.getY() );
|
||||
case Y:
|
||||
return new Vector( point.getX(), offset, point.getY() );
|
||||
case Z:
|
||||
return new Vector( point.getX(), point.getY(), offset );
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "No normal set!" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo( final MinecraftPlane o ) {
|
||||
if ( normal == o.normal ) {
|
||||
return Double.compare( offset, o.offset );
|
||||
}
|
||||
return normal.compareTo( o.normal );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((normal == null) ? 0 : normal.hashCode());
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(offset);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MinecraftPlane other = (MinecraftPlane) obj;
|
||||
if (normal != other.normal)
|
||||
return false;
|
||||
if (Double.doubleToLongBits(offset) != Double.doubleToLongBits(other.offset))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MinecraftPlane{axis=" + normal + ",offset=" + offset + "}";
|
||||
}
|
||||
|
||||
public static enum PlaneAxis {
|
||||
X, Y, Z
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
|
||||
public class PlaneMesh {
|
||||
public Collection< Polygon > generatedRegions;
|
||||
public Collection< IndexedPolygon > completedPolygons;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class VectorReference implements Comparable< VectorReference > {
|
||||
public Vector vector;
|
||||
public int referenceCount = 0;
|
||||
public int index = 0;
|
||||
|
||||
public VectorReference( final Vector vector ) {
|
||||
this.vector = vector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo( final VectorReference o ) {
|
||||
final Vector oVec = o.vector;
|
||||
|
||||
if ( vector.getX() == oVec.getX() ) {
|
||||
if ( vector.getY() == oVec.getY() ) {
|
||||
if ( vector.getZ() == oVec.getZ() ) {
|
||||
return 0;
|
||||
}
|
||||
return Double.compare( vector.getZ(), oVec.getZ() );
|
||||
}
|
||||
return Double.compare( vector.getY(), oVec.getY() );
|
||||
}
|
||||
return Double.compare( vector.getX(), oVec.getX() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Facet {
|
||||
public Vector normal;
|
||||
public List< Vector > points = new ArrayList< Vector >();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class LineSegment {
|
||||
public Vector point1, point2;
|
||||
|
||||
public LineSegment( Vector point1, Vector point2 ) {
|
||||
if ( point1.getX() < point2.getX() ) {
|
||||
this.point1 = point1;
|
||||
this.point2 = point2;
|
||||
} else if ( point1.getX() > point2.getX() ) {
|
||||
this.point1 = point2;
|
||||
this.point2 = point1;
|
||||
} else if ( point1.getY() < point2.getY() ) {
|
||||
this.point1 = point1;
|
||||
this.point2 = point2;
|
||||
} else if ( point1.getY() > point2.getY() ) {
|
||||
this.point1 = point2;
|
||||
this.point2 = point1;
|
||||
} else if ( point1.getZ() < point2.getZ() ) {
|
||||
this.point1 = point1;
|
||||
this.point2 = point2;
|
||||
} else if ( point1.getZ() > point2.getZ() ) {
|
||||
this.point1 = point2;
|
||||
this.point2 = point1;
|
||||
} else {
|
||||
// point 1 and 2 are the same, I guess...
|
||||
this.point1 = point1;
|
||||
this.point2 = point2;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPoint() {
|
||||
return point1.equals( point2 );
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return point1.distance( point2 );
|
||||
}
|
||||
|
||||
public Vector getVector() {
|
||||
return point2.clone().subtract( point1 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class MeshBuilder {
|
||||
public List< Plane > planes = new ArrayList< Plane >();
|
||||
|
||||
public Plane addFacet( Facet facet ) {
|
||||
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();
|
||||
plane.normal = facet.normal;
|
||||
Optional< Vector > ref = facet.points.stream().filter( v -> v.lengthSquared() > 0.001 && Math.abs( v.clone().normalize().dot( facet.normal ) ) < .999999 ).findAny();
|
||||
if ( ref.isPresent() ) {
|
||||
plane.point = ref.get();
|
||||
}
|
||||
plane.addShape( facet );
|
||||
planes.add( plane );
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
import org.joml.Matrix3d;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
|
||||
public class Plane {
|
||||
public Vector normal;
|
||||
public Vector point;
|
||||
public double distance;
|
||||
|
||||
private boolean set = false;
|
||||
|
||||
public List< Polygon > polygons = new ArrayList< Polygon >();
|
||||
|
||||
private Matrix3d mat;
|
||||
private Matrix3d transpose;
|
||||
|
||||
public void addShape( Facet facet ) {
|
||||
final Matrix3d mat = getProjectionMatrix();
|
||||
|
||||
List< Point > polygon = new ArrayList< Point >();
|
||||
for ( Vector p : facet.points ) {
|
||||
double t = point.clone().subtract( p ).dot( normal );
|
||||
Vector i = normal.clone().multiply( t ).add( p );
|
||||
|
||||
Vector3d v = new Vector3d( i.getX(), i.getY(), i.getZ() );
|
||||
|
||||
Vector3d r = v.mul( mat );
|
||||
|
||||
polygon.add( new Point( r.x(), r.y() ) );
|
||||
|
||||
if ( !set ) {
|
||||
distance = r.z();
|
||||
|
||||
set = true;
|
||||
} else if ( Math.abs( r.z() - distance ) > 1e-7 ) {
|
||||
throw new IllegalStateException( "Bad point in facet!" );
|
||||
}
|
||||
}
|
||||
|
||||
polygons.add( new Polygon( polygon ) );
|
||||
}
|
||||
|
||||
public Vector convert( final Point p ) {
|
||||
final Vector3d plane = new Vector3d( p.getX(), p.getY(), distance );
|
||||
|
||||
final Vector3d res = plane.mul( getInverseProjectionMatrix() );
|
||||
|
||||
return new Vector( res.x(), res.y(), res.z() );
|
||||
}
|
||||
|
||||
public Facet convert( final Polygon polygon ) {
|
||||
final Facet facet = new Facet();
|
||||
|
||||
facet.normal = normal;
|
||||
for ( final Point p : polygon.getPoints() ) {
|
||||
facet.points.add( convert( p ) );
|
||||
}
|
||||
|
||||
return facet;
|
||||
}
|
||||
|
||||
public Collection< Facet > convert( final Collection< Polygon > polygons ) {
|
||||
return polygons.parallelStream().map( this::convert ).collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
public boolean matches( Vector normal, Vector point ) {
|
||||
return Math.abs( this.normal.dot( normal ) ) > 0.99 && Math.abs( this.point.clone().subtract( point ).dot( normal ) ) < 0.01;
|
||||
}
|
||||
|
||||
private Matrix3d getProjectionMatrix() {
|
||||
if ( mat == null ) {
|
||||
final Vector basis1 = normal.clone().multiply( point.clone().multiply( -1 ).dot( normal ) ).add( point ).normalize();
|
||||
final Vector3d b1 = new Vector3d( basis1.getX(), basis1.getY(), basis1.getZ() );
|
||||
final Vector3d b3 = new Vector3d( normal.getX(), normal.getY(), normal.getZ() );
|
||||
final Vector3d b2 = new Vector3d( b1 ).cross( b3 ).normalize();
|
||||
mat = new Matrix3d( b1, b2, b3 ).transpose();
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private Matrix3d getInverseProjectionMatrix() {
|
||||
if ( transpose == null ) {
|
||||
transpose = getProjectionMatrix().transpose();
|
||||
}
|
||||
return transpose;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Big/little endian binary writer
|
||||
*/
|
||||
public class BinaryWriter {
|
||||
protected final ByteArrayOutputStream buffer;
|
||||
|
||||
public BinaryWriter() {
|
||||
buffer = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
public BinaryWriter( final int startSize ) {
|
||||
buffer = new ByteArrayOutputStream( startSize );
|
||||
}
|
||||
|
||||
public void write( final long val, final int length ) {
|
||||
for ( int i = length - 1; i >= 0; i-- ) {
|
||||
write( ( byte ) ( ( val >> ( i << 3 ) ) & 0xFFL ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void write( final int val ) {
|
||||
write( val, 4 );
|
||||
}
|
||||
|
||||
public void write( final short val ) {
|
||||
write( val, 2 );
|
||||
}
|
||||
|
||||
public void write( final double val ) {
|
||||
final byte[] bytes = new byte[ 8 ];
|
||||
ByteBuffer.wrap( bytes ).putDouble( val );
|
||||
write( bytes );
|
||||
}
|
||||
|
||||
public void writeLittle( final long val, final int length ) {
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
write( ( byte ) ( ( val >> ( i << 3 ) ) & 0xFFL ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void write( final long[] a, final int length ) {
|
||||
for ( long l : a ) {
|
||||
write( l, length );
|
||||
}
|
||||
}
|
||||
|
||||
public void write( final byte b ) {
|
||||
buffer.write( b );
|
||||
}
|
||||
|
||||
public void write( String str, final int length ) {
|
||||
final byte[] bytes = str.getBytes();
|
||||
final byte[] res = new byte[ length ];
|
||||
for ( int i = 0; i < bytes.length && i < length; ++i ) {
|
||||
res[ i ] = bytes[ i ];
|
||||
}
|
||||
|
||||
write( res );
|
||||
}
|
||||
|
||||
public void write( final byte[] a ) {
|
||||
buffer.write( a, 0, a.length );
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.util;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginLoader;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.plugin.java.JavaPluginLoader;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.MiniePlugin;
|
||||
|
||||
public class BukkitUtil {
|
||||
private static Constructor< PluginCommand > PLUGINCOMMAND_CONSTRUCTOR;
|
||||
private static Field SIMPLEPLUGINMANAGER_FILEASSOCIATIONS;
|
||||
private static Method JAVAPLUGINLOADER_GETCLASS_1;
|
||||
private static Method JAVAPLUGINLOADER_GETCLASS_2;
|
||||
private static Method JAVAPLUGINLOADER_SETCLASS;
|
||||
private static Method JAVAPLUGINLOADER_REMOVECLASS_1;
|
||||
private static Method JAVAPLUGINLOADER_REMOVECLASS_2;
|
||||
|
||||
static {
|
||||
try {
|
||||
PLUGINCOMMAND_CONSTRUCTOR = PluginCommand.class.getDeclaredConstructor( String.class, Plugin.class );
|
||||
PLUGINCOMMAND_CONSTRUCTOR.setAccessible( true );
|
||||
SIMPLEPLUGINMANAGER_FILEASSOCIATIONS = SimplePluginManager.class.getDeclaredField( "fileAssociations" );
|
||||
SIMPLEPLUGINMANAGER_FILEASSOCIATIONS.setAccessible( true );
|
||||
JAVAPLUGINLOADER_SETCLASS = JavaPluginLoader.class.getDeclaredMethod( "setClass", String.class, Class.class );
|
||||
JAVAPLUGINLOADER_SETCLASS.setAccessible( true );
|
||||
} catch ( NoSuchMethodException | SecurityException | NoSuchFieldException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
JAVAPLUGINLOADER_GETCLASS_1 = JavaPluginLoader.class.getDeclaredMethod( "getClassByName", String.class );
|
||||
JAVAPLUGINLOADER_GETCLASS_1.setAccessible( true );
|
||||
} catch ( NoSuchMethodException | SecurityException e ) {
|
||||
try {
|
||||
JAVAPLUGINLOADER_GETCLASS_2 = JavaPluginLoader.class.getDeclaredMethod( "getClassByName", String.class, boolean.class, PluginDescriptionFile.class );
|
||||
JAVAPLUGINLOADER_GETCLASS_2.setAccessible( true );
|
||||
} catch ( NoSuchMethodException | SecurityException e1 ) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
JAVAPLUGINLOADER_REMOVECLASS_1 = JavaPluginLoader.class.getDeclaredMethod( "removeClass", String.class );
|
||||
JAVAPLUGINLOADER_REMOVECLASS_1.setAccessible( true );
|
||||
} catch ( NoSuchMethodException | SecurityException e ) {
|
||||
try {
|
||||
JAVAPLUGINLOADER_REMOVECLASS_2 = JavaPluginLoader.class.getDeclaredMethod( "removeClass", Class.class );
|
||||
JAVAPLUGINLOADER_REMOVECLASS_2.setAccessible( true );
|
||||
} catch ( NoSuchMethodException | SecurityException e1 ) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PluginCommand constructCommand( String id ) {
|
||||
PluginCommand command = null;
|
||||
try {
|
||||
command = PLUGINCOMMAND_CONSTRUCTOR.newInstance( id, JavaPlugin.getPlugin( MiniePlugin.class ) );
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call an event synchronously, on the main thread
|
||||
*/
|
||||
public static void callEventSync( Event event ) {
|
||||
if ( Bukkit.getServer().isPrimaryThread() ) {
|
||||
Bukkit.getPluginManager().callEvent( event );
|
||||
} else {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask( JavaPlugin.getPlugin( MiniePlugin.class ), () -> { callEventSync( event ); } );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin given is loaded
|
||||
*
|
||||
* @param id
|
||||
* case sensitive plugin id
|
||||
* @return
|
||||
* If the plugin is loaded and enabled
|
||||
*/
|
||||
public static boolean isPluginLoaded( String id ) {
|
||||
return Bukkit.getPluginManager().isPluginEnabled( id );
|
||||
}
|
||||
|
||||
public static Class< ? > getClassByName( String name ) {
|
||||
PluginManager manager = Bukkit.getPluginManager();
|
||||
if ( manager instanceof SimplePluginManager ) {
|
||||
SimplePluginManager sManager = ( SimplePluginManager ) manager;
|
||||
try {
|
||||
Map< Pattern, PluginLoader > associations = ( Map< Pattern, PluginLoader > ) SIMPLEPLUGINMANAGER_FILEASSOCIATIONS.get( sManager );
|
||||
for ( PluginLoader loader : associations.values() ) {
|
||||
if ( loader instanceof JavaPluginLoader ) {
|
||||
if ( JAVAPLUGINLOADER_GETCLASS_1 != null ) {
|
||||
return ( Class< ? > ) JAVAPLUGINLOADER_GETCLASS_1.invoke( loader, name );
|
||||
} else {
|
||||
return ( Class< ? > ) JAVAPLUGINLOADER_GETCLASS_2.invoke( loader, name, true, JavaPlugin.getPlugin( MiniePlugin.class ).getDescription() );
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setClassToJavaPluginLoader( String name, Class< ? > clazz ) {
|
||||
PluginManager manager = Bukkit.getPluginManager();
|
||||
if ( manager instanceof SimplePluginManager ) {
|
||||
SimplePluginManager sManager = ( SimplePluginManager ) manager;
|
||||
try {
|
||||
Map< Pattern, PluginLoader > associations = ( Map< Pattern, PluginLoader > ) SIMPLEPLUGINMANAGER_FILEASSOCIATIONS.get( sManager );
|
||||
for ( PluginLoader loader : associations.values() ) {
|
||||
if ( loader instanceof JavaPluginLoader ) {
|
||||
JAVAPLUGINLOADER_SETCLASS.invoke( loader, name, clazz );
|
||||
}
|
||||
}
|
||||
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean removeClassFromJavaPluginLoader( Class clazz ) {
|
||||
if ( JAVAPLUGINLOADER_REMOVECLASS_2 != null ) {
|
||||
PluginManager manager = Bukkit.getPluginManager();
|
||||
if ( manager instanceof SimplePluginManager ) {
|
||||
SimplePluginManager sManager = ( SimplePluginManager ) manager;
|
||||
try {
|
||||
Map< Pattern, PluginLoader > associations = ( Map< Pattern, PluginLoader > ) SIMPLEPLUGINMANAGER_FILEASSOCIATIONS.get( sManager );
|
||||
for ( PluginLoader loader : associations.values() ) {
|
||||
if ( loader instanceof JavaPluginLoader ) {
|
||||
JAVAPLUGINLOADER_REMOVECLASS_2.invoke( loader, clazz );
|
||||
}
|
||||
}
|
||||
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean removeClassFromJavaPluginLoader( String name ) {
|
||||
if ( JAVAPLUGINLOADER_REMOVECLASS_1 != null ) {
|
||||
PluginManager manager = Bukkit.getPluginManager();
|
||||
if ( manager instanceof SimplePluginManager ) {
|
||||
SimplePluginManager sManager = ( SimplePluginManager ) manager;
|
||||
try {
|
||||
Map< Pattern, PluginLoader > associations = ( Map< Pattern, PluginLoader > ) SIMPLEPLUGINMANAGER_FILEASSOCIATIONS.get( sManager );
|
||||
for ( PluginLoader loader : associations.values() ) {
|
||||
if ( loader instanceof JavaPluginLoader ) {
|
||||
JAVAPLUGINLOADER_REMOVECLASS_1.invoke( loader, name );
|
||||
}
|
||||
}
|
||||
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.util;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh;
|
||||
|
||||
public class ChunkMeshUtil {
|
||||
public static byte[] serialize( final ChunkMesh mesh ) {
|
||||
final BinaryWriter writer = new BinaryWriter();
|
||||
|
||||
// Header
|
||||
writer.write( "cmesh1", 16 );
|
||||
|
||||
// How many chunk meshes are we providing
|
||||
writer.write( 1, 4 );
|
||||
|
||||
mesh.writeTo( writer );
|
||||
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
public static ChunkMesh deserialize( final byte[] data ) {
|
||||
final ByteBuffer reader = ByteBuffer.wrap( data );
|
||||
|
||||
final byte[] headerArr = new byte[ 16 ];
|
||||
reader.get( headerArr );
|
||||
final String header = new String( headerArr ).trim();
|
||||
if ( header.equals( "cmesh1" ) ) {
|
||||
// This is how many chunk meshes this data contains
|
||||
final int amount = reader.getInt();
|
||||
|
||||
if ( amount > 1 ) {
|
||||
// Some data is getting discarded...
|
||||
}
|
||||
|
||||
return ChunkMesh.readFrom( reader );
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Cannot read invalid header: '" + header + "'" );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
/**
|
||||
* Simple file management including YAML updaters
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
public final class FileUtil {
|
||||
public static void saveAndUpdate( InputStream stream, File output, boolean trim ) {
|
||||
if ( !output.exists() ) {
|
||||
saveToFile( stream, output, false );
|
||||
}
|
||||
updateConfigFromFile( output, stream, trim );
|
||||
}
|
||||
|
||||
public static void saveToFile( InputStream stream, File output, boolean force ) {
|
||||
if ( force && output.exists() ) {
|
||||
output.delete();
|
||||
}
|
||||
if ( !output.exists() ) {
|
||||
output.getParentFile().mkdirs();
|
||||
try ( OutputStream outStream = new FileOutputStream( output ) ) {
|
||||
byte[] buffer = new byte[ stream.available() ];
|
||||
|
||||
int len;
|
||||
while ( ( len = stream.read( buffer ) ) > 0) {
|
||||
outStream.write( buffer, 0, len );
|
||||
}
|
||||
stream.close();
|
||||
} catch ( FileNotFoundException e ) {
|
||||
e.printStackTrace();
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateConfigFromFile( File toUpdate, InputStream toCopy ) {
|
||||
updateConfigFromFile( toUpdate, toCopy, false );
|
||||
}
|
||||
|
||||
public static void updateConfigFromFile( File toUpdate, InputStream toCopy, boolean trim ) {
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration( new InputStreamReader( toCopy ) );
|
||||
FileConfiguration old = YamlConfiguration.loadConfiguration( toUpdate );
|
||||
|
||||
for ( String key : config.getKeys( true ) ) {
|
||||
if ( !old.contains( key ) ) {
|
||||
old.set( key, config.get( key ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( trim ) {
|
||||
for ( String key : old.getKeys( true ) ) {
|
||||
if ( !config.contains( key ) ) {
|
||||
old.set( key, null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
old.save( toUpdate );
|
||||
} catch ( Exception exception ) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean move( File original, File dest, boolean force ) {
|
||||
if ( dest.exists() ) {
|
||||
if ( !force ) {
|
||||
return false;
|
||||
} else {
|
||||
recursiveDelete( dest );
|
||||
}
|
||||
}
|
||||
dest.getParentFile().mkdirs();
|
||||
original.renameTo( dest );
|
||||
recursiveDelete( original );
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void recursiveDelete( File file ) {
|
||||
if ( !file.exists() ) {
|
||||
return;
|
||||
}
|
||||
if ( file.isDirectory() ) {
|
||||
for ( File lower : file.listFiles() ) {
|
||||
recursiveDelete( lower );
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
|
||||
public static < T extends Serializable > T readObject( File file ) throws IOException, ClassNotFoundException {
|
||||
if ( !file.exists() ) {
|
||||
return null;
|
||||
}
|
||||
T head = null;
|
||||
FileInputStream fis = new FileInputStream( file );
|
||||
ObjectInputStream ois = new ObjectInputStream( fis );
|
||||
|
||||
head = ( T ) ois.readObject();
|
||||
|
||||
ois.close();
|
||||
fis.close();
|
||||
return head;
|
||||
}
|
||||
|
||||
public static void writeObject( Serializable object, File file ) {
|
||||
file.getParentFile().mkdirs();
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream( file );
|
||||
ObjectOutputStream oos = new ObjectOutputStream( fos );
|
||||
|
||||
oos.writeObject( object );
|
||||
|
||||
oos.close();
|
||||
fos.close();
|
||||
} catch ( Exception exception ) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static File getImageFile( File dir, String prefix ) {
|
||||
File image = new File( dir + "/" + prefix + ".png" );
|
||||
for ( File file : dir.listFiles() ) {
|
||||
String fileName = file.getName();
|
||||
if ( fileName.equalsIgnoreCase( prefix + ".gif" ) ) {
|
||||
return file;
|
||||
} else if ( fileName.equalsIgnoreCase( prefix + ".png" ) || fileName.equalsIgnoreCase( prefix + ".jpg" ) || fileName.equalsIgnoreCase( prefix + ".jpeg" ) || fileName.equalsIgnoreCase( prefix + ".bmp" ) ) {
|
||||
image = file;
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
67
plugin/src/main/resources/README.md
Normal file
67
plugin/src/main/resources/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Tess4j(name subject to change)
|
||||
## About
|
||||
Tess4j(name subject to change) is an all purpose 2d polygon tessellator inspired by the GLUTesselator and libtess2. This tessellator focuses around
|
||||
user defined rules for interior/exterior regions, and aims to be a robust algorithm with a focus on reducing triangle
|
||||
count while preserving the original shapes.
|
||||
|
||||
Since it _is_ written in java, speed is not the main priority. Rather, this aims to serve as an easy to understand
|
||||
and easy to modify library for anyone interested in adding improvements.
|
||||
|
||||
## Restrictions
|
||||
Tess4j(name subject to change) accepts one or more polygons. Polygons are defined as a collection of one or more 2d points
|
||||
that form a loop. Steiner points are not (currently)supported. There may be some rounding errors for especially for very large
|
||||
and small points, but this library aims for consistency, with a small tolerance for adjustments.
|
||||
|
||||
## Algorithm
|
||||
This library utilizes a very basic single sweep monotone partitioning algorithm, then greedily creates triangle fans and strips.
|
||||
Additional monotone regions may be broken down into smaller regions if doing so would decrease the overall amount of triangles.
|
||||
|
||||
### Invariants
|
||||
Tess4j(name subject to change) assumes no responsibility or liability for any errors or omissions in the execution of this program.
|
||||
The output returned by this program is provided on an "as is" basis with no guarantees of completeness, accuracy, usefulness or timeliness.
|
||||
That said, here are a list of invariants that _should_ be upheld:
|
||||
- Any vertex created during the tessellation will be greater than the current event vertex
|
||||
|
||||
### The actual algorithm
|
||||
Tess4j(name subject to change) begins by accepting a list of one or more polygons. The polygons are split into collections of connected
|
||||
half-edges and vertices. The vertices are then added to a priority queue where they are sorted from left to right(-x to x),
|
||||
bottom to top(-y to y) order.
|
||||
|
||||
Once all polygons have been inserted and processed, sweeping of the vertices begins. The lowest unique vertex is extracted from
|
||||
the priority queue. Any subsequent vertices with the same coordinates are merged together, and any new vertices created during the
|
||||
sweep event are guaranteed to be greater than the current vertex.
|
||||
|
||||
Sweep over each vertex and mark which ones are an interior non-monotone vertex. Process all left going edges, and mark the faces attached
|
||||
as interior/exterior. Register all right going edges. Split any intersections into new vertices and edges, and add the vertices to the
|
||||
event queue.
|
||||
|
||||
**TODO**
|
||||
Now that each face has been fully completed... reduce the amount of vertices before adding in additional edges for monotone regions.
|
||||
To do this, get all colinear edges that share the same face, and does not intersect with any other edges???
|
||||
Loop through each edge and keep it in the list until it would intersect with another edge, and mark which edges/vertices it is
|
||||
colinear with, as well as any potential new edges it might collide with. Solve which combination of edges would reduce
|
||||
vertex count by the greatest amount. 2 vertices if connecting to an edge, 1 vertex if connecting to a vertex. Traveling salesman problem?
|
||||
When checking for intersections with other support edges, only need to check all active potential aux edges... Consolidate colinears
|
||||
|
||||
For each edge of a face, advance around the face until it intersects with an edge or vertex. Advance backwards as well, checking ONLY vertex intersections.
|
||||
This will capture all possible conlinear reductions as new edges.
|
||||
|
||||
How to determine which combination can reduce the total amount of vertices? Build clusters of intersecting vertices,
|
||||
|
||||
After all vertex reduction edges have been added, get all faces and calculate the upper and lower sweep edge of
|
||||
each vertex of each face for monotone partitioning.
|
||||
|
||||
When processing the marked vertices to form monotone regions, some data must be stored for each point so
|
||||
that a valid point with which to connect can be located afterwards:
|
||||
- The immediate upper and lower edge of the vertex when it is being swept.
|
||||
This allows the forward/backward monotone connecting to consistently find a valid point. When checking for valid points:
|
||||
- The point must be less/greater than the current point if iterating forwards/backwards, respectively.
|
||||
- The point must fulfill at least one of the following conditions:
|
||||
- It shares the same upper edge
|
||||
- It is part of the upper edge
|
||||
- It shares the same lower edge
|
||||
- It is part of the lower edge
|
||||
Once a marked vertex has been connected, either by finding another valid vertex, or by being connected to from a marked
|
||||
vertex, it can be unmarked and removed from the queue of marked vertices.
|
||||
|
||||
Now that each face is a monotone region, split it into triangle fans and strips.
|
||||
14
plugin/src/main/resources/plugin.yml
Normal file
14
plugin/src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
name: MinieTest
|
||||
main: com.aaaaahhhhhhh.bananapuncher714.minietest.MiniePlugin
|
||||
version: 0.0.1
|
||||
description: Minecraft with Minie
|
||||
author: BananaPuncher714
|
||||
api-version: 1.13
|
||||
#libraries:
|
||||
#- com.github.stephengold:Minie:7.5.0
|
||||
|
||||
commands:
|
||||
minie:
|
||||
description: Main Minie command
|
||||
aliases: []
|
||||
permission: minie
|
||||
Reference in New Issue
Block a user