Add texturing for motors

This commit is contained in:
SiboVG 2023-08-21 02:42:51 +02:00
parent 4fc3901f00
commit d63e2e9084
3 changed files with 137 additions and 17 deletions

View File

@ -74,10 +74,10 @@ public class MotorExporter {
List<Integer> foreRingVertices = new ArrayList<>(); List<Integer> foreRingVertices = new ArrayList<>();
List<Integer> aftRingVertices = new ArrayList<>(); List<Integer> aftRingVertices = new ArrayList<>();
CylinderExporter.addCylinderMesh(obj, transformer, null, (float) radius, (float) length, numSides, false, true, CylinderExporter.addCylinderMesh(obj, transformer, null, (float) radius, (float) length, numSides, false, true,
foreRingVertices, aftRingVertices); 0, 1, 0.125f, 0.875f, foreRingVertices, aftRingVertices);
// Close the fore end // Close the fore end
DiskExporter.closeDiskMesh(obj, transformer, null, foreRingVertices, false, true); DiskExporter.closeDiskMesh(obj, transformer, null, foreRingVertices, false, true, 0, 1, 0.875f, 1);
// Generate the aft end inner ring vertices // Generate the aft end inner ring vertices
List<Integer> aftInnerRingVertices = new ArrayList<>(); List<Integer> aftInnerRingVertices = new ArrayList<>();
@ -100,12 +100,26 @@ public class MotorExporter {
} }
// Close outer and inner aft ring // Close outer and inner aft ring
DiskExporter.closeDiskMesh(obj, transformer, null, aftRingVertices, aftInnerRingVertices, false, false); DiskExporter.closeDiskMesh(obj, transformer, null, aftRingVertices, aftInnerRingVertices, false, false, 0, 1, 0.125f, 0.1f);
// Add cone tip vertex // Add cone tip vertex
obj.addVertex(transformer.convertLoc(length - coneLength, 0, 0)); obj.addVertex(transformer.convertLoc(length - coneLength, 0, 0));
obj.addNormal(transformer.convertLocWithoutOriginOffs(1, 0, 0)); obj.addNormal(transformer.convertLocWithoutOriginOffs(1, 0, 0));
// Add texture coordinates
final int texCoordsStartIdx = obj.getNumTexCoords();
//// Inner aft ring
for (int i = 0; i <= numSides; i++) {
final float u = ((float) i) / numSides;
obj.addTexCoord(u, 0.1f);
}
//// Cone tip
for (int i = 0; i <= numSides; i++) {
final float u = ((float) i) / numSides;
obj.addTexCoord(u, 0f);
}
int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added int endIdx = Math.max(obj.getNumVertices() - 1, startIdx); // Clamp in case no vertices were added
int normalsEndIdx = Math.max(obj.getNumNormals() - 1, normalsStartIdx); int normalsEndIdx = Math.max(obj.getNumNormals() - 1, normalsStartIdx);
@ -122,8 +136,14 @@ public class MotorExporter {
normalsStartIdx + nextIdx, normalsStartIdx + nextIdx,
normalsStartIdx + i, normalsStartIdx + i,
}; };
final int[] texCoordIndices = new int[] {
numSides+1 + i,
i+1,
i,
};
ObjUtils.offsetIndex(texCoordIndices, texCoordsStartIdx);
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices); DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordIndices, normalIndices);
obj.addFace(face); obj.addFace(face);
} }

View File

