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

drawLines with alpha looks much better (thinner) #95

Open m-7761 opened 4 years ago

m-7761 commented 4 years ago

I did some experimenting. Making drawLines transparent doesn't make the lines seem any less white, but they appear much thinner, which gives an impression of higher resolution. About 1/3rd looks best.

After this the red lines can be drawn opaque with 1px width. This looks better, since you don't have weird rounding artifacts, and the red lines still appear thicker, owing to the blending illusion.

Also, the line scan be drawn with glPolygonMode to cut the bandwidth in half. I played around with trying to make back-face lines dark to cut down on some noise, but could not get it to work ultimately because I didn't want silhouette lines to be half-and-half. It's doable in pure wireframe, but takes two-pass.

Having a back-face option for the ortho mode could help sometimes. The lines clip somewhat in ortho mode. I don't know if that's to be expected or if something is wrong.

CORRECTION: Turns out my back-face setting was not initializing correctly.

CORRECTION-CORRECTION: Turns out I was right all along: #96 (Sorry.)

m-7761 commented 4 years ago

EDITED: I've updated this code to draw edges only once using a two-pass loop. I was adding this effect to the UV editor when it became apparent the edges in the 3D model appeared brighter owing to most of their edges being shared.

EDITED: After changing to GL_LESS some edges didn't clear glPolygonOffset. I think it needs a nonzero Units parameter. See follow-up reply below for details.

void Model::drawLines()// Used for orthographic projections
{
    bool colorSelected = false;

    //TESTING
    //CAUTION: I don't know what the blend mode is
    //at this point?
    //https://github.com/zturtleman/mm3d/issues/95
    float a = 0.5f;

    enum{ alpha=1 };

    if(alpha) glEnable(GL_BLEND);
    if(alpha) glDepthFunc(GL_LESS);

    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

    glDisable(GL_TEXTURE_2D);

    if(m_initialized) for(int i=0;i<=alpha;i++)
    {
        //The 2-pass system prevents touching 
        //triangles from double-drawing their
        //alpha blended edges. It's a problem
        //if lone polygons are too dim to see.
        if(i) glDepthFunc(GL_LEQUAL);

        if(m_animationMode)
        {
            if(m_animationMode==ANIMMODE_SKELETAL&&m_currentAnim<m_skelAnims.size())
            {
                glBegin(GL_TRIANGLES);
                glColor4f(1.0,1.0,1.0,a);
                for(unsigned t = 0; t<m_triangles.size(); t++)
                {
                    Triangle *triangle = m_triangles[t];

                    if(triangle->m_visible)
                    {
                        if(triangle->m_selected)
                        {
                            if(alpha&&!i) continue;

                            if(colorSelected==false)
                            {
                                glColor4f(1,0,0,1);
                            }
                            colorSelected = true;
                        }
                        else
                        {
                            if(alpha&&i) continue;

                            if(colorSelected==true)
                            {
                                glColor4f(1.0,1.0,1.0,a);
                            }
                            colorSelected = false;
                        }

                        double vertices[3][3];

                        for(int v = 0; v<3; v++)
                        {
                            Vertex *vertex = (m_vertices[triangle->m_vertexIndices[v]]);

                            vertices[v][0] = vertex->m_drawSource[0];
                            vertices[v][1] = vertex->m_drawSource[1];
                            vertices[v][2] = vertex->m_drawSource[2];
                        }

                        glVertex3dv(vertices[0]);
                        //glVertex3dv(vertices[1]);

                        glVertex3dv(vertices[1]);
                        //glVertex3dv(vertices[2]);

                        glVertex3dv(vertices[2]);
                        //glVertex3dv(vertices[0]);
                    }
                }
                glEnd();
            }
            else if(m_animationMode==ANIMMODE_FRAME&&m_currentAnim<m_frameAnims.size())
            {
                //log_debug("drawing animation '%s' frame %d\n",m_frameAnims[m_currentAnim]->m_name.c_str(),m_currentFrame);

                if(m_currentFrame<m_frameAnims[m_currentAnim]->m_frameData.size()&&m_frameAnims[m_currentAnim]->m_frameData[m_currentFrame]->m_frameVertices->size()>0)
                {
                    glBegin(GL_TRIANGLES);
                    glColor4f(1.0,1.0,1.0,a);
                    for(unsigned t = 0; t<m_triangles.size(); t++)
                    {
                        Triangle *triangle = m_triangles[t];
                        triangle->m_marked = true;

                        if(triangle->m_visible)
                        {
                            if(triangle->m_selected)
                            {
                                if(alpha&&!i) continue;

                                if(colorSelected==false)
                                {
                                    glColor4f(1,0,0,1);
                                }
                                colorSelected = true;
                            }
                            else
                            {
                                if(alpha&&i) continue;

                                if(colorSelected==true)
                                {
                                    glColor4f(1.0,1.0,1.0,a);
                                }
                                colorSelected = false;
                            }

                            double vertices[3][3];

                            for(int v = 0; v<3; v++)
                            {
                                FrameAnimVertex *vertex = ((*m_frameAnims[m_currentAnim]->m_frameData[m_currentFrame]->m_frameVertices)[triangle->m_vertexIndices[v]]);

                                vertices[v][0] = vertex->m_coord[0];
                                vertices[v][1] = vertex->m_coord[1];
                                vertices[v][2] = vertex->m_coord[2];
                            }

                            glVertex3dv(vertices[0]);
                            //glVertex3dv(vertices[1]);

                            glVertex3dv(vertices[1]);
                            //glVertex3dv(vertices[2]);

                            glVertex3dv(vertices[2]);
                            //glVertex3dv(vertices[0]);
                        }
                    }
                    glEnd();
                }
                else
                {
                    log_error("No frame,(or no vertices)for this animation frame.\n");
                }
            }
        }
        else
        {
            //glLineWidth(1.0);
            glBegin(GL_TRIANGLES);
            glColor4f(1.0,1.0,1.0,a);
            for(unsigned t = 0; t<m_triangles.size(); t++)
            {
                Triangle *triangle = m_triangles[t];
                triangle->m_marked = true;

                if(triangle->m_visible)
                {
                    if(triangle->m_selected)
                    {
                        if(alpha&&!i) continue;

                        if(colorSelected==false)
                        {
                            //glEnd();
                            //glLineWidth(1.6); //???
                            //glBegin(GL_LINES);
                            glColor4f(1,0,0,1);
                        }
                        colorSelected = true;
                    }
                    else
                    {
                        if(alpha&&i) continue;

                        if(colorSelected==true)
                        {
                            //glEnd();
                            //glLineWidth(1.0);
                            //glBegin(GL_LINES);
                            glColor4f(1.0,1.0,1.0,a);
                        }
                        colorSelected = false;
                    }

                    double vertices[3][3];

                    for(int v = 0; v<3; v++)
                    {
                        Vertex *vertex = (m_vertices[triangle->m_vertexIndices[v]]);

                        vertices[v][0] = vertex->m_coord[0];
                        vertices[v][1] = vertex->m_coord[1];
                        vertices[v][2] = vertex->m_coord[2];
                    }

                    glVertex3dv(vertices[0]);
                    //glVertex3dv(vertices[1]);

                    glVertex3dv(vertices[1]);
                    //glVertex3dv(vertices[2]);

                    glVertex3dv(vertices[2]);
                    //glVertex3dv(vertices[0]);
                }
            }
            glEnd();
        }
    }

    //glLineWidth(1.0);

    if(alpha) glDisable(GL_BLEND);
    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
}
m-7761 commented 4 years ago

