 مساء الخير يا مستخدمي هبرة. لم أكتب منذ فترة طويلة وربما كان شخص ما ينتظر مقالات مني - بالطبع لا. منذ أن أصبح وقت
مساء الخير يا مستخدمي هبرة. لم أكتب منذ فترة طويلة وربما كان شخص ما ينتظر مقالات مني - بالطبع لا. منذ أن أصبح وقت فراغي أكثر قليلاً ، وكان GitHub فارغًا تمامًا ، قررت كتابة استنساخ Mein kampf Minecraft. مع احتمالية عالية ، سأوثق هذا - اتبع مقالاتي على habr.com . اليوم سأوضح كيف قمت بتغليف بدائل OpenGL بأسلوب RAII ، إذا كانت مثيرة للاهتمام - تحت القط.اكتب في التعليقات ما تريد أن تقرأ عنه. هل يجب أن أكتب ماين كرافت من المقالات المؤقتة؟ الآن دعنا نبدأ. سأعرض كائن المخزن كمثال . أول شيء سنفعله هو إجراء المكالمات الرئيسية ( glGenBuffers ، glDeleteBuffers ) في فصل منفصل.في مكان ما مثل هذا:class buffer_object
{
public:
    buffer_object() noexcept {
        glGenBuffers(1, m_object);
    }
    
    ~buffer_object() noexcept {
        glDeleteBuffers(1, m_object);
    }
    
    uint32_t get_object() const noexcept {
        return m_object;
    }
private:
    uint32_t m_object;
};
الإيجابيات:- لا حاجة لرصد حذف المخزن المؤقت
- لا يتم تمكين رأس OpenGL (إذا قمنا بنقل تعريف الوظائف إلى * .cpp)
السلبيات:- لا يمكننا تحديد نوع المخزن المؤقت (الصفيف ، العنصر ...) عند الربط
- لا يمكن إنشاء العديد من المخازن المؤقتة باستدعاء واحد لـ glGenBuffers
- مشاكل في الوصول إلى كائن بعيد إذا تم نسخ المخزن المؤقت
لحل مشكلة النسخ ، نحن ببساطة نحظرها. للقيام بذلك ، قم بإنشاء فئة غير قابلة للنسخ ورثها.مثال على فئة غير قابلة للنسخ:struct noncopyable
{
    noncopyable() = default;
            
    noncopyable(noncopyable&&) = default;
    noncopyable& operator = (noncopyable&&) = default;
            
    noncopyable(const noncopyable&) = delete;
    noncopyable& operator = (const noncopyable&) = delete;
};
عظيم ، ناقص مشكلة واحدة. نفكر أكثر ، كيف يمكننا حل المشكلة بالربط ... إضافة وظائف الربط / عدم الربط:void bind(buffer_type type) noexcept {
    glBindBuffer(static_cast<GLenum>(type), m_object);
}
    
void unbind() noexcept {
    glBindBuffer(static_cast<GLenum>(type), 0);
}
buffer_type هو فئة تعداد ، ولكن يمكنك عمل شرطات خارجية (في * .hpp) ، وفي (* .cpp) تفعل شيئًا مثل:const uint32_t array_buffer = GL_ARRAY_BUFFER;
مرة أخرى ، هذا لن يبرز#include <GL/glew.h>
الإيجابيات:- لا حاجة لرصد حذف المخزن المؤقت
- لا يتم تمكين رأس OpenGL (إذا قمنا بنقل تعريف الوظائف إلى * .cpp)
- يمكننا الإشارة إلى نوع المخزن المؤقت (الصفيف ، العنصر ...) عند الربط
- لا نسخ - لا توجد مشاكل في النسخ
السلبيات:- لا يمكن إنشاء العديد من المخازن المؤقتة باستدعاء واحد لـ glGenBuffers
لذلك ، كل شيء تقريبًا رائع ، ولكن لا يزال يتعذر علينا إنشاء العديد من المخازن المؤقتة ... لنصلحها.يمكن أن يأخذ glGenBuffers مؤشر إلى صفيف وعدد المخازن المؤقتة المراد إنشاؤها.نحن بحاجة إلى مصفوفة ، يمكننا استخدام std :: vector ، لكننا بحاجة فقط إلى تخصيص الذاكرة مرة واحدة فقط ، وأنا أفضل std :: array هنا ، على الرغم من أنه سيتعين علينا في المستقبل القيام بمستوى واحد آخر من التجريد بسبب هذا.نعيد كتابة صفنا إلى std :: array ونضيف القليل من القوالب:constexpr uint8_t default_object_count = 1;
constexpr size_t  default_index        = 0;
template <type::buffer_type type, uint32_t count = default_object_count>
class buffer_object : noncopyable
{
public:
    buffer_object() noexcept {
        glGenBuffers(count, m_object.data());
    }
    
