make instanced render with this code, i use opengl 4.5. Do minimal changes: void OptimizedMeshRenderer::initialize(std::unordered_map<uint16_t, Mesh>& origmeshes,
    std::vector<MeshInstance>& _instances) 
{
    instances = std::move(_instances);
    originalMeshes.insert(origmeshes.begin(), origmeshes.end());
    for (const auto& [modelId, mesh] : origmeshes) {
        const float* verts = mesh.getVerts();
        const int* tris = mesh.getTris();
        int ntris = mesh.getTriCount();

        std::vector<float> vertexData;
        vertexData.reserve(ntris * 3 * 6); // 6 floats per vertex (3 for position, 3 for normal)

        for (int i = 0; i < ntris; ++i) {
            float norm[3];
            mesh.calculateNormal(i, norm);

            for (int j = 0; j < 3; ++j) {
                const float* v = &verts[tris[i * 3 + j] * 3];
                // Position
                vertexData.insert(vertexData.end(), { v[0], v[1], v[2] });
                // Normal
                vertexData.insert(vertexData.end(), { norm[0], norm[1], norm[2] });
            }
        }

        MeshData meshData;
        meshData.vertexCount = ntris * 3;
        if (classifyMesh(&mesh) == MeshCategory::Road) {
            meshData.color = glm::vec4(0.0f, 0.8f, 0.4f, 1.0f);
        }
        else if (classifyMesh(&mesh) == MeshCategory::Building) {
            meshData.color = glm::vec4(0.2f, 0.6f, 0.4f, 1.0f);
        } else meshData.color = glm::vec4(0.0f, 0.8f, 0.4f, 1.0f);

        glGenVertexArrays(1, &meshData.vao);
        glGenBuffers(1, &meshData.vbo);

        glBindVertexArray(meshData.vao);
        glBindBuffer(GL_ARRAY_BUFFER, meshData.vbo);
        glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);

        // Position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        // Normal attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);

        meshes[modelId] = meshData;
    }
    // Vertex Shader
    const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 mvpMatrix;
uniform mat4 modelMatrix;

void main() {
    FragPos = vec3(modelMatrix * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(modelMatrix))) * aNormal;
    gl_Position = mvpMatrix * vec4(aPos, 1.0);
}
)";

    // Fragment Shader
    const char* fragmentShaderSource = R"(
#version 330 core
in vec3 FragPos;
in vec3 Normal;

out vec4 FragColor;

uniform vec3 viewPos;
uniform vec3 lightDir;
uniform float ambientStrength;
uniform float diffuseStrength;
uniform float rimStrength;
uniform vec4 meshColor;
uniform bool isSelected;

void main()
{
    vec3 normalizedLightDir = normalize(lightDir);
    vec3 normalizedNormal = normalize(Normal);
    vec3 viewDir = normalize(viewPos - FragPos);
    
    // Ambient
    vec3 ambient = ambientStrength * meshColor.rgb;
    
    // Diffuse
    float diff = max(dot(normalizedNormal, normalizedLightDir), 0.0);
    vec3 diffuse = diff * diffuseStrength * meshColor.rgb;
    
    // Rim lighting
    float rim = 1.0 - max(dot(viewDir, normalizedNormal), 0.0);
    rim = smoothstep(0.6, 1.0, rim);
    vec3 rimLight = rimStrength * rim * meshColor.rgb;
    
    // Combine lighting
    vec3 result = ambient + diffuse + rimLight;
    
    // Subtle two-sided lighting
    result += max(-dot(normalizedNormal, normalizedLightDir), 0.0) * diffuseStrength * 0.2 * meshColor.rgb;
    
    if (isSelected) {
        result = mix(result, vec3(1.0, 0.0, 0.0), 0.3); // Subtle red tint for selected objects
    }
    
    FragColor = vec4(result, meshColor.a);
}
)";
    shader = new Shader(vertexShaderSource, fragmentShaderSource);
}

void OptimizedMeshRenderer::render(const glm::mat4& viewProjectionMatrix, const glm::vec3& cameraPosition) {
    shader->use();
    shader->setVec3("lightDir", glm::vec3(1.0f, 1.0f, 1.0f));
    shader->setFloat("ambientStrength", 0.3f);
    shader->setFloat("diffuseStrength", 0.7f);
    shader->setVec3("viewPos", cameraPosition);
    shader->setFloat("rimStrength", 0.3f);      // Subtle rim lighting
    shader->setVec3("viewPos", cameraPosition);
    for (const auto& instance : instances) {
        const auto& meshIt = meshes.find(instance.modelId);
        if (meshIt == meshes.end()) continue;

        const auto& meshData = meshIt->second;

        // Calculate model matrix from instance transform
        glm::mat4 modelMatrix = glm::mat4(1.0f);
        modelMatrix = glm::translate(modelMatrix, glm::vec3(instance.transform.Position.x, instance.transform.Position.y, instance.transform.Position.z));

        // Convert quaternion to rotation matrix
        glm::quat rotation(instance.transform.Rotation.w, instance.transform.Rotation.x, instance.transform.Rotation.y, instance.transform.Rotation.z);
        modelMatrix *= glm::mat4_cast(rotation);

        // Calculate MVP matrix
        glm::mat4 mvpMatrix = viewProjectionMatrix * modelMatrix;

        shader->setMat4("mvpMatrix", mvpMatrix);
        shader->setMat4("modelMatrix", modelMatrix);  // New: Set model matrix
        shader->setVec4("meshColor", meshData.color);
        shader->setBool("isSelected", instance.uniqueId == selectedMesh);

        glBindVertexArray(meshData.vao);
        glDrawArrays(GL_TRIANGLES, 0, meshData.vertexCount);
    }
    glBindVertexArray(0);
}

OptimizedMeshRenderer::~OptimizedMeshRenderer() {
    for (const auto& [meshId, meshData] : meshes) {
        glDeleteVertexArrays(1, &meshData.vao);
        glDeleteBuffers(1, &meshData.vbo);
    }
}