Add vertex scaling

This commit is contained in:
SiboVG 2023-08-15 03:18:08 +02:00
parent 15d3bce807
commit bff72d3532
3 changed files with 104 additions and 14 deletions

View File

@ -203,6 +203,63 @@ public class ObjUtils {
} }
} }
/**
* Scale the vertices of an object around a certain origin.
* <b>NOTE: this uses the Wavefront OBJ coordinate system</b>
* @param obj The object to scale the vertices of
* @param startIdx The starting vertex index to scale
* @param endIdx The ending vertex index to scale (inclusive)
* @param scaleX The scaling factor in the x direction
* @param scaleY The scaling factor in the y direction
* @param scaleZ The scaling factor in the z direction
* @param origX The x coordinate of the origin of the scaling
* @param origY The y coordinate of the origin of the scaling
* @param origZ The z coordinate of the origin of the scaling
*/
public static void scaleVertices(DefaultObj obj, int startIdx, int endIdx,
float scaleX, float scaleY, float scaleZ,
float origX, float origY, float origZ) {
verifyIndexRange(obj, startIdx, endIdx);
if (Float.compare(scaleX, 1) == 0 && Float.compare(scaleY, 1) == 0 && Float.compare(scaleZ, 1) == 0) {
return;
}
for (int i = startIdx; i <= endIdx; i++) {
FloatTuple vertex = obj.getVertex(i);
// Translate vertex to origin
final float x = vertex.getX() - origX;
final float y = vertex.getY() - origY;
final float z = vertex.getZ() - origZ;
// Apply scaling
float scaledX = x * scaleX;
float scaledY = y * scaleY;
float scaledZ = z * scaleZ;
// Translate vertex back to its original position
scaledX += origX;
scaledY += origY;
scaledZ += origZ;
FloatTuple scaledVertex = new DefaultFloatTuple(scaledX, scaledY, scaledZ);
obj.setVertex(i, scaledVertex);
}
}
/**
* Scale the vertices of an object around the origin
* <b>NOTE: this uses the Wavefront OBJ coordinate system</b>
* @param obj The object to scale the vertices of
* @param scaling The uniform scaling factor
*/
public static void scaleVertices(DefaultObj obj, float scaling) {
scaleVertices(obj, 0, obj.getNumVertices() - 1,
scaling, scaling, scaling,
0, 0, 0);
}
private static void verifyIndexRange(DefaultObj obj, int startIdx, int endIdx) { private static void verifyIndexRange(DefaultObj obj, int startIdx, int endIdx) {
if (startIdx < 0 || startIdx >= obj.getNumVertices()) { if (startIdx < 0 || startIdx >= obj.getNumVertices()) {
throw new IllegalArgumentException("startIdx must be between 0 and the number of vertices"); throw new IllegalArgumentException("startIdx must be between 0 and the number of vertices");

View File

@ -36,6 +36,10 @@ public class OBJExportOptions {
* This is used to convert the coordinates from the rocket's coordinate system to the OBJ coordinate system (which is arbitrary). * This is used to convert the coordinates from the rocket's coordinate system to the OBJ coordinate system (which is arbitrary).
*/ */
private CoordTransform transformer; private CoordTransform transformer;
/**
* The scaling factor to use for the export (1 = no scaling).
*/
private float scaling;
// TODO: scaling (to mm = x1000, or SI units) // TODO: scaling (to mm = x1000, or SI units)
@ -47,6 +51,7 @@ public class OBJExportOptions {
this.triangulate = false; this.triangulate = false;
this.LOD = ObjUtils.LevelOfDetail.NORMAL; this.LOD = ObjUtils.LevelOfDetail.NORMAL;
this.transformer = new DefaultCoordTransform(rocket.getLength()); this.transformer = new DefaultCoordTransform(rocket.getLength());
this.scaling = 1.0f;
} }
public boolean isExportChildren() { public boolean isExportChildren() {
@ -104,4 +109,12 @@ public class OBJExportOptions {
public void setExportAsSeparateFiles(boolean exportAsSeparateFiles) { public void setExportAsSeparateFiles(boolean exportAsSeparateFiles) {
this.exportAsSeparateFiles = exportAsSeparateFiles; this.exportAsSeparateFiles = exportAsSeparateFiles;
} }
public float getScaling() {
return scaling;
}
public void setScaling(float scaling) {
this.scaling = scaling;
}
} }

View File

@ -34,6 +34,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -92,8 +93,15 @@ public class OBJExporterFactory {
*/ */
public void doExport() { public void doExport() {
DefaultObj obj = new DefaultObj(); DefaultObj obj = new DefaultObj();
Map<String, DefaultObj> objFileMap;
boolean exportAsSeparateFiles = this.options.isExportAsSeparateFiles(); boolean exportAsSeparateFiles = this.options.isExportAsSeparateFiles();
if (exportAsSeparateFiles) {
objFileMap = new HashMap<>();
} else {
objFileMap = Map.of(this.filePath, obj);
}
// Get all the components to export // Get all the components to export
Set<RocketComponent> componentsToExport = new HashSet<>(this.components); Set<RocketComponent> componentsToExport = new HashSet<>(this.components);
if (this.options.isExportChildren()) { if (this.options.isExportChildren()) {
@ -127,30 +135,42 @@ public class OBJExporterFactory {
String groupName = idx + "_" + component.getName(); String groupName = idx + "_" + component.getName();
handleComponent(obj, this.configuration, this.options.getTransformer(), component, groupName, this.options.getLOD()); handleComponent(obj, this.configuration, this.options.getTransformer(), component, groupName, this.options.getLOD());
// If separate export, already need to write the OBJ here // If separate export, add this object to the map of objects to export
if (exportAsSeparateFiles) { if (exportAsSeparateFiles) {
String path = FileUtils.removeExtension(this.filePath) + "_" + groupName + ".obj"; String path = FileUtils.removeExtension(this.filePath) + "_" + groupName + ".obj";
writeObj(obj, path); objFileMap.put(path, obj);
} }
idx++; idx++;
} }
if (this.options.isTriangulate()) { // Apply export options and write the OBJ files
obj = de.javagl.obj.ObjUtils.triangulate(obj, new DefaultObj()); for (Map.Entry<String, DefaultObj> entry : objFileMap.entrySet()) {
} String filePath = entry.getKey();
obj = entry.getValue();
if (this.options.isRemoveOffset()) { // Triangulate mesh
// Because of some rotation and translation operations when creating the meshes, the bounds can be inaccurate. if (this.options.isTriangulate()) {
// Therefore, we will recalculate them to be sure. obj = de.javagl.obj.ObjUtils.triangulate(obj, new DefaultObj());
// Is a bit computationally expensive, but it's the only way to be sure... }
obj.recalculateAllVertexBounds();
ObjUtils.removeVertexOffset(obj, this.options.getTransformer()); // Remove position offset
} if (this.options.isRemoveOffset()) {
// Because of some rotation and translation operations when creating the meshes, the bounds can be inaccurate.
// Therefore, we will recalculate them to be sure.
// Is a bit computationally expensive, but it's the only way to be sure...
obj.recalculateAllVertexBounds();
if (!exportAsSeparateFiles) { ObjUtils.removeVertexOffset(obj, this.options.getTransformer());
writeObj(obj, this.filePath); }
// Perform scaling
if (Float.compare(options.getScaling(), 1) != 0) {
ObjUtils.scaleVertices(obj, options.getScaling());
}
// Write the OBJ file
writeObj(obj, filePath);
} }
} }