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);
}
}