From bff72d3532803c2c51271fcb72d71695b1bbf599 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 15 Aug 2023 03:18:08 +0200 Subject: [PATCH] Add vertex scaling --- .../file/wavefrontobj/ObjUtils.java | 57 +++++++++++++++++++ .../wavefrontobj/export/OBJExportOptions.java | 13 +++++ .../export/OBJExporterFactory.java | 48 +++++++++++----- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/core/src/net/sf/openrocket/file/wavefrontobj/ObjUtils.java b/core/src/net/sf/openrocket/file/wavefrontobj/ObjUtils.java index d1963aa6d..f44a797c7 100644 --- a/core/src/net/sf/openrocket/file/wavefrontobj/ObjUtils.java +++ b/core/src/net/sf/openrocket/file/wavefrontobj/ObjUtils.java @@ -203,6 +203,63 @@ public class ObjUtils { } } + /** + * Scale the vertices of an object around a certain origin. + * NOTE: this uses the Wavefront OBJ coordinate system + * @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 + * NOTE: this uses the Wavefront OBJ coordinate system + * @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) { if (startIdx < 0 || startIdx >= obj.getNumVertices()) { throw new IllegalArgumentException("startIdx must be between 0 and the number of vertices"); diff --git a/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExportOptions.java b/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExportOptions.java index 283cd8868..605b1fd40 100644 --- a/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExportOptions.java +++ b/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExportOptions.java @@ -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). */ 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) @@ -47,6 +51,7 @@ public class OBJExportOptions { this.triangulate = false; this.LOD = ObjUtils.LevelOfDetail.NORMAL; this.transformer = new DefaultCoordTransform(rocket.getLength()); + this.scaling = 1.0f; } public boolean isExportChildren() { @@ -104,4 +109,12 @@ public class OBJExportOptions { public void setExportAsSeparateFiles(boolean exportAsSeparateFiles) { this.exportAsSeparateFiles = exportAsSeparateFiles; } + + public float getScaling() { + return scaling; + } + + public void setScaling(float scaling) { + this.scaling = scaling; + } } diff --git a/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExporterFactory.java b/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExporterFactory.java index 8c35f3e7f..b77eeb40d 100644 --- a/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExporterFactory.java +++ b/core/src/net/sf/openrocket/file/wavefrontobj/export/OBJExporterFactory.java @@ -34,6 +34,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -92,8 +93,15 @@ public class OBJExporterFactory { */ public void doExport() { DefaultObj obj = new DefaultObj(); + Map objFileMap; boolean exportAsSeparateFiles = this.options.isExportAsSeparateFiles(); + if (exportAsSeparateFiles) { + objFileMap = new HashMap<>(); + } else { + objFileMap = Map.of(this.filePath, obj); + } + // Get all the components to export Set componentsToExport = new HashSet<>(this.components); if (this.options.isExportChildren()) { @@ -127,30 +135,42 @@ public class OBJExporterFactory { String groupName = idx + "_" + component.getName(); 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) { String path = FileUtils.removeExtension(this.filePath) + "_" + groupName + ".obj"; - writeObj(obj, path); + objFileMap.put(path, obj); } idx++; } - if (this.options.isTriangulate()) { - obj = de.javagl.obj.ObjUtils.triangulate(obj, new DefaultObj()); - } + // Apply export options and write the OBJ files + for (Map.Entry entry : objFileMap.entrySet()) { + String filePath = entry.getKey(); + obj = entry.getValue(); - 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(); + // Triangulate mesh + if (this.options.isTriangulate()) { + obj = de.javagl.obj.ObjUtils.triangulate(obj, new DefaultObj()); + } - 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) { - writeObj(obj, this.filePath); + ObjUtils.removeVertexOffset(obj, this.options.getTransformer()); + } + + // Perform scaling + if (Float.compare(options.getScaling(), 1) != 0) { + ObjUtils.scaleVertices(obj, options.getScaling()); + } + + // Write the OBJ file + writeObj(obj, filePath); } }