From e9b8bea944289d63d04b4a0a93cc127bf0a26fd6 Mon Sep 17 00:00:00 2001 From: BananaPuncher714 Date: Sat, 24 May 2025 00:06:11 -0400 Subject: [PATCH] Convert meshed polygons back into facet Write facets to ply format --- .gitignore | 4 +- .../minietest/MeshingTest2.java | 24 +- .../minietest/MeshingTest3.java | 6 - .../minietest/MeshingTest4.java | 293 ++++++++++++++++++ .../minietest/objects/mesh/Plane.java | 63 +++- 5 files changed, 363 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest4.java diff --git a/.gitignore b/.gitignore index fd9c5d0..65dcd5c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ target .settings chunks new_chunks -images \ No newline at end of file +images +chunk_models +chunk_models_new \ No newline at end of file diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java index c26f06e..73d7e5e 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java @@ -130,19 +130,19 @@ public class MeshingTest2 extends JPanel { // } List< Plane > masterPlanes = new ArrayList< Plane >(); -// System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length ); -// int limit = 1; -// for ( File file : CHUNK_DIR.listFiles() ) { -// masterPlanes.addAll( mesh( file ) ); -// if ( --limit <= 0 ) { -// break; -// } else { -// System.out.println( "Remaining: " + limit ); -// } -// } - final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) ); + System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length ); + int limit = 1; + for ( File file : CHUNK_DIR.listFiles() ) { + masterPlanes.addAll( mesh( file ) ); + if ( --limit <= 0 ) { + break; + } else { + System.out.println( "Remaining: " + limit ); + } + } +// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) ); // masterPlanes.add( planes.get( 177 ) ); - masterPlanes.addAll( planes ); +// masterPlanes.addAll( planes ); // masterPlanes.add( getTestPlane() ); diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest3.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest3.java index 01489a2..100f446 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest3.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest3.java @@ -39,8 +39,6 @@ public class MeshingTest3 { } catch ( IllegalStateException e ) { e.printStackTrace(); - semiProcess( p ); - System.exit( 1 ); } } ); @@ -71,10 +69,6 @@ public class MeshingTest3 { } } - private static void semiProcess( final Plane plane ) { - - } - private static List< Plane > mesh( File file ) { String name = file.getName(); String[] split = name.split( "," ); diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest4.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest4.java new file mode 100644 index 0000000..e06b43b --- /dev/null +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest4.java @@ -0,0 +1,293 @@ +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.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.stream.Collectors; + +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; + +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" ); + for ( int i = 0; i < CHUNK_DIR.listFiles().length; ++i ) { + final File file = CHUNK_DIR.listFiles()[ i ]; + System.out.println( ( i + 1 ) + "/" + CHUNK_DIR.listFiles().length + ":\tMeshing " + file ); + long start = System.currentTimeMillis(); + final List< Plane > planes = mesh( file ); + savePly2( new File( MODEL_DIR, file.getName() + ".ply" ), planes.parallelStream().flatMap( p -> process( p ).parallelStream().map( p::convert ) ).collect( Collectors.toSet() ) ); + long time = System.currentTimeMillis() - start; + + 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 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 ) { + String name = file.getName(); + String[] split = name.split( "," ); + ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) ); + + System.out.println( "\tParsing chunk " + location ); + + // First parse the file to get a list of all bounding boxes that we can use + List< AABB > boxes = new ArrayList< AABB >(); + try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) { + 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 ); + } + } + + System.out.println( "\tFound " + boxes.size() + " boxes and " + builder.planes.size() + " planes" ); + + 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; + } + +} diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/mesh/Plane.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/mesh/Plane.java index 191cbff..df6d965 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/mesh/Plane.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/mesh/Plane.java @@ -1,7 +1,9 @@ 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; @@ -13,39 +15,84 @@ 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 ) { - Matrix3d mat = getProjectionMatrix(); + final Matrix3d mat = getProjectionMatrix(); List< Point > polygon = new ArrayList< Point >(); for ( Vector p : facet.points ) { double t = point.clone().subtract( p ).dot( normal ); - // TODO Subtract point or to not subtract point?!? - Vector i = normal.clone().multiply( t ).add( p ).subtract( point ); + 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() { - 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(); - final Matrix3d mat = new Matrix3d( b1, b2, b3 ).transpose(); + 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; + } }