yunusemregul / bsp_viewer

Source Engine BSP file viewer based on OpenGL
GNU General Public License v3.0
8 stars 0 forks source link

Face displacement algorithm #1

Open ImanolFotia opened 1 year ago

ImanolFotia commented 1 year ago

Hello there!
I'm doing my own Source BSP renderer as well and in researching the face displacement algorithm I found your repo, and saw that you were having problems with it too.
I just spent 2 days reverse engineering the format and found the solution.
I don't have much time to open a PR so I'll leave the code here:

void generatePatch(const std::vector<glm::vec3>& inVertices, const std::vector<unsigned int>& inIndices, std::vector<glm::vec3>& outVertices, std::vector<glm::vec2>& uvs, std::vector<unsigned int>& indices, ddispinfo_t dispInfo, float tv[2][4], dface_t face) {

        auto lerp = [](glm::vec3 a, glm::vec3 b, float t) -> glm::vec3 {
            return a + t * (b - a);
        };

        std::vector<glm::vec3> v = { inVertices[0], inVertices[1], inVertices[2], inVertices[3] };

        //check which corner has the minimum value, to use it as starting point
        float startDist = 10000000000000; //big number, nothing special
        int startIndex = -1;
        for (int j = 0; j < 4; j++) {
            const float dist = glm::distance(v[j], dispInfo.startPosition);
            if (dist < startDist) {
                startIndex = j;
                startDist = dist;
            }
        }

        //rotate the vector to put the lowest point first
        if (startIndex != 0)
            std::rotate(v.begin(), v.begin() + startIndex, v.end());

        int power = dispInfo.power;
        int numVerts = glm::pow((glm::pow(2, power) + 1), 2);
        int length = (1 << power) + 1;

        outVertices.resize(numVerts);

        glm::vec3 v0{};
        glm::vec3 v1{};

        for (int i = 0; i < length; i++) {
            const float ty = (float)i / (float)(length - 1);
            v0 = lerp(v[0], v[1], ty);
            v1 = lerp(v[3], v[2], ty);

            for (int j = 0; j < length; j++) {
                int index = dispInfo.DispVertStart + (i * length) + j;

                auto& dispVertex = m_pBspFileData.vertDisps[index];

                const float tx = (float)j / (float)(length - 1.0f);
                glm::vec3 vtx = lerp(v0, v1, tx);

                                //Generate texcoords
                float u = (tv[0][0] * vtx.x) + (tv[0][1] * vtx.y) + (tv[0][2] * vtx.z) + tv[0][3];
                float v = (tv[1][0] * vtx.x) + (tv[1][1] * vtx.y) + (tv[1][2] * vtx.z) + tv[1][3];
                uvs.push_back(glm::vec2(u, v));

                //displace
                vtx += dispVertex.vec * dispVertex.dist;

                outVertices[i * length + j] = vtx;
            }
        }

        //Triangulate
        int edge = 0;
        for (int i = 0; i < numVerts - length; i++) {
            if (edge == length - 1) {
                edge = 0;
                continue;
            }

            indices.push_back(i + 1);
            indices.push_back(i);
            indices.push_back(i + length);

            indices.push_back(i + 1);
            indices.push_back(i + length);
            indices.push_back(i + length + 1);

            edge++;
        }
    }

Anyway, I hope you find this useful
Good luck!

yunusemregul commented 1 year ago

I am glad for your help, thanks a lot!