Skip to content

Commit

Permalink
VertexMesh
Browse files Browse the repository at this point in the history
- Changed so that there is a separate set of indexes for vector normals
OBJ Files
- Work in progress for supporting textures
  • Loading branch information
lessthanoptimal committed Jun 23, 2024
1 parent a2049f8 commit 0e1d6a4
Show file tree
Hide file tree
Showing 19 changed files with 366 additions and 149 deletions.
Original file line number Diff line number Diff line change
@@ -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).
*
Expand Down Expand Up @@ -40,7 +40,7 @@ public class MeshColorizeOps {
* @return SurfaceColor
*/
public static RenderMesh.SurfaceColor colorizeByVertex( VertexMesh mesh, int[] vertexColor ) {
return ( shapeIdx ) -> vertexColor[mesh.indexes.get(mesh.offsets.get(shapeIdx))];
return ( shapeIdx ) -> vertexColor[mesh.faceVertexes.get(mesh.faceOffsets.get(shapeIdx))];
}

/**
Expand All @@ -57,7 +57,7 @@ public static RenderMesh.SurfaceColor colorizeByNormal( VertexMesh mesh ) {
var axisZ = new Vector3D_F64(0, 0, 1);

for (int i = 0; i < mesh.size(); i++) {
mesh.getShape(i, facet);
mesh.getFaceVectors(i, facet);

// Handle case of invalid facet gracefully by assigning it to an arbitrary color
if (facet.size < 3) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void createFlatSquareScene( CameraPinhole intrinsics, Random rand ) {
shape.get(2).setTo(x1, y1, z);
shape.get(3).setTo(x0, y1, z);

mesh.addShape(shape.toList());
mesh.addFaceVectors(shape.toList());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,13 @@ public VertexMesh toMesh( @Nullable VertexMesh out ) {
out = new VertexMesh();

out.vertexes.setTo(vertexes);
out.indexes.setTo(facetVertsIdx);
out.faceVertexes.setTo(facetVertsIdx);

// All facets are triangles
out.offsets.resize(facetCount() + 1);
out.offsets.set(0, 0);
for (int i = 1; i < out.offsets.size; i++) {
out.offsets.set(i, i*3);
out.faceOffsets.resize(facetCount() + 1);
out.faceOffsets.set(0, 0);
for (int i = 1; i < out.faceOffsets.size; i++) {
out.faceOffsets.set(i, i*3);
}

return out;
Expand Down
Original file line number Diff line number Diff line change
@@ -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).
*
Expand All @@ -21,6 +21,8 @@
import boofcv.alg.cloud.PointCloudReader;
import boofcv.alg.cloud.PointCloudWriter;
import boofcv.struct.mesh.VertexMesh;
import georegression.struct.point.Point2D_F32;
import georegression.struct.point.Point3D_F32;
import georegression.struct.point.Point3D_F64;
import org.ddogleg.struct.DogArray_I32;

Expand Down Expand Up @@ -62,15 +64,36 @@ public static void save( VertexMesh mesh, Writer writer ) throws IOException {
obj.addVertex(p.x, p.y, p.z);
}

// Save vertex normals
for (int i = 0; i < mesh.normals.size(); i++) {
Point3D_F32 p = mesh.normals.getTemp(i);
obj.addVertex(p.x, p.y, p.z);
}

// Save vertex textures
for (int i = 0; i < mesh.texture.size(); i++) {
Point2D_F32 p = mesh.texture.getTemp(i);
obj.addTextureVertex(p.x, p.y);
}

// See how many different types of vertexes need to be saved
int count = 0;
if (mesh.vertexes.size() > 0)
count++;
if (mesh.normals.size() > 0)
count++;
if (mesh.texture.size() > 0)
count++;

// Create the faces
var indexes = new DogArray_I32();
for (int i = 1; i < mesh.offsets.size; i++) {
int idx0 = mesh.offsets.get(i - 1);
int idx1 = mesh.offsets.get(i);
for (int i = 1; i < mesh.faceOffsets.size; i++) {
int idx0 = mesh.faceOffsets.get(i - 1);
int idx1 = mesh.faceOffsets.get(i);

indexes.reset();
indexes.addAll(mesh.indexes.data, idx0, idx1);
obj.addFace(indexes);
indexes.addAll(mesh.faceVertexes.data, idx0, idx1);
obj.addFace(indexes, count);
}
}

Expand All @@ -79,9 +102,16 @@ public static void load( InputStream input, PointCloudWriter output ) throws IOE
@Override protected void addVertex( double x, double y, double z ) {
output.add(x, y, z, 0);
}

@Override protected void addVertexNormal( double x, double y, double z ) {}

@Override protected void addVertexTexture( double x, double y ) {}

@Override protected void addPoint( int vertex ) {}

@Override protected void addLine( DogArray_I32 vertexes ) {}
@Override protected void addFace( DogArray_I32 vertexes ) {}

@Override protected void addFace( DogArray_I32 indexes, int vertexCount ) {}
};
obj.parse(new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)));
}
Expand All @@ -92,11 +122,30 @@ public static void load( InputStream input, VertexMesh output ) throws IOExcepti
@Override protected void addVertex( double x, double y, double z ) {
output.vertexes.append(x, y, z);
}

@Override protected void addVertexNormal( double x, double y, double z ) {
output.normals.append((float)x, (float)y, (float)z);
}

@Override protected void addVertexTexture( double x, double y ) {
output.texture.append((float)x, (float)y);
}

@Override protected void addPoint( int vertex ) {}

@Override protected void addLine( DogArray_I32 vertexes ) {}
@Override protected void addFace( DogArray_I32 vertexes ) {
output.indexes.addAll(vertexes);
output.offsets.add(output.indexes.size);

@Override protected void addFace( DogArray_I32 indexes, int vertexCount ) {
int types = indexes.size/vertexCount;
for (int idxVert = 0; idxVert < vertexCount; idxVert++) {
output.faceVertexes.add(indexes.get(idxVert)/types);
for (int i = 1; i < types; i++) {
if (indexes.get(i - 1) != indexes.get(i))
throw new RuntimeException("Only vertexes types with the same index supported");
}
}

output.faceOffsets.add(output.faceVertexes.size);
}
};
obj.parse(new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)));
Expand Down
Original file line number Diff line number Diff line change
@@ -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).
*
Expand Down Expand Up @@ -29,12 +29,16 @@
public abstract class ObjFileReader {
DogArray_I32 vertexIndexes = new DogArray_I32();
int vertexCount = 0;
int vertexTextureCount = 0;
int vertexNormalCount = 0;

/**
* Decodes / reads the OBJ file encoded as text in the reader
*/
public void parse( BufferedReader reader ) throws IOException {
vertexCount = 0;
vertexTextureCount = 0;
vertexNormalCount = 0;

var builder = new StringBuilder();
int actualLineCount = 0;
Expand Down Expand Up @@ -77,14 +81,30 @@ public void parse( BufferedReader reader ) throws IOException {
addVertex(x, y, z);
vertexCount++;
}

case "vn" -> {
double x = Double.parseDouble(words[1]);
double y = Double.parseDouble(words[2]);
double z = Double.parseDouble(words[3]);
addVertexNormal(x, y, z);
vertexNormalCount++;
}

case "vt" -> {
double x = Double.parseDouble(words[1]);
double y = Double.parseDouble(words[2]);
addVertexTexture(x, y);
vertexTextureCount++;
}

case "p" -> addPoint(ensureIndex(Integer.parseInt(words[1])));
case "l" -> {
readPoints(words);
addLine(vertexIndexes);
}
case "f" -> {
readPoints(words);
addFace(vertexIndexes);
readFaceIndexes(words);
addFace(vertexIndexes, words.length - 1);
}
default -> handleError(actualLineCount + " Unknown object type. '" + words[0] + "'");
}
Expand Down Expand Up @@ -115,13 +135,39 @@ private void readPoints( String[] words ) {
}
}

