VPN termux about China.net

3d sdl2 webasembly

 class.cpp

 #include <SDL.h>

#include <SDL_image.h>

#include <GLES2/gl2.h>

#include <emscripten.h>

#include <assimp/Importer.hpp>

#include <assimp/scene.h>

#include <assimp/postprocess.h>


#include <vector>

#include <string>

#include <cmath>

#include <iostream>


// --- Globalne zmienne ---

SDL_Window* window = nullptr;

SDL_GLContext glContext = nullptr;

GLuint program = 0;

float rotX = 0, rotY = 0;

bool mouseDown = false;

int lastX, lastY;


// --- Shadery ---

const char* vs = R"(

attribute vec3 aPos;

attribute vec3 aNormal;

attribute vec2 aUV;


varying vec3 vNormal;

varying vec2 vUV;


uniform float rotX, rotY;


void main(){

    float cx = cos(rotX), sx = sin(rotX);

    float cy = cos(rotY), sy = sin(rotY);

    mat3 Rx = mat3(1, 0, 0, 0, cx, -sx, 0, sx, cx);

    mat3 Ry = mat3(cy, 0, sy, 0, 1, 0, -sy, 0, cy);

    vec3 p = Ry * Rx * aPos;

    gl_Position = vec4(p * 0.1, 1.0);

    

    vNormal = normalize(Ry * Rx * aNormal);

    vUV = aUV;

}

)";


const char* fs = R"(

precision mediump float;


uniform sampler2D tex;

varying vec2 vUV;

varying vec3 vNormal;


void main() {

    vec3 texColor = texture2D(tex, vUV).rgb;


    // Światło – niech podkreśla teksturę

    vec3 lightDir = normalize(vec3(0.5, 1.0, 0.3));

    float diff = max(dot(normalize(vNormal), lightDir), 0.0);


    // Tekstura z lekkim światłem – nie za jasno, żeby model był przyciemniony

    vec3 color = texColor * (0.4 + 0.6 * diff); // 0.4 bazowe + światło


    gl_FragColor = vec4(color, 1.0);

}


)";


// --- Deklaracje klas ---

struct Material {

    GLuint diffuse = 0;

    GLuint specular = 0;

    GLuint normal = 0;

    GLuint emissive = 0;


    void bind(GLuint program) const;

    void cleanup();

};


class Mesh {

public:

    GLuint vbo = 0, ibo = 0;

    size_t indexCount = 0;

    Material material;


    void render(GLuint program);

    void cleanup();

};


class Model {

public:

    std::vector<Mesh> meshes;


    Model() = default;

    void load(const std::string& modelPath, const std::string& textureDir);

    void render(GLuint program, float rotX, float rotY);

    void cleanup();

};


// --- Implementacja klas ---

void Material::bind(GLuint program) const {

    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2D, diffuse);

    glUniform1i(glGetUniformLocation(program, "tex"), 0);


    glActiveTexture(GL_TEXTURE1);

    glBindTexture(GL_TEXTURE_2D, specular);

    glUniform1i(glGetUniformLocation(program, "specularMap"), 1);


    glActiveTexture(GL_TEXTURE2);

    glBindTexture(GL_TEXTURE_2D, normal);

    glUniform1i(glGetUniformLocation(program, "normalMap"), 2);


    glActiveTexture(GL_TEXTURE3);

    glBindTexture(GL_TEXTURE_2D, emissive);

    glUniform1i(glGetUniformLocation(program, "emissiveMap"), 3);

}


void Material::cleanup() {

    if (diffuse) glDeleteTextures(1, &diffuse);

    if (specular) glDeleteTextures(1, &specular);

    if (normal) glDeleteTextures(1, &normal);

    if (emissive) glDeleteTextures(1, &emissive);

}


void Mesh::render(GLuint program) {

    material.bind(program);


    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);


    GLint posLoc = glGetAttribLocation(program, "aPos");

    GLint normalLoc = glGetAttribLocation(program, "aNormal");

    GLint uvLoc = glGetAttribLocation(program, "aUV");


    glEnableVertexAttribArray(posLoc);

    glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)0);


    glEnableVertexAttribArray(normalLoc);

    glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 3));


    glEnableVertexAttribArray(uvLoc);

    glVertexAttribPointer(uvLoc, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void*)(sizeof(float) * 6));


    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (void*)0);

}


void Mesh::cleanup() {

    glDeleteBuffers(1, &vbo);

    glDeleteBuffers(1, &ibo);

    material.cleanup();

}


