diff --git a/applications/src/main/java/boofcv/app/MeshViewerApp.java b/applications/src/main/java/boofcv/app/MeshViewerApp.java index 7095a42120..d738664289 100644 --- a/applications/src/main/java/boofcv/app/MeshViewerApp.java +++ b/applications/src/main/java/boofcv/app/MeshViewerApp.java @@ -27,7 +27,6 @@ import boofcv.struct.image.InterleavedU8; import boofcv.struct.mesh.VertexMesh; import org.apache.commons.io.FilenameUtils; -import org.ddogleg.struct.DogArray_I32; import javax.swing.*; import java.awt.*; @@ -49,7 +48,6 @@ public MeshViewerApp() { private static void loadFile( File file ) { // Load the mesh var mesh = new VertexMesh(); - var colors = new DogArray_I32(); String extension = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.ENGLISH); var type = switch (extension) { case "ply" -> PointCloudIO.Format.PLY; @@ -61,7 +59,7 @@ private static void loadFile( File file ) { }; try (var input = new FileInputStream(file)) { - PointCloudIO.load(type, input, mesh, colors); + PointCloudIO.load(type, input, mesh); } catch (IOException e) { e.printStackTrace(System.err); System.exit(1); @@ -86,8 +84,8 @@ private static void loadFile( File file ) { SwingUtilities.invokeLater(() -> { var panel = new MeshViewerPanel(); panel.setMesh(mesh, false); - if (colors.size > 0) - panel.setVertexColors("RGB", colors.data); + if (mesh.rgb.size > 0) + panel.setVertexColors("RGB", mesh.rgb.data); if (_image != null) panel.setTextureImage(_image); panel.setPreferredSize(new Dimension(500, 500)); diff --git a/main/boofcv-geo/src/main/java/boofcv/alg/cloud/DisparityToColorPointCloud.java b/main/boofcv-geo/src/main/java/boofcv/alg/cloud/DisparityToColorPointCloud.java index 5a1ea921a3..dd4d432a5f 100644 --- a/main/boofcv-geo/src/main/java/boofcv/alg/cloud/DisparityToColorPointCloud.java +++ b/main/boofcv-geo/src/main/java/boofcv/alg/cloud/DisparityToColorPointCloud.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Peter Abeles. All Rights Reserved. + * Copyright (c) 2024, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * @@ -175,7 +175,10 @@ private void process( GrayU8 disparity, ColorImage color, PointCloudWriter outpu // Bring it back into left camera frame GeometryMath_F32.multTran(rectifiedR, p, p); - output.add(p.x, p.y, p.z, getColor(color, pixelX, pixelY)); + output.startPoint(); + output.location(p.x, p.y, p.z); + output.color(getColor(color, pixelX, pixelY)); + output.stopPoint(); } } } @@ -217,7 +220,10 @@ private void process( GrayF32 disparity, ColorImage color, PointCloudWriter outp // Bring it back into left camera frame GeometryMath_F32.multTran(rectifiedR, p, p); - output.add(p.x, p.y, p.z, getColor(color, pixelX, pixelY)); + output.startPoint(); + output.location(p.x, p.y, p.z); + output.color(getColor(color, pixelX, pixelY)); + output.stopPoint(); } } } diff --git a/main/boofcv-geo/src/main/java/boofcv/alg/cloud/PointCloudWriter.java b/main/boofcv-geo/src/main/java/boofcv/alg/cloud/PointCloudWriter.java index 47a818661b..6745f1c9cc 100644 --- a/main/boofcv-geo/src/main/java/boofcv/alg/cloud/PointCloudWriter.java +++ b/main/boofcv-geo/src/main/java/boofcv/alg/cloud/PointCloudWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Peter Abeles. All Rights Reserved. + * Copyright (c) 2024, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * @@ -40,10 +40,21 @@ public interface PointCloudWriter { */ void initialize( int size, boolean hasColor ); + /** A new point is being added with new attributes to follow */ + void startPoint(); + + /** It's done specifying attributes for the point */ + void stopPoint(); + /** - * Adds a 3D point with color information + * Set the 3D location of the point */ - void add( double x, double y, double z, int rgb ); + void location( double x, double y, double z ); + + /** + * Sets the points color + */ + void color( int rgb ); class CloudArraysF32 implements PointCloudWriter { // Storage for point cloud @@ -57,10 +68,17 @@ class CloudArraysF32 implements PointCloudWriter { cloudXyz.reserve(size*3); } - @Override public void add( double x, double y, double z, int rgb ) { + @Override public void startPoint() {} + + @Override public void stopPoint() {} + + @Override public void location( double x, double y, double z ) { cloudXyz.add((float)x); cloudXyz.add((float)y); cloudXyz.add((float)z); + } + + @Override public void color( int rgb ) { cloudRgb.add(rgb); } @@ -79,10 +97,15 @@ static PointCloudWriter wrapF32( DogArray cloud ) { cloud.reset(); } - @Override - public void add( double x, double y, double z, int rgb ) { + @Override public void startPoint() {} + + @Override public void stopPoint() {} + + @Override public void location( double x, double y, double z ) { cloud.grow().setTo((float)x, (float)y, (float)z); } + + @Override public void color( int rgb ) {} }; } @@ -93,8 +116,13 @@ static PointCloudWriter wrapF64( DogArray cloud ) { cloud.reset(); } - @Override - public void add( double x, double y, double z, int rgb ) { + @Override public void startPoint() {} + + @Override public void stopPoint() {} + + @Override public void color( int rgb ) {} + + @Override public void location( double x, double y, double z ) { cloud.grow().setTo(x, y, z); } }; @@ -107,9 +135,18 @@ static PointCloudWriter wrapF32RGB( DogArray cloud ) { cloud.reset(); } - @Override - public void add( double x, double y, double z, int rgb ) { - cloud.grow().setTo((float)x, (float)y, (float)z, rgb); + @Override public void startPoint() { + cloud.grow(); + } + + @Override public void stopPoint() {} + + @Override public void location( double x, double y, double z ) { + cloud.getTail().setTo((float)x, (float)y, (float)z); + } + + @Override public void color( int rgb ) { + cloud.getTail().rgb = rgb; } }; } @@ -120,9 +157,18 @@ static PointCloudWriter wrapF64RGB( DogArray cloud ) { cloud.reset(); } - @Override - public void add( double x, double y, double z, int rgb ) { - cloud.grow().setTo(x, y, z, rgb); + @Override public void startPoint() { + cloud.grow(); + } + + @Override public void stopPoint() {} + + @Override public void location( double x, double y, double z ) { + cloud.getTail().setTo(x, y, z); + } + + @Override public void color( int rgb ) { + cloud.getTail().rgb = rgb; } }; } diff --git a/main/boofcv-geo/src/test/java/boofcv/alg/cloud/TestPointCloudWriter.java b/main/boofcv-geo/src/test/java/boofcv/alg/cloud/TestPointCloudWriter.java index dce2b559bd..7c6297b568 100644 --- a/main/boofcv-geo/src/test/java/boofcv/alg/cloud/TestPointCloudWriter.java +++ b/main/boofcv-geo/src/test/java/boofcv/alg/cloud/TestPointCloudWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Peter Abeles. All Rights Reserved. + * Copyright (c) 2024, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * @@ -35,32 +35,41 @@ class TestPointCloudWriter extends BoofStandardJUnit { static abstract class PcwTests { boolean supportsColor = true; + public abstract PointCloudWriter create(); + public abstract int size( PointCloudWriter data ); - public abstract Point3D_F64 getPoint(PointCloudWriter data , int i ); - public abstract int getColor(PointCloudWriter data , int i); + + public abstract Point3D_F64 getPoint( PointCloudWriter data, int i ); + + public abstract int getColor( PointCloudWriter data, int i ); @Test void simpleXyzColor() { PointCloudWriter alg = create(); - assertEquals(0,size(alg)); - alg.add(0,1,2, 345); - assertEquals(1,size(alg)); - alg.add(2,1,3,3434); - assertEquals(2,size(alg)); - - if( supportsColor ) { + assertEquals(0, size(alg)); + alg.startPoint(); + alg.location(0, 1, 2); + alg.color(345); + alg.stopPoint(); + assertEquals(1, size(alg)); + alg.startPoint(); + alg.location(2, 1, 3); + alg.color(3434); + alg.stopPoint(); + assertEquals(2, size(alg)); + + if (supportsColor) { assertEquals(345, getColor(alg, 0)); assertEquals(3434, getColor(alg, 1)); } - Point3D_F64 found = getPoint(alg,1); - assertEquals(2,found.x, UtilEjml.TEST_F64); - assertEquals(1,found.y, UtilEjml.TEST_F64); - assertEquals(3,found.z, UtilEjml.TEST_F64); + Point3D_F64 found = getPoint(alg, 1); + assertEquals(2, found.x, UtilEjml.TEST_F64); + assertEquals(1, found.y, UtilEjml.TEST_F64); + assertEquals(3, found.z, UtilEjml.TEST_F64); } - } @Nested @@ -72,18 +81,18 @@ public PointCloudWriter create() { } @Override - public int size(PointCloudWriter data) { + public int size( PointCloudWriter data ) { return ((CloudArraysF32)data).cloudXyz.size/3; } @Override - public Point3D_F64 getPoint(PointCloudWriter data, int i) { + public Point3D_F64 getPoint( PointCloudWriter data, int i ) { var list = ((CloudArraysF32)data).cloudXyz; - return new Point3D_F64( list.get(i*3), list.get(i*3+1), list.get(i*3+2)); + return new Point3D_F64(list.get(i*3), list.get(i*3 + 1), list.get(i*3 + 2)); } @Override - public int getColor(PointCloudWriter data, int i) { + public int getColor( PointCloudWriter data, int i ) { var list = ((CloudArraysF32)data).cloudRgb; return list.get(i); } @@ -102,18 +111,18 @@ public PointCloudWriter create() { } @Override - public int size(PointCloudWriter data) { + public int size( PointCloudWriter data ) { return cloud.size; } @Override - public Point3D_F64 getPoint(PointCloudWriter data, int i) { + public Point3D_F64 getPoint( PointCloudWriter data, int i ) { Point3D_F32 c = cloud.get(i); - return new Point3D_F64( c.x, c.y, c.z); + return new Point3D_F64(c.x, c.y, c.z); } @Override - public int getColor(PointCloudWriter data, int i) { + public int getColor( PointCloudWriter data, int i ) { throw new RuntimeException("Not supported"); } } @@ -131,17 +140,17 @@ public PointCloudWriter create() { } @Override - public int size(PointCloudWriter data) { + public int size( PointCloudWriter data ) { return cloud.size; } @Override - public Point3D_F64 getPoint(PointCloudWriter data, int i) { + public Point3D_F64 getPoint( PointCloudWriter data, int i ) { return cloud.get(i); } @Override - public int getColor(PointCloudWriter data, int i) { + public int getColor( PointCloudWriter data, int i ) { throw new RuntimeException("Not supported"); } } @@ -158,18 +167,18 @@ public PointCloudWriter create() { } @Override - public int size(PointCloudWriter data) { + public int size( PointCloudWriter data ) { return cloud.size; } @Override - public Point3D_F64 getPoint(PointCloudWriter data, int i) { + public Point3D_F64 getPoint( PointCloudWriter data, int i ) { Point3D_F32 c = cloud.get(i); - return new Point3D_F64( c.x, c.y, c.z); + return new Point3D_F64(c.x, c.y, c.z); } @Override - public int getColor(PointCloudWriter data, int i) { + public int getColor( PointCloudWriter data, int i ) { return cloud.get(i).rgb; } } @@ -186,19 +195,18 @@ public PointCloudWriter create() { } @Override - public int size(PointCloudWriter data) { + public int size( PointCloudWriter data ) { return cloud.size; } @Override - public Point3D_F64 getPoint(PointCloudWriter data, int i) { + public Point3D_F64 getPoint( PointCloudWriter data, int i ) { return cloud.get(i); } @Override - public int getColor(PointCloudWriter data, int i) { + public int getColor( PointCloudWriter data, int i ) { return cloud.get(i).rgb; } } - } diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/PointCloudIO.java b/main/boofcv-io/src/main/java/boofcv/io/points/PointCloudIO.java index 565bf5a38c..37d5a5a82e 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/PointCloudIO.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/PointCloudIO.java @@ -150,21 +150,18 @@ public static void load( Format format, InputStream input, PointCloudWriter outp * * @param file Which file it should load * @param mesh (Output) storage for the mesh - * @param colors (Output) Storage for vertex colors */ - public static void load( File file, VertexMesh mesh, DogArray_I32 colors ) throws IOException { + public static void load( File file, VertexMesh mesh ) throws IOException { String extension = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.ENGLISH); var type = switch (extension) { case "ply" -> PointCloudIO.Format.PLY; case "stl" -> PointCloudIO.Format.STL; case "obj" -> PointCloudIO.Format.OBJ; - default -> { - throw new RuntimeException("Unknown file type: " + extension); - } + default -> throw new RuntimeException("Unknown file type: " + extension); }; try (var input = new FileInputStream(file)) { - PointCloudIO.load(type, input, mesh, colors); + PointCloudIO.load(type, input, mesh); } } @@ -174,11 +171,10 @@ public static void load( File file, VertexMesh mesh, DogArray_I32 colors ) throw * @param format Storage format * @param input Input stream * @param mesh (Output) 3D mesh - * @param vertexRgb (Output) color of each vertex */ - public static void load( Format format, InputStream input, VertexMesh mesh, DogArray_I32 vertexRgb ) throws IOException { + public static void load( Format format, InputStream input, VertexMesh mesh ) throws IOException { switch (format) { - case PLY -> PlyCodec.readMesh(input, mesh, vertexRgb); + case PLY -> PlyCodec.readMesh(input, mesh); case OBJ -> ObjFileCodec.load(input, mesh); case STL -> { var stlMesh = new StlDataStructure(); @@ -197,10 +193,23 @@ public static void load( Format format, InputStream input, VertexMesh mesh, DogA */ public static void load( Format format, InputStream input, FunctionalWriter output ) throws IOException { PointCloudWriter pcw = new PointCloudWriter() { + Point3D_F64 location = new Point3D_F64(); + int color = 0; + @Override public void initialize( int size, boolean hasColor ) {} - @Override public void add( double x, double y, double z, int rgb ) { - output.add(x, y, z, rgb); + @Override public void startPoint() {} + + @Override public void stopPoint() { + output.add(location.x, location.y, location.z, color); + } + + @Override public void location( double x, double y, double z ) { + this.location.setTo(x, y, z); + } + + @Override public void color( int rgb ) { + this.color = rgb; } }; load(format, input, pcw); diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileCodec.java b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileCodec.java index 402c75e437..bee6f4bc93 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileCodec.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileCodec.java @@ -100,7 +100,18 @@ public static void save( VertexMesh mesh, Writer writer ) throws IOException { public static void load( InputStream input, PointCloudWriter output ) throws IOException { var obj = new ObjFileReader() { @Override protected void addVertex( double x, double y, double z ) { - output.add(x, y, z, 0); + output.startPoint(); + output.location(x, y, z); + output.stopPoint(); + } + + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + int rgb = ((int)(255*red)) << 16 | ((int)(255*green)) << 8 | ((int)(255*blue)); + output.startPoint(); + output.location(x, y, z); + output.color(rgb); + output.stopPoint(); } @Override protected void addVertexNormal( double x, double y, double z ) {} @@ -123,6 +134,13 @@ public static void load( InputStream input, VertexMesh output ) throws IOExcepti output.vertexes.append(x, y, z); } + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + int rgb = ((int)(255*red)) << 16 | ((int)(255*green)) << 8 | ((int)(255*blue)); + output.vertexes.append(x, y, z); + output.rgb.add(rgb); + } + @Override protected void addVertexNormal( double x, double y, double z ) { output.normals.append((float)x, (float)y, (float)z); } diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileReader.java b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileReader.java index bc2f3479ca..9265823f68 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileReader.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileReader.java @@ -78,7 +78,17 @@ public void parse( BufferedReader reader ) throws IOException { double x = Double.parseDouble(words[1]); double y = Double.parseDouble(words[2]); double z = Double.parseDouble(words[3]); - addVertex(x, y, z); + + // See if it also has the optional RGB values for each vertex + if (words.length == 7) { + double r = Double.parseDouble(words[4]); + double g = Double.parseDouble(words[5]); + double b = Double.parseDouble(words[6]); + addVertexWithColor(x, y, z, r, g, b); + } else { + addVertex(x, y, z); + } + vertexCount++; } @@ -153,6 +163,9 @@ private void readFaceIndexes( String[] words ) { protected abstract void addVertex( double x, double y, double z ); + /** Adds a color for a vertex. RGB is specified in a range of 0 to 1.0 */ + protected abstract void addVertexWithColor( double x, double y, double z, double red, double green, double blue ); + protected abstract void addVertexNormal( double x, double y, double z ); protected abstract void addVertexTexture( double x, double y ); diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileWriter.java b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileWriter.java index 31e2b100a2..c06ff0a6e8 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileWriter.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/impl/ObjFileWriter.java @@ -52,6 +52,17 @@ public void addVertex( double x, double y, double z ) throws IOException { vertexCount++; } + public void addVertex( double x, double y, double z, double r, double g , double b ) throws IOException { + writer.write('v'); + writer.write(String.format(" " + formatFloat, x)); + writer.write(String.format(" " + formatFloat, y)); + writer.write(String.format(" " + formatFloat, z)); + writer.write(String.format(" " + formatFloat, r)); + writer.write(String.format(" " + formatFloat, g)); + writer.write(String.format(" " + formatFloat + "\n", b)); + vertexCount++; + } + public void addTextureVertex( double x, double y ) throws IOException { writer.write("vt"); writer.write(String.format(" " + formatFloat, x)); diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyCodec.java b/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyCodec.java index 0df2e2552a..23800d27fb 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyCodec.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyCodec.java @@ -411,11 +411,23 @@ public static void readCloud( InputStream input, PointCloudWriter output ) throw output.initialize(vertexes, color); } - @Override public void addVertex( double x, double y, double z, int rgb ) { - output.add(x, y, z, rgb); + @Override public void startVertex() { + output.startPoint(); } - @Override public void addVertexNormal( double nx, double ny, double nz ) {} + @Override public void stopVertex() { + output.stopPoint(); + } + + @Override public void setVertexLocation( double x, double y, double z ) { + output.location(x, y, z); + } + + @Override public void setVertexColor( int r, int g, int b ) { + output.color(r << 16 | g << 8 | b); + } + + @Override public void setVertexNormal( double nx, double ny, double nz ) {} @Override public void addPolygon( int[] indexes, int offset, int length ) {} @@ -425,21 +437,27 @@ public static void readCloud( InputStream input, PointCloudWriter output ) throw }); } - public static void readMesh( InputStream input, VertexMesh mesh, DogArray_I32 colorRGB ) throws IOException { + public static void readMesh( InputStream input, VertexMesh mesh ) throws IOException { read(input, new PlyReader() { @Override public void initialize( int vertexes, int triangles, boolean color ) { - colorRGB.reset(); mesh.reset(); mesh.vertexes.reserve(vertexes); mesh.faceVertexes.reserve(triangles*3); } - @Override public void addVertex( double x, double y, double z, int rgb ) { + @Override public void startVertex() {} + + @Override public void stopVertex() {} + + @Override public void setVertexLocation( double x, double y, double z ) { mesh.vertexes.append(x, y, z); - colorRGB.add(rgb); } - @Override public void addVertexNormal( double nx, double ny, double nz ) { + @Override public void setVertexColor( int r, int g, int b ) { + mesh.rgb.add(r << 16 | g << 8 | b); + } + + @Override public void setVertexNormal( double nx, double ny, double nz ) { mesh.normals.append((float)nx, (float)ny, (float)nz); } @@ -517,11 +535,16 @@ private static void readAscii( PlyReader output, InputStream reader, Header head } } } - output.addVertex(x, y, z, r << 16 | g << 8 | b); + // Creates the vertex and specify its attributes + output.startVertex(); + output.setVertexLocation(x, y, z); + if (header.rgb) + output.setVertexColor(r, g, b); if (header.normals) { - output.addVertexNormal(nx, ny, nz); + output.setVertexNormal(nx, ny, nz); } + output.stopVertex(); } int[] indexes = new int[100]; @@ -607,11 +630,15 @@ private static void readCloudBinary( PlyReader output, InputStream reader, Heade } } - output.addVertex(x, y, z, r << 16 | g << 8 | b); + // Creates the vertex and specify its attributes + output.startVertex(); + output.setVertexLocation(x, y, z); + if (header.rgb) + output.setVertexColor(r, g, b); if (header.normals) { - output.addVertexNormal(nx, ny, nz); + output.setVertexNormal(nx, ny, nz); } - + output.stopVertex(); //System.out.printf("vertex: %.1e %.1e %.1e | %2x %2x %2x\n", x, y, z, r, g, b); } diff --git a/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyReader.java b/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyReader.java index 16d7e3b8b6..7e2e5bb137 100644 --- a/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyReader.java +++ b/main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyReader.java @@ -26,9 +26,17 @@ public interface PlyReader { void initialize( int vertexes, int triangles, boolean color ); - void addVertex( double x, double y, double z, int rgb ); + /** Creates a new vertex */ + void startVertex(); - void addVertexNormal( double nx, double ny, double nz ); + /** Done adding attributes to the vertex and saves the results */ + void stopVertex(); + + void setVertexLocation( double x, double y, double z ); + + void setVertexColor( int r, int g, int b ); + + void setVertexNormal( double nx, double ny, double nz ); void addPolygon( int[] indexes, int offset, int length ); diff --git a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileReader.java b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileReader.java index d58b33a1c9..f74c795b95 100644 --- a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileReader.java +++ b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileReader.java @@ -52,6 +52,11 @@ public class TestObjFileReader extends BoofStandardJUnit { vertexes.grow().setTo(x, y, z); } + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + throw new RuntimeException("Egads"); + } + @Override protected void addVertexNormal( double x, double y, double z ) {} @Override protected void addVertexTexture( double x, double y ) {} @@ -95,6 +100,11 @@ public class TestObjFileReader extends BoofStandardJUnit { vertexes.grow().setTo(x, y, z); } + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + vertexes.grow().setTo(x, y, z); + } + @Override protected void addVertexNormal( double x, double y, double z ) {normals.grow().setTo(x, y, z);} @Override protected void addVertexTexture( double x, double y ) {textures.grow().setTo(x, y);} @@ -121,6 +131,47 @@ public class TestObjFileReader extends BoofStandardJUnit { assertEquals(0.0, textures.get(2).distance(1.1, 0.1), UtilEjml.TEST_F64); } + /** test case where vectors also have colors */ + @Test void vectorColor() throws IOException { + String text = """ + # Simple Wavefront file + v 0.0 0.0 0.0 0.2 0.3 0.4 + v 0.0 1.0 0.0 0.3 0.2 0.1 + v 1.0 0.0 0.0 0.4 0.3 0.2 + """; + + var vertexes = new DogArray<>(Point3D_F64::new, Point3D_F64::zero); + var colors = new DogArray<>(Point3D_F64::new, Point3D_F64::zero); + + var reader = new DummyReader() { + @Override protected void addVertex( double x, double y, double z ) { + vertexes.grow().setTo(x, y, z); + } + + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + vertexes.grow().setTo(x, y, z); + colors.grow().setTo(red, green, blue); + } + + @Override protected void addVertexNormal( double x, double y, double z ) {} + + @Override protected void addVertexTexture( double x, double y ) {} + + @Override protected void addFace( DogArray_I32 vertexes, int vertexCount ) {} + }; + reader.parse(new BufferedReader(new StringReader(text))); + + assertEquals(3, vertexes.size); + assertEquals(0.0, vertexes.get(0).distance(0, 0, 0), UtilEjml.TEST_F64); + assertEquals(0.0, vertexes.get(1).distance(0, 1, 0), UtilEjml.TEST_F64); + assertEquals(0.0, vertexes.get(2).distance(1, 0, 0), UtilEjml.TEST_F64); + assertEquals(3, colors.size); + assertEquals(0.0, colors.get(0).distance(0.2, 0.3, 0.4), UtilEjml.TEST_F64); + assertEquals(0.0, colors.get(1).distance(0.3, 0.2, 0.1), UtilEjml.TEST_F64); + assertEquals(0.0, colors.get(2).distance(0.4, 0.3, 0.2), UtilEjml.TEST_F64); + } + /** * See if negative indexes are handled correct. Add two faces. Negative to be relative to current number * of read in vertexes @@ -143,6 +194,9 @@ public class TestObjFileReader extends BoofStandardJUnit { @Override protected void addVertex( double x, double y, double z ) {} + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) {} + @Override protected void addVertexNormal( double x, double y, double z ) {} @Override protected void addVertexTexture( double x, double y ) {} @@ -178,6 +232,11 @@ public class TestObjFileReader extends BoofStandardJUnit { @Override protected void addVertex( double x, double y, double z ) {vertexCount++;} + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + vertexCount++; + } + @Override protected void addVertexNormal( double x, double y, double z ) {} @Override protected void addVertexTexture( double x, double y ) {} @@ -196,6 +255,9 @@ public class TestObjFileReader extends BoofStandardJUnit { var reader = new DummyReader() { @Override protected void addVertex( double x, double y, double z ) {} + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) {} + @Override protected void addVertexNormal( double x, double y, double z ) {} @Override protected void addVertexTexture( double x, double y ) {} diff --git a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileWriter.java b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileWriter.java index 64f3cc94f8..a79f865662 100644 --- a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileWriter.java +++ b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestObjFileWriter.java @@ -39,7 +39,7 @@ public class TestObjFileWriter extends BoofStandardJUnit { var alg = new ObjFileWriter(output); alg.addVertex(1, 0, 0); alg.addVertex(0, 1, 0); - alg.addVertex(0, 0, 1); + alg.addVertex(0, 0, 1, 0.1, 0.2, 0.3); alg.addTextureVertex(1, 0); alg.addTextureVertex(0, 1); alg.addTextureVertex(1, 1); @@ -56,6 +56,7 @@ public class TestObjFileWriter extends BoofStandardJUnit { String text = output.toString(); var reader = new TestObjFileReader.DummyReader() { int countVertex = 0; + int countVertexColor = 0; int countTexture = 0; int countFaces = 0; @@ -71,6 +72,15 @@ public class TestObjFileWriter extends BoofStandardJUnit { countVertex++; } + @Override + protected void addVertexWithColor( double x, double y, double z, double red, double green, double blue ) { + addVertex(x, y, z); + assertEquals(0.1, red ); + assertEquals(0.2, green ); + assertEquals(0.3, blue ); + countVertexColor++; + } + @Override protected void addVertexNormal( double x, double y, double z ) {} @Override protected void addVertexTexture( double x, double y ) { @@ -95,6 +105,7 @@ public class TestObjFileWriter extends BoofStandardJUnit { }; reader.parse(new BufferedReader(new StringReader(text))); assertEquals(6, reader.countVertex); + assertEquals(1, reader.countVertexColor); assertEquals(2, reader.countFaces); } } diff --git a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestPlyCodec.java b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestPlyCodec.java index 5150a0a169..4202be93b6 100644 --- a/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestPlyCodec.java +++ b/main/boofcv-io/src/test/java/boofcv/io/points/impl/TestPlyCodec.java @@ -165,17 +165,16 @@ class TestPlyCodec extends BoofStandardJUnit { PlyCodec.saveMeshBinary(mesh, colors, endian, true, output); var foundMesh = new VertexMesh(); - var foundColors = new DogArray_I32(); var input = new ByteArrayInputStream(output.toByteArray()); - PlyCodec.readMesh(input, foundMesh, foundColors); + PlyCodec.readMesh(input, foundMesh); assertEquals(mesh.vertexes.size(), mesh.vertexes.size()); assertEquals(mesh.normals.size(), mesh.normals.size()); assertEquals(mesh.texture.size(), mesh.texture.size()); assertTrue(mesh.faceVertexes.isEquals(foundMesh.faceVertexes)); assertTrue(mesh.faceOffsets.isEquals(foundMesh.faceOffsets)); - assertTrue(colors.isEquals(foundColors)); + assertTrue(colors.isEquals(foundMesh.rgb)); for (int i = 0; i < mesh.vertexes.size(); i++) { Point3D_F64 expected = mesh.vertexes.getTemp(i); @@ -206,7 +205,7 @@ class TestPlyCodec extends BoofStandardJUnit { var input = new ByteArrayInputStream(output.toByteArray()); var foundMesh = new VertexMesh(); - PlyCodec.readMesh(input, foundMesh, new DogArray_I32()); + PlyCodec.readMesh(input, foundMesh); assertEquals("foo", foundMesh.textureName); } diff --git a/main/boofcv-types/src/main/java/boofcv/struct/mesh/VertexMesh.java b/main/boofcv-types/src/main/java/boofcv/struct/mesh/VertexMesh.java index a573dd29a6..91dc489d51 100644 --- a/main/boofcv-types/src/main/java/boofcv/struct/mesh/VertexMesh.java +++ b/main/boofcv-types/src/main/java/boofcv/struct/mesh/VertexMesh.java @@ -47,6 +47,9 @@ public class VertexMesh { /** Optional precomputed normals for each vertex */ public final PackedArrayPoint3D_F32 normals = new PackedArrayPoint3D_F32(); + /** Optional vertex colors in RGB format */ + public final DogArray_I32 rgb = new DogArray_I32(); + /** Which indexes correspond to normal for a face */ public final DogArray_I32 faceVertexes = new DogArray_I32(); @@ -199,6 +202,7 @@ public VertexMesh setTo( VertexMesh src ) { this.vertexes.setTo(src.vertexes); this.texture.setTo(src.texture); this.normals.setTo(src.normals); + this.rgb.setTo(src.rgb); this.faceVertexes.setTo(src.faceVertexes); this.faceNormals.setTo(src.faceNormals); this.faceOffsets.setTo(src.faceOffsets); @@ -211,6 +215,7 @@ public void reset() { vertexes.reset(); texture.reset(); normals.reset(); + rgb.reset(); faceVertexes.reset(); faceNormals.reset(); faceOffsets.reset().add(0);