private void readFaceIndexes( String[] words ) {
vertexIndexes.reset();
for (int i = 1; i < words.length; i++) {
String word = words[i];

int idx0 = 0;
int idx1 = word.indexOf('/');
while (idx1 != -1) {
vertexIndexes.add(ensureIndex(Integer.parseInt(word.substring(idx0, idx1))));
idx0 = idx1 + 1;
idx1 = word.indexOf('/', idx0);
}
vertexIndexes.add(ensureIndex(Integer.parseInt(word.substring(idx0))));
}
}

protected abstract void addVertex( double x, double y, double z );

protected abstract void addVertexNormal( double x, double y, double z );

protected abstract void addVertexTexture( double x, double y );

protected abstract void addPoint( int vertex );

protected abstract void addLine( DogArray_I32 vertexes );

protected abstract void addFace( DogArray_I32 vertexes );
/**
* Adds a face. Indexes are interleaved in vertex, normal, and texture order.
*
* @param indexes Indexes of vertex values
* @param vertexCount Number of vertexes in the face
*/
protected abstract void addFace( DogArray_I32 indexes, int vertexCount );

/**
* If something goes where it's passed here
Expand Down
Original file line number Diff line number Diff line change
@@ -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).
*
Expand Down Expand Up @@ -90,7 +90,7 @@ public void addPoint( int vertex ) throws IOException {
* @param vertexes Index of vertexes in this face. If null then automatic.
*/
public void addLine( @Nullable DogArray_I32 vertexes ) throws IOException {
writeObject('l', vertexes);
writeObject('l', vertexes, 1);
}