// --- Funkcje pomocnicze do ładowania zasobów ---

GLuint compileShader(GLenum type, const char* source) {

    GLuint shader = glCreateShader(type);

    glShaderSource(shader, 1, &source, nullptr);

    glCompileShader(shader);


    GLint success;

    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

    if (!success) {

        char infoLog[512];

        glGetShaderInfoLog(shader, 512, nullptr, infoLog);

        std::cerr << "Shader compilation error: " << infoLog << "\n";

    }

    return shader;

}

void printAllMaterialTextures(aiMaterial* material) {

    std::vector<std::pair<aiTextureType, const char*>> textureTypes = {

        {aiTextureType_DIFFUSE, "DIFFUSE"},

        {aiTextureType_SPECULAR, "SPECULAR"},

        {aiTextureType_NORMALS, "NORMALS"},

        {aiTextureType_HEIGHT, "HEIGHT"},

        {aiTextureType_EMISSIVE, "EMISSIVE"},

        {aiTextureType_OPACITY, "OPACITY"},

        {aiTextureType_AMBIENT, "AMBIENT"}

    };


    for (const std::pair<aiTextureType, const char*>& pair : textureTypes) {

        aiTextureType type = pair.first;

        const char* name = pair.second;


        int count = material->GetTextureCount(type);

        std::cout << " - " << name << ": " << count << " tekstur\n";


        for (int i = 0; i < count; ++i) {

            aiString path;

            if (material->GetTexture(type, i, &path) == AI_SUCCESS) {

                std::cout << "    -> " << path.C_Str() << "\n";

            }

        }

    }

}

void PrintAnimationsAndBones(const aiScene* scene) {

    std::cout << "Animacje w scenie: " << scene->mNumAnimations << std::endl;

    for (unsigned int i = 0; i < scene->mNumAnimations; i++) {

        std::cout << "Animacja " << i << ": " << scene->mAnimations[i]->mName.C_Str() << std::endl;

    }

    std::cout << "Kości w scenie: " << std::endl;

    for (unsigned int m = 0; m < scene->mNumMeshes; m++) {

        for (unsigned int b = 0; b < scene->mMeshes[m]->mNumBones; b++) {

            std::cout << " - Kość: " << scene->mMeshes[m]->mBones[b]->mName.C_Str() << std::endl;

        }

    }

}


GLuint loadTextureFromMaterial(aiMaterial* mat, aiTextureType type, const std::string& directory) {

    if (mat->GetTextureCount(type) > 0) {

        aiString path;

        mat->GetTexture(type, 0, &path);

        //std::string fullPath = directory + "/Hair.png"; // Wczytujemy "na sztywno"

        

        //std::cout << "Proba zaladowania tekstury: " << fullPath << "\n";


        

    std::string fullPath = directory + "/" + std::string(path.C_Str());

        std::cout << "Proba zaladowania tekstury: " << fullPath << "\n";


        SDL_Surface* surface = IMG_Load(fullPath.c_str());

        if (!surface) {

            std::cerr << "Nie udalo sie zaladowac tekstury: " << fullPath << ", SDL_image Error: " << IMG_GetError() << "\n";

            return 0;

        }

        std::cout << "Tekstura zaladowana: " << fullPath << "\n";


        GLuint texID;

        glGenTextures(1, &texID);

        glBindTexture(GL_TEXTURE_2D, texID);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


        GLenum format = (surface->format->BytesPerPixel == 4) ? GL_RGBA : GL_RGB;

        glTexImage2D(GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, surface->pixels);

        //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surface->w, surface->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);

    glGenerateMipmap(GL_TEXTURE_2D);

        

       // glGenerateMipmap(GL_TEXTURE_2D);


        SDL_FreeSurface(surface);

        return texID;

    }

    return 0;

}


// W funkcji loadMaterial

