/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// // GLES2 device local context (Emscripten) #include "OdaCommon.h" #include "TrGL2LocalContext.h" #include "GLES2Include.h" #include #include #include #include #include #include using namespace emscripten; class OdGLES2LocalContextImpl : public OdTrGL2LocalContext { protected: int m_WebGLContext; static int m_usedCount; bool m_bContextCreated; bool m_isDirectionCanvas; std::string m_directionCanvasId; bool m_hasDirectBuffer; private: char* createRenderCanvas() { return (char*) EM_ASM_INT({ if (! Module['canvas']) { console.error('Canvas is not set to module, please use module.canvas = document.querySelector(\'#canvas\');'); return 0; } var target = '#canvas'; if (Module['canvas']['id']) { target = '#' + Module['canvas']['id']; } var lengthBytes = lengthBytesUTF8(target) + 1; var stringOnWasmHeap = _malloc(lengthBytes); stringToUTF8(target, stringOnWasmHeap, lengthBytes); return stringOnWasmHeap; }, 0); } char* createDirectRenderCanvas(OdTrVisRenderClient *pDevice) { OdUInt32 pixelWidth = 0, pixelHeight = 0; pDevice->getDirectRenderBuffer(&pixelWidth, &pixelHeight); char *pTarget = (char*) EM_ASM_INT({ var ensureHiddenCanvas = function(w, h, suffix) { var id = 'oda_direct_canvas_' + suffix; var c = document.getElementById(id); if (!c) { c = document.createElement('canvas'); c.id = id; c.style.display = 'none'; c.width = w; c.height = h; document.body.appendChild(c); } return '#' + id; }; var target = ensureHiddenCanvas($0, $1, $2); var lengthBytes = lengthBytesUTF8(target) + 1; var stringOnWasmHeap = _malloc(lengthBytes); stringToUTF8(target, stringOnWasmHeap, lengthBytes); return stringOnWasmHeap; }, (int)pixelWidth, (int)pixelHeight, m_usedCount); m_isDirectionCanvas = true; if (pTarget) { std::string sel = std::string(pTarget); if (!sel.empty() && sel[0] == '#') m_directionCanvasId = sel.substr(1); } return pTarget; } public: OdGLES2LocalContextImpl() : OdTrGL2LocalContext() , m_WebGLContext(-1) , m_bContextCreated(false) , m_isDirectionCanvas(false) , m_directionCanvasId() , m_hasDirectBuffer(false) { } ~OdGLES2LocalContextImpl() { -- m_usedCount; destroyContext(); } void createContext(OdTrVisRenderClient *pDevice) { if (pDevice->hasProperty(OD_T("CreateContext")) && !pDevice->getProperty(OD_T("CreateContext"))->getBool()) { m_bContextCreated = true; return; } m_hasDirectBuffer = pDevice->hasDirectRenderBuffer(); char* pTarget = m_hasDirectBuffer ? createDirectRenderCanvas(pDevice) : createRenderCanvas(); if (!pTarget) { m_bContextCreated = false; return; } EmscriptenWebGLContextAttributes attrs; emscripten_webgl_init_context_attributes(&attrs); attrs.alpha = false; attrs.depth = true; attrs.stencil = true; attrs.antialias = false; //attrs.preserveDrawingBuffer = true; m_WebGLContext = emscripten_webgl_create_context(pTarget, &attrs); free(pTarget); ++ m_usedCount ; m_bContextCreated = true; } void destroyContext() { m_bContextCreated = false; if (m_WebGLContext > 0) { EMSCRIPTEN_RESULT res = emscripten_webgl_destroy_context(m_WebGLContext); m_WebGLContext = -1; } if (m_isDirectionCanvas && !m_directionCanvasId.empty()) { EM_ASM_({ var id = UTF8ToString($0); var c = document.getElementById(id); if (c && c.parentNode) c.parentNode.removeChild(c); }, m_directionCanvasId.c_str()); m_isDirectionCanvas = false; m_directionCanvasId.clear(); } } bool isContextCreated() const { return m_bContextCreated; } void makeCurrentContext() { if (m_WebGLContext > 0) { EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(m_WebGLContext); } } void presentContext() { } void updateContext(OdTrVisRenderClient *pDevice) override { if (m_WebGLContext > 0) { if (m_hasDirectBuffer && m_isDirectionCanvas && !m_directionCanvasId.empty()) { OdUInt32 pixelWidth = 0, pixelHeight = 0; pDevice->getDirectRenderBuffer(&pixelWidth, &pixelHeight); EM_ASM_({ var id = UTF8ToString($0); var c = document.getElementById(id); if (c && (c.width !== $1 || c.height !== $2)) { c.width = $1; c.height = $2; } }, m_directionCanvasId.c_str(), (int)pixelWidth, (int)pixelHeight); } else { const int w = pDevice->outputWindowWidth(); const int h = pDevice->outputWindowHeight(); EM_ASM_({ var c = Module['canvas']; if (c && (c.width !== $0 || c.height !== $1)) { c.width = $0; c.height = $1; } }, w, h); } } } bool isExtensionSupported(const char * pExtensionName) { if (m_WebGLContext > 0) { return emscripten_webgl_enable_extension(m_WebGLContext, pExtensionName); } return false; } void *acquireExtensionFunctionPtr(const char *pFunctionName) { return (void*)::eglGetProcAddress(pFunctionName); } }; OdSmartPtr OdTrGL2LocalContext::createLocalContext(OdTrVisRenderClient *pDevice) { OdTrGL2LocalContextPtr pContext = OdRxObjectImpl::createObject(); pContext->createContext(pDevice); return pContext; } int OdGLES2LocalContextImpl::m_usedCount = 0;