zturtleman / mm3d

Maverick Model 3D is a 3D model editor and animator for games.
https://clover.moe/mm3d
GNU General Public License v2.0
114 stars 22 forks source link

RFC: Joints used to be displayed as octohedrons? #118

Open m-7761 opened 4 years ago

m-7761 commented 4 years ago

I noticed that in my current code drawJoints seems to produce joints that look like fins. I don't think I've changed anything that could have caused that. But maybe. In the release version (Windows binary) I have the joints are octohedrons.

I looked at the version history of model_draw.cc (I think) and didn't see any notes about such a change.

I just want to make sure I haven't broken something. I think the octohedrons are maybe a little bit easier to see (especially from multiple angles) but I can see the value in fins too. I wonder if this should be a display option, and if there is old code handy.

P.S. I see in the release version the Projection lines are missing an edge, but I guess this got fixed too? I wonder how old is the release binaries.

zturtleman commented 4 years ago

Joints are still octohedrons in Maverick. The only change since Maverick 1.3.12 release binaries affecting joint rendering is https://github.com/zturtleman/mm3d/commit/f21f69ae33cbee6f3a93de83ac8f0765e059934c (which should have no affect).

I don't know what Projection edge you're referring to. It's rendering should be the same as Misfit Model 3D 1.3.8 in 2009.

Maverick 1.3.12 release binaries are from October 2019. https://github.com/zturtleman/mm3d/compare/v1.3.12...master

m-7761 commented 4 years ago

My release version is 1.3.11. But so is my source code where the problem isn't there. I think it's not rendering the lines that wrap from the last to first vertex. I mean it couldn't be anything else. It could be something that only happens in release builds with an optimization switch.

On "fins" ... it did occur to me there are too many glVertex calls for fins. (My code addressed stack objects as if an array/struct.) But again, I think fins have merit. I've seen many model viewers that use them. But I think both do. So I will try to add something if I don't forget. (I think the octohedrons look a little harsh with the changes I made to use alpha on the wireframes. I will see about applying alpha to them also. In theory they should even be a little ghostly.)

CORRECTION: I thought I filed an issue on the type field, but just mentioned it in passing. I'm not sure it requires one, since it isn't causing a problem and will presumably be fixed.

I removed the "type" field from points (https://github.com/zturtleman/mm3d/issues/114#issuecomment-574447679) because the MM3D writer wrote junk to that field, so it's not reliable going forward, but I was thinking that I can see why maybe it was there, because it would probably be easier to add things like lights, cameras (e.g. for the viewport coordinates) and background as variations on the Point object, since there's already too many kinds of primitives in the toolbar and not enough space on the keyboard to keep inventing more.

I would like to add "objects" too, though it's hard to think of how to implement them (COLLADA doesn't even have an object element, which makes things quite difficult, basically you have to use extensions for that) but since I've assigned Point to O on the keyboard, I think making objects a type of Point is probably a place to start in thinking about them. (EDITED: Maybe even we could call them objects instead of points, and make Point a kind of generic object.)

m-7761 commented 4 years ago

EDITED: I've removed setRotationToPoint in favor of constructing a matrix with an up vector that doesn't roll chaotically. See below comment.

Off-topic: Below is a fix for drawing the octohedron when the child is directly below the parent.

void Quaternion::setRotationToPoint(const double &faceX, const double &faceY, const double &faceZ,
        const double &pointX, const double &pointY, const double &pointZ)
{
    //???
    Vector v1(faceX,faceY,faceZ); // Facing
    Vector v2(pointX,pointY,pointZ); // Want to face
    v1.normalize3();
    v2.normalize3();

    //This fails if the angle is Pi (180) but I'm not sure 
    //what is the best fix. The normal will be degenerated.
    double anglec = v1.dot3(v2);
    Vector normal;
    if(anglec>-1&&anglec<1) //2020
    {
        normal = v1.cross3(v2);
        normal.normalize3();
    }
    else normal.setAll(v2.get(1),v2.get(0),v2.get(3)); //HACK

    setRotationOnAxis(normal.getVector(),acos(anglec));
}

There's also something very wrong with how the joints spin randomly as they're rotated along the basis axes. IOW the octohedron sides aren't aligned with the joint itself.