@ -5,6 +5,7 @@ import net.sf.openrocket.file.wavefrontobj.CoordTransform;
import net.sf.openrocket.file.wavefrontobj.DefaultObj; import net.sf.openrocket.file.wavefrontobj.DefaultObj;
import net.sf.openrocket.file.wavefrontobj.DefaultObjFace; import net.sf.openrocket.file.wavefrontobj.DefaultObjFace;
import net.sf.openrocket.file.wavefrontobj.ObjUtils; import net.sf.openrocket.file.wavefrontobj.ObjUtils;
import net.sf.openrocket.util.MathUtil;
import java.util.List; import java.util.List;
@ -22,6 +23,10 @@ public class CylinderExporter {
* NOTE: Culling is not really thought of for the hollow cylinder; this mode is really meant to be * NOTE: Culling is not really thought of for the hollow cylinder; this mode is really meant to be
* combined with other objects * combined with other objects
* @param isOutside Whether the cylinder is an outside face (true) or inside face (false) * @param isOutside Whether the cylinder is an outside face (true) or inside face (false)
* @param uMin The minimum u texture coordinate
* @param uMax The maximum u texture coordinate
* @param vMin The minimum v texture coordinate
* @param vMax The maximum v texture coordinate
* @param foreRingVertices A list to add the fore (top) ring vertex indices to * @param foreRingVertices A list to add the fore (top) ring vertex indices to
* @param aftRingVertices A list to add the aft (bottom) ring vertex indices to * @param aftRingVertices A list to add the aft (bottom) ring vertex indices to
* @param foreRingNormals A list to add the fore (top) ring normal indices to * @param foreRingNormals A list to add the fore (top) ring normal indices to
@ -29,6 +34,7 @@ public class CylinderExporter {
*/ */
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName, public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside, float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside,
float uMin, float uMax, float vMin, float vMax,
List<Integer> foreRingVertices, List<Integer> aftRingVertices, List<Integer> foreRingVertices, List<Integer> aftRingVertices,
List<Integer> foreRingNormals, List<Integer> aftRingNormals) { List<Integer> foreRingNormals, List<Integer> aftRingNormals) {
// Set the new group // Set the new group
@ -52,10 +58,12 @@ public class CylinderExporter {
} }
// Generate side top vertices // Generate side top vertices
generateRingVertices(obj, transformer, numSides, 0, length, length, foreRadius, aftRadius, isOutside, foreRingVertices, foreRingNormals); generateRingVertices(obj, transformer, numSides, 0, length, length, foreRadius, aftRadius, isOutside,
uMin, uMax, vMin, vMax, foreRingVertices, foreRingNormals);
// Generate side bottom vertices // Generate side bottom vertices
generateRingVertices(obj, transformer, numSides, length, 0, length, aftRadius, foreRadius, isOutside, aftRingVertices, aftRingNormals); generateRingVertices(obj, transformer, numSides, length, 0, length, aftRadius, foreRadius, isOutside,
uMin, uMax, vMin, vMax, aftRingVertices, aftRingNormals);
// Create faces for the bottom and top // Create faces for the bottom and top
if (solid) { if (solid) {
@ -138,6 +146,21 @@ public class CylinderExporter {
obj.addFace(face); obj.addFace(face);
} }
} }
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside,
List<Integer> foreRingVertices, List<Integer> aftRingVertices,
List<Integer> foreRingNormals, List<Integer> aftRingNormals) {
addCylinderMesh(obj, transformer, groupName, foreRadius, aftRadius, length, numSides, solid, isOutside,
0, 1, 0, 1, foreRingVertices, aftRingVertices, foreRingNormals, aftRingNormals);
}
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside,
float uMin, float uMax, float vMin, float vMax,
List<Integer> foreRingVertices, List<Integer> aftRingVertices) {
addCylinderMesh(obj, transformer, groupName, foreRadius, aftRadius, length, numSides, solid, isOutside,
uMin, uMax, vMin, vMax, foreRingVertices, aftRingVertices, null, null);
}
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName, public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside, float foreRadius, float aftRadius, float length, int numSides, boolean solid, boolean isOutside,
@ -146,6 +169,14 @@ public class CylinderExporter {
foreRingVertices, aftRingVertices, null, null); foreRingVertices, aftRingVertices, null, null);
} }
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float radius, float length, int numSides, boolean solid, boolean isOutside,
float uMin, float uMax, float vMin, float vMax,
List<Integer> foreRingVertices, List<Integer> aftRingVertices) {
addCylinderMesh(obj, transformer, groupName, radius, radius, length, numSides, solid, isOutside,
uMin, uMax, vMin, vMax, foreRingVertices, aftRingVertices);
}
public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName, public static void addCylinderMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
float radius, float length, int numSides, boolean solid, boolean isOutside, float radius, float length, int numSides, boolean solid, boolean isOutside,
List<Integer> foreRingVertices, List<Integer> aftRingVertices) { List<Integer> foreRingVertices, List<Integer> aftRingVertices) {
@ -172,7 +203,8 @@ public class CylinderExporter {
public static void generateRingVertices(DefaultObj obj, CoordTransform transformer, public static void generateRingVertices(DefaultObj obj, CoordTransform transformer,
int numSides, float x, float nextX, float xMax, float radius, float nextRadius, int numSides, float x, float nextX, float xMax, float radius, float nextRadius,
boolean isOutside, List<Integer> vertexList, List<Integer> normalList) { boolean isOutside, float uMin, float uMax, float vMin, float vMax,
List<Integer> vertexList, List<Integer> normalList) {
int startIdx = obj.getNumVertices(); int startIdx = obj.getNumVertices();
int normalsStartIdx = obj.getNumNormals(); int normalsStartIdx = obj.getNumNormals();
@ -181,10 +213,11 @@ public class CylinderExporter {
final float y = radius * (float) Math.cos(angle); final float y = radius * (float) Math.cos(angle);
final float z = radius * (float) Math.sin(angle); final float z = radius * (float) Math.sin(angle);
// Side top vertices // Vertex
obj.addVertex(transformer.convertLoc(x, y, z)); obj.addVertex(transformer.convertLoc(x, y, z));
// We need special nx normal when the radius changes // Normal
//// We need special nx normal when the radius changes
float nx; float nx;
if (Float.compare(radius, nextRadius) != 0) { if (Float.compare(radius, nextRadius) != 0) {
final double slopeAngle = Math.atan(Math.abs(nextX - x) / (nextRadius - radius)); final double slopeAngle = Math.atan(Math.abs(nextX - x) / (nextRadius - radius));
@ -209,13 +242,17 @@ public class CylinderExporter {
} }
// Texture coordinates // 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 float u = ((float) i) / numSides;
u = (float) MathUtil.map(u, 0, 1, uMin, uMax);
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
v = (float) MathUtil.map(v, 0, 1, vMin, vMax);
obj.addTexCoord(u, v); obj.addTexCoord(u, v);
} }
// Need to add a last texture coordinate for the end of the texture // Need to add a last texture coordinate for the end of the texture
final float v = isOutside ? (xMax - x) / xMax : x / xMax; float v = isOutside ? (xMax - x) / xMax : x / xMax;
obj.addTexCoord(1f, v); v = (float) MathUtil.map(v, 0, 1, vMin, vMax);
obj.addTexCoord(uMax, v);
} }
} }

