Convert meshed polygons back into facet
Write facets to ply format
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ target
|
|||||||
chunks
|
chunks
|
||||||
new_chunks
|
new_chunks
|
||||||
images
|
images
|
||||||
|
chunk_models
|
||||||
|
chunk_models_new
|
||||||
@@ -130,19 +130,19 @@ public class MeshingTest2 extends JPanel {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
List< Plane > masterPlanes = new ArrayList< Plane >();
|
List< Plane > masterPlanes = new ArrayList< Plane >();
|
||||||
// System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
||||||
// int limit = 1;
|
int limit = 1;
|
||||||
// for ( File file : CHUNK_DIR.listFiles() ) {
|
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||||
// masterPlanes.addAll( mesh( file ) );
|
masterPlanes.addAll( mesh( file ) );
|
||||||
// if ( --limit <= 0 ) {
|
if ( --limit <= 0 ) {
|
||||||
// break;
|
break;
|
||||||
// } else {
|
} else {
|
||||||
// System.out.println( "Remaining: " + limit );
|
System.out.println( "Remaining: " + limit );
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) );
|
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-4,-6" ) );
|
||||||
// masterPlanes.add( planes.get( 177 ) );
|
// masterPlanes.add( planes.get( 177 ) );
|
||||||
masterPlanes.addAll( planes );
|
// masterPlanes.addAll( planes );
|
||||||
|
|
||||||
// masterPlanes.add( getTestPlane() );
|
// masterPlanes.add( getTestPlane() );
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ public class MeshingTest3 {
|
|||||||
} catch ( IllegalStateException e ) {
|
} catch ( IllegalStateException e ) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
semiProcess( p );
|
|
||||||
|
|
||||||
System.exit( 1 );
|
System.exit( 1 );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
@@ -71,10 +69,6 @@ public class MeshingTest3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void semiProcess( final Plane plane ) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List< Plane > mesh( File file ) {
|
private static List< Plane > mesh( File file ) {
|
||||||
String name = file.getName();
|
String name = file.getName();
|
||||||
String[] split = name.split( "," );
|
String[] split = name.split( "," );
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.joml.Matrix3d;
|
import org.joml.Matrix3d;
|
||||||
@@ -13,39 +15,84 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
|||||||
public class Plane {
|
public class Plane {
|
||||||
public Vector normal;
|
public Vector normal;
|
||||||
public Vector point;
|
public Vector point;
|
||||||
|
public double distance;
|
||||||
|
|
||||||
|
private boolean set = false;
|
||||||
|
|
||||||
public List< Polygon > polygons = new ArrayList< Polygon >();
|
public List< Polygon > polygons = new ArrayList< Polygon >();
|
||||||
|
|
||||||
|
private Matrix3d mat;
|
||||||
|
private Matrix3d transpose;
|
||||||
|
|
||||||
public void addShape( Facet facet ) {
|
public void addShape( Facet facet ) {
|
||||||
Matrix3d mat = getProjectionMatrix();
|
final Matrix3d mat = getProjectionMatrix();
|
||||||
|
|
||||||
List< Point > polygon = new ArrayList< Point >();
|
List< Point > polygon = new ArrayList< Point >();
|
||||||
for ( Vector p : facet.points ) {
|
for ( Vector p : facet.points ) {
|
||||||
double t = point.clone().subtract( p ).dot( normal );
|
double t = point.clone().subtract( p ).dot( normal );
|
||||||
// TODO Subtract point or to not subtract point?!?
|
Vector i = normal.clone().multiply( t ).add( p );
|
||||||
Vector i = normal.clone().multiply( t ).add( p ).subtract( point );
|
|
||||||
|
|
||||||
Vector3d v = new Vector3d( i.getX(), i.getY(), i.getZ() );
|
Vector3d v = new Vector3d( i.getX(), i.getY(), i.getZ() );
|
||||||
|
|
||||||
Vector3d r = v.mul( mat );
|
Vector3d r = v.mul( mat );
|
||||||
|
|
||||||
polygon.add( new Point( r.x(), r.y() ) );
|
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 ) );
|
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 ) {
|
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;
|
return Math.abs( this.normal.dot( normal ) ) > 0.99 && Math.abs( this.point.clone().subtract( point ).dot( normal ) ) < 0.01;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Matrix3d getProjectionMatrix() {
|
private Matrix3d getProjectionMatrix() {
|
||||||
final Vector basis1 = normal.clone().multiply( point.clone().multiply( -1 ).dot( normal ) ).add( point ).normalize();
|
if ( mat == null ) {
|
||||||
final Vector3d b1 = new Vector3d( basis1.getX(), basis1.getY(), basis1.getZ() );
|
final Vector basis1 = normal.clone().multiply( point.clone().multiply( -1 ).dot( normal ) ).add( point ).normalize();
|
||||||
final Vector3d b3 = new Vector3d( normal.getX(), normal.getY(), normal.getZ() );
|
final Vector3d b1 = new Vector3d( basis1.getX(), basis1.getY(), basis1.getZ() );
|
||||||
final Vector3d b2 = new Vector3d( b1 ).cross( b3 ).normalize();
|
final Vector3d b3 = new Vector3d( normal.getX(), normal.getY(), normal.getZ() );
|
||||||
final Matrix3d mat = new Matrix3d( b1, b2, b3 ).transpose();
|
final Vector3d b2 = new Vector3d( b1 ).cross( b3 ).normalize();
|
||||||
|
mat = new Matrix3d( b1, b2, b3 ).transpose();
|
||||||
|
}
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Matrix3d getInverseProjectionMatrix() {
|
||||||
|
if ( transpose == null ) {
|
||||||
|
transpose = getProjectionMatrix().transpose();
|
||||||
|
}
|
||||||
|
return transpose;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user