BTW: I think it would look better to scale the vertices somehow, based on zoom. They feel weird because they become larger as you zoom out, which is opposite of expectation.

If nothing else, probably keeping them opaque helps to seem them better on transparent lines... so they don't just blend into the lines when clustered.

m-7761 commented 4 years ago

While I was making the UV editor work like this, it appeared much dimmer. I realized I was low-balling the alpha setting because the 3D edges were drawing multiple triangles on top of another. So I've updated the code with a two-pass depth-buffer based fix. It's two pass because the selected triangles are drawn after, guaranteeing they are visible, and bright red.

m-7761 commented 4 years ago

After changing to GL_LESS some edges didn't clear glPolygonOffset, so I changed it from 1 to 1.1. I think it's based on the depth-buffer precision, but 2 showed lines poking through the polygons, so I'm not sure what it is. I know it's partly an "implementation-defined" facility.

m-7761 commented 4 years ago

EDITED: Actually, 1.1 didn't help at all with the orthographic views. I think the Units parameter to glPolygonOffset really should not be 0. Using 0 relies on GL_LEQUAL to work.

That may have been the original intent... or not... but it seems useful to be able to use GL_LESS. I changed it to (1,1) just to be safe. I think the orthographic depth buffer may have issues... but I think -1,1 for near/far is what gluOrtho2d uses, so maybe that's just how orthographic is, I don't know.

http://www.glprogramming.com/red/chapter06.html#name4

m-7761 commented 4 years ago

FINAL THOUGHTS

This gets hairier when you factor in "free" vertices and the Select Vertex tool that selects vertices independent of triangles, since you can't assign those to triangles for back-face rendering.

What I ended up with is to use the m_marked and m_marked2 members of Vertex to mark vertices that match the triangles, and draw the leftovers as points. It works out because I also realized you can still select elements when they are hidden by the back-face option... so I made it so the red selection pass always disables back-face culling.

I was going to disable the depth-test, but felt it was too messy to interleave the free vertices between the two passes, which also do the marking...

CORRECTION: I ended up disabling the depth-test after all, and did not interleave anyway. The problem came down to my Intel drivers don't really support LINE and POINT modes with glPolygonOffset. I realized lines only worked via FILL mode. And not only that, the offseted lines degraded the alpha-effect that is the subject of this topic. So basically disabling the depth-test was the only way to get the red lines/vertices to display clearly, without sacrificing the nice effect. It fails to look perfect if the depths produced by lines disagree. Anyway, I think this is the simplest solution. Messing the offset is too device sensitive, and is hard to set up.

I don't know if this means anything to you Zack, but I think it's interesting to others perhaps. I'm not ready to make a separate GitHub project for my work yet. I think it would be better either way to keep the discussion in one place. On or the other. MM3D could become more attractive if it gains serious attention. I mean, like people willing to roll up their sleeves for it. That's only possible if the code sees a real turnaround first.

m-7761 commented 4 years ago

Sorry, I've posted a final correction. (I ended up at the no depth-test solution for red lines/points after all.)

Oh, here's a screenshot, as long as I'm posting (again and again) that shows alpha edges, and less cluttered/confusing wireframe with back-faces out of sight. Except for the selected back face, that shows through. The red tint helps with that, but it's not always present when the lighting is saturated. The nipples on the vertices are a quirk of my drivers. They are kind of interesting looking.

Widgets 95 MM3D 3