I tried my hand yesterday at softening the bones. I found that the purple and blue modes can be indistinguishable on green backgrounds... that's basically a problem in general, like obviously the blue mode is indistinguishable on a blue background too... but I did some stuff by stippling and thickening the lines that run directly between the joints to make them more readable, and to tell when it's in its skeleton animation mode. I made the points larger too, for projections as well I believe. I basically made setting glPointSize explicit without cleanup.

I used glDepthRange(0,0) to be able to use alpha. That's more extreme than just using GL_LESS. The idea is to have depth testing but not let the alpha lines draw over one another since that tends to ruin the alpha effect. You want a blend of the colors behind the bones, not double blends of the bones.

m-7761 commented 4 years ago

EDITED: I actually think the octohedrons would look prettier if they oriented themselves toward the camera. That would make the weird spinning a feature. They have a tendency to double their edges. Maybe if they were just spun 90 degrees that tendency would mostly go away. EDITED: Or not. It would block the line through the middle more often. And would still double up (or triple up) in the middle. Maybe this is another argument for fins. It's tricky I suppose. Fins might be more likely to disappear (degenerate) being a single triangle.

m-7761 commented 4 years ago

Unhappy I played with this further, and concluded that black is the best color for the joints lines. And made dark blue (closer to the drawProjections color) the color when the tree is selected in the skeleton animation mode.

I have the alpha level passed as a parameter, but chose 0.35 instead of 50% as I'm using for line drawing. I wanted to optimize for a crisp line that isn't overwhelming. The alpha is increased slightly for the blue color.

In wire frame it's blended with the teal background so the color is more like dark green. Which is also the color of an XOR lasso that doesn't disappear on gray.

Even though the color is pretty similar to the grid color and xor color I feel it still looks best. White and black tend to look best on a variety of background colors. In an inverted mode you might draw the wire frames with black and joints with white instead.

(I also made the joints inputs in the sidebar to hide the unused inputs on the end of the list since I felt it looked bad to have the joint selection menu enabled and the corresponding weight menu disabled. That may be because I don't put them on the same line side-by-side. I don't believe the joints should be limited to 4. I don't believe there's any part of the data-structure that's limited to 4 either. Maybe exporters are so. Oh yeah, yesterday I did some work to have vertices reflect changes to their joints/weights... that I think the Maverick release doesn't let those things be edited in the skeletal mode. It's not a difficult change.)

m-7761 commented 4 years ago

On setRotationToPoint (https://github.com/zturtleman/mm3d/issues/118#issuecomment-577533813) I went back to drawJoints to see what can be done about its chaotic rolling behavior and ended up removing it and using a Matrix construction instead. I implemented it with Quaternion first... out of curiosity... but it wasn't efficient. I think setRotationToPoint doesn't have a legit use case owing to its rolling.

How MM3D works is to not give joints their own local coordinate frame. I guess that's kind of in line with keeping all vertices in global coordinates but it means that there aren't any real bearings on the joints. I also removed the rotation argument to https://github.com/zturtleman/mm3d/blob/ebbf3bb1951aa5f7e3617a2bce208fb5bf98b91d/src/mm3dcore/tool.cc#L40 since it wasn't used and stopped using rotation for joints altogether because it just cancels out in the bind-pose matrix.

By not rolling around the Top view shows the top of bones as you would expect it to. Only bones that cut across more than one dimension roll when moved... typically they resist rolling until they can no more and do a flip onto their back, but it depends on the severity of their transverse angles.

That's the best you can do without establishing a local coordinate frame for joints. Of course there's no way to orient them better and if you do use a local coordinate frame that would necessitate removing the up-vector logic and so would not look as nice. So this looks nice in a purely without local coordinate frame system as it exists, which arguably has its pros and cons. But it's an interesting decision I think. I'm interested in making an easy to grasp 3D modeling package so anything that removes variables is worth consideration in my book.

P.S. I don't know how crazy I am about the glmath.h classes but short of making an effort to replace them wrestling with them for the meantime is the only alternative. I find projects like this one usually have their own vector/matrix classes, which are not difficult to maintain, but this one is not as friendly.