Material loadMaterial(const aiScene* scene, const aiMesh* mesh, const std::string& directory) {

    Material mat;

    if (!scene->HasMaterials()) {

        std::cerr << "Brak materialow w scenie.\n";

        return mat;

    }


    aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

printAllMaterialTextures(material);

    PrintAnimationsAndBones(scene);


/*

    std::cout << "Assimp znalazl material dla mesha o indeksie: " << mesh->mMaterialIndex << std::endl;


    Wypisuj liczbę tekstur dla wszystkich głównych typów

    std::cout << "  - Tekstur dyfuzyjnych (DIFFUSE): " << material->GetTextureCount(aiTextureType_DIFFUSE) << std::endl;

    std::cout << "  - Tekstur normalnych (NORMALS): " << material->GetTextureCount(aiTextureType_NORMALS) << std::endl;

    std::cout << "  - Tekstur spekularnych (SPECULAR): " << material->GetTextureCount(aiTextureType_SPECULAR) << std::endl;

    std::cout << "  - Tekstur emisyjnych (EMISSIVE): " << material->GetTextureCount(aiTextureType_EMISSIVE) << std::endl;

*/

    // Tutaj nadal próbujemy załadować tylko dyfuzyjną

    mat.diffuse = loadTextureFromMaterial(material, aiTextureType_DIFFUSE, directory);

    // ... (reszta kodu ladowania materialu)


    return mat;

}

// --- Model (implementacja metod) ---

void Model::load(const std::string& path, const std::string& textureDir) {

    Assimp::Importer importer;

    const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals | aiProcess_CalcTangentSpace);


    if (!scene || !scene->HasMeshes()) {

        std::cerr << "Nie udalo sie zaladowac modelu: " << importer.GetErrorString() << "\n";

        return;

    }


    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {

        const aiMesh* mesh = scene->mMeshes[i];

        Mesh newMesh;


        std::vector<float> vertices;

        std::vector<unsigned int> indices;


        for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {

            vertices.push_back(mesh->mVertices[j].x);

            vertices.push_back(mesh->mVertices[j].y);

            vertices.push_back(mesh->mVertices[j].z);


            if (mesh->HasNormals()) {

                vertices.push_back(mesh->mNormals[j].x);

                vertices.push_back(mesh->mNormals[j].y);

                vertices.push_back(mesh->mNormals[j].z);

            } else {

                vertices.insert(vertices.end(), {0.0f, 0.0f, 0.0f});

            }


            if (mesh->HasTextureCoords(0)) {

                vertices.push_back(mesh->mTextureCoords[0][j].x);

                vertices.push_back(mesh->mTextureCoords[0][j].y);

            } else {

                vertices.insert(vertices.end(), {0.0f, 0.0f});

            }

        }


        for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {

            const aiFace& face = mesh->mFaces[j];

            for (unsigned int k = 0; k < face.mNumIndices; ++k) {

                indices.push_back(face.mIndices[k]);

            }

        }


        glGenBuffers(1, &newMesh.vbo);

        glBindBuffer(GL_ARRAY_BUFFER, newMesh.vbo);

        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);


        glGenBuffers(1, &newMesh.ibo);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, newMesh.ibo);

        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);


        newMesh.indexCount = indices.size();

        newMesh.material = loadMaterial(scene, mesh, textureDir);

        meshes.push_back(newMesh);

    }

}


void Model::render(GLuint program, float rotX, float rotY) {

    glUseProgram(program);


    GLint rotXLoc = glGetUniformLocation(program, "rotX");

    GLint rotYLoc = glGetUniformLocation(program, "rotY");

    glUniform1f(rotXLoc, rotX);

    glUniform1f(rotYLoc, rotY);


    for (auto& mesh : meshes) {

        mesh.render(program);

    }

}


void Model::cleanup() {

    for (auto& mesh : meshes) {

        mesh.cleanup();

    }

    meshes.clear();

}


// Deklaracja globalnego obiektu modelu

Model harpyModel;


// --- Funkcje główne programu ---

bool init() {

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {

        std::cerr << "SDL_Init Error: " << SDL_GetError() << "\n";

        return false;

    }

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);


    window = SDL_CreateWindow("Model Loader", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL);

    if (!window) {

        std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << "\n";

        return false;

    }

    glContext = SDL_GL_CreateContext(window);

    if (!glContext) {

        std::cerr << "SDL_GL_CreateContext Error: " << SDL_GetError() << "\n";

        return false;

    }


    glViewport(0, 0, 640, 480);

    glClearColor(0.2f, 0.9f, 0.2f, 1.0f);

    glEnable(GL_DEPTH_TEST);


    int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;

    if (!(IMG_Init(imgFlags) & imgFlags)) {

        std::cerr << "SDL_image Error: " << IMG_GetError() << "\n";

        return false;

    }


    GLuint vsId = compileShader(GL_VERTEX_SHADER, vs);

    GLuint fsId = compileShader(GL_FRAGMENT_SHADER, fs);

    program = glCreateProgram();

    glAttachShader(program, vsId);

    glAttachShader(program, fsId);

    glLinkProgram(program);


    return true;

}