    ~buffer_object() noexcept {
        glDeleteBuffers(count, m_object.data());
    }
    
    template <size_t index = default_index>
    void bind() noexcept
    {
        static_assert(index < count, "index larger than array size");
        glBindBuffer(static_cast<GLenum>(type), m_object[index]);
    }
    
    void unbind() noexcept {
        glBindBuffer(static_cast<GLenum>(type), 0);
    }
    
    buffer_object(buffer_object&&) = default;
    buffer_object& operator = (buffer_object&&) = default;
    
private:
    std::array<uint32_t, count> m_object;
};
وهذا ما حصلنا عليه.الإيجابيات:- لا حاجة لرصد حذف المخزن المؤقت
- يمكننا الإشارة إلى نوع المخزن المؤقت (الصفيف ، العنصر ...) عند الربط
- لا نسخ - لا توجد مشاكل في النسخ
- يمكننا إنشاء العديد من المخازن المؤقتة باستدعاء واحد لـ glGenBuffers
السلبيات:- لا يمكن ربط مخازن مختلفة على نفس الكائن
- تمكين رأس OpenGL يبرز
حسنا ... الكثير من السلبيات ، ما الذي يمكن عمله؟يمكنك إزالة إدراج GL / glew.h عن طريق إضافة مستوى تجريد آخر سيتم فيه استدعاء وظائف OpenGL (على أي حال ، يجب القيام بذلك إذا كان دعم OpenGL + DirectX مخططًا). يعد الربط مع المخازن المؤقتة المختلفة أكثر تعقيدًا بعض الشيء ، حيث يمكننا أن ننسى الفهرس الذي تم حظر المخزن المؤقت به ، كخيار ، أضف صفيفًا آخر وكتابة نوع المخزن المؤقت إليه. لم أفعل ذلك بعد ؛ في هذه المرحلة من التطور ، هذا يكفي بالنسبة لي.علاوة
من أجل استخدام أكثر ملاءمة ، قمت بعمل فصل ربط نطاق. ها هو:constexpr uint32_t default_bind_index = 0;
template <class T>
class binder : utils::noncopyable
{
public:
      template <uint32_t index = default_bind_index>
      binder(T& ref) : m_ref(ref) { m_ref.template bind<index>(); }
    ~binder()                            { m_ref.unbind();}
        
private:
    T& m_ref;
};
قد يكون البعض في حيرة من هذا الخط:m_ref.template bind<index>();
إذا لم نحدد الكلمة الرئيسية للقالب ، فإننا نتلقى الخطأ التالي:binder.hpp: 22: 46: كلمة رئيسية "قالب" مفقودة قبل اسم القالب "ربط"وهذا يعني أنه في هذه الحالة ، عندما تكون T نوعًا تابعًا. لا يعرف المترجم حتى الآن ما هو نوع m_ref. نظرًا لعدم وجود ربط حتى الآن ، يقوم المترجم بمعالجته بشكل نحوي بحت ، لذلك ، يتم تفسير <على أنه عامل تشغيل أقل من. للإشارة إلى المترجم أن هذا ، في الواقع ، تخصص لقالب وظيفة عضو ، يجب عليك إضافة الكلمة الأساسية للقالب مباشرة بعد عامل النقطة.ومثال على الاستخدام:GLuint VAO;
    
primitive::buffer_object<type::buffer_type::array> vbo;
primitive::buffer_object<type::buffer_type::element> ebo;
    
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
    
{
    primitive::binder bind{vbo};
        
    glBufferData(GL_ARRAY_BUFFER, ...);
        
    glVertexAttribPointer(...);
    glEnableVertexAttribArray(...);
        
    glVertexAttribPointer(...);
    glEnableVertexAttribArray(...);
}
    
{
    primitive::binder bind{ebo};
        
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, ...);
        
    glBindVertexArray(0);
}
لا يستخدم هذا الرمز الكائن في Vertex Array ، لأنه لا يعمل ، لم أحسب الأسباب بعد ، لكنني سأكتشف ذلك قريبًا xD إنأغلفة بيانات التخزين المؤقت ومكالمات OpenGL ليست جاهزة بعد ، ولا تتم إعادة كتابة المكالمات إلى DSA (الوصول المباشر للدولة) .شكرا لأولئك الذين قرأوها حتى النهاية. سأكون سعيدًا جدًا بالنقد والتعليقات.