Add textures (WIP)
This commit is contained in:
parent
765c6cde63
commit
4fc3901f00
@ -1498,6 +1498,8 @@ OBJOptionChooser.checkbox.removeOffset.ttip = <html>If true, remove the offset o
|
||||
OBJOptionChooser.btn.showAdvanced = Show Advanced options
|
||||
OBJOptionChooser.checkbox.triangulate = Triangulate mesh
|
||||
OBJOptionChooser.checkbox.triangulate.ttip = If true, triangulate the mesh before exporting (convert all quads or high-order polygons to a triangle).
|
||||
OBJOptionChooser.checkbox.sRGB = Export colors in sRGB
|
||||
OBJOptionChooser.checkbox.sRGB.ttip = <html>If true, export colors in sRGB instead of a linear color scheme.<br>Is useful for instance when exporting for use in Blender.</html>
|
||||
OBJOptionChooser.lbl.LevelOfDetail = Level of detail:
|
||||
OBJOptionChooser.lbl.LevelOfDetail.ttip = Select the desired level of detail of the geometry export.
|
||||
|
||||
|
@ -1,11 +1,15 @@
|
||||
package net.sf.openrocket.appearance.defaults;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.sf.openrocket.appearance.DecalImage;
|
||||
import net.sf.openrocket.util.FileUtils;
|
||||
import net.sf.openrocket.util.StateChangeListener;
|
||||
|
||||
/**
|
||||
@ -46,6 +50,14 @@ public class ResourceDecalImage implements DecalImage {
|
||||
|
||||
@Override
|
||||
public void exportImage(File file) throws IOException {
|
||||
InputStream is;
|
||||
is = getBytes();
|
||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
|
||||
|
||||
FileUtils.copy(is, os);
|
||||
|
||||
is.close();
|
||||
os.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,17 +1,22 @@
|
||||
package net.sf.openrocket.file.wavefrontobj.export;
|
||||
|
||||
import de.javagl.obj.FloatTuple;
|
||||
import net.sf.openrocket.appearance.Appearance;
|
||||
import net.sf.openrocket.appearance.Decal;
|
||||
import net.sf.openrocket.appearance.DecalImage;
|
||||
import net.sf.openrocket.appearance.defaults.DefaultAppearance;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultFloatTuple;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultMtl;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultObj;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultTextureOptions;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Color;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -21,21 +26,30 @@ import java.util.List;
|
||||
*/
|
||||
public class AppearanceExporter {
|
||||
private final DefaultObj obj;
|
||||
private final RocketComponent component;
|
||||
private final Appearance appearance;
|
||||
private final File file;
|
||||
private final OBJExportOptions options;
|
||||
private final String materialName;
|
||||
private final List<DefaultMtl> materials;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AppearanceExporter.class);
|
||||
|
||||
/**
|
||||
* Export the appearance of a rocket component
|
||||
* <b>NOTE: </b> you still have to call {@link #doExport()} to actually export the appearance.
|
||||
* @param obj The obj file that will use the material
|
||||
* @param component The component to export the appearance of
|
||||
* @param appearance The appearance to export
|
||||
* @param file The file that the OBJ is exported to
|
||||
* @param options The options to use for exporting the OBJ
|
||||
* @param materialName The name of the material to generate
|
||||
* @param materials The list of materials to add the new material(s) to
|
||||
*/
|
||||
public AppearanceExporter(DefaultObj obj, RocketComponent component, String materialName, List<DefaultMtl> materials) {
|
||||
public AppearanceExporter(DefaultObj obj, Appearance appearance, File file, OBJExportOptions options,
|
||||
String materialName, List<DefaultMtl> materials) {
|
||||
this.obj = obj;
|
||||
this.component = component;
|
||||
this.appearance = appearance;
|
||||
this.file = file;
|
||||
this.options = options;
|
||||
this.materialName = materialName;
|
||||
this.materials = materials;
|
||||
}
|
||||
@ -48,22 +62,18 @@ public class AppearanceExporter {
|
||||
obj.setActiveMaterialGroupName(materialName);
|
||||
DefaultMtl material = new DefaultMtl(materialName);
|
||||
|
||||
// Get the component appearance
|
||||
Appearance appearance = component.getAppearance();
|
||||
if (appearance == null) {
|
||||
appearance = DefaultAppearance.getDefaultAppearance(component);
|
||||
}
|
||||
|
||||
// Apply coloring
|
||||
applyColoring(appearance, material);
|
||||
applyColoring(appearance, material, options);
|
||||
|
||||
// Apply texture
|
||||
applyTexture(appearance, material);
|
||||
|
||||
materials.add(material);
|
||||
|
||||
// TODO: default back to default material?
|
||||
}
|
||||
|
||||
private static void applyTexture(Appearance appearance, DefaultMtl material) {
|
||||
private void applyTexture(Appearance appearance, DefaultMtl material) {
|
||||
Decal texture = appearance.getTexture();
|
||||
if (texture == null) {
|
||||
return;
|
||||
@ -71,39 +81,74 @@ public class AppearanceExporter {
|
||||
|
||||
final DefaultTextureOptions textureOptions = new DefaultTextureOptions();
|
||||
|
||||
// TODO: file name (save externally if saved inside .ork)
|
||||
//String filePath = texture.getImage().getDecalFile().getAbsolutePath();
|
||||
String filePath = "/Users/SiboVanGool/Downloads/hello.jpeg";
|
||||
textureOptions.setFileName(filePath);
|
||||
// The decal file is stored inside the .ork, so first export it to the export directory
|
||||
final File decalFile;
|
||||
try {
|
||||
String exportDir = file.getParent();
|
||||
String fileName = FileUtils.removeExtension(file.getName());
|
||||
Path decalDir = Path.of(exportDir, fileName + "_img");
|
||||
Files.createDirectories(decalDir);
|
||||
|
||||
// Texture offset
|
||||
final Coordinate origin = texture.getOffset();
|
||||
Float origX = (float) origin.x;
|
||||
Float origY = (float) origin.y;
|
||||
textureOptions.setO(origX, origY, 0f);
|
||||
DecalImage decal = texture.getImage();
|
||||
String decalName = FileUtils.getFileNameFromPath(decal.getName());
|
||||
decalFile = new File(decalDir.toString(), decalName); // TODO: should name be unique?
|
||||
decalFile.createNewFile(); // TODO: check if you want to overwrite?
|
||||
decal.exportImage(decalFile);
|
||||
log.info("Exported decal image to {}", decalFile.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to export decal image", e);
|
||||
return;
|
||||
}
|
||||
|
||||
textureOptions.setFileName(decalFile.getAbsolutePath());
|
||||
|
||||
// Texture scale
|
||||
final Coordinate scale = texture.getScale();
|
||||
Float scaleX = (float) scale.x;
|
||||
Float scaleY = (float) scale.y;
|
||||
float scaleX = (float) scale.x;
|
||||
float scaleY = (float) scale.y;
|
||||
textureOptions.setS(scaleX, scaleY, 1f);
|
||||
|
||||
// TODO: rotation
|
||||
// Texture offset
|
||||
final Coordinate origin = texture.getOffset();
|
||||
float origX = (float) origin.x;
|
||||
float origY = (float) (origin.y - 1 - 1/scaleY); // Need an extra offset because the texture scale origin is different in OR
|
||||
textureOptions.setO(origX, origY, 0f);
|
||||
|
||||
// Texture rotation is not possible in MTL...
|
||||
|
||||
// Texture repeat (not very extensive in MTL...)
|
||||
Decal.EdgeMode edgeMode = texture.getEdgeMode();
|
||||
switch (edgeMode) {
|
||||
case REPEAT, MIRROR -> textureOptions.setClamp(false);
|
||||
default -> textureOptions.setClamp(true);
|
||||
}
|
||||
|
||||
// Apply the texture
|
||||
material.setMapKdOptions(textureOptions);
|
||||
}
|
||||
|
||||
private static void applyColoring(Appearance appearance, DefaultMtl material) {
|
||||
private static void applyColoring(Appearance appearance, DefaultMtl material, OBJExportOptions options) {
|
||||
Color color = appearance.getPaint();
|
||||
final float r = color.getRed()/255f;
|
||||
final float g = color.getGreen()/255f;
|
||||
final float b = color.getBlue()/255f;
|
||||
final float r = convertColorToFloat(color.getRed(), options.isUseSRGB());
|
||||
final float g = convertColorToFloat(color.getGreen(), options.isUseSRGB());
|
||||
final float b = convertColorToFloat(color.getBlue(), options.isUseSRGB());
|
||||
material.setKd(r, g, b); // Diffuse color
|
||||
material.setKa(0f, 0f, 0f); // No emission
|
||||
material.setKs(1f, 1f, 1f); // Use white specular highlights
|
||||
material.setKs(.25f, .25f, .25f); // Not too strong specular highlights
|
||||
material.setD(color.getAlpha()/255f); // Opacity
|
||||
material.setNs((float) appearance.getShine() * 750); // Shine (max is 1000, but this too strong compared to OpenRocket's max)
|
||||
material.setIllum(2); // Use Phong reflection (specular highlights etc.)
|
||||
}
|
||||
|
||||
private static float convertColorToFloat(int color, boolean sRGB) {
|
||||
float convertedColor = color / 255f;
|
||||
if (sRGB) {
|
||||
convertedColor = linearTosRGB(convertedColor);
|
||||
}
|
||||
return convertedColor;
|
||||
}
|
||||
|
||||
private static float linearTosRGB(float linear) {
|
||||
return (float) Math.pow(linear, 2.2);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,10 @@ public class OBJExportOptions {
|
||||
* If true, triangulate all faces (convert quads and higher-order polygons to triangles)
|
||||
*/
|
||||
private boolean triangulate;
|
||||
/**
|
||||
* If true, use sRGB colors instead of linear color space.
|
||||
*/
|
||||
private boolean useSRGB;
|
||||
/**
|
||||
* The level of detail to use for the export (e.g. low-quality, normal quality...).
|
||||
*/
|
||||
@ -41,8 +45,6 @@ public class OBJExportOptions {
|
||||
*/
|
||||
private float scaling;
|
||||
|
||||
// TODO: scaling (to mm = x1000, or SI units)
|
||||
|
||||
public OBJExportOptions(Rocket rocket) {
|
||||
this.exportChildren = false;
|
||||
this.exportAppearance = false;
|
||||
@ -117,4 +119,12 @@ public class OBJExportOptions {
|
||||
public void setScaling(float scaling) {
|
||||
this.scaling = scaling;
|
||||
}
|
||||
|
||||
public boolean isUseSRGB() {
|
||||
return useSRGB;
|
||||
}
|
||||
|
||||
public void setUseSRGB(boolean useSRGB) {
|
||||
this.useSRGB = useSRGB;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.sf.openrocket.file.wavefrontobj.export;
|
||||
|
||||
import de.javagl.obj.ObjWriter;
|
||||
import net.sf.openrocket.appearance.Appearance;
|
||||
import net.sf.openrocket.appearance.defaults.DefaultAppearance;
|
||||
import net.sf.openrocket.file.wavefrontobj.CoordTransform;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultMtl;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultMtlWriter;
|
||||
@ -16,6 +18,8 @@ import net.sf.openrocket.file.wavefrontobj.export.components.RocketComponentExpo
|
||||
import net.sf.openrocket.file.wavefrontobj.export.components.RingComponentExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.components.TransitionExporter;
|
||||
import net.sf.openrocket.file.wavefrontobj.export.components.TubeFinSetExporter;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
@ -32,6 +36,7 @@ import net.sf.openrocket.rocketcomponent.Transition;
|
||||
import net.sf.openrocket.rocketcomponent.TubeFinSet;
|
||||
import net.sf.openrocket.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@ -61,7 +66,7 @@ public class OBJExporterFactory {
|
||||
private final List<RocketComponent> components;
|
||||
private final FlightConfiguration configuration;
|
||||
private final OBJExportOptions options;
|
||||
private final String filePath;
|
||||
private final File file;
|
||||
|
||||
// The different exporters for each component
|
||||
private static final Map<Class<? extends RocketComponent>, ExporterFactory<?>> EXPORTER_MAP = Map.of(
|
||||
@ -81,13 +86,13 @@ public class OBJExporterFactory {
|
||||
* @param components List of components to export
|
||||
* @param configuration Flight configuration to use for the export
|
||||
* @param options Options to use for the export
|
||||
* @param filePath Path to the file to export to
|
||||
* @param file The file to export the OBJ to
|
||||
*/
|
||||
public OBJExporterFactory(List<RocketComponent> components, FlightConfiguration configuration, String filePath,
|
||||
public OBJExporterFactory(List<RocketComponent> components, FlightConfiguration configuration, File file,
|
||||
OBJExportOptions options) {
|
||||
this.components = components;
|
||||
this.configuration = configuration;
|
||||
this.filePath = filePath;
|
||||
this.file = file;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@ -104,7 +109,7 @@ public class OBJExporterFactory {
|
||||
if (exportAsSeparateFiles) {
|
||||
objFileMap = new HashMap<>();
|
||||
} else {
|
||||
objFileMap = Map.of(this.filePath, obj);
|
||||
objFileMap = Map.of(this.file.getAbsolutePath(), obj);
|
||||
}
|
||||
|
||||
// Get all the components to export
|
||||
@ -147,7 +152,7 @@ public class OBJExporterFactory {
|
||||
|
||||
// If separate export, add this object to the map of objects to export
|
||||
if (exportAsSeparateFiles) {
|
||||
String path = FileUtils.removeExtension(this.filePath) + "_" + groupName + ".obj";
|
||||
String path = FileUtils.removeExtension(this.file.getAbsolutePath()) + "_" + groupName + ".obj";
|
||||
objFileMap.put(path, obj);
|
||||
}
|
||||
|
||||
@ -223,7 +228,15 @@ public class OBJExporterFactory {
|
||||
|
||||
// Export material
|
||||
if (options.isExportAppearance()) {
|
||||
AppearanceExporter appearanceExporter = new AppearanceExporter(obj, component, "mat_" + groupName, materials);
|
||||
String materialName = "mat_" + groupName;
|
||||
|
||||
// Get the component appearance
|
||||
Appearance appearance = component.getAppearance();
|
||||
if (appearance == null) {
|
||||
appearance = DefaultAppearance.getDefaultAppearance(component);
|
||||
}
|
||||
|
||||
AppearanceExporter appearanceExporter = new AppearanceExporter(obj, appearance, file, options, materialName, materials);
|
||||
appearanceExporter.doExport();
|
||||
}
|
||||
|
||||
@ -233,6 +246,19 @@ public class OBJExporterFactory {
|
||||
|
||||
// Export motor
|
||||
if (component instanceof MotorMount) {
|
||||
// Get the motor
|
||||
MotorConfiguration motoConfig = ((MotorMount) component).getMotorConfig(config.getId());
|
||||
Motor motor = motoConfig.getMotor();
|
||||
|
||||
// Export the motor appearance
|
||||
if (options.isExportAppearance() && motor != null) {
|
||||
String materialName = "mat_" + groupName + "_" + motor.getMotorName();
|
||||
Appearance appearance = DefaultAppearance.getDefaultAppearance(motor);
|
||||
AppearanceExporter appearanceExporter = new AppearanceExporter(obj, appearance, file, options, materialName, materials);
|
||||
appearanceExporter.doExport();
|
||||
}
|
||||
|
||||
// Export the motor geometry
|
||||
MotorExporter motorExporter = new MotorExporter(obj, config, transformer, component, groupName, LOD);
|
||||
motorExporter.addToObj();
|
||||
}
|
||||
|
@ -33,11 +33,12 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
private void generateMesh(int numSides, int numStacks, InstanceContext context) {
|
||||
// Other meshes may have been added to the obj, so we need to keep track of the starting indices
|
||||
int startIdx = obj.getNumVertices();
|
||||
int texCoordsStartIdx = obj.getNumTexCoords();
|
||||
int normalsStartIdx = obj.getNumNormals();
|
||||
double dx = component.getLength() / numStacks;
|
||||
double da = 2.0f * Math.PI / numSides;
|
||||
|
||||
// Generate vertices and normals
|
||||
// Generate vertices, normals and UVs
|
||||
for (int j = 0; j <= numStacks; j++) {
|
||||
double x = j * dx;
|
||||
|
||||
@ -45,6 +46,11 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
// Add a center vertex
|
||||
obj.addVertex(transformer.convertLoc(x, 0, 0));
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(j == 0 ? -1 : 1, 0, 0));
|
||||
for (int i = 0; i <= numSides; i++) {
|
||||
final float u = (float) i / numSides;
|
||||
final float v = j == 0 ? 1 : 0;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
} else {
|
||||
// Add a vertex for each side
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
@ -53,9 +59,10 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
double y = r * Math.cos(angle);
|
||||
double z = r * Math.sin(angle);
|
||||
|
||||
// Vertex
|
||||
obj.addVertex(transformer.convertLoc(x, y, z));
|
||||
|
||||
// Add normals
|
||||
// Normal
|
||||
if (Double.compare(r, component.getRadius()) == 0) { // If in cylindrical section, use cylinder normals
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(0, y, z));
|
||||
} else {
|
||||
@ -67,13 +74,23 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
}
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(x - xCenter, y, z)); // For smooth shading
|
||||
}
|
||||
|
||||
// Texture coordinate
|
||||
final float u = (float) i / numSides;
|
||||
final float v = (float) (numStacks-j) / numStacks;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
// Add final UV coordinate to close the texture
|
||||
final float u = 1f;
|
||||
final float v = (float) (numStacks-j) / numStacks;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
}
|
||||
|
||||
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
|
||||
|
||||
// Create bottom tip faces
|
||||
// Create top tip faces
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
int nextIdx = (i + 1) % numSides;
|
||||
|
||||
@ -83,11 +100,17 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
1 + nextIdx,
|
||||
};
|
||||
int[] normalIndices = vertexIndices.clone(); // For a smooth surface, the vertex and normal indices are the same
|
||||
int[] texCoordIndices = new int[] {
|
||||
i,
|
||||
numSides+1 + i,
|
||||
numSides+1 + i+1,
|
||||
};
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(texCoordIndices, texCoordsStartIdx);
|
||||
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);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
|
||||
@ -103,16 +126,23 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
1 + j * numSides + nextIdx
|
||||
};
|
||||
int[] normalIndices = vertexIndices.clone(); // For a smooth surface, the vertex and normal indices are the same
|
||||
int[] texCoordIndices = new int[] {
|
||||
j + j * numSides + i,
|
||||
j+1 + (j + 1) * numSides + i,
|
||||
j+1 + (j + 1) * numSides + i+1,
|
||||
j + j * numSides + i+1
|
||||
};
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(texCoordIndices, texCoordsStartIdx + numSides+1);
|
||||
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);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
}
|
||||
|
||||
// Create top tip faces
|
||||
// Create bottom tip faces
|
||||
final int normalEndIdx = obj.getNumNormals() - 1;
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
int nextIdx = (i + 1) % numSides;
|
||||
@ -126,10 +156,16 @@ public class MassObjectExporter extends RocketComponentExporter<MassObject> {
|
||||
normalEndIdx - numSides + nextIdx,
|
||||
normalEndIdx - numSides + i,
|
||||
};
|
||||
int[] texCoordIndices = new int[] {
|
||||
numSides+1 + i,
|
||||
i+1,
|
||||
i,
|
||||
};
|
||||
|
||||
// Don't offset! We reference from the last index
|
||||
ObjUtils.offsetIndex(texCoordIndices, texCoordsStartIdx + ((numStacks-1) * (numSides + 1)) );
|
||||
// Don't offset vertices or normals! We reference from the last index
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class MotorExporter {
|
||||
return;
|
||||
}
|
||||
|
||||
obj.setActiveGroupNames(motor.getMotorName());
|
||||
obj.setActiveGroupNames(groupName + "_" + motor.getMotorName());
|
||||
|
||||
for (InstanceContext context : config.getActiveInstances().getInstanceContexts(mount)) {
|
||||
generateMesh(motor, context);
|
||||
|
@ -119,6 +119,7 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
// Other meshes may have been added to the obj, so we need to keep track of the starting indices
|
||||
final int startIdx = obj.getNumVertices();
|
||||
final int normalsStartIdx = obj.getNumNormals();
|
||||
final int texCoordsStartIdx = obj.getNumTexCoords();
|
||||
|
||||
final double dxBase = component.getLength() / numStacks; // Base step size in the longitudinal direction
|
||||
final double actualLength = estimateActualLength(offsetRadius, dxBase); // Actual length of the transition (due to reduced step size near the fore/aft end)
|
||||
@ -176,6 +177,12 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
float xTip = getTipLocation(x, xNext, offsetRadius, epsilon);
|
||||
obj.addVertex(transformer.convertLoc(xTip, 0, 0));
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(isOutside ? -1 : 1, 0, 0));
|
||||
for (int i = 0; i <= numSides; i++) {
|
||||
float u = (float) i / numSides;
|
||||
float v = 1f;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
isForeTip = true;
|
||||
|
||||
// The fore tip is the first fore "ring"
|
||||
@ -218,6 +225,11 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
float xTip = getTipLocation(x, xNext, offsetRadius, epsilon);
|
||||
obj.addVertex(transformer.convertLoc(xTip, 0, 0));
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(isOutside ? 1 : -1, 0, 0));
|
||||
for (int i = 0; i <= numSides; i++) {
|
||||
float u = (float) i / numSides;
|
||||
float v = 0f;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
isAftTip = true;
|
||||
|
||||
// The aft tip is the aft "ring"
|
||||
@ -251,17 +263,20 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
// Create regular faces
|
||||
int corrVStartIdx = isForeTip ? startIdx + 1 : startIdx;
|
||||
int corrNStartIdx = isForeTip ? normalsStartIdx + 1 : normalsStartIdx;
|
||||
addQuadFaces(numSlices, actualNumStacks, corrVStartIdx, corrNStartIdx, isOutside);
|
||||
int corrTStartIdx = isForeTip ? texCoordsStartIdx + numSides+1 : texCoordsStartIdx;
|
||||
addQuadFaces(numSides, actualNumStacks, corrVStartIdx, corrNStartIdx, corrTStartIdx, isOutside);
|
||||
}
|
||||
|
||||
private void addTipFaces(int numSlices, boolean isOutside, boolean isForeTip, int startIdx, int normalsStartIdx) {
|
||||
private void addTipFaces(int numSides, boolean isOutside, boolean isForeTip, int startIdx, int normalsStartIdx, int texCoordsStartIdx) {
|
||||
final int lastIdx = obj.getNumVertices() - 1;
|
||||
for (int i = 0; i < numSlices; i++) {
|
||||
int nextIdx = (i + 1) % numSlices;
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
int nextIdx = (i + 1) % numSides;
|
||||
int[] vertexIndices;
|
||||
int[] normalIndices;
|
||||
int[] texCoordsIndices;
|
||||
// Fore tip
|
||||
if (isForeTip) {
|
||||
// Vertices
|
||||
vertexIndices = new int[] {
|
||||
0, // Fore tip vertex
|
||||
1 + i,
|
||||
@ -269,46 +284,69 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
};
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
|
||||
// Normals
|
||||
normalIndices = vertexIndices.clone(); // No need to reverse, already done by vertices
|
||||
|
||||
// Texture coordinates
|
||||
texCoordsIndices = new int[] {
|
||||
i,
|
||||
numSides+1 + i,
|
||||
numSides+1 + i+1,
|
||||
};
|
||||
texCoordsIndices = ObjUtils.reverseIndexWinding(texCoordsIndices, !isOutside);
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(texCoordsIndices, texCoordsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
}
|
||||
// Aft tip
|
||||
else {
|
||||
// Vertices
|
||||
vertexIndices = new int[] {
|
||||
lastIdx, // Aft tip vertex
|
||||
lastIdx - numSlices + nextIdx,
|
||||
lastIdx - numSlices + i,
|
||||
lastIdx - numSides + nextIdx,
|
||||
lastIdx - numSides + i,
|
||||
};
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
|
||||
// Normals
|
||||
int lastNormalIdx = obj.getNumNormals() - 1;
|
||||
normalIndices = new int[] {
|
||||
lastNormalIdx, // Aft tip vertex
|
||||
lastNormalIdx - numSlices + nextIdx,
|
||||
lastNormalIdx - numSlices + i,
|
||||
lastNormalIdx - numSides + nextIdx,
|
||||
lastNormalIdx - numSides + i,
|
||||
};
|
||||
normalIndices = ObjUtils.reverseIndexWinding(normalIndices, !isOutside);
|
||||
|
||||
// Texture coordinates
|
||||
int lastTexCoordsIdx = obj.getNumTexCoords() - 1;
|
||||
texCoordsIndices = new int[] {
|
||||
lastTexCoordsIdx - (numSides+1) + i, // Aft tip vertex
|
||||
lastTexCoordsIdx - 2*(numSides+1) + i+1,
|
||||
lastTexCoordsIdx - 2*(numSides+1) + i,
|
||||
};
|
||||
|
||||
// No need to offset the indices, because we reference the last vertex (this caused a lot of debugging frustration hmfmwl)
|
||||
}
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
}
|
||||
|
||||
private void addQuadVertices(int numSlices, List<Integer> foreRingVertices, List<Integer> aftRingVertices,
|
||||
private void addQuadVertices(int numSides, List<Integer> foreRingVertices, List<Integer> aftRingVertices,
|
||||
double r, double rNext, float x, float xNext, boolean isForeRing, boolean isAftRing, boolean isOutside) {
|
||||
for (int i = 0; i < numSlices; i++) {
|
||||
double angle = 2 * Math.PI * i / numSlices;
|
||||
final double length = component.getLength();
|
||||
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
double angle = 2 * Math.PI * i / numSides;
|
||||
float y = (float) (r * Math.cos(angle));
|
||||
float z = (float) (r * Math.sin(angle));
|
||||
|
||||
// Vertex
|
||||
obj.addVertex(transformer.convertLoc(x, y, z));
|
||||
|
||||
// Add the ring vertices to the lists
|
||||
//// Add the ring vertices to the lists
|
||||
if (isForeRing) {
|
||||
foreRingVertices.add(obj.getNumVertices()-1);
|
||||
}
|
||||
@ -316,8 +354,8 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
aftRingVertices.add(obj.getNumVertices()-1);
|
||||
}
|
||||
|
||||
// Calculate the normal
|
||||
// We need special nx normal when the radius changes
|
||||
// Normal
|
||||
//// We need special nx normal when the radius changes
|
||||
float nx;
|
||||
if (Double.compare(r, rNext) != 0) {
|
||||
final double slopeAngle = Math.atan(Math.abs(xNext - x) / (rNext - r));
|
||||
@ -332,27 +370,49 @@ public class TransitionExporter extends RocketComponentExporter<Transition> {
|
||||
ny = isOutside ? ny : -ny;
|
||||
nz = isOutside ? nz : -nz;
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(nx, ny, nz));
|
||||
|
||||
// Texture coordinates
|
||||
float u = (float) i / numSides;
|
||||
float v = (float) ((length-x) / length);
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
// Need to add a last texture coordinate to close the texture
|
||||
final float v = (float) ((length-x) / length);
|
||||
obj.addTexCoord(1f, v);
|
||||
}
|
||||
|
||||
private void addQuadFaces(int numSlices, int numStacks, int startIdx, int normalsStartIdx, boolean isOutside) {
|
||||
private void addQuadFaces(int numSides, int numStacks, int startIdx, int normalsStartIdx, int texCoordsStartIdx, boolean isOutside) {
|
||||
for (int i = 0; i < numStacks - 1; i++) {
|
||||
for (int j = 0; j < numSlices; j++) {
|
||||
final int nextIdx = (j + 1) % numSlices;
|
||||
for (int j = 0; j < numSides; j++) {
|
||||
final int nextIdx = (j + 1) % numSides;
|
||||
|
||||
// Vertices
|
||||
int[] vertexIndices = new int[] {
|
||||
i * numSlices + j, // Bottom-left of quad
|
||||
(i + 1) * numSlices + j, // Top-left of quad
|
||||
(i + 1) * numSlices + nextIdx, // Top-right of quad
|
||||
i * numSlices + nextIdx, // Bottom-right of quad
|
||||
i * numSides + j, // Bottom-left of quad
|
||||
(i + 1) * numSides + j, // Top-left of quad
|
||||
(i + 1) * numSides + nextIdx, // Top-right of quad
|
||||
i * numSides + nextIdx, // Bottom-right of quad
|
||||
};
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
|
||||
// Normals
|
||||
int[] normalIndices = vertexIndices.clone(); // No reversing needed, already done by vertices
|
||||
|
||||
// Texture coordinates
|
||||
int[] texCoordsIndices = new int[] {
|
||||
i * (numSides+1) + j,
|
||||
(i + 1) * (numSides+1) + j,
|
||||
(i + 1) * (numSides+1) + j+1,
|
||||
i * (numSides+1) + j+1,
|
||||
};
|
||||
texCoordsIndices = ObjUtils.reverseIndexWinding(texCoordsIndices, !isOutside);
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
ObjUtils.offsetIndex(texCoordsIndices, texCoordsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx); // Do this last, otherwise the normal indices will be wrong
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ public class CylinderExporter {
|
||||
|
||||
// Other meshes may have been added to the obj, so we need to keep track of the starting indices
|
||||
int startIdx = obj.getNumVertices();
|
||||
int texCoordsStartIdx = obj.getNumTexCoords();
|
||||
int normalsStartIdx = obj.getNumNormals();
|
||||
|
||||
if (solid) {
|
||||
@ -51,16 +52,18 @@ public class CylinderExporter {
|
||||
}
|
||||
|
||||
// Generate side top vertices
|
||||
generateRingVertices(obj, transformer, numSides, 0, length, foreRadius, aftRadius, isOutside, foreRingVertices, foreRingNormals);
|
||||
generateRingVertices(obj, transformer, numSides, 0, length, length, foreRadius, aftRadius, isOutside, foreRingVertices, foreRingNormals);
|
||||
|
||||
// Generate side bottom vertices
|
||||
generateRingVertices(obj, transformer, numSides, length, 0, aftRadius, foreRadius, isOutside, aftRingVertices, aftRingNormals);
|
||||
generateRingVertices(obj, transformer, numSides, length, 0, length, aftRadius, foreRadius, isOutside, aftRingVertices, aftRingNormals);
|
||||
|
||||
// Create faces for the bottom and top
|
||||
if (solid) {
|
||||
for (int i = 0; i < numSides; i++) {
|
||||
int nextIdx = (i + 1) % numSides;
|
||||
|
||||
// Bottom face
|
||||
//// Vertices
|
||||
int[] vertexIndices = new int[] {
|
||||
0, // Bottom center vertex
|
||||
2 + i,
|
||||
@ -69,13 +72,18 @@ public class CylinderExporter {
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx);
|
||||
|
||||
//// Normals
|
||||
int[] normalIndices = new int[]{0, 0, 0};
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
|
||||
//// Texture coordinates
|
||||
// TODO?
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
obj.addFace(face);
|
||||
|
||||
// Top face
|
||||
//// Vertices
|
||||
vertexIndices = new int[] {
|
||||
1, // Top center vertex
|
||||
2 + numSides + ((i + 1) % numSides),
|
||||
@ -84,9 +92,13 @@ public class CylinderExporter {
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx);
|
||||
|
||||
//// Normals
|
||||
normalIndices = new int[] {1, 1, 1};
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx);
|
||||
|
||||
//// Texture coordinates
|
||||
// TODO
|
||||
|
||||
face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
@ -97,6 +109,7 @@ public class CylinderExporter {
|
||||
final int nextIdx = (i + 1) % numSides;
|
||||
final int offset = solid ? 2 : 0; // Offset by 2 to skip the bottom and top center vertices
|
||||
|
||||
//// Vertices
|
||||
int[] vertexIndices = new int[]{
|
||||
i, // Bottom-left of quad
|
||||
numSides + i, // Top-left of quad
|
||||
@ -105,12 +118,23 @@ public class CylinderExporter {
|
||||
};
|
||||
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, !isOutside);
|
||||
|
||||
//// Normals
|
||||
int[] normalIndices = vertexIndices.clone(); // No need to reverse winding, already done by vertices
|
||||
|
||||
//// Texture coordinates
|
||||
int[] texCoordIndices = new int[]{
|
||||
i, // Bottom-left of quad
|
||||
numSides+1 + i, // Top-left of quad
|
||||
numSides+1 + i+1, // Top-right of quad (don't use nextIdx, we don't want roll-over)
|
||||
i+1, // Bottom-right of quad (don't use nextIdx, we don't want roll-over)
|
||||
};
|
||||
texCoordIndices = ObjUtils.reverseIndexWinding(texCoordIndices, !isOutside);
|
||||
|
||||
ObjUtils.offsetIndex(normalIndices, normalsStartIdx + offset);
|
||||
ObjUtils.offsetIndex(texCoordIndices, texCoordsStartIdx);
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx + offset); // ! Only add offset here, otherwise you mess up the indices for the normals
|
||||
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
}
|
||||
}
|
||||
@ -147,7 +171,7 @@ public class CylinderExporter {
|
||||
}
|
||||
|
||||
public static void generateRingVertices(DefaultObj obj, CoordTransform transformer,
|
||||
int numSides, float x, float nextX, float radius, float nextRadius,
|
||||
int numSides, float x, float nextX, float xMax, float radius, float nextRadius,
|
||||
boolean isOutside, List<Integer> vertexList, List<Integer> normalList) {
|
||||
int startIdx = obj.getNumVertices();
|
||||
int normalsStartIdx = obj.getNumNormals();
|
||||
@ -183,6 +207,15 @@ public class CylinderExporter {
|
||||
if (normalList != null) {
|
||||
normalList.add(normalsStartIdx + i);
|
||||
}
|
||||
|
||||
// Texture coordinates
|
||||
final float u = (float) i / numSides;
|
||||
final float v = isOutside ? (xMax - x) / xMax : x / xMax; // For some reason, the texture is vertically flipped in OR for inside cylinders. Don't really like it, but it is what it is
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
// Need to add a last texture coordinate for the end of the texture
|
||||
final float v = isOutside ? (xMax - x) / xMax : x / xMax;
|
||||
obj.addTexCoord(1f, v);
|
||||
}
|
||||
}
|
||||
|
@ -32,39 +32,60 @@ public class PolygonExporter {
|
||||
|
||||
// NOTE: "front" is the side view with the normal pointing towards the viewer, "back" is the side with the normal pointing away from the viewer
|
||||
|
||||
// Calculate the boundaries of the polygon
|
||||
Boundaries boundaries = new Boundaries(pointLocationsX, pointLocationsY);
|
||||
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(0, 0, -1)); // Front faces normal
|
||||
obj.addNormal(transformer.convertLocWithoutOriginOffs(0, 0, 1)); // Back faces normal
|
||||
|
||||
// Generate front face vertices
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
obj.addVertex(transformer.convertLoc(pointLocationsX[i], pointLocationsY[i], -thickness/2));
|
||||
|
||||
// Compute texture coordinates based on normalized position
|
||||
float u = (pointLocationsX[i] - boundaries.getMinX()) / (boundaries.getMaxX() - boundaries.getMinX());
|
||||
u = 1f - u;
|
||||
float v = (pointLocationsY[i] - boundaries.getMinY()) / (boundaries.getMaxY() - boundaries.getMinY());
|
||||
v = 1f - v;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
// Generate back face vertices
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
obj.addVertex(transformer.convertLoc(pointLocationsX[i], pointLocationsY[i], thickness/2));
|
||||
|
||||
// Compute texture coordinates based on normalized position
|
||||
float u = (pointLocationsX[i] - boundaries.getMinX()) / (boundaries.getMaxX() - boundaries.getMinX());
|
||||
u = 1f - u;
|
||||
float v = (pointLocationsY[i] - boundaries.getMinY()) / (boundaries.getMaxY() - boundaries.getMinY());
|
||||
v = 1f - v;
|
||||
obj.addTexCoord(u, v);
|
||||
}
|
||||
|
||||
// Create front face
|
||||
int[] vertexIndices = new int[pointLocationsX.length];
|
||||
int[] normalIndices = new int[pointLocationsX.length];
|
||||
int[] texCoordsIndices = new int[pointLocationsX.length];
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
vertexIndices[i] = pointLocationsX.length-1 - i;
|
||||
normalIndices[i] = normalsStartIdx;
|
||||
texCoordsIndices[i] = pointLocationsX.length-1 - i;
|
||||
}
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
|
||||
// Create back face
|
||||
vertexIndices = new int[pointLocationsX.length];
|
||||
normalIndices = new int[pointLocationsX.length];
|
||||
texCoordsIndices = new int[pointLocationsX.length];
|
||||
for (int i = 0; i < pointLocationsX.length; i++) {
|
||||
vertexIndices[i] = pointLocationsX.length + i;
|
||||
normalIndices[i] = normalsStartIdx + 1;
|
||||
texCoordsIndices[i] = pointLocationsX.length + i;
|
||||
}
|
||||
ObjUtils.offsetIndex(vertexIndices, startIdx);
|
||||
face = new DefaultObjFace(vertexIndices, null, normalIndices);
|
||||
face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
|
||||
obj.addFace(face);
|
||||
|
||||
// Create side faces
|
||||
@ -111,4 +132,55 @@ public class PolygonExporter {
|
||||
throw new IllegalArgumentException("The first and last points must be different");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the boundaries of a polygon.
|
||||
*/
|
||||
private static class Boundaries {
|
||||
private float minX;
|
||||
private float maxX;
|
||||
private float minY;
|
||||
private float maxY;
|
||||
|
||||
public Boundaries(float[] pointsX, float[] pointsY) {
|
||||
this.minX = Float.MAX_VALUE;
|
||||
this.maxX = Float.MIN_VALUE;
|
||||
this.minY = Float.MAX_VALUE;
|
||||
this.maxY = Float.MIN_VALUE;
|
||||
|
||||
for (int i = 0; i < pointsX.length; i++) {
|
||||
float x = pointsX[i];
|
||||
float y = pointsY[i];
|
||||
|
||||
if (x < minX) {
|
||||
minX = x;
|
||||
}
|
||||
if (x > maxX) {
|
||||
maxX = x;
|
||||
}
|
||||
if (y < minY) {
|
||||
minY = y;
|
||||
}
|
||||
if (y > maxY) {
|
||||
maxY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float getMinX() {
|
||||
return minX;
|
||||
}
|
||||
|
||||
public float getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
|
||||
public float getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
public float getMaxY() {
|
||||
return maxY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public abstract class FileUtils {
|
||||
|
||||
@ -51,4 +52,13 @@ public abstract class FileUtils {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name from a path.
|
||||
* @param pathString the path (e.g. "my/file/path.txt")
|
||||
* @return the file name (e.g. "path.txt")
|
||||
*/
|
||||
public static String getFileNameFromPath(String pathString) {
|
||||
return Paths.get(pathString).getFileName().toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import net.sf.openrocket.file.RocketLoadException;
|
||||
import net.sf.openrocket.file.openrocket.OpenRocketSaverTest;
|
||||
import net.sf.openrocket.file.wavefrontobj.CoordTransform;
|
||||
import net.sf.openrocket.file.wavefrontobj.DefaultCoordTransform;
|
||||
import net.sf.openrocket.file.wavefrontobj.ObjUtils;
|
||||
import net.sf.openrocket.l10n.DebugTranslator;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.plugin.PluginModule;
|
||||
@ -158,29 +159,29 @@ public class OBJExporterFactoryTest {
|
||||
|
||||
// Create a temp file for storing the exported OBJ
|
||||
Path tempFile = Files.createTempFile("testExport", ".obj");
|
||||
String filePath = tempFile.toAbsolutePath().toString();
|
||||
|
||||
|
||||
filePath = "/Users/SiboVanGool/Downloads/testExport.obj"; // TODO: remove this line
|
||||
|
||||
|
||||
// Do the exporting
|
||||
OBJExportOptions options = new OBJExportOptions(rocket);
|
||||
options.setScaling(30);
|
||||
options.setExportChildren(true);
|
||||
OBJExporterFactory exporterFactory = new OBJExporterFactory(components, rocket.getSelectedConfiguration(), filePath, options);
|
||||
options.setExportAppearance(true);
|
||||
options.setRemoveOffset(true);
|
||||
OBJExporterFactory exporterFactory = new OBJExporterFactory(components, rocket.getSelectedConfiguration(), tempFile.toFile(), options);
|
||||
exporterFactory.doExport();
|
||||
|
||||
|
||||
// Test with other parameters
|
||||
/*noseCone.setShoulderCapped(false);
|
||||
noseCone.setShoulderCapped(false);
|
||||
railButton.setScrewHeight(0);
|
||||
bodyTube.setFilled(true);
|
||||
|
||||
transformer = new DefaultCoordTransform(rocket.getLength());
|
||||
exporterFactory = new OBJExporterFactory(components, rocket.getSelectedConfiguration(), true, false, false,
|
||||
transformer, filePath);
|
||||
exporterFactory.doExport();*/
|
||||
options.setTriangulate(true);
|
||||
options.setRemoveOffset(false);
|
||||
options.setScaling(1000);
|
||||
options.setLOD(ObjUtils.LevelOfDetail.LOW_QUALITY);
|
||||
|
||||
exporterFactory = new OBJExporterFactory(components, rocket.getSelectedConfiguration(), tempFile.toFile(), options);
|
||||
exporterFactory.doExport();
|
||||
|
||||
// Clean up
|
||||
Files.delete(tempFile);
|
||||
|
@ -26,6 +26,7 @@ public class OBJOptionChooser extends JPanel {
|
||||
private final JCheckBox exportAsSeparateFiles;
|
||||
private final JCheckBox removeOffset;
|
||||
private final JCheckBox triangulate;
|
||||
private final JCheckBox sRGB;
|
||||
private final JComboBox<ObjUtils.LevelOfDetail> LOD;
|
||||
|
||||
private final List<RocketComponent> selectedComponents;
|
||||
@ -73,6 +74,10 @@ public class OBJOptionChooser extends JPanel {
|
||||
this.triangulate.setToolTipText(trans.get("OBJOptionChooser.checkbox.triangulate.ttip"));
|
||||
advancedOptionsPanel.add(triangulate, "spanx, wrap");
|
||||
|
||||
//// Export colors in sRGB
|
||||
this.sRGB = new JCheckBox(trans.get("OBJOptionChooser.checkbox.sRGB"));
|
||||
this.sRGB.setToolTipText(trans.get("OBJOptionChooser.checkbox.sRGB.ttip"));
|
||||
advancedOptionsPanel.add(sRGB, "spanx, wrap");
|
||||
|
||||
//// Level of detail
|
||||
JLabel LODLabel = new JLabel(trans.get("OBJOptionChooser.lbl.LevelOfDetail"));
|
||||
@ -124,6 +129,7 @@ public class OBJOptionChooser extends JPanel {
|
||||
this.exportAsSeparateFiles.setSelected(opts.isExportAsSeparateFiles());
|
||||
this.removeOffset.setSelected(opts.isRemoveOffset());
|
||||
this.triangulate.setSelected(opts.isTriangulate());
|
||||
this.sRGB.setSelected(opts.isUseSRGB());
|
||||
|
||||
this.LOD.setSelectedItem(opts.getLOD());
|
||||
}
|
||||
@ -144,6 +150,7 @@ public class OBJOptionChooser extends JPanel {
|
||||
opts.setExportAsSeparateFiles(exportAsSeparateFiles.isSelected());
|
||||
opts.setRemoveOffset(removeOffset.isSelected());
|
||||
opts.setTriangulate(triangulate.isSelected());
|
||||
opts.setUseSRGB(sRGB.isSelected());
|
||||
opts.setLOD((ObjUtils.LevelOfDetail) LOD.getSelectedItem());
|
||||
}
|
||||
|
||||
|
@ -1678,7 +1678,7 @@ public class BasicFrame extends JFrame {
|
||||
*/
|
||||
private boolean saveWavefrontOBJFile(File file, OBJExportOptions options) {
|
||||
OBJExporterFactory exporter = new OBJExporterFactory(getSelectedComponents(), rocket.getSelectedConfiguration(),
|
||||
file.getAbsolutePath(), options);
|
||||
file, options);
|
||||
exporter.doExport();
|
||||
|
||||
return true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user