void cleanup() {

    harpyModel.cleanup();

    SDL_GL_DeleteContext(glContext);

    SDL_DestroyWindow(window);

    SDL_Quit();

    IMG_Quit();

}


// Funkcja renderująca, która zostanie wywołana w pętli głównej

void render() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    harpyModel.render(program, rotX, rotY);

    SDL_GL_SwapWindow(window);

}


// Główna pętla, która będzie wywoływana przez Emscripten

void main_loop() {

    SDL_Event e;

    while (SDL_PollEvent(&e)) {

        if (e.type == SDL_QUIT) {

            emscripten_cancel_main_loop();

        } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) {

            mouseDown = true;

            lastX = e.button.x;

            lastY = e.button.y;

        } else if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT) {

            mouseDown = false;

        } else if (e.type == SDL_MOUSEMOTION && mouseDown) {

            rotY += (e.motion.x - lastX) * 0.01f;

            rotX += (e.motion.y - lastY) * 0.01f;

            lastX = e.motion.x;

            lastY = e.motion.y;

        }

    }

    render();

}


int main() {

    if (!init()) {

        std::cerr << "Inicjalizacja nie powiodla sie.\n";

        return 1;

    }

std::cout << "Ladowanie modelu..." << std::endl;

harpyModel.load("asserts/Harpy.fbx", "asserts");

std::cout << "Model zaladowany. Liczba meshy: " << harpyModel.meshes.size() << std::endl;


    //harpyModel.load("assets/Harpy.fbx", "assets");

    

    emscripten_set_main_loop(main_loop, 0, 1);

    

    cleanup();


    return 0;

}

main.yml

name: Build and Deply SDL2 OpenGL Emscripten


on:

  push:

    branches:

      - main


jobs:

  build-and-deploy:

    runs-on: ubuntu-latest


    steps:

      - uses: actions/checkout@v4


      - name: Setup Emscripten SDK

        run: |

          git clone https://github.com/emscripten-core/emsdk.git

          cd emsdk

          ./emsdk install 3.1.65

          ./emsdk activate 3.1.65

        shell: bash

 

      - name: Build Assimp (Emscripten)

        run: |

          source ./emsdk/emsdk_env.sh

          git clone https://github.com/assimp/assimp.git

          cd assimp

          emcmake cmake \

            -DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF \

            -DASSIMP_BUILD_OBJ_IMPORTER=ON \

            -DASSIMP_BUILD_OBJ_IMPORTER=ON \

            -DASSIMP_BUILD_FBX_IMPORTER=ON \

            -DASSIMP_BUILD_GLTF_IMPORTER=OFF \

            -DASSIMP_BUILD_SHARED_LIBS=OFF \

            -DASSIMP_NO_EXPORT=ON \

            -DASSIMP_BUILD_ZLIB=ON \

            -DASSIMP_BUILD_TESTS=OFF \

            -DCMAKE_BUILD_TYPE=Release \

            -DCMAKE_TOOLCHAIN_FILE=../emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \

            -DCMAKE_CROSSCOMPILING_EMULATOR=../emsdk/node/22.16.0_64bit/bin/node \

            .

          emmake make -j

        shell: bash


      - name: Check Assimp build output

        run: |

          ls -l assimp

          ls -l assimp/lib

          ls -l assimp/lib

        shell: bash

  

      - name: Compile C++ to WebAssembly with Assimp

        run: |

          source ./emsdk/emsdk_env.sh

          mkdir -p dist

          em++ cass.cpp \

            -Iassimp/include \

            -Iassimp/code \

            -Lassimp/lib \

            -lassimp \

            -s WASM=1 \

            -s USE_SDL=2 \

            -s USE_ZLIB=1 \

            -s USE_SDL_IMAGE=2 \

            -s SDL2_IMAGE_FORMATS='["png"]' \

            -s FULL_ES2=1 \

            -s MIN_WEBGL_VERSION=1 \

            -s MAX_WEBGL_VERSION=1 \

            --preload-file asserts \

            -s ALLOW_MEMORY_GROWTH=1 \

            -s ASYNCIFY \

            -o dist/index.html

        shell: bash

      - name: Deploy to GitHub Pages

        uses: peaceiris/actions-gh-pages@v4

        if: github.ref == 'refs/heads/main'

        with:

    g      github_token: ${{ secrets.GITHUB_TOKEN }}

          publish_dir: ./dist

No comments:

Post a Comment