View File

@ -19,10 +19,15 @@ public class DiskExporter {
* @param innerVertices The indices of the inner vertices, or null if the disk is solid * @param innerVertices The indices of the inner vertices, or null if the disk is solid
* @param isClockwise Whether the vertices are in clockwise order (true) or counter-clockwise order (false) * @param isClockwise Whether the vertices are in clockwise order (true) or counter-clockwise order (false)
* @param isTopFace Whether the disk is a top face (true) or bottom face (false) * @param isTopFace Whether the disk is a top face (true) or bottom face (false)
* @param uMin The minimum u texture coordinate
* @param uMax The maximum u texture coordinate
* @param vMin The minimum v texture coordinate
* @param vMax The maximum v texture coordinate
*/ */
public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName, public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
@NotNull List<Integer> outerVertices, List<Integer> innerVertices, @NotNull List<Integer> outerVertices, List<Integer> innerVertices,
boolean isClockwise, boolean isTopFace) { boolean isClockwise, boolean isTopFace,
float uMin, float uMax, float vMin, float vMax) {
if (outerVertices.isEmpty()) { if (outerVertices.isEmpty()) {
throw new IllegalArgumentException("Outer vertices cannot be empty"); throw new IllegalArgumentException("Outer vertices cannot be empty");
} }
@ -45,6 +50,21 @@ public class DiskExporter {
obj.addNormal(transformer.convertLocWithoutOriginOffs(isTopFace ? -1 : 1, 0, 0)); // TODO: hm, what if the object is rotated? If the disk is not drawn in the y direction? obj.addNormal(transformer.convertLocWithoutOriginOffs(isTopFace ? -1 : 1, 0, 0)); // TODO: hm, what if the object is rotated? If the disk is not drawn in the y direction?
final int normalIndex = obj.getNumNormals() - 1; final int normalIndex = obj.getNumNormals() - 1;
final int texCoordsStartIdx = obj.getNumTexCoords();
final int numSides = outerVertices.size();
// Create the texture coordinates
//// Outer vertices
for (int i = 0; i <= numSides; i++) {
final float u = uMin + ((float) i) / numSides * (uMax - uMin);
obj.addTexCoord(u, vMin);
}
//// Inner vertices
for (int i = 0; i <= numSides; i++) {
final float u = uMin + ((float) i) / numSides * (uMax - uMin);
obj.addTexCoord(u, vMax);
}
if (isSolid) { if (isSolid) {
// Add the center vertex // Add the center vertex
final int centerVertexIdx; final int centerVertexIdx;
@ -59,6 +79,8 @@ public class DiskExporter {
// Add the triangle faces // Add the triangle faces
for (int i = 0; i < outerVertices.size(); i++) { for (int i = 0; i < outerVertices.size(); i++) {
int nextIdx = (i + 1) % outerVertices.size(); int nextIdx = (i + 1) % outerVertices.size();
// Vertices
int[] vertexIndices = new int[] { int[] vertexIndices = new int[] {
centerVertexIdx, centerVertexIdx,
outerVertices.get(nextIdx), outerVertices.get(nextIdx),
@ -66,14 +88,27 @@ public class DiskExporter {
}; };
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, isTopFace != isClockwise); vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, isTopFace != isClockwise);
// Normals
int[] normalIndices = new int[] { normalIndex, normalIndex, normalIndex }; int[] normalIndices = new int[] { normalIndex, normalIndex, normalIndex };
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
// Texture coordinates
int[] texCoordsIndices = new int[] {
numSides+1 + i,
i+1,
i,
};
texCoordsIndices = ObjUtils.reverseIndexWinding(texCoordsIndices, isTopFace != isClockwise);
ObjUtils.offsetIndex(texCoordsIndices, texCoordsStartIdx);
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
obj.addFace(face); obj.addFace(face);
} }
} else { } else {
// Add the quad faces // Add the quad faces
for (int i = 0; i < outerVertices.size(); i++) { for (int i = 0; i < outerVertices.size(); i++) {
int nextIdx = (i + 1) % outerVertices.size(); int nextIdx = (i + 1) % outerVertices.size();
// Vertices
int[] vertexIndices = new int[] { int[] vertexIndices = new int[] {
outerVertices.get(i), // Bottom-left of quad outerVertices.get(i), // Bottom-left of quad
innerVertices.get(i), // Top-left of quad innerVertices.get(i), // Top-left of quad
@ -82,13 +117,40 @@ public class DiskExporter {
}; };
vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, isTopFace != isClockwise); vertexIndices = ObjUtils.reverseIndexWinding(vertexIndices, isTopFace != isClockwise);
// Normals
int[] normalIndices = new int[] { normalIndex, normalIndex, normalIndex, normalIndex }; int[] normalIndices = new int[] { normalIndex, normalIndex, normalIndex, normalIndex };
DefaultObjFace face = new DefaultObjFace(vertexIndices, null, normalIndices);
// Texture coordinates
int[] texCoordsIndices = new int[] {
i,
numSides+1 + i,
numSides+1 + i+1,
i+1,
};
texCoordsIndices = ObjUtils.reverseIndexWinding(texCoordsIndices, isTopFace != isClockwise);
ObjUtils.offsetIndex(texCoordsIndices, texCoordsStartIdx);
DefaultObjFace face = new DefaultObjFace(vertexIndices, texCoordsIndices, normalIndices);
obj.addFace(face); obj.addFace(face);
} }
} }
} }
public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
@NotNull List<Integer> outerVertices, List<Integer> innerVertices,
boolean isClockwise, boolean isTopFace) {
// By default, OpenRocket doesn't really render textures on disks (often edges of tubes), so we don't either
closeDiskMesh(obj, transformer, groupName, outerVertices, innerVertices, isClockwise, isTopFace, 0, 0, 0, 0);
}
public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
@NotNull List<Integer> outerVertices, boolean isClockwise, boolean isTopFace,
float uMin, float uMax, float vMin, float vMax) {
closeDiskMesh(obj, transformer, groupName, outerVertices, null, isClockwise, isTopFace, uMin, uMax, vMin, vMax);
}
/** /**
* Adds a (closed) disk mesh to the obj by using existing outer and inner vertices * Adds a (closed) disk mesh to the obj by using existing outer and inner vertices
* @param obj The obj to add the mesh to * @param obj The obj to add the mesh to
@ -100,7 +162,8 @@ public class DiskExporter {
*/ */
public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName, public static void closeDiskMesh(@NotNull DefaultObj obj, @NotNull CoordTransform transformer, String groupName,
@NotNull List<Integer> outerVertices, boolean isClockwise, boolean isTopFace) { @NotNull List<Integer> outerVertices, boolean isClockwise, boolean isTopFace) {
closeDiskMesh(obj, transformer, groupName, outerVertices, null, isClockwise, isTopFace); // By default, OpenRocket doesn't really render textures on disks (often edges of tubes), so we don't either
closeDiskMesh(obj, transformer, groupName, outerVertices, isClockwise, isTopFace, 0, 0, 0, 0);
} }
} }