WIP - FIX COORDINATE SYSTEM!!
This commit is contained in:
parent
081afdb635
commit
6efd39b453
@ -4,6 +4,8 @@ import de.javagl.obj.FloatTuple;
|
||||
import de.javagl.obj.Obj;
|
||||
import de.javagl.obj.ObjFace;
|
||||
import de.javagl.obj.ObjGroup;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -298,6 +300,29 @@ public class ObjUtils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translates the vertices in the obj file so that the component is at the specified location.
|
||||
* See explanation in {@link net.sf.openrocket.file.wavefrontobj.export.OBJExporterFactory} about the difference in
|
||||
* coordinate system between OpenRocket and Wavefront OBJ.
|
||||
* @param obj The obj file to translate
|
||||
* @param component The component to translate
|
||||
* @param startIdx The index of the first vertex to translate
|
||||
* @param endIdx The index of the last vertex to translate (inclusive)
|
||||
* @param location The location to translate the component to (in OpenRocket coordinate system)
|
||||
* @param yOffset The offset to apply to the y coordinate of the location
|
||||
*/
|
||||
public static void translateVerticesFromComponentLocation(DefaultObj obj, RocketComponent component,
|
||||
int startIdx, int endIdx, Coordinate location, double yOffset) {
|
||||
final double rocketLength = component.getRocket().getLength();
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength + yOffset - location.x);
|
||||
final float z = (float) - location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a list of objs into a single obj.
|
||||
* @param objs The objs to merge
|
||||
|
@ -32,6 +32,20 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Exporter for rocket components to a Wavefront OBJ file.
|
||||
* <b>NOTE: </b> The coordinate system of the Wavefront OBJ file and OpenRocket is different.
|
||||
* An OBJ file has the y-axis pointing up, the z-axis pointing towards the viewer, and the x-axis pointing to the right (right-handed system).
|
||||
* OpenRocket uses a left-handed system with the y-axis pointing up, the z-axis pointing away from the viewer, and the
|
||||
* x-axis pointing to the right (in the side view). Its origin is also at the tip of the rocket, whereas for the OBJ it
|
||||
* would be the bottom of the rocket.
|
||||
* => the following transformation applies from OBJ coordinate system to OpenRocket coordinate system:
|
||||
* x = y
|
||||
* y = rocketLength - x
|
||||
* z = -z
|
||||
*
|
||||
* @author Sibo Van Gool <sibo.vangool@hotmail.com>
|
||||
*/
|
||||
public class OBJExporterFactory {
|
||||
|
||||
private final List<RocketComponent> components;
|
||||
|
@ -5,7 +5,6 @@ import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.CylinderExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.TubeExporter;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
public class BodyTubeExporter extends RocketComponentExporter {
|
||||
@ -21,17 +20,16 @@ public class BodyTubeExporter extends RocketComponentExporter {
|
||||
final float innerRadius = (float) bodyTube.getInnerRadius();
|
||||
final float length = (float) bodyTube.getLength();
|
||||
final boolean isFilled = bodyTube.isFilled();
|
||||
final double rocketLength = bodyTube.getRocket().getLength();
|
||||
final Coordinate[] locations = bodyTube.getComponentLocations();
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(outerRadius, innerRadius, length, isFilled, rocketLength, location);
|
||||
generateMesh(bodyTube, outerRadius, innerRadius, length, isFilled, location);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(float outerRadius, float innerRadius, float length, boolean isFilled,
|
||||
double rocketLength, Coordinate location) {
|
||||
private void generateMesh(BodyTube bodyTube, float outerRadius, float innerRadius, float length, boolean isFilled,
|
||||
Coordinate location) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
|
||||
if (isFilled || Float.compare(innerRadius, 0) == 0) {
|
||||
@ -46,10 +44,7 @@ public class BodyTubeExporter extends RocketComponentExporter {
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - length - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, bodyTube, startIdx, endIdx, location, -length);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,6 @@ import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.PolygonExporter;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
|
||||
import net.sf.openrocket.util.ArrayUtils;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -43,12 +40,12 @@ public class FinSetExporter extends RocketComponentExporter {
|
||||
|
||||
// Generate the fin meshes
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
generateMesh(finSet,floatPoints, floatTabPoints, thickness, hasTabs, rocketLength, locations[i], angles[i]);
|
||||
generateMesh(finSet,floatPoints, floatTabPoints, thickness, hasTabs, locations[i], angles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(FinSet finSet, FloatPoints floatPoints, FloatPoints floatTabPoints, float thickness,
|
||||
boolean hasTabs, double rocketLength, Coordinate location, double angle) {
|
||||
boolean hasTabs, Coordinate location, double angle) {
|
||||
// Generate the mesh
|
||||
final int startIdx = obj.getNumVertices();
|
||||
final int normalsStartIdx = obj.getNumNormals();
|
||||
@ -58,7 +55,6 @@ public class FinSetExporter extends RocketComponentExporter {
|
||||
floatPoints.getXCoords(), floatPoints.getYCoords(), thickness);
|
||||
|
||||
// Generate the fin tabs
|
||||
final int tabStartIdx = obj.getNumVertices();
|
||||
if (hasTabs) {
|
||||
PolygonExporter.addPolygonMesh(obj, null,
|
||||
floatTabPoints.getXCoords(), floatTabPoints.getYCoords(), thickness);
|
||||
@ -67,23 +63,18 @@ public class FinSetExporter extends RocketComponentExporter {
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
int normalsEndIdx = Math.max(obj.getNumNormals() - 1, normalsStartIdx); // Clamp in case no normals were added
|
||||
|
||||
// First rotate the fin for a correct orientation
|
||||
final float axialRot = (float) angle;
|
||||
final float cantAngle = (float) finSet.getCantAngle();
|
||||
// First rotate for the cant angle
|
||||
final float cantAngle = (float) - finSet.getCantAngle();
|
||||
ObjUtils.rotateVertices(obj, startIdx, endIdx, normalsStartIdx, normalsEndIdx,
|
||||
-(float) Math.PI/2, cantAngle, axialRot, 0, 0, 0);
|
||||
cantAngle, 0, 0, 0, (float) - finSet.getLength(), 0);
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
// Then do the axial rotation
|
||||
final float axialRot = (float) angle;
|
||||
ObjUtils.rotateVertices(obj, startIdx, endIdx, normalsStartIdx, normalsEndIdx,
|
||||
0, axialRot, 0, 0, 0, 0);
|
||||
|
||||
// Offset the tabs
|
||||
if (hasTabs) {
|
||||
float yTab = - (float) finSet.getTabPosition(AxialMethod.TOP);
|
||||
ObjUtils.translateVertices(obj, tabStartIdx, endIdx, 0, yTab, 0);
|
||||
}
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, finSet, startIdx, endIdx, location, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.CylinderExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.TubeExporter;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
public class LaunchLugExporter extends RocketComponentExporter {
|
||||
@ -18,19 +17,18 @@ public class LaunchLugExporter extends RocketComponentExporter {
|
||||
final LaunchLug lug = (LaunchLug) component;
|
||||
|
||||
final Coordinate[] locations = lug.getComponentLocations();
|
||||
final double rocketLength = lug.getRocket().getLength();
|
||||
final float outerRadius = (float) lug.getOuterRadius();
|
||||
final float innerRadius = (float) lug.getInnerRadius();
|
||||
final float length = (float) lug.getLength();
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(lug, outerRadius, innerRadius, length, rocketLength, location);
|
||||
generateMesh(lug, outerRadius, innerRadius, length, location);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(LaunchLug lug, float outerRadius, float innerRadius, float length, double rocketLength, Coordinate location) {
|
||||
int startIdx2 = obj.getNumVertices();
|
||||
private void generateMesh(LaunchLug lug, float outerRadius, float innerRadius, float length, Coordinate location) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
|
||||
// Generate the instance mesh
|
||||
if (Float.compare(innerRadius, 0) == 0) {
|
||||
@ -43,12 +41,9 @@ public class LaunchLugExporter extends RocketComponentExporter {
|
||||
}
|
||||
}
|
||||
|
||||
int endIdx2 = Math.max(obj.getNumVertices() - 1, startIdx2); // Clamp in case no vertices were added
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the lug instance
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - lug.getLength() - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx2, endIdx2, x, y, z);
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, lug, startIdx, endIdx, location, -length);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObjFace;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.rocketcomponent.MassObject;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.RocketComponentUtils;
|
||||
|
||||
@ -20,20 +19,18 @@ public class MassObjectExporter extends RocketComponentExporter {
|
||||
obj.setActiveGroupNames(groupName);
|
||||
|
||||
final Coordinate[] locations = massObject.getComponentLocations();
|
||||
final double rocketLength = massObject.getRocket().getLength();
|
||||
final int numSides = LOD.getValue() / 2;
|
||||
final int numStacks = LOD.getValue() / 2;
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(massObject, numSides, numStacks, rocketLength, location);
|
||||
generateMesh(massObject, numSides, numStacks, location);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void generateMesh(MassObject massObject, int numSides, int numStacks, double rocketLength, Coordinate location) {
|
||||
private void generateMesh(MassObject massObject, int numSides, int numStacks, Coordinate location) {
|
||||
// Other meshes may have been added to the obj, so we need to keep track of the starting indices
|
||||
int verticesStartIdx = obj.getNumVertices();
|
||||
int startIdx = obj.getNumVertices();
|
||||
int normalsStartIdx = obj.getNumNormals();
|
||||
double dy = massObject.getLength() / numStacks;
|
||||
double da = 2.0f * Math.PI / numSides;
|
||||
@ -72,7 +69,7 @@ public class MassObjectExporter extends RocketComponentExporter {
|
||||
}
|
||||
}
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, verticesStartIdx); // Clamp in case no vertices were added
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Create bottom tip faces
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
@ -86,7 +83,7 @@ public class MassObjectExporter extends RocketComponentExporter {
|
||||
int[] normalIndices = vertexIndices.clone(); // For a smooth surface, the vertex and normal indices are the same
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, verticesStartIdx); // Only do this after normals are added, since the vertex indices are used for normals
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Only do this after normals are added, since the vertex indices are used for normals
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
obj.addFace(face);
|
||||
@ -106,7 +103,7 @@ public class MassObjectExporter extends RocketComponentExporter {
|
||||
int[] normalIndices = vertexIndices.clone(); // For a smooth surface, the vertex and normal indices are the same
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, verticesStartIdx); // Only do this after normals are added, since the vertex indices are used for normals
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Only do this after normals are added, since the vertex indices are used for normals
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
obj.addFace(face);
|
||||
@ -134,12 +131,18 @@ public class MassObjectExporter extends RocketComponentExporter {
|
||||
obj.addFace(face);
|
||||
}
|
||||
|
||||
// Translate the mesh
|
||||
// Translate the mesh to the position in the rocket
|
||||
// We will create an offset location that has the same effect as the axial rotation of the mass object
|
||||
Coordinate offsetLocation = getOffsetLocation(massObject, location);
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, massObject, startIdx, endIdx, offsetLocation, -massObject.getLength());
|
||||
}
|
||||
|
||||
private static Coordinate getOffsetLocation(MassObject massObject, Coordinate location) {
|
||||
// ! This is all still referenced to the OpenRocket coordinate system, not the OBJ one
|
||||
final double radialPosition = massObject.getRadialPosition();
|
||||
final double radialDirection = massObject.getRadialDirection();
|
||||
final float x = (float) (location.y + radialPosition * Math.cos(radialDirection));
|
||||
final float y = (float) (rocketLength - massObject.getLength() - location.x);
|
||||
final float z = (float) (location.z + radialPosition * Math.sin(radialDirection));
|
||||
ObjUtils.translateVertices(obj, verticesStartIdx, endIdx, x, y, z);
|
||||
final double y = location.y + radialPosition * Math.cos(radialDirection);
|
||||
final double z = location.z + radialPosition * Math.sin(radialDirection);
|
||||
return new Coordinate(location.x, y, z);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.CylinderExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.TubeExporter;
|
||||
import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
public class RadiusRingComponentExporter extends RocketComponentExporter {
|
||||
@ -25,11 +24,11 @@ public class RadiusRingComponentExporter extends RocketComponentExporter {
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(outerRadius, innerRadius, thickness, rocketLength, location);
|
||||
generateMesh(outerRadius, innerRadius, thickness, location);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(float outerRadius, float innerRadius, float thickness, double rocketLength, Coordinate location) {
|
||||
private void generateMesh(float outerRadius, float innerRadius, float thickness, Coordinate location) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
|
||||
if (Float.compare(innerRadius, 0) == 0) {
|
||||
@ -44,10 +43,7 @@ public class RadiusRingComponentExporter extends RocketComponentExporter {
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - thickness - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, component, startIdx, endIdx, location, -thickness);
|
||||
}
|
||||
}
|
||||
|
@ -38,19 +38,18 @@ public class RailButtonExporter extends RocketComponentExporter {
|
||||
final float flangeHeight = (float) railButton.getFlangeHeight();
|
||||
final float screwHeight = (float) railButton.getScrewHeight();
|
||||
final Coordinate[] locations = railButton.getComponentLocations();
|
||||
final double[] angles = railButton.getInstanceAngles();
|
||||
final double rocketLength = railButton.getRocket().getLength();
|
||||
final double[] angles = railButton.getComponentAngles();
|
||||
|
||||
// Generate the mesh
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
generateMesh(outerRadius, innerRadius, baseHeight, innerHeight, flangeHeight, screwHeight,
|
||||
rocketLength, locations[i], angles[i]);
|
||||
locations[i], angles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(float outerRadius, float innerRadius, float baseHeight, float innerHeight, float flangeHeight,
|
||||
float screwHeight, double rocketLength, Coordinate location, double angle) {
|
||||
final int vertexStartIdx = obj.getNumVertices();
|
||||
float screwHeight, Coordinate location, double angle) {
|
||||
final int startIdx = obj.getNumVertices();
|
||||
final int normalStartIdx = obj.getNumNormals();
|
||||
|
||||
// Generate base cylinder
|
||||
@ -94,21 +93,20 @@ public class RailButtonExporter extends RocketComponentExporter {
|
||||
}
|
||||
|
||||
|
||||
final int vertexEndIdx = Math.max(obj.getNumVertices() - 1, vertexStartIdx);
|
||||
final int endIdx = Math.max(obj.getNumVertices() - 1, startIdx);
|
||||
final int normalEndIdx = Math.max(obj.getNumNormals() - 1, normalStartIdx);
|
||||
|
||||
// Rotate the mesh (also PI/2!)
|
||||
final float rX = - (float) Math.PI / 2;
|
||||
final float rX = 0;
|
||||
final float rY = (float) angle;
|
||||
final float rZ = 0;
|
||||
ObjUtils.rotateVertices(obj, vertexStartIdx, vertexEndIdx, normalStartIdx, normalEndIdx,
|
||||
final float rZ = (float) - Math.PI / 2;
|
||||
ObjUtils.rotateVertices(obj, startIdx, endIdx, normalStartIdx, normalEndIdx,
|
||||
rX, rY, rZ, 0, 0, 0);
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) location.x;
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, vertexStartIdx, vertexEndIdx, x, y, z);
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, 1, 0, 0);
|
||||
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, component, startIdx, endIdx, location, 0);
|
||||
}
|
||||
|
||||
private void addScrew(DefaultObj obj, float baseHeight, float innerHeight, float flangeHeight, float outerRadius,
|
||||
@ -170,7 +168,7 @@ public class RailButtonExporter extends RocketComponentExporter {
|
||||
}
|
||||
|
||||
// Generate the quad mesh faces (no tip)
|
||||
for (int i = 0; i <= nrOfStacks-2; i++) {
|
||||
for (int i = 0; i <= nrOfStacks-3; i++) { // We do -3 instead of -2 because we offset the i entirely by starting at 0 instead of 1 (so we don't have to offset the indices)
|
||||
for (int j = 0; j < nrOfSlices; j++) {
|
||||
int nextIdx = (j + 1) % nrOfSlices;
|
||||
int[] vertexIndices = new int[] {
|
||||
|
@ -4,7 +4,6 @@ import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.CylinderExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.TubeExporter;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.ThicknessRingComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
@ -20,16 +19,15 @@ public class ThicknessRingComponentExporter extends RocketComponentExporter {
|
||||
final float outerRadius = (float) thicknessRing.getOuterRadius();
|
||||
final float innerRadius = (float) thicknessRing.getInnerRadius();
|
||||
final float length = (float) thicknessRing.getLength();
|
||||
final double rocketLength = thicknessRing.getRocket().getLength();
|
||||
final Coordinate[] locations = thicknessRing.getComponentLocations();
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(outerRadius, innerRadius, length, rocketLength, location);
|
||||
generateMesh(outerRadius, innerRadius, length, location);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(float outerRadius, float innerRadius, float length, double rocketLength, Coordinate location) {
|
||||
private void generateMesh(float outerRadius, float innerRadius, float length, Coordinate location) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
|
||||
if (Float.compare(innerRadius, 0) == 0) {
|
||||
@ -44,10 +42,7 @@ public class ThicknessRingComponentExporter extends RocketComponentExporter {
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - length - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, component, startIdx, endIdx, location, -length);
|
||||
}
|
||||
}
|
||||
|
@ -35,15 +35,14 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
obj.setActiveGroupNames(groupName);
|
||||
|
||||
final Coordinate[] locations = transition.getComponentLocations();
|
||||
final double rocketLength = transition.getRocket().getLength();
|
||||
|
||||
// Generate the mesh
|
||||
for (Coordinate location : locations) {
|
||||
generateMesh(transition, rocketLength, location);
|
||||
generateMesh(transition, location);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMesh(Transition transition, double rocketLength, Coordinate location) {
|
||||
private void generateMesh(Transition transition, Coordinate location) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
|
||||
final boolean hasForeShoulder = Double.compare(transition.getForeShoulderRadius(), 0) > 0
|
||||
@ -104,11 +103,8 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the mesh
|
||||
final float x = (float) location.y;
|
||||
final float y = (float) (rocketLength - transition.getLength() - location.x);
|
||||
final float z = (float) location.z;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
// Translate the mesh to the position in the rocket
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, transition, startIdx, endIdx, location, -transition.getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,7 +123,7 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
List<Integer> foreRingVertices, List<Integer> aftRingVertices,
|
||||
boolean hasForeShoulder, boolean hasAftShoulder) {
|
||||
// Other meshes may have been added to the obj, so we need to keep track of the starting indices
|
||||
final int verticesStartIdx = obj.getNumVertices();
|
||||
final int startIdx = obj.getNumVertices();
|
||||
final int normalsStartIdx = obj.getNumNormals();
|
||||
|
||||
final double dyBase = transition.getLength() / numStacks; // Base step size in the longitudinal direction
|
||||
@ -255,16 +251,16 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
|
||||
// Create aft/fore tip faces
|
||||
if (isAftTip || isForeTip) {
|
||||
addTipFaces(obj, numSlices, isOutside, isAftTip, normalsStartIdx, verticesStartIdx);
|
||||
addTipFaces(obj, numSlices, isOutside, isAftTip, startIdx, normalsStartIdx);
|
||||
}
|
||||
|
||||
// Create regular faces
|
||||
int corrVStartIdx = isAftTip ? verticesStartIdx + 1 : verticesStartIdx;
|
||||
int corrVStartIdx = isAftTip ? startIdx + 1 : startIdx;
|
||||
int corrNStartIdx = isAftTip ? normalsStartIdx + 1 : normalsStartIdx;
|
||||
addQuadFaces(obj, numSlices, actualNumStacks, corrVStartIdx, corrNStartIdx, isOutside);
|
||||
}
|
||||
|
||||
private static void addTipFaces(DefaultObj obj, int numSlices, boolean isOutside, boolean isAftTip, int normalsStartIdx, int verticesStartIdx) {
|
||||
private static void addTipFaces(DefaultObj obj, int numSlices, boolean isOutside, boolean isAftTip, int startIdx, int normalsStartIdx) {
|
||||
final int lastIdx = obj.getNumVertices() - 1;
|
||||
for (int i = 0; i < numSlices; i++) {
|
||||
int nextIdx = (i + 1) % numSlices;
|
||||
@ -282,7 +278,7 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
normalIndices = vertexIndices.clone(); // No need to reverse, already done by vertices
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, verticesStartIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
}
|
||||
// Fore tip
|
||||
else {
|
||||
@ -334,7 +330,7 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addQuadFaces(DefaultObj obj, int numSlices, int numStacks, int verticesStartIdx, int normalsStartIdx, boolean isOutside) {
|
||||
private static void addQuadFaces(DefaultObj obj, int numSlices, int numStacks, int startIdx, int normalsStartIdx, boolean isOutside) {
|
||||
for (int i = 0; i < numStacks - 1; i++) {
|
||||
for (int j = 0; j < numSlices; j++) {
|
||||
final int nextIdx = (j + 1) % numSlices;
|
||||
@ -349,7 +345,7 @@ public class TransitionExporter extends RocketComponentExporter {
|
||||
int[] normalIndices = vertexIndices.clone(); // No reversing needed, already done by vertices
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, verticesStartIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
obj.addFace(face);
|
||||
|
@ -2,9 +2,7 @@ package net.sf.openrocket.file.wavefrontobj.export.components;
|
||||
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.PolygonExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.shapes.TubeExporter;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.TubeFinSet;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
@ -45,12 +43,18 @@ public class TubeFinSetExporter extends RocketComponentExporter {
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Translate the mesh
|
||||
final float dx = outerRadius * (float) Math.cos(angle);
|
||||
// Translate the mesh to the position in the rocket
|
||||
// We will create an offset location that has the same effect as the axial rotation of the launch lug
|
||||
Coordinate offsetLocation = getOffsetLocation(outerRadius, location, angle);
|
||||
ObjUtils.translateVerticesFromComponentLocation(obj, component, startIdx, endIdx, offsetLocation, -length);
|
||||
}
|
||||
|
||||
private static Coordinate getOffsetLocation(float outerRadius, Coordinate location, double angle) {
|
||||
// ! This is all still referenced to the OpenRocket coordinate system, not the OBJ one
|
||||
final float dy = outerRadius * (float) Math.cos(angle);
|
||||
final float dz = outerRadius * (float) Math.sin(angle);
|
||||
final float x = (float) location.y + dx;
|
||||
final float y = (float) (rocketLength - length - location.x);
|
||||
final float z = (float) location.z + dz;
|
||||
ObjUtils.translateVertices(obj, startIdx, endIdx, x, y, z);
|
||||
final double y = location.y + dy;
|
||||
final double z = location.z + dz;
|
||||
return new Coordinate(location.x, y, z);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package net.sf.openrocket.file.wavefrontobj.export.shapes;
|
||||
|
||||
import de.javagl.obj.ObjWriter;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObjFace;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class CylinderExporter {
|
||||
@ -171,12 +168,4 @@ public class CylinderExporter {
|
||||
public static void addCylinderMesh(DefaultObj obj, String groupName, float radius, float height, boolean solid) {
|
||||
addCylinderMesh(obj, groupName, radius, height, solid, ObjUtils.LevelOfDetail.NORMAL);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DefaultObj obj = new DefaultObj();
|
||||
addCylinderMesh(obj, "cylinder", 1, 2, 15, true, null, null);
|
||||
try (OutputStream objOutputStream = new FileOutputStream("/Users/SiboVanGool/Downloads/cylinder.obj")) {
|
||||
ObjWriter.write(obj, objOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,13 @@
|
||||
package net.sf.openrocket.file.wavefrontobj.export.shapes;
|
||||
|
||||
import de.javagl.obj.ObjWriter;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObjFace;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObjGroup;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class PolygonExporter {
|
||||
|
||||
/**
|
||||
* Add a polygon mesh to the obj. It is drawn in the XY plane with the bottom left corner at the origin.
|
||||
* Add a polygon mesh to the obj. It is drawn in the X-Y plane (negative Y) with the bottom left corner at the origin.
|
||||
* @param obj The obj to add the mesh to
|
||||
* @param groupName The name of the group to add the mesh to, or null if no group should be added (use the active group)
|
||||
* @param pointLocationsX The x locations of the points --> NOTE: points should follow a clockwise direction
|
||||
@ -37,12 +32,12 @@ public class PolygonExporter {
|
||||
|
||||
// Generate front face vertices
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
obj.addVertex(pointLocationsX[i], pointLocationsY[i], thickness/2);
|
||||
obj.addVertex(pointLocationsY[i], -pointLocationsX[i], thickness/2);
|
||||
}
|
||||
|
||||
// Generate back face vertices
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
obj.addVertex(pointLocationsX[i], pointLocationsY[i], -thickness/2);
|
||||
obj.addVertex(pointLocationsY[i], -pointLocationsX[i], -thickness/2);
|
||||
}
|
||||
|
||||
// Create front face
|
||||
@ -79,8 +74,8 @@ public class PolygonExporter {
|
||||
ObjUtils.offsetIndex(vertexIndices, verticesStartIdx);
|
||||
|
||||
// Calculate normals for side faces
|
||||
final float dx = pointLocationsX[nextIdx] - pointLocationsX[i];
|
||||
final float dy = pointLocationsY[nextIdx] - pointLocationsY[i];
|
||||
final float dx = pointLocationsY[nextIdx] - pointLocationsY[i];
|
||||
final float dy = pointLocationsX[nextIdx] - pointLocationsX[i];
|
||||
|
||||
// Perpendicular vector in 2D (for clockwise vertices)
|
||||
final float nx = -dy;
|
||||
@ -111,14 +106,4 @@ public class PolygonExporter {
|
||||
throw new IllegalArgumentException("The first and last points must be different");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DefaultObj obj = new DefaultObj();
|
||||
float[] x = new float[]{0, 0.3f, 1, 0.7f};
|
||||
float[] y = new float[]{0, 0.5f, 0.5f, 0};
|
||||
addPolygonMesh(obj, "polygon", x, y, 0.025f);
|
||||
try (OutputStream objOutputStream = new FileOutputStream("/Users/SiboVanGool/Downloads/poly.obj")) {
|
||||
ObjWriter.write(obj, objOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package net.sf.openrocket.file.wavefrontobj.export.shapes;
|
||||
|
||||
import de.javagl.obj.ObjWriter;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObjFace;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class TubeExporter {
|
||||
@ -237,13 +234,4 @@ public class TubeExporter {
|
||||
public static void addTubeMesh(DefaultObj obj, String groupName, float outerRadius, float innerRadius, float height) {
|
||||
addTubeMesh(obj, groupName, outerRadius, outerRadius, innerRadius, innerRadius, height);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DefaultObj obj = new DefaultObj();
|
||||
//addTubeMesh(obj, "tube", 0.1f, 0.085f, 0.3f);
|
||||
addTubeMesh(obj, "tube", 0.14f, 0.06f, 0.13f, 0.05f, 0.3f);
|
||||
try (OutputStream objOutputStream = new FileOutputStream("/Users/SiboVanGool/Downloads/tube.obj")) {
|
||||
ObjWriter.write(obj, objOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.sf.openrocket.file.wavefrontobj.export;
|
||||
import net.sf.openrocket.document.OpenRocketDocumentFactory;
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
import net.sf.openrocket.rocketcomponent.NoseCone;
|
||||
import net.sf.openrocket.rocketcomponent.Parachute;
|
||||
@ -17,11 +16,14 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class OBJExporterFactoryTest extends BaseTestCase {
|
||||
@Test
|
||||
public void testExport() {
|
||||
public void testExport() throws IOException {
|
||||
Rocket rocket = OpenRocketDocumentFactory.createNewRocket().getRocket();
|
||||
AxialStage sustainer = rocket.getStage(0);
|
||||
|
||||
@ -52,7 +54,8 @@ public class OBJExporterFactoryTest extends BaseTestCase {
|
||||
finSet.setRootChord(0.05);
|
||||
finSet.setTabLength(0.03);
|
||||
finSet.setTabHeight(0.01);
|
||||
finSet.setTabOffset(0);
|
||||
finSet.setTabOffset(-0.0075);
|
||||
finSet.setCantAngle(Math.toRadians(10));
|
||||
bodyTube.addChild(finSet);
|
||||
|
||||
TubeFinSet tubeFinSet = new TubeFinSet();
|
||||
@ -82,12 +85,20 @@ public class OBJExporterFactoryTest extends BaseTestCase {
|
||||
|
||||
RailButton railButton = new RailButton();
|
||||
railButton.setScrewHeight(0.0025);
|
||||
railButton.setAngleOffset(Math.toRadians(67));
|
||||
bodyTube.addChild(railButton);
|
||||
List<RocketComponent> components = List.of(rocket);
|
||||
|
||||
List<RocketComponent> components = List.of(noseCone);
|
||||
Path tempFile = Files.createTempFile("testExport", ".obj");
|
||||
|
||||
OBJExporterFactory exporterFactory = new OBJExporterFactory(components, false, false, true,
|
||||
finSet.setFinCount(1);
|
||||
finSet.setAngleOffset(Math.toRadians(45));
|
||||
|
||||
TestRockets.dumpRocket(rocket, "/Users/SiboVanGool/Downloads/test.ork");
|
||||
OBJExporterFactory exporterFactory = new OBJExporterFactory(components, true, false, true,
|
||||
"/Users/SiboVanGool/Downloads/testExport.obj");
|
||||
exporterFactory.doExport();
|
||||
|
||||
Files.delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user