Add BVHSettings and allow writing end nodes (#483)

This commit is contained in:
Butterscotch!
2023-01-18 14:23:56 -05:00
committed by GitHub
parent 564be6112a
commit 9cde4058ad
2 changed files with 111 additions and 20 deletions

View File

@@ -12,10 +12,10 @@ import java.io.*;
public class BVHFileStream extends PoseDataStream {
private static final int LONG_MAX_VALUE_DIGITS = Long.toString(Long.MAX_VALUE).length();
private static final float OFFSET_SCALE = 100f;
private static final float POSITION_SCALE = 100f;
private BVHSettings bvhSettings = BVHSettings.DEFAULT;
private final BufferedWriter writer;
private long frameCount = 0;
private long frameCountOffset;
@@ -31,16 +31,39 @@ public class BVHFileStream extends PoseDataStream {
writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096);
}
public BVHFileStream(OutputStream outputStream, BVHSettings bvhSettings) {
this(outputStream);
this.bvhSettings = bvhSettings;
}
public BVHFileStream(File file) throws FileNotFoundException {
super(file);
writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096);
}
public BVHFileStream(File file, BVHSettings bvhSettings) throws FileNotFoundException {
this(file);
this.bvhSettings = bvhSettings;
}
public BVHFileStream(String file) throws FileNotFoundException {
super(file);
writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096);
}
public BVHFileStream(String file, BVHSettings bvhSettings) throws FileNotFoundException {
this(file);
this.bvhSettings = bvhSettings;
}
public BVHSettings getBvhSettings() {
return bvhSettings;
}
public void setBvhSettings(BVHSettings bvhSettings) {
this.bvhSettings = bvhSettings;
}
private String getBufferedFrameCount(long frameCount) {
String frameString = Long.toString(frameCount);
int bufferCount = LONG_MAX_VALUE_DIGITS - frameString.length();
@@ -72,13 +95,28 @@ public class BVHFileStream extends PoseDataStream {
return TransformNodeWrapper.wrapFullHierarchy(rootNode);
}
private boolean isEndNode(TransformNodeWrapper node) {
return node == null || (!bvhSettings.shouldWriteEndNodes() && node.children.isEmpty());
}
private void writeNodeHierarchy(TransformNodeWrapper node) throws IOException {
writeNodeHierarchy(node, 0);
}
private void writeNodeHierarchy(TransformNodeWrapper node, int level) throws IOException {
// Don't write end sites at populated nodes
if (node.children.isEmpty() && node.getParent().children.size() > 1) {
// Treat null as an end node, this allows for simply writing empty end
// nodes
boolean isEndNode = isEndNode(node);
// Don't write end sites at populated nodes, most BVH parsers don't like
// this
// Ex case caught: `joint{ joint{ end }, end, end }` outputs `joint{ end
// }` instead
// Ex case let through: `joint{ end }`
boolean isSingleChild = node == null
|| node.getParent() == null
|| node.getParent().children.size() <= 1;
if (isEndNode && !isSingleChild) {
return;
}
@@ -86,7 +124,7 @@ public class BVHFileStream extends PoseDataStream {
String nextIndentLevel = indentLevel + "\t";
// Handle ends
if (node.children.isEmpty()) {
if (isEndNode) {
writer.write(indentLevel + "End Site\n");
} else {
writer.write((level > 0 ? indentLevel + "JOINT " : "ROOT ") + node.getName() + "\n");
@@ -94,18 +132,19 @@ public class BVHFileStream extends PoseDataStream {
writer.write(indentLevel + "{\n");
// Ignore the root offset and original root offset
if (level > 0 && node.wrappedNode.getParent() != null) {
if (level > 0 && node != null && node.wrappedNode.getParent() != null) {
Vector3f offset = node.localTransform.getTranslation();
float reverseMultiplier = node.hasReversedHierarchy() ? -1 : 1;
float offsetScale = bvhSettings.getOffsetScale() * reverseMultiplier;
writer
.write(
nextIndentLevel
+ "OFFSET "
+ offset.getX() * OFFSET_SCALE * reverseMultiplier
+ offset.getX() * offsetScale
+ " "
+ offset.getY() * OFFSET_SCALE * reverseMultiplier
+ offset.getY() * offsetScale
+ " "
+ offset.getZ() * OFFSET_SCALE * reverseMultiplier
+ offset.getZ() * offsetScale
+ "\n"
);
} else {
@@ -113,7 +152,7 @@ public class BVHFileStream extends PoseDataStream {
}
// Handle ends
if (!node.children.isEmpty()) {
if (!isEndNode) {
// Only give position for root
if (level > 0) {
writer.write(nextIndentLevel + "CHANNELS 3 Zrotation Xrotation Yrotation\n");
@@ -125,8 +164,14 @@ public class BVHFileStream extends PoseDataStream {
);
}
for (TransformNodeWrapper childNode : node.children) {
writeNodeHierarchy(childNode, level + 1);
// If the node has children
if (!node.children.isEmpty()) {
for (TransformNodeWrapper childNode : node.children) {
writeNodeHierarchy(childNode, level + 1);
}
} else {
// Write an empty end node
writeNodeHierarchy(null, level + 1);
}
}
@@ -215,9 +260,6 @@ public class BVHFileStream extends PoseDataStream {
rotBuf = inverseRootRot.mult(rotBuf, rotBuf);
}
// Yaw (Z), roll (X), pitch (Y) (intrinsic)
// angleBuf = rotBuf.toAngles(angleBuf);
// Roll (X), pitch (Y), yaw (Z) (intrinsic)
angleBuf = quatToXyzAngles(rotBuf.normalizeLocal(), angleBuf);
@@ -235,7 +277,7 @@ public class BVHFileStream extends PoseDataStream {
if (!node.children.isEmpty()) {
Quaternion inverseRot = transform.getRotation().inverse();
for (TransformNodeWrapper childNode : node.children) {
if (childNode.children.isEmpty()) {
if (isEndNode(childNode)) {
// If it's an end node, skip
continue;
}
@@ -258,13 +300,14 @@ public class BVHFileStream extends PoseDataStream {
Vector3f rootPos = rootNode.worldTransform.getTranslation();
// Write root position
float positionScale = bvhSettings.getPositionScale();
writer
.write(
rootPos.getX() * POSITION_SCALE
rootPos.getX() * positionScale
+ " "
+ rootPos.getY() * POSITION_SCALE
+ rootPos.getY() * positionScale
+ " "
+ rootPos.getZ() * POSITION_SCALE
+ rootPos.getZ() * positionScale
+ " "
);
writeNodeHierarchyRotation(rootNode, null);

View File

@@ -0,0 +1,48 @@
package dev.slimevr.posestreamer;
public class BVHSettings {
private float offsetScale = 100f;
private float positionScale = 100f;
private boolean writeEndNodes = false;
public static final BVHSettings DEFAULT = new BVHSettings();
public static final BVHSettings BLENDER = new BVHSettings(DEFAULT)
.setOffsetScale(1f)
.setPositionScale(1f);
public BVHSettings() {
}
public BVHSettings(BVHSettings source) {
this.offsetScale = source.offsetScale;
this.positionScale = source.positionScale;
this.writeEndNodes = source.writeEndNodes;
}
public float getOffsetScale() {
return offsetScale;
}
public BVHSettings setOffsetScale(float offsetScale) {
this.offsetScale = offsetScale;
return this;
}
public float getPositionScale() {
return positionScale;
}
public BVHSettings setPositionScale(float positionScale) {
this.positionScale = positionScale;
return this;
}
public boolean shouldWriteEndNodes() {
return writeEndNodes;
}
public BVHSettings setWriteEndNodes(boolean writeEndNodes) {
this.writeEndNodes = writeEndNodes;
return this;
}
}