/**
Expand All @@ -99,13 +99,21 @@ public void addLine( @Nullable DogArray_I32 vertexes ) throws IOException {
*
* <p>NOTE: Vertexes indexes are 0-indexed. Not 1-indexed like they are in OBJ files.</p>
*
*
* @param count Number of different vertex types, i.e. 3D, texture, normal. All are assumed to
* reference a point with the same index.
* @param vertexes Index of vertexes in this face. If null then automatic.
*/
public void addFace( @Nullable DogArray_I32 vertexes ) throws IOException {
writeObject('f', vertexes);
public void addFace( @Nullable DogArray_I32 vertexes, int count ) throws IOException {
writeObject('f', vertexes, count);
}

private void writeObject( char name, @Nullable DogArray_I32 vertexes ) throws IOException {
/**
*
* @param count How many times it should duplicate the index. It's assumed all vertexes reference
* objects with the same index values.
*/
private void writeObject( char name, @Nullable DogArray_I32 vertexes, int count ) throws IOException {
writer.write(name);
if (vertexes == null || vertexes.size == 0) {
int v = -1;
Expand All @@ -125,7 +133,11 @@ else if (vertIndex <= -vertexCount)
// Write the index and compensate for the file format being 1-indexed.
if (vertIndex >= 0)
vertIndex++;

writer.write(" " + vertIndex);
for (int idxCount = 1; idxCount < count; idxCount++) {
writer.write("/" + vertIndex);
}
}
}
writer.write('\n');
Expand Down
24 changes: 12 additions & 12 deletions main/boofcv-io/src/main/java/boofcv/io/points/impl/PlyCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ public static void readMesh( InputStream input, VertexMesh mesh, DogArray_I32 co
colorRGB.reset();
mesh.reset();
mesh.vertexes.reserve(vertexes);
mesh.indexes.reserve(triangles*3);
mesh.faceVertexes.reserve(triangles*3);
}

@Override public void addVertex( double x, double y, double z, int rgb ) {
Expand All @@ -440,12 +440,12 @@ public static void readMesh( InputStream input, VertexMesh mesh, DogArray_I32 co
}

@Override public void addVertexNormal( double nx, double ny, double nz ) {
mesh.vertexNormals.append((float)nx, (float)ny, (float)nz);
mesh.normals.append((float)nx, (float)ny, (float)nz);
}

@Override public void addPolygon( int[] indexes, int offset, int length ) {
mesh.offsets.add(mesh.indexes.size + length);
mesh.indexes.addAll(indexes, offset, offset + length);
mesh.faceOffsets.add(mesh.faceVertexes.size + length);
mesh.faceVertexes.addAll(indexes, offset, offset + length);
}

@Override public void setTextureName( String textureName ) {
Expand Down Expand Up @@ -688,7 +688,7 @@ private static PlyWriter wrapMeshForWriting( VertexMesh mesh, @Nullable DogArray
return new PlyWriter() {
@Override public int getVertexCount() {return mesh.vertexes.size();}

@Override public int getPolygonCount() {return mesh.offsets.size - 1;}
@Override public int getPolygonCount() {return mesh.faceOffsets.size - 1;}

@Override public boolean isColor() {return colorRGB != null;}

Expand All @@ -697,7 +697,7 @@ private static PlyWriter wrapMeshForWriting( VertexMesh mesh, @Nullable DogArray
}

@Override public boolean isVertexNormals() {
return mesh.vertexNormals.size() > 0;
return mesh.normals.size() > 0;
}

@Override public String getTextureName() {
Expand All @@ -707,27 +707,27 @@ private static PlyWriter wrapMeshForWriting( VertexMesh mesh, @Nullable DogArray
@Override public void getVertex( int which, Point3D_F64 vertex ) {mesh.vertexes.getCopy(which, vertex);}

@Override public void getVertexNormal( int which, GeoTuple3D_F64<?> normal ) {
Point3D_F32 tmp = mesh.vertexNormals.getTemp(which);
Point3D_F32 tmp = mesh.normals.getTemp(which);
normal.setTo(tmp.x, tmp.y, tmp.z);
}

@SuppressWarnings("NullAway")
@Override public int getColor( int which ) {return colorRGB.get(which);}

@Override public int getIndexes( int which, int[] indexes ) {
int idx0 = mesh.offsets.get(which);
int idx1 = mesh.offsets.get(which + 1);
int idx0 = mesh.faceOffsets.get(which);
int idx1 = mesh.faceOffsets.get(which + 1);

for (int i = idx0; i < idx1; i++) {
indexes[i - idx0] = mesh.indexes.get(i);
indexes[i - idx0] = mesh.faceVertexes.get(i);
}

return idx1 - idx0;
}

@Override public int getTextureCoors( int which, float[] coordinates ) {
int idx0 = mesh.offsets.get(which);
int idx1 = mesh.offsets.get(which + 1);
int idx0 = mesh.faceOffsets.get(which);
int idx1 = mesh.faceOffsets.get(which + 1);

int idxArray = 0;
for (int i = idx0; i < idx1; i++) {
Expand Down
Loading

0 comments on commit 0e1d6a4

Please sign in to comment.