forked from aya/aya
Initial commit
This commit is contained in:
213
engine/gfx/CMakeLists.txt
Normal file
213
engine/gfx/CMakeLists.txt
Normal file
@@ -0,0 +1,213 @@
|
||||
add_library(Graphics STATIC)
|
||||
|
||||
set(SOURCES
|
||||
# GfxBase
|
||||
src/Base/Adorn.cpp
|
||||
src/Base/Adorn.hpp
|
||||
src/Base/AdornBillboarder.cpp
|
||||
src/Base/AdornBillboarder.hpp
|
||||
src/Base/AdornBillboarder2D.cpp
|
||||
src/Base/AdornBillboarder2D.hpp
|
||||
src/Base/AdornSurface.cpp
|
||||
src/Base/AdornSurface.hpp
|
||||
src/Base/AsyncResult.hpp
|
||||
src/Base/FileMeshData.cpp
|
||||
src/Base/FileMeshData.hpp
|
||||
src/Base/FrameRateManager.cpp
|
||||
src/Base/FrameRateManager.hpp
|
||||
src/Base/GfxPart.cpp
|
||||
src/Base/GfxPart.hpp
|
||||
src/Base/IAdornable.hpp
|
||||
src/Base/IAdornableCollector.cpp
|
||||
src/Base/IAdornableCollector.hpp
|
||||
src/Base/Image.hpp
|
||||
src/Base/MeshFileStructs.hpp
|
||||
src/Base/MeshGen.hpp
|
||||
src/Base/ObjLoader.hpp
|
||||
src/Base/Part.hpp
|
||||
src/Base/PartIdentifier.cpp
|
||||
src/Base/PartIdentifier.hpp
|
||||
src/Base/RenderCaps.cpp
|
||||
src/Base/RenderCaps.hpp
|
||||
src/Base/RenderSettings.cpp
|
||||
src/Base/RenderSettings.hpp
|
||||
src/Base/RenderStats.cpp
|
||||
src/Base/RenderStats.hpp
|
||||
src/Base/TextureProxyBase.hpp
|
||||
src/Base/Type.hpp
|
||||
src/Base/Typesetter.hpp
|
||||
src/Base/ViewBase.cpp
|
||||
src/Base/ViewBase.hpp
|
||||
src/Base/ViewportBillboarder.cpp
|
||||
src/Base/ViewportBillboarder.hpp
|
||||
|
||||
# GfxCore
|
||||
src/Core/Device.cpp
|
||||
src/Core/Device.hpp
|
||||
src/Core/DeviceCreate.cpp
|
||||
src/Core/Framebuffer.cpp
|
||||
src/Core/Framebuffer.hpp
|
||||
src/Core/Geometry.cpp
|
||||
src/Core/Geometry.hpp
|
||||
src/Core/Pix.cpp
|
||||
src/Core/Pix.hpp
|
||||
src/Core/Resource.cpp
|
||||
src/Core/Resource.hpp
|
||||
src/Core/Shader.cpp
|
||||
src/Core/Shader.hpp
|
||||
src/Core/States.cpp
|
||||
src/Core/States.hpp
|
||||
src/Core/Texture.cpp
|
||||
src/Core/Texture.hpp
|
||||
|
||||
# GfxRender
|
||||
src/Render/AdornRender.cpp
|
||||
src/Render/AdornRender.hpp
|
||||
src/Render/CullableSceneNode.cpp
|
||||
src/Render/CullableSceneNode.hpp
|
||||
src/Render/CustomEmitter.cpp
|
||||
src/Render/CustomEmitter.hpp
|
||||
src/Render/Emitter.cpp
|
||||
src/Render/Emitter.hpp
|
||||
src/Render/EmitterShared.hpp
|
||||
src/Render/EnvMap.cpp
|
||||
src/Render/EnvMap.hpp
|
||||
src/Render/ExplosionEmitter.cpp
|
||||
src/Render/ExplosionEmitter.hpp
|
||||
src/Render/FastCluster.cpp
|
||||
src/Render/FastCluster.hpp
|
||||
src/Render/GeometryGenerator.cpp
|
||||
src/Render/GeometryGenerator.hpp
|
||||
src/Render/GlobalShaderData.cpp
|
||||
src/Render/GlobalShaderData.hpp
|
||||
src/Render/Image.cpp
|
||||
src/Render/Image.hpp
|
||||
src/Render/ImageInfo.hpp
|
||||
src/Render/LightGrid.cpp
|
||||
src/Render/LightGrid.hpp
|
||||
src/Render/LightObject.cpp
|
||||
src/Render/LightObject.hpp
|
||||
src/Render/Material.cpp
|
||||
src/Render/Material.hpp
|
||||
src/Render/MaterialGenerator.cpp
|
||||
src/Render/MaterialGenerator.hpp
|
||||
src/Render/MegaCluster.cpp
|
||||
src/Render/MegaCluster.hpp
|
||||
src/Render/ObjectExporter.cpp
|
||||
src/Render/ObjectExporter.hpp
|
||||
src/Render/ParticleEmitter.cpp
|
||||
src/Render/ParticleEmitter.hpp
|
||||
src/Render/RenderCamera.cpp
|
||||
src/Render/RenderCamera.hpp
|
||||
src/Render/RenderNode.cpp
|
||||
src/Render/RenderNode.hpp
|
||||
src/Render/RenderQueue.cpp
|
||||
src/Render/RenderQueue.hpp
|
||||
src/Render/RenderView.cpp
|
||||
src/Render/RenderView.hpp
|
||||
src/Render/SceneManager.cpp
|
||||
src/Render/SceneManager.hpp
|
||||
src/Render/SceneUpdater.cpp
|
||||
src/Render/SceneUpdater.hpp
|
||||
src/Render/ScreenSpaceEffect.cpp
|
||||
src/Render/ScreenSpaceEffect.hpp
|
||||
src/Render/ShaderManager.cpp
|
||||
src/Render/ShaderManager.hpp
|
||||
src/Render/Sky.cpp
|
||||
src/Render/Sky.hpp
|
||||
src/Render/SmoothCluster.cpp
|
||||
src/Render/SmoothCluster.hpp
|
||||
src/Render/SpatialGrid.hpp
|
||||
src/Render/SpatialHashedScene.cpp
|
||||
src/Render/SpatialHashedScene.hpp
|
||||
src/Render/SSAO.cpp
|
||||
src/Render/SSAO.hpp
|
||||
src/Render/SuperCluster.cpp
|
||||
src/Render/SuperCluster.hpp
|
||||
src/Render/TextureAtlas.cpp
|
||||
src/Render/TextureAtlas.hpp
|
||||
src/Render/TextureCompositor.cpp
|
||||
src/Render/TextureCompositor.hpp
|
||||
src/Render/TextureManager.cpp
|
||||
src/Render/TextureManager.hpp
|
||||
src/Render/TextureRef.cpp
|
||||
src/Render/TextureRef.hpp
|
||||
src/Render/TypesetterDynamic.cpp
|
||||
src/Render/TypesetterDynamic.hpp
|
||||
src/Render/Util.hpp
|
||||
src/Render/VertexStreamer.cpp
|
||||
src/Render/VertexStreamer.hpp
|
||||
src/Render/VisualEngine.cpp
|
||||
src/Render/VisualEngine.hpp
|
||||
src/Render/Water.cpp
|
||||
src/Render/Water.hpp
|
||||
)
|
||||
|
||||
# BGFX API integration
|
||||
list(APPEND SOURCES
|
||||
src/API/BGFX/DeviceContextBGFX.cpp
|
||||
src/API/BGFX/DeviceBGFX.cpp
|
||||
src/API/BGFX/DeviceBGFX.hpp
|
||||
src/API/BGFX/FramebufferBGFX.cpp
|
||||
src/API/BGFX/FramebufferBGFX.hpp
|
||||
src/API/BGFX/GeometryBGFX.cpp
|
||||
src/API/BGFX/GeometryBGFX.hpp
|
||||
src/API/BGFX/HeadersBGFX.hpp
|
||||
src/API/BGFX/ShaderBGFX.cpp
|
||||
src/API/BGFX/ShaderBGFX.hpp
|
||||
src/API/BGFX/TextureBGFX.cpp
|
||||
src/API/BGFX/TextureBGFX.hpp
|
||||
)
|
||||
|
||||
# OpenGL API integration
|
||||
list(APPEND SOURCES
|
||||
src/API/GL/ContextGL.hpp
|
||||
src/API/GL/DeviceContextGL.cpp
|
||||
src/API/GL/DeviceGL.cpp
|
||||
src/API/GL/DeviceGL.hpp
|
||||
src/API/GL/FramebufferGL.cpp
|
||||
src/API/GL/FramebufferGL.hpp
|
||||
src/API/GL/GeometryGL.cpp
|
||||
src/API/GL/GeometryGL.hpp
|
||||
src/API/GL/HeadersGL.hpp
|
||||
src/API/GL/ShaderGL.cpp
|
||||
src/API/GL/ShaderGL.hpp
|
||||
src/API/GL/TextureGL.cpp
|
||||
src/API/GL/TextureGL.hpp
|
||||
src/API/GL/glad/include/glad/gl.h
|
||||
src/API/GL/glad/src/gl.c
|
||||
)
|
||||
|
||||
if(AYA_OS_WINDOWS)
|
||||
list(APPEND SOURCES
|
||||
src/API/GL/glad/include/glad/wgl.h
|
||||
src/API/GL/glad/src/wgl.c
|
||||
src/API/GL/ContextGLWin32.cpp
|
||||
)
|
||||
elseif(AYA_OS_LINUX)
|
||||
list(APPEND SOURCES
|
||||
src/API/GL/glad/include/EGL/eglplatform.h
|
||||
src/API/GL/glad/include/glad/egl.h
|
||||
src/API/GL/glad/src/egl.c
|
||||
src/API/GL/ContextEGL.cpp
|
||||
)
|
||||
elseif(AYA_OS_MACOS)
|
||||
list(APPEND SOURCES src/API/GL/ContextGLMac.mm)
|
||||
elseif(AYA_OS_ANDROID)
|
||||
list(APPEND SOURCES src/API/GL/ContextGLAndroid.cpp)
|
||||
endif()
|
||||
|
||||
target_sources(Graphics PRIVATE ${SOURCES})
|
||||
|
||||
target_include_directories(Graphics
|
||||
PUBLIC
|
||||
${ENGINE_DIR}/3d/src
|
||||
src
|
||||
PRIVATE
|
||||
src/API/GL/glad/include
|
||||
${THIRD_PARTY_DIR}/BulletPhysics/src
|
||||
${ENGINE_DIR}/app/src
|
||||
${ENGINE_DIR}/core/src
|
||||
)
|
||||
|
||||
target_link_libraries(Graphics $<TARGET_OBJECTS:Core> $<TARGET_OBJECTS:3D>)
|
||||
314
engine/gfx/src/API/BGFX/DeviceBGFX.cpp
Normal file
314
engine/gfx/src/API/BGFX/DeviceBGFX.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
#include "DeviceBGFX.hpp"
|
||||
|
||||
#include "GeometryBGFX.hpp"
|
||||
#include "ShaderBGFX.hpp"
|
||||
#include "TextureBGFX.hpp"
|
||||
#include "FramebufferBGFX.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
#include "ImGui.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <bgfx/platform.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#ifdef __linux
|
||||
#include <wayland-client.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
FASTFLAGVARIABLE(DebugGraphicsBGFX, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static DeviceCapsBGFX createDeviceCaps()
|
||||
{
|
||||
const bgfx::Caps* bgfxCaps = bgfx::getCaps();
|
||||
|
||||
DeviceCapsBGFX caps;
|
||||
caps.caps = bgfxCaps;
|
||||
|
||||
caps.supportsFramebuffer = true;
|
||||
caps.supportsShaders = true;
|
||||
caps.supportsFFP = false;
|
||||
caps.supportsStencil = true;
|
||||
caps.supportsIndex32 = bgfxCaps->supported & BGFX_CAPS_INDEX32;
|
||||
|
||||
caps.supportsTextureDXT = (bgfxCaps->formats[bgfx::TextureFormat::BC1] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
|
||||
caps.supportsTexturePVR = (bgfxCaps->formats[bgfx::TextureFormat::PTC12] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
|
||||
caps.supportsTextureHalfFloat = (bgfxCaps->formats[bgfx::TextureFormat::R16F] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
|
||||
caps.supportsTexture3D = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_3D) != 0;
|
||||
caps.supportsTextureNPOT = true;
|
||||
caps.supportsTextureETC1 = (bgfxCaps->formats[bgfx::TextureFormat::ETC1] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
|
||||
caps.supportsTexturePartialMipChain = true;
|
||||
|
||||
caps.maxDrawBuffers = bgfxCaps->limits.maxFBAttachments;
|
||||
caps.maxSamples = 1; // BGFX doesn't expose max MSAA samples directly
|
||||
caps.maxTextureSize = bgfxCaps->limits.maxTextureSize;
|
||||
caps.maxTextureUnits = bgfxCaps->limits.maxTextureSamplers;
|
||||
|
||||
caps.colorOrderBGR = bgfxCaps->rendererType == bgfx::RendererType::Direct3D11 || bgfxCaps->rendererType == bgfx::RendererType::Direct3D12;
|
||||
caps.requiresRenderTargetFlipping =
|
||||
bgfxCaps->rendererType == bgfx::RendererType::OpenGL || bgfxCaps->rendererType == bgfx::RendererType::OpenGLES;
|
||||
|
||||
caps.retina = false;
|
||||
|
||||
caps.supportsCompute = (bgfxCaps->supported & BGFX_CAPS_COMPUTE) != 0;
|
||||
caps.supportsInstancing = (bgfxCaps->supported & BGFX_CAPS_INSTANCING) != 0;
|
||||
caps.supportsTextureBlits = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_BLIT) != 0;
|
||||
caps.supportsTextureReadBack = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_READ_BACK) != 0;
|
||||
caps.supportsOcclusionQuery = (bgfxCaps->supported & BGFX_CAPS_OCCLUSION_QUERY) != 0;
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
enum class DisplayBackend
|
||||
{
|
||||
Wayland,
|
||||
X11,
|
||||
Unknown
|
||||
};
|
||||
|
||||
static DisplayBackend detectDisplayBackend()
|
||||
{
|
||||
const char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
|
||||
const char* x11Display = std::getenv("DISPLAY");
|
||||
|
||||
if (waylandDisplay && waylandDisplay[0])
|
||||
return DisplayBackend::Wayland;
|
||||
if (x11Display && x11Display[0])
|
||||
return DisplayBackend::X11;
|
||||
return DisplayBackend::Unknown;
|
||||
}
|
||||
|
||||
DeviceBGFX::DeviceBGFX(void* windowHandle, void* displayHandle)
|
||||
: width(0)
|
||||
, height(0)
|
||||
, frameNumber(0)
|
||||
{
|
||||
bgfx::Init init;
|
||||
init.type = bgfx::RendererType::Count; // Auto-select
|
||||
init.platformData.nwh = windowHandle;
|
||||
|
||||
#ifdef __linux__
|
||||
DisplayBackend backend = detectDisplayBackend();
|
||||
if (backend == DisplayBackend::Wayland)
|
||||
{
|
||||
init.platformData.ndt = static_cast<wl_display*>(displayHandle);
|
||||
init.platformData.type = bgfx::NativeWindowHandleType::Wayland;
|
||||
}
|
||||
else if (backend == DisplayBackend::X11)
|
||||
{
|
||||
init.platformData.ndt = static_cast<::Display*>(displayHandle);
|
||||
init.platformData.type = bgfx::NativeWindowHandleType::Default; // X11 is default on Linux
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bgfx::init(init))
|
||||
{
|
||||
throw Aya::runtime_error("Failed to initialize BGFX");
|
||||
}
|
||||
|
||||
rendererType = bgfx::getCaps()->rendererType;
|
||||
caps = createDeviceCaps();
|
||||
|
||||
const char* rendererName = bgfx::getRendererName(rendererType);
|
||||
FASTLOGS(FLog::Graphics, "BGFX Renderer: %s", rendererName);
|
||||
|
||||
caps.dumpToFLog(FLog::Graphics);
|
||||
|
||||
FASTLOG5(FLog::Graphics, "Caps: Compute %d Instancing %d TextureBlits %d TextureReadBack %d OcclusionQuery %d", caps.supportsCompute,
|
||||
caps.supportsInstancing, caps.supportsTextureBlits, caps.supportsTextureReadBack, caps.supportsOcclusionQuery);
|
||||
|
||||
immediateContext.reset(new DeviceContextBGFX(this));
|
||||
mainFramebuffer.reset(new FramebufferBGFX(this, BGFX_INVALID_HANDLE));
|
||||
|
||||
bgfx::setDebug(FFlag::DebugGraphicsBGFX ? BGFX_DEBUG_TEXT : BGFX_DEBUG_NONE);
|
||||
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
Profiler::gpuInit(0);
|
||||
#endif
|
||||
ImGui::gpuInit();
|
||||
}
|
||||
|
||||
DeviceBGFX::~DeviceBGFX()
|
||||
{
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
Profiler::gpuShutdown();
|
||||
#endif
|
||||
ImGui::gpuShutdown();
|
||||
|
||||
immediateContext.reset();
|
||||
mainFramebuffer.reset();
|
||||
|
||||
bgfx::shutdown();
|
||||
}
|
||||
|
||||
void DeviceBGFX::resize(int w, int h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
bgfx::reset(width, height, BGFX_RESET_VSYNC);
|
||||
}
|
||||
|
||||
std::string DeviceBGFX::getAPIName()
|
||||
{
|
||||
return bgfx::getRendererName(rendererType);
|
||||
}
|
||||
|
||||
std::string DeviceBGFX::getFeatureLevel()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << bgfx::getRendererName(rendererType);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool DeviceBGFX::validate()
|
||||
{
|
||||
// BGFX handles window resizing internally
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceContext* DeviceBGFX::beginFrame()
|
||||
{
|
||||
immediateContext->setCurrentView(0);
|
||||
immediateContext->bindFramebuffer(mainFramebuffer.get());
|
||||
immediateContext->clearStates();
|
||||
|
||||
return immediateContext.get();
|
||||
}
|
||||
|
||||
void DeviceBGFX::endFrame()
|
||||
{
|
||||
bgfx::frame();
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
Framebuffer* DeviceBGFX::getMainFramebuffer()
|
||||
{
|
||||
return mainFramebuffer.get();
|
||||
}
|
||||
|
||||
void DeviceBGFX::defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants)
|
||||
{
|
||||
AYAASSERT(globalConstants.empty());
|
||||
AYAASSERT(!constants.empty());
|
||||
|
||||
globalConstants = constants;
|
||||
|
||||
immediateContext->defineGlobalConstants(dataSize);
|
||||
}
|
||||
|
||||
|
||||
std::string DeviceBGFX::createShaderSource(
|
||||
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback)
|
||||
{
|
||||
return fileCallback(path);
|
||||
}
|
||||
|
||||
std::vector<char> DeviceBGFX::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint)
|
||||
{
|
||||
AYAASSERT(false && "BGFX shaders must be pre-compiled");
|
||||
return std::vector<char>();
|
||||
}
|
||||
|
||||
|
||||
std::string DeviceBGFX::getShadingLanguage()
|
||||
{
|
||||
switch (rendererType)
|
||||
{
|
||||
case bgfx::RendererType::Direct3D11:
|
||||
return "dx12";
|
||||
case bgfx::RendererType::Direct3D12:
|
||||
return "dx11";
|
||||
case bgfx::RendererType::Metal:
|
||||
return "metal";
|
||||
case bgfx::RendererType::OpenGL:
|
||||
return "glsl";
|
||||
case bgfx::RendererType::OpenGLES:
|
||||
return "glsles";
|
||||
case bgfx::RendererType::Vulkan:
|
||||
return "spirv";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<VertexShader> DeviceBGFX::createVertexShader(const std::vector<char>& bytecode)
|
||||
{
|
||||
return shared_ptr<VertexShader>(new VertexShaderBGFX(this, bytecode));
|
||||
}
|
||||
|
||||
shared_ptr<FragmentShader> DeviceBGFX::createFragmentShader(const std::vector<char>& bytecode)
|
||||
{
|
||||
return shared_ptr<FragmentShader>(new FragmentShaderBGFX(this, bytecode));
|
||||
}
|
||||
|
||||
shared_ptr<ShaderProgram> DeviceBGFX::createShaderProgram(
|
||||
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
|
||||
{
|
||||
return shared_ptr<ShaderProgram>(new ShaderProgramBGFX(this, vertexShader, fragmentShader));
|
||||
}
|
||||
|
||||
shared_ptr<ShaderProgram> DeviceBGFX::createShaderProgramFFP()
|
||||
{
|
||||
throw Aya::runtime_error("No FFP support in BGFX");
|
||||
}
|
||||
|
||||
shared_ptr<VertexBuffer> DeviceBGFX::createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage)
|
||||
{
|
||||
return shared_ptr<VertexBuffer>(new VertexBufferBGFX(this, elementSize, elementCount, usage));
|
||||
}
|
||||
|
||||
shared_ptr<IndexBuffer> DeviceBGFX::createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage)
|
||||
{
|
||||
return shared_ptr<IndexBuffer>(new IndexBufferBGFX(this, elementSize, elementCount, usage));
|
||||
}
|
||||
|
||||
shared_ptr<VertexLayout> DeviceBGFX::createVertexLayout(const std::vector<VertexLayout::Element>& elements)
|
||||
{
|
||||
return shared_ptr<VertexLayout>(new VertexLayoutBGFX(this, elements));
|
||||
}
|
||||
|
||||
shared_ptr<Texture> DeviceBGFX::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, Texture::Usage usage)
|
||||
{
|
||||
return shared_ptr<Texture>(new TextureBGFX(this, type, format, width, height, depth, mipLevels, usage));
|
||||
}
|
||||
|
||||
shared_ptr<Renderbuffer> DeviceBGFX::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
|
||||
{
|
||||
return shared_ptr<Renderbuffer>(new RenderbufferBGFX(this, format, width, height, samples));
|
||||
}
|
||||
|
||||
shared_ptr<Geometry> DeviceBGFX::createGeometryImpl(const shared_ptr<VertexLayout>& layout,
|
||||
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
{
|
||||
return shared_ptr<Geometry>(new GeometryBGFX(this, layout, vertexBuffers, indexBuffer, baseVertexIndex));
|
||||
}
|
||||
|
||||
shared_ptr<Framebuffer> DeviceBGFX::createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
{
|
||||
return shared_ptr<Framebuffer>(new FramebufferBGFX(this, color, depth));
|
||||
}
|
||||
|
||||
DeviceStats DeviceBGFX::getStatistics() const
|
||||
{
|
||||
DeviceStats result = {};
|
||||
|
||||
const bgfx::Stats* stats = bgfx::getStats();
|
||||
|
||||
result.gpuFrameTime = static_cast<float>(stats->gpuTimeEnd - stats->gpuTimeBegin) / stats->gpuTimerFreq * 1000.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
195
engine/gfx/src/API/BGFX/DeviceBGFX.hpp
Normal file
195
engine/gfx/src/API/BGFX/DeviceBGFX.hpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Device.hpp"
|
||||
#include "Core/States.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class FramebufferBGFX;
|
||||
class ShaderProgramBGFX;
|
||||
class TextureBGFX;
|
||||
class DeviceBGFX;
|
||||
|
||||
struct DeviceCapsBGFX : DeviceCaps
|
||||
{
|
||||
const bgfx::Caps* caps;
|
||||
|
||||
bool supportsCompute;
|
||||
bool supportsInstancing;
|
||||
bool supportsTextureBlits;
|
||||
bool supportsTextureReadBack;
|
||||
bool supportsOcclusionQuery;
|
||||
};
|
||||
|
||||
class DeviceContextBGFX : public DeviceContext
|
||||
{
|
||||
public:
|
||||
DeviceContextBGFX(DeviceBGFX* dev);
|
||||
~DeviceContextBGFX();
|
||||
|
||||
void defineGlobalConstants(size_t dataSize);
|
||||
|
||||
void clearStates();
|
||||
|
||||
void invalidateCachedProgram();
|
||||
void invalidateCachedTexture(Texture* texture);
|
||||
void invalidateCachedTextureStage(unsigned int stage);
|
||||
|
||||
void handleStencilState();
|
||||
|
||||
virtual void setDefaultAnisotropy(unsigned int value);
|
||||
|
||||
virtual void updateGlobalConstants(const void* data, size_t dataSize);
|
||||
|
||||
virtual void bindFramebuffer(Framebuffer* buffer);
|
||||
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil);
|
||||
|
||||
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset);
|
||||
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask);
|
||||
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask);
|
||||
|
||||
virtual void bindProgram(ShaderProgram* program);
|
||||
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount);
|
||||
virtual void setConstant(int handle, const float* data, size_t vectorCount);
|
||||
|
||||
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state);
|
||||
|
||||
virtual void setRasterizerState(const RasterizerState& state);
|
||||
virtual void setBlendState(const BlendState& state);
|
||||
virtual void setDepthState(const DepthState& state);
|
||||
|
||||
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
|
||||
unsigned int indexRangeEnd);
|
||||
|
||||
virtual void pushDebugMarkerGroup(const char* text);
|
||||
virtual void popDebugMarkerGroup();
|
||||
virtual void setDebugMarker(const char* text);
|
||||
|
||||
bgfx::ViewId getCurrentView() const
|
||||
{
|
||||
return currentView;
|
||||
}
|
||||
void setCurrentView(bgfx::ViewId view)
|
||||
{
|
||||
currentView = view;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> globalData;
|
||||
bgfx::UniformHandle globalUniform;
|
||||
unsigned int globalDataVersion;
|
||||
|
||||
unsigned int defaultAnisotropy;
|
||||
|
||||
ShaderProgramBGFX* cachedProgram;
|
||||
TextureBGFX* cachedTextures[16];
|
||||
|
||||
// Sampler uniforms for each texture stage
|
||||
bgfx::UniformHandle samplerUniforms[16];
|
||||
|
||||
RasterizerState cachedRasterizerState;
|
||||
BlendState cachedBlendState;
|
||||
DepthState cachedDepthState;
|
||||
|
||||
uint64_t cachedStateFlags;
|
||||
|
||||
bgfx::ViewId currentView;
|
||||
|
||||
DeviceBGFX* device;
|
||||
};
|
||||
|
||||
class DeviceBGFX : public Device
|
||||
{
|
||||
public:
|
||||
DeviceBGFX(void* windowHandle, void* displayHandle);
|
||||
~DeviceBGFX();
|
||||
|
||||
virtual bool validate();
|
||||
virtual void resize(int w, int h);
|
||||
virtual DeviceContext* beginFrame();
|
||||
virtual void endFrame();
|
||||
|
||||
virtual Framebuffer* getMainFramebuffer();
|
||||
|
||||
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants);
|
||||
|
||||
virtual std::string getAPIName();
|
||||
virtual std::string getFeatureLevel();
|
||||
virtual std::string getShadingLanguage();
|
||||
virtual std::string createShaderSource(
|
||||
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback);
|
||||
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint);
|
||||
|
||||
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode);
|
||||
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode);
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgram(
|
||||
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgramFFP();
|
||||
|
||||
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
|
||||
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
|
||||
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements);
|
||||
|
||||
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, Texture::Usage usage);
|
||||
|
||||
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
|
||||
|
||||
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
|
||||
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
|
||||
|
||||
virtual shared_ptr<Framebuffer> createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
|
||||
|
||||
virtual const DeviceCaps& getCaps() const
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
virtual DeviceStats getStatistics() const;
|
||||
|
||||
DeviceContextBGFX* getImmediateContextBGFX()
|
||||
{
|
||||
return immediateContext.get();
|
||||
}
|
||||
|
||||
const DeviceCapsBGFX& getCapsBGFX() const
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
const std::vector<ShaderGlobalConstant>& getGlobalConstants() const
|
||||
{
|
||||
return globalConstants;
|
||||
}
|
||||
|
||||
bgfx::RendererType::Enum getRendererType() const
|
||||
{
|
||||
return rendererType;
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceCapsBGFX caps;
|
||||
|
||||
scoped_ptr<DeviceContextBGFX> immediateContext;
|
||||
|
||||
scoped_ptr<FramebufferBGFX> mainFramebuffer;
|
||||
|
||||
std::vector<ShaderGlobalConstant> globalConstants;
|
||||
|
||||
bgfx::RendererType::Enum rendererType;
|
||||
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
uint32_t frameNumber;
|
||||
|
||||
float gpuTime;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
552
engine/gfx/src/API/BGFX/DeviceContextBGFX.cpp
Normal file
552
engine/gfx/src/API/BGFX/DeviceContextBGFX.cpp
Normal file
@@ -0,0 +1,552 @@
|
||||
#include "DeviceBGFX.hpp"
|
||||
|
||||
#include "GeometryBGFX.hpp"
|
||||
#include "ShaderBGFX.hpp"
|
||||
#include "TextureBGFX.hpp"
|
||||
#include "FramebufferBGFX.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
#ifdef AYA_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
// Convert RasterizerState cull mode to BGFX state flags
|
||||
static uint64_t getCullStateFlags(RasterizerState::CullMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case RasterizerState::Cull_None:
|
||||
return BGFX_STATE_CULL_CW | BGFX_STATE_CULL_CCW; // Disable culling
|
||||
case RasterizerState::Cull_Back:
|
||||
return BGFX_STATE_CULL_CW;
|
||||
case RasterizerState::Cull_Front:
|
||||
return BGFX_STATE_CULL_CCW;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert BlendState factors to BGFX blend equation
|
||||
static uint64_t getBlendStateFlags(const BlendState& state)
|
||||
{
|
||||
if (!state.blendingNeeded())
|
||||
return 0;
|
||||
|
||||
static const uint64_t blendFactorsBGFX[BlendState::Factor_Count] = {
|
||||
BGFX_STATE_BLEND_ONE, // Factor_One
|
||||
BGFX_STATE_BLEND_ZERO, // Factor_Zero
|
||||
BGFX_STATE_BLEND_DST_COLOR, // Factor_DstColor
|
||||
BGFX_STATE_BLEND_SRC_ALPHA, // Factor_SrcAlpha
|
||||
BGFX_STATE_BLEND_INV_SRC_ALPHA, // Factor_InvSrcAlpha
|
||||
BGFX_STATE_BLEND_DST_ALPHA, // Factor_DstAlpha
|
||||
BGFX_STATE_BLEND_INV_DST_ALPHA // Factor_InvDstAlpha
|
||||
};
|
||||
|
||||
uint64_t srcBlend = blendFactorsBGFX[state.getColorSrc()];
|
||||
uint64_t dstBlend = blendFactorsBGFX[state.getColorDst()];
|
||||
|
||||
uint64_t flags = BGFX_STATE_BLEND_FUNC(srcBlend, dstBlend);
|
||||
|
||||
if (state.separateAlphaBlend())
|
||||
{
|
||||
uint64_t srcAlpha = blendFactorsBGFX[state.getAlphaSrc()];
|
||||
uint64_t dstAlpha = blendFactorsBGFX[state.getAlphaDst()];
|
||||
flags = BGFX_STATE_BLEND_FUNC_SEPARATE(srcBlend, dstBlend, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Convert DepthState to BGFX state flags
|
||||
static uint64_t getDepthStateFlags(const DepthState& state)
|
||||
{
|
||||
uint64_t flags = 0;
|
||||
|
||||
if (state.getFunction() != DepthState::Function_None)
|
||||
{
|
||||
switch (state.getFunction())
|
||||
{
|
||||
case DepthState::Function_Always:
|
||||
flags |= BGFX_STATE_DEPTH_TEST_ALWAYS;
|
||||
break;
|
||||
case DepthState::Function_Less:
|
||||
flags |= BGFX_STATE_DEPTH_TEST_LESS;
|
||||
break;
|
||||
case DepthState::Function_LessEqual:
|
||||
flags |= BGFX_STATE_DEPTH_TEST_LEQUAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.getWrite())
|
||||
{
|
||||
flags |= BGFX_STATE_WRITE_Z;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Convert color mask to BGFX state flags
|
||||
static uint64_t getColorMaskFlags(unsigned int colorMask)
|
||||
{
|
||||
uint64_t flags = 0;
|
||||
|
||||
if (colorMask & BlendState::Color_R)
|
||||
flags |= BGFX_STATE_WRITE_R;
|
||||
if (colorMask & BlendState::Color_G)
|
||||
flags |= BGFX_STATE_WRITE_G;
|
||||
if (colorMask & BlendState::Color_B)
|
||||
flags |= BGFX_STATE_WRITE_B;
|
||||
if (colorMask & BlendState::Color_A)
|
||||
flags |= BGFX_STATE_WRITE_A;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
DeviceContextBGFX::DeviceContextBGFX(DeviceBGFX* dev)
|
||||
: globalDataVersion(0)
|
||||
, device(dev)
|
||||
, defaultAnisotropy(1)
|
||||
, cachedProgram(nullptr)
|
||||
, cachedRasterizerState(RasterizerState::Cull_None)
|
||||
, cachedBlendState(BlendState::Mode_None)
|
||||
, cachedDepthState(DepthState::Function_Always, false)
|
||||
, cachedStateFlags(0)
|
||||
, currentView(0)
|
||||
{
|
||||
globalUniform = BGFX_INVALID_HANDLE;
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
|
||||
{
|
||||
cachedTextures[i] = nullptr;
|
||||
|
||||
// Create sampler uniforms for each texture stage
|
||||
char uniformName[32];
|
||||
snprintf(uniformName, sizeof(uniformName), "s_texColor%d", (int)i);
|
||||
samplerUniforms[i] = bgfx::createUniform(uniformName, bgfx::UniformType::Sampler);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceContextBGFX::~DeviceContextBGFX()
|
||||
{
|
||||
if (bgfx::isValid(globalUniform))
|
||||
{
|
||||
bgfx::destroy(globalUniform);
|
||||
globalUniform = BGFX_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// Destroy sampler uniforms
|
||||
for (size_t i = 0; i < ARRAYSIZE(samplerUniforms); ++i)
|
||||
{
|
||||
if (bgfx::isValid(samplerUniforms[i]))
|
||||
{
|
||||
bgfx::destroy(samplerUniforms[i]);
|
||||
samplerUniforms[i] = BGFX_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::clearStates()
|
||||
{
|
||||
// Clear program cache
|
||||
cachedProgram = nullptr;
|
||||
|
||||
// Clear texture cache
|
||||
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
|
||||
cachedTextures[i] = nullptr;
|
||||
|
||||
// Clear states to invalid values to guarantee a cache miss on the next setup
|
||||
cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count);
|
||||
cachedBlendState = BlendState(BlendState::Mode_Count);
|
||||
cachedDepthState = DepthState(DepthState::Function_Count, false);
|
||||
|
||||
cachedStateFlags = 0;
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::invalidateCachedProgram()
|
||||
{
|
||||
cachedProgram = nullptr;
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::invalidateCachedTexture(Texture* texture)
|
||||
{
|
||||
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
|
||||
for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextures); ++stage)
|
||||
if (cachedTextures[stage] == textureBGFX)
|
||||
cachedTextures[stage] = nullptr;
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::invalidateCachedTextureStage(unsigned int stage)
|
||||
{
|
||||
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
|
||||
cachedTextures[stage] = nullptr;
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::defineGlobalConstants(size_t dataSize)
|
||||
{
|
||||
AYAASSERT(globalData.empty());
|
||||
AYAASSERT(dataSize > 0);
|
||||
|
||||
globalData.resize(dataSize);
|
||||
|
||||
// Create a uniform buffer for global constants
|
||||
char uniformName[256];
|
||||
snprintf(uniformName, sizeof(uniformName), "u_globals");
|
||||
globalUniform = bgfx::createUniform(uniformName, bgfx::UniformType::Vec4, (dataSize + 15) / 16);
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setDefaultAnisotropy(unsigned int value)
|
||||
{
|
||||
defaultAnisotropy = value;
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::updateGlobalConstants(const void* data, size_t dataSize)
|
||||
{
|
||||
AYAASSERT(dataSize == globalData.size());
|
||||
|
||||
memcpy(&globalData[0], data, dataSize);
|
||||
globalDataVersion++;
|
||||
|
||||
// Update the uniform buffer
|
||||
if (bgfx::isValid(globalUniform))
|
||||
{
|
||||
bgfx::setUniform(globalUniform, &globalData[0], (dataSize + 15) / 16);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::bindFramebuffer(Framebuffer* buffer)
|
||||
{
|
||||
FramebufferBGFX* framebufferBGFX = static_cast<FramebufferBGFX*>(buffer);
|
||||
|
||||
// Set the framebuffer for the current view
|
||||
bgfx::setViewFrameBuffer(currentView, framebufferBGFX->getHandle());
|
||||
bgfx::setViewRect(currentView, 0, 0, uint16_t(buffer->getWidth()), uint16_t(buffer->getHeight()));
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil)
|
||||
{
|
||||
uint16_t clearFlags = 0;
|
||||
|
||||
if (mask & Buffer_Color)
|
||||
{
|
||||
clearFlags |= BGFX_CLEAR_COLOR;
|
||||
}
|
||||
|
||||
if (mask & Buffer_Depth)
|
||||
{
|
||||
clearFlags |= BGFX_CLEAR_DEPTH;
|
||||
}
|
||||
|
||||
if (mask & Buffer_Stencil)
|
||||
{
|
||||
clearFlags |= BGFX_CLEAR_STENCIL;
|
||||
}
|
||||
|
||||
uint32_t rgba = 0;
|
||||
if (mask & Buffer_Color)
|
||||
{
|
||||
rgba = (static_cast<uint32_t>(color[0] * 255.0f) << 24) | (static_cast<uint32_t>(color[1] * 255.0f) << 16) |
|
||||
(static_cast<uint32_t>(color[2] * 255.0f) << 8) | (static_cast<uint32_t>(color[3] * 255.0f));
|
||||
}
|
||||
|
||||
bgfx::setViewClear(currentView, clearFlags, rgba, depth, static_cast<uint8_t>(stencil));
|
||||
|
||||
// Touch the view to ensure clear happens
|
||||
bgfx::touch(currentView);
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset)
|
||||
{
|
||||
AYAASSERT(texture->getType() == Texture::Type_2D);
|
||||
AYAASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight());
|
||||
|
||||
if (!device->getCapsBGFX().supportsTextureBlits)
|
||||
{
|
||||
// Fall back to a different method or log warning
|
||||
return;
|
||||
}
|
||||
|
||||
FramebufferBGFX* framebufferBGFX = static_cast<FramebufferBGFX*>(buffer);
|
||||
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
|
||||
|
||||
// BGFX blit from framebuffer to texture
|
||||
// Note: BGFX handles this differently - would need access to the framebuffer's texture attachments
|
||||
invalidateCachedTextureStage(0);
|
||||
|
||||
// bgfx::blit(currentView, textureBGFX->getHandle(), 0, xOffset, yOffset, framebufferTexture, 0, 0, 0, width, height);
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask)
|
||||
{
|
||||
AYAASSERT(msaaBuffer->getSamples() > 1);
|
||||
AYAASSERT(buffer->getSamples() == 1);
|
||||
AYAASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight());
|
||||
|
||||
// BGFX handles MSAA resolve automatically when rendering to a MSAA framebuffer
|
||||
// and then using it as a texture or blitting to a non-MSAA target
|
||||
// This may not need explicit implementation
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::discardFramebuffer(Framebuffer* buffer, unsigned int mask)
|
||||
{
|
||||
// BGFX handles framebuffer discard internally
|
||||
// bgfx::discard() is used differently - it discards all pending draw calls
|
||||
uint8_t discardFlags = 0;
|
||||
|
||||
// BGFX doesn't have per-buffer discard flags like GL
|
||||
// The discard() function discards all state changes
|
||||
if (mask != 0)
|
||||
{
|
||||
bgfx::discard();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::bindProgram(ShaderProgram* program)
|
||||
{
|
||||
ShaderProgramBGFX* programBGFX = static_cast<ShaderProgramBGFX*>(program);
|
||||
|
||||
cachedProgram = programBGFX;
|
||||
|
||||
// BGFX sets the program per-draw call via bgfx::submit, not here
|
||||
// Just cache the program for later use
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setWorldTransforms4x3(const float* data, size_t matrixCount)
|
||||
{
|
||||
if (cachedProgram)
|
||||
{
|
||||
cachedProgram->setWorldTransforms4x3(data, matrixCount);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setConstant(int handle, const float* data, size_t vectorCount)
|
||||
{
|
||||
if (cachedProgram)
|
||||
{
|
||||
cachedProgram->setConstant(handle, data, vectorCount);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state)
|
||||
{
|
||||
SamplerState realState = (state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0)
|
||||
? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy)
|
||||
: state;
|
||||
|
||||
AYAASSERT(stage < device->getCaps().maxTextureUnits);
|
||||
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
|
||||
|
||||
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
|
||||
if (textureBGFX != cachedTextures[stage])
|
||||
{
|
||||
cachedTextures[stage] = textureBGFX;
|
||||
}
|
||||
|
||||
// Set texture with proper sampler uniform
|
||||
textureBGFX->bindWithUniform(stage, samplerUniforms[stage], realState);
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setRasterizerState(const RasterizerState& state)
|
||||
{
|
||||
if (cachedRasterizerState != state)
|
||||
{
|
||||
cachedRasterizerState = state;
|
||||
|
||||
// Update cached state flags
|
||||
cachedStateFlags &= ~(BGFX_STATE_CULL_MASK);
|
||||
cachedStateFlags |= getCullStateFlags(state.getCullMode());
|
||||
|
||||
// Handle depth bias (polygon offset)
|
||||
if (state.getDepthBias() != 0)
|
||||
{
|
||||
// BGFX doesn't have a direct polygon offset equivalent in state flags
|
||||
// This would need to be handled differently, possibly in shader or via uniforms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setBlendState(const BlendState& state)
|
||||
{
|
||||
if (cachedBlendState != state)
|
||||
{
|
||||
cachedBlendState = state;
|
||||
|
||||
// Update cached state flags
|
||||
cachedStateFlags &= ~(BGFX_STATE_BLEND_MASK | BGFX_STATE_WRITE_MASK);
|
||||
cachedStateFlags |= getBlendStateFlags(state);
|
||||
cachedStateFlags |= getColorMaskFlags(state.getColorMask());
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setDepthState(const DepthState& state)
|
||||
{
|
||||
if (cachedDepthState != state)
|
||||
{
|
||||
cachedDepthState = state;
|
||||
|
||||
// Update cached state flags
|
||||
cachedStateFlags &= ~(BGFX_STATE_DEPTH_TEST_MASK | BGFX_STATE_WRITE_Z);
|
||||
cachedStateFlags |= getDepthStateFlags(state);
|
||||
|
||||
// Handle stencil modes - BGFX uses bgfx::setStencil() separately
|
||||
// For now, we'll handle basic stencil modes
|
||||
// More complex stencil operations would need to be set per-draw call
|
||||
switch (state.getStencilMode())
|
||||
{
|
||||
case DepthState::Stencil_None:
|
||||
// No stencil test
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_IsNotZero:
|
||||
case DepthState::Stencil_UpdateZFail:
|
||||
case DepthState::Stencil_Increment:
|
||||
case DepthState::Stencil_Decrement:
|
||||
case DepthState::Stencil_IsNotZeroReplace:
|
||||
// Stencil operations in BGFX require setting stencil per-draw
|
||||
// Mark that stencil is needed
|
||||
break;
|
||||
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::handleStencilState()
|
||||
{
|
||||
// Convert DepthState stencil modes to BGFX stencil operations
|
||||
uint32_t frontStencil = BGFX_STENCIL_NONE;
|
||||
uint32_t backStencil = BGFX_STENCIL_NONE;
|
||||
|
||||
switch (cachedDepthState.getStencilMode())
|
||||
{
|
||||
case DepthState::Stencil_None:
|
||||
// No stencil test
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_IsNotZero:
|
||||
frontStencil = BGFX_STENCIL_TEST_NOTEQUAL | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_KEEP;
|
||||
backStencil = frontStencil;
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_UpdateZFail:
|
||||
// Front faces: decrement on z-fail
|
||||
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_DECR | BGFX_STENCIL_OP_PASS_Z_KEEP;
|
||||
// Back faces: increment on z-fail
|
||||
backStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_INCR | BGFX_STENCIL_OP_PASS_Z_KEEP;
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_Increment:
|
||||
// This mode has special requirements - it modifies the state flags
|
||||
// Disable color writes, enable front-face culling, increment stencil
|
||||
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_INCR;
|
||||
backStencil = frontStencil;
|
||||
|
||||
// Modify state flags: disable color writes, enable culling of front faces
|
||||
cachedStateFlags &= ~BGFX_STATE_WRITE_MASK;
|
||||
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
|
||||
cachedStateFlags |= BGFX_STATE_CULL_CCW; // Cull front faces
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_Decrement:
|
||||
// Continue from Increment - now cull back faces and decrement
|
||||
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_DECR;
|
||||
backStencil = frontStencil;
|
||||
|
||||
// Modify state flags: disable color writes, enable culling of back faces
|
||||
cachedStateFlags &= ~BGFX_STATE_WRITE_MASK;
|
||||
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
|
||||
cachedStateFlags |= BGFX_STATE_CULL_CW; // Cull back faces
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_IsNotZeroReplace:
|
||||
// Test stencil < 1, enable blending and color writes
|
||||
frontStencil = BGFX_STENCIL_TEST_LESS | BGFX_STENCIL_FUNC_REF(1) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
|
||||
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_KEEP;
|
||||
backStencil = frontStencil;
|
||||
|
||||
// Re-enable color writes, disable culling, enable blending
|
||||
cachedStateFlags |= BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A;
|
||||
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
|
||||
cachedStateFlags |= BGFX_STATE_CULL_CW | BGFX_STATE_CULL_CCW; // Disable culling
|
||||
|
||||
// Enable alpha blending for shadow compositing
|
||||
cachedStateFlags &= ~BGFX_STATE_BLEND_MASK;
|
||||
cachedStateFlags |= BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA);
|
||||
break;
|
||||
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
}
|
||||
|
||||
if (frontStencil != BGFX_STENCIL_NONE || backStencil != BGFX_STENCIL_NONE)
|
||||
{
|
||||
bgfx::setStencil(frontStencil, backStencil);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
|
||||
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
|
||||
{
|
||||
AYAASSERT(cachedProgram != nullptr);
|
||||
|
||||
// Update global constants if needed
|
||||
if (cachedProgram)
|
||||
{
|
||||
cachedProgram->updateGlobalConstants(&globalData[0], globalDataVersion);
|
||||
}
|
||||
|
||||
// Set the accumulated state flags
|
||||
uint64_t stateFlags = cachedStateFlags;
|
||||
|
||||
// Ensure we always write to RGB and A unless masked
|
||||
if ((stateFlags & BGFX_STATE_WRITE_MASK) == 0)
|
||||
{
|
||||
stateFlags |= BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A;
|
||||
}
|
||||
|
||||
bgfx::setState(stateFlags);
|
||||
|
||||
// Handle stencil state separately
|
||||
handleStencilState();
|
||||
|
||||
// Set vertex and index buffers - GeometryBGFX handles binding
|
||||
GeometryBGFX* geometryBGFX = static_cast<GeometryBGFX*>(geometry);
|
||||
geometryBGFX->bindBuffers(offset, count);
|
||||
|
||||
// CRITICAL: Actually submit the draw call with the shader program
|
||||
// This is what actually triggers rendering in BGFX!
|
||||
bgfx::submit(currentView, cachedProgram->getHandle());
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::pushDebugMarkerGroup(const char* text)
|
||||
{
|
||||
bgfx::setViewName(currentView, text);
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::popDebugMarkerGroup()
|
||||
{
|
||||
// BGFX doesn't have a direct equivalent to pop marker groups
|
||||
// Markers are set per-view
|
||||
}
|
||||
|
||||
void DeviceContextBGFX::setDebugMarker(const char* text)
|
||||
{
|
||||
bgfx::setMarker(text);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
148
engine/gfx/src/API/BGFX/FramebufferBGFX.cpp
Normal file
148
engine/gfx/src/API/BGFX/FramebufferBGFX.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "FramebufferBGFX.hpp"
|
||||
#include "TextureBGFX.hpp"
|
||||
#include "DeviceBGFX.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
RenderbufferBGFX::RenderbufferBGFX(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
|
||||
: Renderbuffer(device, format, width, height, samples)
|
||||
{
|
||||
AYAASSERT(samples);
|
||||
|
||||
if (samples > device->getCaps().maxSamples)
|
||||
{
|
||||
throw Aya::runtime_error("Unsupported renderbuffer: too many samples (%d)", samples);
|
||||
}
|
||||
|
||||
// Create a texture that will be used as a renderbuffer
|
||||
bgfx::TextureFormat::Enum bgfxFormat = TextureBGFX::getBGFXFormat(format);
|
||||
|
||||
if (bgfxFormat == bgfx::TextureFormat::Unknown)
|
||||
{
|
||||
throw Aya::runtime_error("Unsupported renderbuffer format");
|
||||
}
|
||||
|
||||
uint64_t flags = BGFX_TEXTURE_RT;
|
||||
|
||||
if (samples > 1)
|
||||
{
|
||||
flags |= BGFX_TEXTURE_RT_MSAA_X4; // BGFX has predefined MSAA levels
|
||||
}
|
||||
|
||||
textureHandle = bgfx::createTexture2D(width, height, false, 1, bgfxFormat, flags);
|
||||
|
||||
if (!bgfx::isValid(textureHandle))
|
||||
{
|
||||
throw Aya::runtime_error("Failed to create renderbuffer texture");
|
||||
}
|
||||
}
|
||||
|
||||
RenderbufferBGFX::~RenderbufferBGFX()
|
||||
{
|
||||
if (bgfx::isValid(textureHandle))
|
||||
{
|
||||
bgfx::destroy(textureHandle);
|
||||
}
|
||||
}
|
||||
|
||||
FramebufferBGFX::FramebufferBGFX(Device* device, bgfx::FrameBufferHandle handle)
|
||||
: Framebuffer(device, 0, 0, 1)
|
||||
, handle(handle)
|
||||
, isMain(true)
|
||||
{
|
||||
// Main framebuffer - dimensions will be set by the window
|
||||
}
|
||||
|
||||
FramebufferBGFX::FramebufferBGFX(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
: Framebuffer(device, 0, 0, 0)
|
||||
, handle(BGFX_INVALID_HANDLE)
|
||||
, isMain(false)
|
||||
, color(color)
|
||||
, depth(depth)
|
||||
{
|
||||
AYAASSERT(!color.empty());
|
||||
|
||||
if (color.size() > device->getCaps().maxDrawBuffers)
|
||||
{
|
||||
throw Aya::runtime_error("Unsupported framebuffer configuration: too many buffers (%d)", (int)color.size());
|
||||
}
|
||||
|
||||
// Collect all texture handles for the framebuffer
|
||||
std::vector<bgfx::TextureHandle> textures;
|
||||
|
||||
for (size_t i = 0; i < color.size(); ++i)
|
||||
{
|
||||
RenderbufferBGFX* buffer = static_cast<RenderbufferBGFX*>(color[i].get());
|
||||
AYAASSERT(buffer);
|
||||
AYAASSERT(!Texture::isFormatDepth(buffer->getFormat()));
|
||||
|
||||
textures.push_back(buffer->getTextureHandle());
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
width = buffer->getWidth();
|
||||
height = buffer->getHeight();
|
||||
samples = buffer->getSamples();
|
||||
}
|
||||
else
|
||||
{
|
||||
AYAASSERT(width == buffer->getWidth());
|
||||
AYAASSERT(height == buffer->getHeight());
|
||||
AYAASSERT(samples == buffer->getSamples());
|
||||
}
|
||||
}
|
||||
|
||||
// Add depth buffer if present
|
||||
if (depth)
|
||||
{
|
||||
RenderbufferBGFX* buffer = static_cast<RenderbufferBGFX*>(depth.get());
|
||||
AYAASSERT(Texture::isFormatDepth(buffer->getFormat()));
|
||||
|
||||
AYAASSERT(width == buffer->getWidth());
|
||||
AYAASSERT(height == buffer->getHeight());
|
||||
AYAASSERT(samples == buffer->getSamples());
|
||||
|
||||
textures.push_back(buffer->getTextureHandle());
|
||||
}
|
||||
|
||||
// Create framebuffer from textures
|
||||
handle = bgfx::createFrameBuffer(textures.size(), textures.data(), false);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw Aya::runtime_error("Failed to create framebuffer");
|
||||
}
|
||||
}
|
||||
|
||||
FramebufferBGFX::~FramebufferBGFX()
|
||||
{
|
||||
if (!isMain && bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferBGFX::download(void* data, unsigned int size)
|
||||
{
|
||||
AYAASSERT(size == width * height * 4);
|
||||
|
||||
// BGFX doesn't support synchronous framebuffer downloads like OpenGL
|
||||
// You would need to use bgfx::readTexture which is asynchronous
|
||||
// and requires a callback
|
||||
|
||||
// For now, we'll just assert that this isn't implemented
|
||||
// In a real implementation, you'd need to:
|
||||
// 1. Create a callback to receive the data
|
||||
// 2. Call bgfx::readTexture on the color attachment
|
||||
// 3. Wait for the callback (which might require frame coordination)
|
||||
|
||||
AYAASSERT(false && "Framebuffer download not implemented for BGFX");
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
53
engine/gfx/src/API/BGFX/FramebufferBGFX.hpp
Normal file
53
engine/gfx/src/API/BGFX/FramebufferBGFX.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Framebuffer.hpp"
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class DeviceBGFX;
|
||||
class TextureBGFX;
|
||||
|
||||
class RenderbufferBGFX : public Renderbuffer
|
||||
{
|
||||
public:
|
||||
RenderbufferBGFX(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
|
||||
~RenderbufferBGFX();
|
||||
|
||||
bgfx::TextureHandle getTextureHandle() const
|
||||
{
|
||||
return textureHandle;
|
||||
}
|
||||
|
||||
private:
|
||||
bgfx::TextureHandle textureHandle;
|
||||
};
|
||||
|
||||
class FramebufferBGFX : public Framebuffer
|
||||
{
|
||||
public:
|
||||
FramebufferBGFX(Device* device, bgfx::FrameBufferHandle handle);
|
||||
FramebufferBGFX(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
|
||||
~FramebufferBGFX();
|
||||
|
||||
virtual void download(void* data, unsigned int size);
|
||||
|
||||
bgfx::FrameBufferHandle getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
bgfx::FrameBufferHandle handle;
|
||||
bool isMain;
|
||||
|
||||
std::vector<shared_ptr<Renderbuffer>> color;
|
||||
shared_ptr<Renderbuffer> depth;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
343
engine/gfx/src/API/BGFX/GeometryBGFX.cpp
Normal file
343
engine/gfx/src/API/BGFX/GeometryBGFX.cpp
Normal file
@@ -0,0 +1,343 @@
|
||||
#include "GeometryBGFX.hpp"
|
||||
#include "DeviceBGFX.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
bgfx::Attrib::Enum VertexLayoutBGFX::getAttribType(unsigned int semanticId)
|
||||
{
|
||||
switch (semanticId)
|
||||
{
|
||||
case 0: // Position
|
||||
return bgfx::Attrib::Position;
|
||||
case 1: // Normal
|
||||
return bgfx::Attrib::Normal;
|
||||
case 2: // Color0
|
||||
return bgfx::Attrib::Color0;
|
||||
case 3: // Color1
|
||||
return bgfx::Attrib::Color1;
|
||||
case 4: // TexCoord0
|
||||
return bgfx::Attrib::TexCoord0;
|
||||
case 5: // TexCoord1
|
||||
return bgfx::Attrib::TexCoord1;
|
||||
case 6: // TexCoord2
|
||||
return bgfx::Attrib::TexCoord2;
|
||||
case 7: // TexCoord3
|
||||
return bgfx::Attrib::TexCoord3;
|
||||
case 8: // TexCoord4
|
||||
return bgfx::Attrib::TexCoord4;
|
||||
case 9: // TexCoord5
|
||||
return bgfx::Attrib::TexCoord5;
|
||||
case 10: // TexCoord6
|
||||
return bgfx::Attrib::TexCoord6;
|
||||
case 11: // TexCoord7
|
||||
return bgfx::Attrib::TexCoord7;
|
||||
default:
|
||||
return bgfx::Attrib::Count;
|
||||
}
|
||||
}
|
||||
|
||||
static bgfx::AttribType::Enum getAttribFormat(VertexLayout::Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VertexLayout::Format_Float1:
|
||||
case VertexLayout::Format_Float2:
|
||||
case VertexLayout::Format_Float3:
|
||||
case VertexLayout::Format_Float4:
|
||||
return bgfx::AttribType::Float;
|
||||
case VertexLayout::Format_Short2:
|
||||
case VertexLayout::Format_Short4:
|
||||
return bgfx::AttribType::Int16;
|
||||
case VertexLayout::Format_UByte4:
|
||||
case VertexLayout::Format_Color:
|
||||
return bgfx::AttribType::Uint8;
|
||||
default:
|
||||
return bgfx::AttribType::Count;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t getAttribCount(VertexLayout::Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VertexLayout::Format_Float1:
|
||||
return 1;
|
||||
case VertexLayout::Format_Float2:
|
||||
case VertexLayout::Format_Short2:
|
||||
return 2;
|
||||
case VertexLayout::Format_Float3:
|
||||
return 3;
|
||||
case VertexLayout::Format_Float4:
|
||||
case VertexLayout::Format_Short4:
|
||||
case VertexLayout::Format_UByte4:
|
||||
case VertexLayout::Format_Color:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isNormalized(VertexLayout::Format format)
|
||||
{
|
||||
return format == VertexLayout::Format_Color;
|
||||
}
|
||||
|
||||
static unsigned int getVertexAttributeId(VertexLayout::Semantic semantic, unsigned int index)
|
||||
{
|
||||
switch (semantic)
|
||||
{
|
||||
case VertexLayout::Semantic_Position:
|
||||
AYAASSERT(index == 0);
|
||||
return 0;
|
||||
case VertexLayout::Semantic_Normal:
|
||||
AYAASSERT(index == 0);
|
||||
return 1;
|
||||
case VertexLayout::Semantic_Color:
|
||||
AYAASSERT(index < 2);
|
||||
return 2 + index;
|
||||
case VertexLayout::Semantic_Texture:
|
||||
AYAASSERT(index < 8);
|
||||
return 4 + index;
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
VertexLayoutBGFX::VertexLayoutBGFX(Device* device, const std::vector<Element>& elements)
|
||||
: VertexLayout(device, elements)
|
||||
{
|
||||
layout.begin();
|
||||
|
||||
for (size_t i = 0; i < elements.size(); ++i)
|
||||
{
|
||||
const Element& e = elements[i];
|
||||
|
||||
unsigned int attribId = getVertexAttributeId(e.semantic, e.semanticIndex);
|
||||
bgfx::Attrib::Enum attrib = getAttribType(attribId);
|
||||
bgfx::AttribType::Enum type = getAttribFormat(e.format);
|
||||
uint8_t count = getAttribCount(e.format);
|
||||
bool normalized = isNormalized(e.format);
|
||||
|
||||
layout.add(attrib, count, type, normalized);
|
||||
}
|
||||
|
||||
layout.end();
|
||||
}
|
||||
|
||||
VertexLayoutBGFX::~VertexLayoutBGFX() {}
|
||||
|
||||
template<typename Base>
|
||||
GeometryBufferBGFX<Base>::GeometryBufferBGFX(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, bool isIndex)
|
||||
: Base(device, elementSize, elementCount, usage)
|
||||
, isIndex(isIndex)
|
||||
, memory(nullptr)
|
||||
, locked(nullptr)
|
||||
{
|
||||
vertexHandle = BGFX_INVALID_HANDLE;
|
||||
indexHandle = BGFX_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferBGFX<Base>::create()
|
||||
{
|
||||
uint32_t size = this->elementSize * this->elementCount;
|
||||
|
||||
if (isIndex)
|
||||
{
|
||||
uint16_t flags = (this->usage == GeometryBuffer::Usage_Dynamic) ? BGFX_BUFFER_NONE : BGFX_BUFFER_NONE;
|
||||
indexHandle = bgfx::createDynamicIndexBuffer(this->elementCount, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vertex buffers need a layout - we'll create the handle later in GeometryBGFX
|
||||
// when we have access to the actual layout
|
||||
vertexHandle = BGFX_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferBGFX<Base>::createVertexBuffer(const bgfx::VertexLayout& layout)
|
||||
{
|
||||
AYAASSERT(!isIndex);
|
||||
AYAASSERT(!bgfx::isValid(vertexHandle));
|
||||
|
||||
uint16_t flags = (this->usage == GeometryBuffer::Usage_Dynamic) ? BGFX_BUFFER_NONE : BGFX_BUFFER_NONE;
|
||||
vertexHandle = bgfx::createDynamicVertexBuffer(this->elementCount, layout, flags);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
GeometryBufferBGFX<Base>::~GeometryBufferBGFX()
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
|
||||
if (bgfx::isValid(vertexHandle))
|
||||
{
|
||||
bgfx::destroy(vertexHandle);
|
||||
}
|
||||
|
||||
if (bgfx::isValid(indexHandle))
|
||||
{
|
||||
bgfx::destroy(indexHandle);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void* GeometryBufferBGFX<Base>::lock(GeometryBuffer::LockMode mode)
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
|
||||
unsigned int size = this->elementSize * this->elementCount;
|
||||
|
||||
// Allocate temporary buffer
|
||||
locked = new char[size];
|
||||
|
||||
AYAASSERT(locked);
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferBGFX<Base>::unlock()
|
||||
{
|
||||
AYAASSERT(locked);
|
||||
|
||||
// Upload data to BGFX
|
||||
upload(0, locked, this->elementSize * this->elementCount);
|
||||
|
||||
delete[] static_cast<char*>(locked);
|
||||
locked = nullptr;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferBGFX<Base>::upload(unsigned int offset, const void* data, unsigned int size)
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
AYAASSERT(offset + size <= this->elementSize * this->elementCount);
|
||||
|
||||
const bgfx::Memory* mem = bgfx::copy(static_cast<const char*>(data) + offset, size);
|
||||
|
||||
if (isIndex)
|
||||
{
|
||||
if (bgfx::isValid(indexHandle))
|
||||
{
|
||||
bgfx::update(indexHandle, 0, mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bgfx::isValid(vertexHandle))
|
||||
{
|
||||
bgfx::update(vertexHandle, 0, mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VertexBufferBGFX::VertexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBufferBGFX<VertexBuffer>(device, elementSize, elementCount, usage, false)
|
||||
{
|
||||
create();
|
||||
}
|
||||
|
||||
VertexBufferBGFX::~VertexBufferBGFX() {}
|
||||
|
||||
IndexBufferBGFX::IndexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBufferBGFX<IndexBuffer>(device, elementSize, elementCount, usage, true)
|
||||
{
|
||||
if (elementSize != 2 && elementSize != 4)
|
||||
throw Aya::runtime_error("Invalid element size: %d", (int)elementSize);
|
||||
|
||||
create();
|
||||
}
|
||||
|
||||
IndexBufferBGFX::~IndexBufferBGFX() {}
|
||||
|
||||
GeometryBGFX::GeometryBGFX(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
: Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex)
|
||||
, indexElementSize(0)
|
||||
{
|
||||
if (indexBuffer)
|
||||
{
|
||||
indexElementSize = indexBuffer->getElementSize();
|
||||
}
|
||||
|
||||
// Create vertex buffers with proper layout
|
||||
VertexLayoutBGFX* layoutBGFX = static_cast<VertexLayoutBGFX*>(layout.get());
|
||||
const bgfx::VertexLayout& bgfxLayout = layoutBGFX->getBGFXLayout();
|
||||
|
||||
for (size_t i = 0; i < vertexBuffers.size(); ++i)
|
||||
{
|
||||
VertexBufferBGFX* vb = static_cast<VertexBufferBGFX*>(vertexBuffers[i].get());
|
||||
|
||||
// Create the vertex buffer handle with the layout if not already created
|
||||
if (!bgfx::isValid(vb->getVertexHandle()))
|
||||
{
|
||||
vb->createVertexBuffer(bgfxLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GeometryBGFX::~GeometryBGFX() {}
|
||||
|
||||
void GeometryBGFX::bindBuffers(unsigned int offset, unsigned int count)
|
||||
{
|
||||
// Set vertex buffers
|
||||
for (size_t i = 0; i < vertexBuffers.size(); ++i)
|
||||
{
|
||||
VertexBufferBGFX* vb = static_cast<VertexBufferBGFX*>(vertexBuffers[i].get());
|
||||
|
||||
if (bgfx::isValid(vb->getVertexHandle()))
|
||||
{
|
||||
bgfx::setVertexBuffer(i, vb->getVertexHandle(), baseVertexIndex, vb->getElementCount());
|
||||
}
|
||||
}
|
||||
|
||||
// Set index buffer if present
|
||||
if (indexBuffer)
|
||||
{
|
||||
IndexBufferBGFX* ib = static_cast<IndexBufferBGFX*>(indexBuffer.get());
|
||||
|
||||
if (bgfx::isValid(ib->getIndexHandle()))
|
||||
{
|
||||
bgfx::setIndexBuffer(ib->getIndexHandle(), offset, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GeometryBGFX::convertPrimitive(Primitive primitive)
|
||||
{
|
||||
// This function is not used in BGFX - primitives are set via state flags
|
||||
// Keeping for potential future use
|
||||
switch (primitive)
|
||||
{
|
||||
case Primitive_Triangles:
|
||||
return BGFX_STATE_PT_TRISTRIP;
|
||||
case Primitive_Lines:
|
||||
return BGFX_STATE_PT_LINES;
|
||||
case Primitive_Points:
|
||||
return BGFX_STATE_PT_POINTS;
|
||||
case Primitive_TriangleStrip:
|
||||
return BGFX_STATE_PT_TRISTRIP;
|
||||
default:
|
||||
return BGFX_STATE_PT_TRISTRIP;
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryBGFX::draw(bgfx::ViewId view, Primitive primitive, unsigned int offset, unsigned int count)
|
||||
{
|
||||
// Legacy method - not used anymore
|
||||
// Drawing is now handled by bindBuffers() + bgfx::submit() in DeviceContextBGFX::drawImpl()
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
template class GeometryBufferBGFX<VertexBuffer>;
|
||||
template class GeometryBufferBGFX<IndexBuffer>;
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
100
engine/gfx/src/API/BGFX/GeometryBGFX.hpp
Normal file
100
engine/gfx/src/API/BGFX/GeometryBGFX.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Geometry.hpp"
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class DeviceBGFX;
|
||||
|
||||
class VertexLayoutBGFX : public VertexLayout
|
||||
{
|
||||
public:
|
||||
VertexLayoutBGFX(Device* device, const std::vector<Element>& elements);
|
||||
~VertexLayoutBGFX();
|
||||
|
||||
const bgfx::VertexLayout& getBGFXLayout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
static bgfx::Attrib::Enum getAttribType(unsigned int semanticId);
|
||||
|
||||
private:
|
||||
bgfx::VertexLayout layout;
|
||||
};
|
||||
|
||||
template<typename Base>
|
||||
class GeometryBufferBGFX : public Base
|
||||
{
|
||||
public:
|
||||
GeometryBufferBGFX(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, bool isIndex);
|
||||
~GeometryBufferBGFX();
|
||||
|
||||
virtual void* lock(GeometryBuffer::LockMode mode);
|
||||
virtual void unlock();
|
||||
|
||||
virtual void upload(unsigned int offset, const void* data, unsigned int size);
|
||||
|
||||
void createVertexBuffer(const bgfx::VertexLayout& layout);
|
||||
|
||||
bgfx::DynamicVertexBufferHandle getVertexHandle() const
|
||||
{
|
||||
return vertexHandle;
|
||||
}
|
||||
bgfx::DynamicIndexBufferHandle getIndexHandle() const
|
||||
{
|
||||
return indexHandle;
|
||||
}
|
||||
|
||||
const bgfx::Memory* getMemory() const
|
||||
{
|
||||
return memory;
|
||||
}
|
||||
|
||||
protected:
|
||||
void create();
|
||||
|
||||
private:
|
||||
bool isIndex;
|
||||
bgfx::DynamicVertexBufferHandle vertexHandle;
|
||||
bgfx::DynamicIndexBufferHandle indexHandle;
|
||||
const bgfx::Memory* memory;
|
||||
void* locked;
|
||||
};
|
||||
|
||||
class VertexBufferBGFX : public GeometryBufferBGFX<VertexBuffer>
|
||||
{
|
||||
public:
|
||||
VertexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
~VertexBufferBGFX();
|
||||
};
|
||||
|
||||
class IndexBufferBGFX : public GeometryBufferBGFX<IndexBuffer>
|
||||
{
|
||||
public:
|
||||
IndexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
~IndexBufferBGFX();
|
||||
};
|
||||
|
||||
class GeometryBGFX : public Geometry
|
||||
{
|
||||
public:
|
||||
GeometryBGFX(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
|
||||
~GeometryBGFX();
|
||||
|
||||
void bindBuffers(unsigned int offset, unsigned int count);
|
||||
void draw(bgfx::ViewId view, Primitive primitive, unsigned int offset, unsigned int count);
|
||||
|
||||
private:
|
||||
unsigned int indexElementSize;
|
||||
|
||||
static uint64_t convertPrimitive(Primitive primitive);
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
14
engine/gfx/src/API/BGFX/HeadersBGFX.hpp
Normal file
14
engine/gfx/src/API/BGFX/HeadersBGFX.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// BGFX headers
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <bgfx/platform.h>
|
||||
|
||||
// Common macros
|
||||
#ifndef AYAASSERT
|
||||
#define AYAASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#endif
|
||||
293
engine/gfx/src/API/BGFX/ShaderBGFX.cpp
Normal file
293
engine/gfx/src/API/BGFX/ShaderBGFX.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
#include "ShaderBGFX.hpp"
|
||||
#include "DeviceBGFX.hpp"
|
||||
#include "GeometryBGFX.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
VertexShaderBGFX::VertexShaderBGFX(Device* device, const std::vector<char>& bytecode)
|
||||
: VertexShader(device)
|
||||
{
|
||||
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
|
||||
handle = bgfx::createShader(mem);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw std::runtime_error("Failed to create vertex shader");
|
||||
}
|
||||
}
|
||||
|
||||
VertexShaderBGFX::~VertexShaderBGFX()
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexShaderBGFX::reloadBytecode(const std::vector<char>& bytecode)
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
|
||||
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
|
||||
handle = bgfx::createShader(mem);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw std::runtime_error("Failed to reload vertex shader");
|
||||
}
|
||||
}
|
||||
|
||||
FragmentShaderBGFX::FragmentShaderBGFX(Device* device, const std::vector<char>& bytecode)
|
||||
: FragmentShader(device)
|
||||
{
|
||||
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
|
||||
handle = bgfx::createShader(mem);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw std::runtime_error("Failed to create fragment shader");
|
||||
}
|
||||
}
|
||||
|
||||
FragmentShaderBGFX::~FragmentShaderBGFX()
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void FragmentShaderBGFX::reloadBytecode(const std::vector<char>& bytecode)
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
|
||||
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
|
||||
handle = bgfx::createShader(mem);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw std::runtime_error("Failed to reload fragment shader");
|
||||
}
|
||||
}
|
||||
|
||||
ShaderProgramBGFX::ShaderProgramBGFX(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
|
||||
: ShaderProgram(device, vertexShader, fragmentShader)
|
||||
, cachedGlobalVersion(0)
|
||||
, maxWorldTransforms(0)
|
||||
, samplerMask(0)
|
||||
, nextConstantHandle(0)
|
||||
{
|
||||
VertexShaderBGFX* vs = static_cast<VertexShaderBGFX*>(vertexShader.get());
|
||||
FragmentShaderBGFX* fs = static_cast<FragmentShaderBGFX*>(fragmentShader.get());
|
||||
|
||||
handle = bgfx::createProgram(vs->getHandle(), fs->getHandle(), false);
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw std::runtime_error("Failed to create shader program");
|
||||
}
|
||||
|
||||
// Create uniforms for world matrices
|
||||
worldMatrixUniform = bgfx::createUniform("u_worldMatrix", bgfx::UniformType::Mat4);
|
||||
worldMatrixArrayUniform = bgfx::createUniform("u_worldMatrixArray", bgfx::UniformType::Vec4);
|
||||
|
||||
if (bgfx::isValid(worldMatrixArrayUniform))
|
||||
{
|
||||
maxWorldTransforms = 32; // Default maximum, can be adjusted
|
||||
}
|
||||
else if (bgfx::isValid(worldMatrixUniform))
|
||||
{
|
||||
maxWorldTransforms = 1;
|
||||
}
|
||||
|
||||
// In BGFX, samplers are bound separately, so we'll assume all texture stages could be used
|
||||
samplerMask = 0xFFFF;
|
||||
}
|
||||
|
||||
ShaderProgramBGFX::~ShaderProgramBGFX()
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
|
||||
if (bgfx::isValid(worldMatrixUniform))
|
||||
{
|
||||
bgfx::destroy(worldMatrixUniform);
|
||||
}
|
||||
|
||||
if (bgfx::isValid(worldMatrixArrayUniform))
|
||||
{
|
||||
bgfx::destroy(worldMatrixArrayUniform);
|
||||
}
|
||||
|
||||
// Destroy all created uniforms
|
||||
for (auto& pair : uniforms)
|
||||
{
|
||||
if (bgfx::isValid(pair.second.handle))
|
||||
{
|
||||
bgfx::destroy(pair.second.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ShaderProgramBGFX::getConstantHandle(const char* name) const
|
||||
{
|
||||
// Check if we already have this uniform
|
||||
for (const auto& pair : handleToName)
|
||||
{
|
||||
if (pair.second == name)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new handle - we need to make this non-const to modify handleToName
|
||||
// In practice, this is fine as we're just caching uniform lookups
|
||||
ShaderProgramBGFX* mutableThis = const_cast<ShaderProgramBGFX*>(this);
|
||||
|
||||
// Create uniform on first use
|
||||
std::string uniformName = name;
|
||||
bgfx::UniformHandle uniformHandle = bgfx::createUniform(uniformName.c_str(), bgfx::UniformType::Vec4);
|
||||
|
||||
if (bgfx::isValid(uniformHandle))
|
||||
{
|
||||
int handle = mutableThis->nextConstantHandle++;
|
||||
|
||||
UniformInfo info;
|
||||
info.handle = uniformHandle;
|
||||
info.type = bgfx::UniformType::Vec4;
|
||||
info.size = 1;
|
||||
|
||||
mutableThis->uniforms[uniformName] = info;
|
||||
mutableThis->handleToName[handle] = uniformName;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int ShaderProgramBGFX::getMaxWorldTransforms() const
|
||||
{
|
||||
return maxWorldTransforms;
|
||||
}
|
||||
|
||||
unsigned int ShaderProgramBGFX::getSamplerMask() const
|
||||
{
|
||||
return samplerMask;
|
||||
}
|
||||
|
||||
void ShaderProgramBGFX::updateGlobalConstants(const void* globalData, unsigned int globalVersion)
|
||||
{
|
||||
if (cachedGlobalVersion == globalVersion)
|
||||
return;
|
||||
|
||||
cachedGlobalVersion = globalVersion;
|
||||
|
||||
// Update global constants
|
||||
const std::vector<ShaderGlobalConstant>& globalConstants = static_cast<DeviceBGFX*>(device)->getGlobalConstants();
|
||||
|
||||
for (size_t i = 0; i < globalConstants.size(); ++i)
|
||||
{
|
||||
const ShaderGlobalConstant& gc = globalConstants[i];
|
||||
|
||||
// Check if we have a uniform for this constant
|
||||
auto it = uniforms.find(gc.name);
|
||||
if (it == uniforms.end())
|
||||
{
|
||||
// Create uniform on first use
|
||||
std::string uniformName = gc.name;
|
||||
bgfx::UniformHandle uniformHandle = bgfx::createUniform(uniformName.c_str(), bgfx::UniformType::Vec4);
|
||||
if (bgfx::isValid(uniformHandle))
|
||||
{
|
||||
UniformInfo info;
|
||||
info.handle = uniformHandle;
|
||||
info.type = bgfx::UniformType::Vec4;
|
||||
info.size = 1;
|
||||
|
||||
uniforms[uniformName] = info;
|
||||
it = uniforms.find(uniformName);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const float* data = reinterpret_cast<const float*>(static_cast<const char*>(globalData) + gc.offset);
|
||||
bgfx::setUniform(it->second.handle, data);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramBGFX::setWorldTransforms4x3(const float* data, size_t matrixCount)
|
||||
{
|
||||
if (matrixCount == 0)
|
||||
return;
|
||||
|
||||
if (matrixCount == 1 && bgfx::isValid(worldMatrixUniform))
|
||||
{
|
||||
// Convert 4x3 matrix to 4x4
|
||||
float matrix[16];
|
||||
matrix[0] = data[0];
|
||||
matrix[1] = data[1];
|
||||
matrix[2] = data[2];
|
||||
matrix[3] = 0.0f;
|
||||
|
||||
matrix[4] = data[4];
|
||||
matrix[5] = data[5];
|
||||
matrix[6] = data[6];
|
||||
matrix[7] = 0.0f;
|
||||
|
||||
matrix[8] = data[8];
|
||||
matrix[9] = data[9];
|
||||
matrix[10] = data[10];
|
||||
matrix[11] = 0.0f;
|
||||
|
||||
matrix[12] = data[12];
|
||||
matrix[13] = data[13];
|
||||
matrix[14] = data[14];
|
||||
matrix[15] = 1.0f;
|
||||
|
||||
bgfx::setUniform(worldMatrixUniform, matrix);
|
||||
}
|
||||
else if (bgfx::isValid(worldMatrixArrayUniform))
|
||||
{
|
||||
AYAASSERT(matrixCount <= maxWorldTransforms);
|
||||
|
||||
// Pass array of vec4s (3 per matrix for 4x3)
|
||||
bgfx::setUniform(worldMatrixArrayUniform, data, matrixCount * 3);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramBGFX::setConstant(int handle, const float* data, size_t vectorCount)
|
||||
{
|
||||
if (handle < 0)
|
||||
return;
|
||||
|
||||
auto it = handleToName.find(handle);
|
||||
if (it == handleToName.end())
|
||||
return;
|
||||
|
||||
auto uniformIt = uniforms.find(it->second);
|
||||
if (uniformIt == uniforms.end())
|
||||
return;
|
||||
|
||||
bgfx::setUniform(uniformIt->second.handle, data, vectorCount);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
93
engine/gfx/src/API/BGFX/ShaderBGFX.hpp
Normal file
93
engine/gfx/src/API/BGFX/ShaderBGFX.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Shader.hpp"
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class DeviceBGFX;
|
||||
|
||||
class VertexShaderBGFX : public VertexShader
|
||||
{
|
||||
public:
|
||||
VertexShaderBGFX(Device* device, const std::vector<char>& bytecode);
|
||||
~VertexShaderBGFX();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode);
|
||||
|
||||
bgfx::ShaderHandle getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
bgfx::ShaderHandle handle;
|
||||
};
|
||||
|
||||
class FragmentShaderBGFX : public FragmentShader
|
||||
{
|
||||
public:
|
||||
FragmentShaderBGFX(Device* device, const std::vector<char>& bytecode);
|
||||
~FragmentShaderBGFX();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode);
|
||||
|
||||
bgfx::ShaderHandle getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
bgfx::ShaderHandle handle;
|
||||
};
|
||||
|
||||
class ShaderProgramBGFX : public ShaderProgram
|
||||
{
|
||||
public:
|
||||
ShaderProgramBGFX(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
|
||||
~ShaderProgramBGFX();
|
||||
|
||||
virtual int getConstantHandle(const char* name) const;
|
||||
|
||||
virtual unsigned int getMaxWorldTransforms() const;
|
||||
virtual unsigned int getSamplerMask() const;
|
||||
|
||||
void updateGlobalConstants(const void* globalData, unsigned int globalVersion);
|
||||
void setWorldTransforms4x3(const float* data, size_t matrixCount);
|
||||
void setConstant(int handle, const float* data, size_t vectorCount);
|
||||
|
||||
bgfx::ProgramHandle getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
bgfx::ProgramHandle handle;
|
||||
|
||||
struct UniformInfo
|
||||
{
|
||||
bgfx::UniformHandle handle;
|
||||
bgfx::UniformType::Enum type;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
std::map<std::string, UniformInfo> uniforms;
|
||||
std::map<int, std::string> handleToName;
|
||||
|
||||
bgfx::UniformHandle worldMatrixUniform;
|
||||
bgfx::UniformHandle worldMatrixArrayUniform;
|
||||
|
||||
unsigned int cachedGlobalVersion;
|
||||
unsigned int maxWorldTransforms;
|
||||
unsigned int samplerMask;
|
||||
|
||||
int nextConstantHandle;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
269
engine/gfx/src/API/BGFX/TextureBGFX.cpp
Normal file
269
engine/gfx/src/API/BGFX/TextureBGFX.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "TextureBGFX.hpp"
|
||||
#include "DeviceBGFX.hpp"
|
||||
#include "FramebufferBGFX.hpp"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
bgfx::TextureFormat::Enum TextureBGFX::getBGFXFormat(Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format_L8:
|
||||
return bgfx::TextureFormat::R8;
|
||||
case Format_LA8:
|
||||
return bgfx::TextureFormat::RG8;
|
||||
case Format_RGB5A1:
|
||||
return bgfx::TextureFormat::RGB5A1;
|
||||
case Format_RGBA8:
|
||||
return bgfx::TextureFormat::RGBA8;
|
||||
case Format_RG16:
|
||||
return bgfx::TextureFormat::RG16;
|
||||
case Format_RGBA16F:
|
||||
return bgfx::TextureFormat::RGBA16F;
|
||||
case Format_BC1:
|
||||
return bgfx::TextureFormat::BC1;
|
||||
case Format_BC2:
|
||||
return bgfx::TextureFormat::BC2;
|
||||
case Format_BC3:
|
||||
return bgfx::TextureFormat::BC3;
|
||||
case Format_PVRTC_RGB2:
|
||||
return bgfx::TextureFormat::PTC12;
|
||||
case Format_PVRTC_RGBA2:
|
||||
return bgfx::TextureFormat::PTC12A;
|
||||
case Format_PVRTC_RGB4:
|
||||
return bgfx::TextureFormat::PTC14;
|
||||
case Format_PVRTC_RGBA4:
|
||||
return bgfx::TextureFormat::PTC14A;
|
||||
case Format_ETC1:
|
||||
return bgfx::TextureFormat::ETC1;
|
||||
case Format_D16:
|
||||
return bgfx::TextureFormat::D16;
|
||||
case Format_D24S8:
|
||||
return bgfx::TextureFormat::D24S8;
|
||||
default:
|
||||
return bgfx::TextureFormat::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t getBGFXTextureFlags(Texture::Usage usage, unsigned int mipLevels)
|
||||
{
|
||||
uint64_t flags = BGFX_TEXTURE_NONE;
|
||||
|
||||
if (usage == Texture::Usage_Renderbuffer)
|
||||
{
|
||||
flags |= BGFX_TEXTURE_RT;
|
||||
}
|
||||
|
||||
if (mipLevels > 1)
|
||||
{
|
||||
flags |= BGFX_TEXTURE_NONE; // Mips are created by default
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static uint32_t getBGFXSamplerFlags(const SamplerState& state)
|
||||
{
|
||||
uint32_t flags = BGFX_SAMPLER_NONE;
|
||||
|
||||
// Filter mode
|
||||
switch (state.getFilter())
|
||||
{
|
||||
case SamplerState::Filter_Point:
|
||||
flags |= BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT;
|
||||
break;
|
||||
case SamplerState::Filter_Linear:
|
||||
flags |= BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC;
|
||||
break;
|
||||
case SamplerState::Filter_Anisotropic:
|
||||
flags |= BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Address mode
|
||||
switch (state.getAddress())
|
||||
{
|
||||
case SamplerState::Address_Wrap:
|
||||
// Wrap is the default - no flags needed
|
||||
break;
|
||||
case SamplerState::Address_Clamp:
|
||||
flags |= BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP;
|
||||
break;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
TextureBGFX::TextureBGFX(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
|
||||
: Texture(device, type, format, width, height, depth, mipLevels, usage)
|
||||
, cachedState(SamplerState::Filter_Count)
|
||||
{
|
||||
bgfx::TextureFormat::Enum bgfxFormat = getBGFXFormat(format);
|
||||
|
||||
if (bgfxFormat == bgfx::TextureFormat::Unknown)
|
||||
{
|
||||
throw Aya::runtime_error("Unsupported texture format");
|
||||
}
|
||||
|
||||
uint64_t flags = getBGFXTextureFlags(usage, mipLevels);
|
||||
|
||||
if (type == Type_3D)
|
||||
{
|
||||
handle = bgfx::createTexture3D(width, height, depth, mipLevels > 1, bgfxFormat, flags);
|
||||
}
|
||||
else if (type == Type_Cube)
|
||||
{
|
||||
handle = bgfx::createTextureCube(width, mipLevels > 1, 1, bgfxFormat, flags);
|
||||
}
|
||||
else if (type == Type_2DMultisampled)
|
||||
{
|
||||
handle = bgfx::createTexture2D(width, height, mipLevels > 1, 1, bgfxFormat, flags | BGFX_TEXTURE_RT_MSAA_X4);
|
||||
}
|
||||
else // Type_2D
|
||||
{
|
||||
handle = bgfx::createTexture2D(width, height, mipLevels > 1, 1, bgfxFormat, flags);
|
||||
}
|
||||
|
||||
if (!bgfx::isValid(handle))
|
||||
{
|
||||
throw Aya::runtime_error("Failed to create texture");
|
||||
}
|
||||
}
|
||||
|
||||
TextureBGFX::~TextureBGFX()
|
||||
{
|
||||
if (bgfx::isValid(handle))
|
||||
{
|
||||
bgfx::destroy(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureBGFX::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
|
||||
{
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
AYAASSERT(mip < mipLevels);
|
||||
|
||||
unsigned int mipWidth = getMipSide(width, mip);
|
||||
unsigned int mipHeight = getMipSide(height, mip);
|
||||
unsigned int mipDepth = getMipSide(depth, mip);
|
||||
|
||||
AYAASSERT(region.x + region.width <= mipWidth);
|
||||
AYAASSERT(region.y + region.height <= mipHeight);
|
||||
AYAASSERT(region.z + region.depth <= mipDepth);
|
||||
AYAASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
|
||||
|
||||
const bgfx::Memory* mem = bgfx::copy(data, size);
|
||||
|
||||
if (type == Type_3D)
|
||||
{
|
||||
bgfx::updateTexture3D(handle, mip, region.x, region.y, region.z, region.width, region.height, region.depth, mem);
|
||||
}
|
||||
else if (type == Type_Cube)
|
||||
{
|
||||
bgfx::updateTextureCube(handle, 0, index, mip, region.x, region.y, region.width, region.height, mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
bgfx::updateTexture2D(handle, 0, mip, region.x, region.y, region.width, region.height, mem);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureBGFX::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
|
||||
{
|
||||
// BGFX doesn't support direct texture download in the same way as GL
|
||||
// This would require reading from a framebuffer that the texture is attached to
|
||||
const DeviceCapsBGFX& caps = static_cast<DeviceBGFX*>(device)->getCapsBGFX();
|
||||
|
||||
if (!caps.supportsTextureReadBack)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reading back texture data would require creating a framebuffer and using bgfx::readTexture
|
||||
// which is async in BGFX
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextureBGFX::supportsLocking() const
|
||||
{
|
||||
// BGFX doesn't support texture locking in the traditional sense
|
||||
return false;
|
||||
}
|
||||
|
||||
Texture::LockResult TextureBGFX::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
|
||||
{
|
||||
// BGFX doesn't support direct texture locking
|
||||
LockResult result = {0, 0, 0};
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureBGFX::unlock(unsigned int index, unsigned int mip)
|
||||
{
|
||||
// BGFX doesn't support direct texture locking
|
||||
}
|
||||
|
||||
shared_ptr<Renderbuffer> TextureBGFX::getRenderbuffer(unsigned int index, unsigned int mip)
|
||||
{
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
AYAASSERT(mip < mipLevels);
|
||||
AYAASSERT(usage == Usage_Renderbuffer);
|
||||
|
||||
std::pair<unsigned, unsigned> key(index, mip);
|
||||
|
||||
shared_ptr<Renderbuffer> result = renderBuffers[key].lock();
|
||||
|
||||
if (!result)
|
||||
{
|
||||
result.reset(new RenderbufferBGFX(device, format, getMipSide(width, mip), getMipSide(height, mip), getType() == Type_2DMultisampled ? 4 : 1));
|
||||
renderBuffers[key] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureBGFX::commitChanges()
|
||||
{
|
||||
// BGFX handles texture updates immediately
|
||||
// No deferred commit needed
|
||||
}
|
||||
|
||||
void TextureBGFX::generateMipmaps()
|
||||
{
|
||||
// BGFX generates mipmaps automatically when creating textures with mipLevels > 1
|
||||
// Or we can request regeneration if needed
|
||||
// Note: Not all backends support runtime mipmap generation
|
||||
}
|
||||
|
||||
void TextureBGFX::bind(unsigned int stage, const SamplerState& state)
|
||||
{
|
||||
uint32_t samplerFlags = getBGFXSamplerFlags(state);
|
||||
|
||||
// In BGFX, textures are set per-draw call, not globally
|
||||
// We just set the texture and sampler for the given stage
|
||||
// Note: This requires a valid sampler uniform handle to be passed from the caller
|
||||
bgfx::setTexture(stage, BGFX_INVALID_HANDLE, handle, samplerFlags);
|
||||
|
||||
cachedState = state;
|
||||
}
|
||||
|
||||
void TextureBGFX::bindWithUniform(unsigned int stage, bgfx::UniformHandle samplerUniform, const SamplerState& state)
|
||||
{
|
||||
uint32_t samplerFlags = getBGFXSamplerFlags(state);
|
||||
|
||||
// Set texture with the proper sampler uniform
|
||||
bgfx::setTexture(stage, samplerUniform, handle, samplerFlags);
|
||||
|
||||
cachedState = state;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
57
engine/gfx/src/API/BGFX/TextureBGFX.hpp
Normal file
57
engine/gfx/src/API/BGFX/TextureBGFX.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Texture.hpp"
|
||||
#include "Core/States.hpp"
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class DeviceBGFX;
|
||||
|
||||
class TextureBGFX
|
||||
: public Texture
|
||||
, public boost::enable_shared_from_this<TextureBGFX>
|
||||
{
|
||||
public:
|
||||
TextureBGFX(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
|
||||
~TextureBGFX();
|
||||
|
||||
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size);
|
||||
|
||||
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size);
|
||||
|
||||
virtual bool supportsLocking() const;
|
||||
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region);
|
||||
virtual void unlock(unsigned int index, unsigned int mip);
|
||||
|
||||
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip);
|
||||
|
||||
virtual void commitChanges();
|
||||
virtual void generateMipmaps();
|
||||
|
||||
void bind(unsigned int stage, const SamplerState& state);
|
||||
void bindWithUniform(unsigned int stage, bgfx::UniformHandle samplerUniform, const SamplerState& state);
|
||||
|
||||
bgfx::TextureHandle getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
static bgfx::TextureFormat::Enum getBGFXFormat(Format format);
|
||||
|
||||
private:
|
||||
bgfx::TextureHandle handle;
|
||||
SamplerState cachedState;
|
||||
|
||||
typedef std::map<std::pair<unsigned, unsigned>, weak_ptr<Renderbuffer>> RenderBufferMap;
|
||||
RenderBufferMap renderBuffers;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
201
engine/gfx/src/API/GL/ContextEGL.cpp
Normal file
201
engine/gfx/src/API/GL/ContextEGL.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "ContextGL.hpp"
|
||||
#include "HeadersGL.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <glad/egl.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <cstdlib>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
enum class DisplayBackend
|
||||
{
|
||||
Wayland,
|
||||
X11,
|
||||
Unknown
|
||||
};
|
||||
|
||||
static DisplayBackend detectDisplayBackend()
|
||||
{
|
||||
const char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
|
||||
const char* x11Display = std::getenv("DISPLAY");
|
||||
|
||||
if (waylandDisplay && waylandDisplay[0])
|
||||
return DisplayBackend::Wayland;
|
||||
if (x11Display && x11Display[0])
|
||||
return DisplayBackend::X11;
|
||||
return DisplayBackend::Unknown;
|
||||
}
|
||||
|
||||
class ContextEGL : public ContextGL
|
||||
{
|
||||
public:
|
||||
ContextEGL(void* windowHandle, void* windowScreen, int w, int h)
|
||||
{
|
||||
DisplayBackend backend = detectDisplayBackend();
|
||||
|
||||
// Load core EGL API
|
||||
gladLoaderLoadEGL(nullptr);
|
||||
|
||||
// Load required EGL extensions
|
||||
auto eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
auto eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
||||
|
||||
if (!eglGetPlatformDisplayEXT || !eglCreatePlatformWindowSurfaceEXT)
|
||||
throw Aya::runtime_error("Missing required EGL extension functions");
|
||||
|
||||
// Create EGL display based on backend
|
||||
if (backend == DisplayBackend::Wayland)
|
||||
{
|
||||
wl_display* wlDisplay = static_cast<wl_display*>(windowScreen);
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, wlDisplay, nullptr);
|
||||
}
|
||||
else if (backend == DisplayBackend::X11)
|
||||
{
|
||||
::Display* x11Display = static_cast<::Display*>(windowScreen);
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, x11Display, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Aya::runtime_error("Unknown or unsupported display backend");
|
||||
}
|
||||
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
throw Aya::runtime_error("eglGetPlatformDisplayEXT failed: %x", eglGetError());
|
||||
|
||||
if (!eglInitialize(display, nullptr, nullptr))
|
||||
throw Aya::runtime_error("eglInitialize failed: %x", eglGetError());
|
||||
|
||||
if (!gladLoaderLoadEGL(display))
|
||||
throw Aya::runtime_error("gladLoaderLoadEGL failed");
|
||||
|
||||
// EGL Config
|
||||
static const EGLint configAttribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE,
|
||||
8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_NONE};
|
||||
|
||||
EGLint numConfigs;
|
||||
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) || numConfigs == 0)
|
||||
throw Aya::runtime_error("eglChooseConfig failed: %x", eglGetError());
|
||||
|
||||
// Surface creation
|
||||
if (windowHandle)
|
||||
{
|
||||
if (backend == DisplayBackend::Wayland)
|
||||
{
|
||||
|
||||
surface = eglCreatePlatformWindowSurfaceEXT(display, config, windowHandle, nullptr);
|
||||
}
|
||||
else // X11
|
||||
{
|
||||
surface = eglCreatePlatformWindowSurfaceEXT(display, config, windowHandle, nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create pbuffer
|
||||
static const EGLint pbufferAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
||||
surface = eglCreatePbufferSurface(display, config, pbufferAttribs);
|
||||
}
|
||||
|
||||
if (surface == EGL_NO_SURFACE)
|
||||
throw Aya::runtime_error("Failed to create EGL surface: %x", eglGetError());
|
||||
|
||||
// Bind OpenGL API
|
||||
eglBindAPI(EGL_OPENGL_API);
|
||||
|
||||
static const EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK,
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
#ifndef NDEBUG
|
||||
EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
|
||||
#endif
|
||||
EGL_NONE};
|
||||
|
||||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
|
||||
if (context == EGL_NO_CONTEXT)
|
||||
throw Aya::runtime_error("eglCreateContext failed: %x", eglGetError());
|
||||
|
||||
if (!eglMakeCurrent(display, surface, surface, context))
|
||||
throw Aya::runtime_error("eglMakeCurrent failed: %x", eglGetError());
|
||||
|
||||
gladLoaderLoadGL();
|
||||
}
|
||||
|
||||
virtual void setCurrent() override
|
||||
{
|
||||
eglMakeCurrent(display, surface, surface, context);
|
||||
}
|
||||
|
||||
virtual void swapBuffers() override
|
||||
{
|
||||
eglSwapBuffers(display, surface);
|
||||
}
|
||||
|
||||
virtual unsigned int getMainFramebufferId() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool isMainFramebufferRetina() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void resizeWindow(int w, int h)
|
||||
{
|
||||
if (egl_window)
|
||||
{
|
||||
wl_egl_window_resize(egl_window, w, h, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) override
|
||||
{
|
||||
EGLint w, h;
|
||||
eglQuerySurface(display, surface, EGL_WIDTH, &w);
|
||||
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
|
||||
return std::make_pair((unsigned int)w, (unsigned int)h);
|
||||
}
|
||||
|
||||
virtual ~ContextEGL()
|
||||
{
|
||||
if (display != EGL_NO_DISPLAY)
|
||||
{
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (context != EGL_NO_CONTEXT)
|
||||
eglDestroyContext(display, context);
|
||||
if (surface != EGL_NO_SURFACE)
|
||||
eglDestroySurface(display, surface);
|
||||
eglTerminate(display);
|
||||
}
|
||||
|
||||
if (egl_window)
|
||||
{
|
||||
wl_egl_window_destroy(egl_window);
|
||||
egl_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
EGLContext context = EGL_NO_CONTEXT;
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
EGLConfig config = nullptr;
|
||||
wl_egl_window* egl_window = nullptr;
|
||||
};
|
||||
|
||||
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
|
||||
{
|
||||
return new ContextEGL(windowHandle, display, w, h);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
28
engine/gfx/src/API/GL/ContextGL.hpp
Normal file
28
engine/gfx/src/API/GL/ContextGL.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class ContextGL
|
||||
{
|
||||
public:
|
||||
static ContextGL* create(void* windowHandle, void* display, int w, int h);
|
||||
|
||||
virtual ~ContextGL() {}
|
||||
|
||||
virtual void setCurrent() = 0;
|
||||
virtual void swapBuffers() = 0;
|
||||
|
||||
virtual unsigned int getMainFramebufferId() = 0;
|
||||
virtual bool isMainFramebufferRetina() = 0;
|
||||
|
||||
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) = 0;
|
||||
virtual void resizeWindow(int width, int height) = 0;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
286
engine/gfx/src/API/GL/ContextGLAndroid.cpp
Normal file
286
engine/gfx/src/API/GL/ContextGLAndroid.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
#include "ContextGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
// GL extensions
|
||||
static GLvoid(GL_APIENTRY* glBindVertexArrayPtr)(GLuint array);
|
||||
static GLvoid(GL_APIENTRY* glDeleteVertexArraysPtr)(GLsizei n, const GLuint* arrays);
|
||||
static GLvoid(GL_APIENTRY* glGenVertexArraysPtr)(GLsizei n, const GLuint* arrays);
|
||||
static GLvoid*(GL_APIENTRY* glMapBufferPtr)(GLenum target, GLenum access);
|
||||
static GLboolean(GL_APIENTRY* glUnmapBufferPtr)(GLenum target);
|
||||
static GLvoid*(GL_APIENTRY* glMapBufferRangePtr)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
static GLvoid(GL_APIENTRY* glTexImage3DPtr)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth,
|
||||
GLint border, GLenum format, GLenum type, const GLvoid* pixels);
|
||||
static GLvoid(GL_APIENTRY* glTexSubImage3DPtr)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height,
|
||||
GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels);
|
||||
static GLvoid(GL_APIENTRY* glTexStorage2DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
static GLvoid(GL_APIENTRY* glTexStorage3DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
|
||||
static GLsync(GL_APIENTRY* glFenceSyncPtr)(GLenum condition, GLbitfield flags);
|
||||
static void(GL_APIENTRY* glDeleteSyncPtr)(GLsync sync);
|
||||
static GLenum(GL_APIENTRY* glClientWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
static void(GL_APIENTRY* glWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
static GLvoid(GL_APIENTRY* glBlitFramebufferPtr)(
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
static GLvoid(GL_APIENTRY* glRenderbufferStorageMultisamplePtr)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
static GLvoid(GL_APIENTRY* glInvalidateFramebufferPtr)(GLenum target, GLsizei numAttachments, const GLenum* attachments);
|
||||
|
||||
template<typename T>
|
||||
static void loadExtensionGL(T& ptr, const char* namecore, const char* nameext)
|
||||
{
|
||||
if (!ptr)
|
||||
ptr = reinterpret_cast<T>(eglGetProcAddress(namecore));
|
||||
|
||||
if (!ptr)
|
||||
ptr = reinterpret_cast<T>(eglGetProcAddress(nameext));
|
||||
}
|
||||
|
||||
#define LOAD_EXTENSION_GL(name, suffix) loadExtensionGL(name##Ptr, #name, #name #suffix)
|
||||
|
||||
// GL stubs
|
||||
GLvoid glBindVertexArray(GLuint array)
|
||||
{
|
||||
glBindVertexArrayPtr(array);
|
||||
}
|
||||
|
||||
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint* arrays)
|
||||
{
|
||||
glDeleteVertexArraysPtr(n, arrays);
|
||||
}
|
||||
|
||||
GLvoid glGenVertexArrays(GLsizei n, GLuint* arrays)
|
||||
{
|
||||
glGenVertexArraysPtr(n, arrays);
|
||||
}
|
||||
|
||||
GLvoid* glMapBuffer(GLenum target, GLenum access)
|
||||
{
|
||||
return glMapBufferPtr(target, access);
|
||||
}
|
||||
|
||||
GLboolean glUnmapBuffer(GLenum target)
|
||||
{
|
||||
return glUnmapBufferPtr(target);
|
||||
}
|
||||
|
||||
GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
|
||||
{
|
||||
return glMapBufferRangePtr(target, offset, length, access);
|
||||
}
|
||||
|
||||
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
|
||||
{
|
||||
glTexStorage2DPtr(target, levels, internalformat, width, height);
|
||||
}
|
||||
|
||||
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format,
|
||||
GLenum type, const GLvoid* pixels)
|
||||
{
|
||||
glTexImage3DPtr(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
||||
}
|
||||
|
||||
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
|
||||
GLenum format, GLenum type, const GLvoid* pixels)
|
||||
{
|
||||
glTexSubImage3DPtr(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
||||
}
|
||||
|
||||
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
|
||||
{
|
||||
glTexStorage3DPtr(target, levels, internalformat, width, height, depth);
|
||||
}
|
||||
|
||||
GLsync glFenceSync(GLenum condition, GLbitfield flags)
|
||||
{
|
||||
return glFenceSyncPtr(condition, flags);
|
||||
}
|
||||
|
||||
void glDeleteSync(GLsync sync)
|
||||
{
|
||||
glDeleteSyncPtr(sync);
|
||||
}
|
||||
|
||||
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
|
||||
{
|
||||
return glClientWaitSyncPtr(sync, flags, timeout);
|
||||
}
|
||||
|
||||
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
|
||||
{
|
||||
glWaitSyncPtr(sync, flags, timeout);
|
||||
}
|
||||
|
||||
GLvoid glBlitFramebuffer(
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
|
||||
{
|
||||
glBlitFramebufferPtr(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
|
||||
{
|
||||
glRenderbufferStorageMultisamplePtr(target, samples, internalformat, width, height);
|
||||
}
|
||||
|
||||
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments)
|
||||
{
|
||||
glInvalidateFramebufferPtr(target, numAttachments, attachments);
|
||||
}
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class ContextGLAndroid : public ContextGL
|
||||
{
|
||||
public:
|
||||
ContextGLAndroid(void* windowHandle)
|
||||
{
|
||||
aNativeWindow = static_cast<ANativeWindow*>(windowHandle);
|
||||
|
||||
ANativeWindow_acquire(aNativeWindow);
|
||||
|
||||
FASTLOG2(FLog::Graphics, "Window size: %dx%d", ANativeWindow_getWidth(aNativeWindow), ANativeWindow_getHeight(aNativeWindow));
|
||||
|
||||
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
throw runtime_error("Error creating context: eglGetDisplay %x", eglGetError());
|
||||
|
||||
if (!eglInitialize(display, 0, 0))
|
||||
throw runtime_error("Error creating context: eglInitialize %x", eglGetError());
|
||||
|
||||
EGLConfig config;
|
||||
|
||||
if (!tryChooseConfig(display, &config, 8, 8, 8, 24, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 16, 0) &&
|
||||
!tryChooseConfig(display, &config, 5, 6, 5, 16, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 24, 1) &&
|
||||
!tryChooseConfig(display, &config, 8, 8, 8, 16, 1) && !tryChooseConfig(display, &config, 5, 6, 5, 16, 1))
|
||||
throw runtime_error("Error creating context: could not find suitable config (%x)", eglGetError());
|
||||
|
||||
surface = eglCreateWindowSurface(display, config, aNativeWindow, 0);
|
||||
|
||||
if (!surface)
|
||||
{
|
||||
throw runtime_error("Error creating context: eglCreateWindowSurface %x", eglGetError());
|
||||
}
|
||||
|
||||
static const EGLint contextAttrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
||||
|
||||
context = eglCreateContext(display, config, NULL, contextAttrs);
|
||||
|
||||
if (!context)
|
||||
throw runtime_error("Error creating context: eglCreateContext %x", eglGetError());
|
||||
|
||||
if (!eglMakeCurrent(display, surface, surface, context))
|
||||
throw runtime_error("Error creating context: eglMakeCurrent %x", eglGetError());
|
||||
|
||||
eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth);
|
||||
eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight);
|
||||
|
||||
FASTLOG4(FLog::Graphics, "Initialized EGL context %p (surface %p) with renderbuffer %dx%d", context, surface, surfaceWidth, surfaceHeight);
|
||||
|
||||
EGLint minSwapInterval = -1;
|
||||
eglGetConfigAttrib(display, config, EGL_MIN_SWAP_INTERVAL, &minSwapInterval);
|
||||
FASTLOG1(FLog::Graphics, "EGL_MIN_SWAP_INTERVAL: %d", minSwapInterval);
|
||||
|
||||
if (eglSwapInterval(display, 0) == EGL_FALSE)
|
||||
{
|
||||
// This should be impossible as the interval is silently clamped to EGL_MIN_SWAP_INTERVAL.
|
||||
FASTLOG(FLog::Graphics, "*** eglSwapInterval EGL_FALSE");
|
||||
}
|
||||
|
||||
LOAD_EXTENSION_GL(glBindVertexArray, OES);
|
||||
LOAD_EXTENSION_GL(glDeleteVertexArrays, OES);
|
||||
LOAD_EXTENSION_GL(glGenVertexArrays, OES);
|
||||
LOAD_EXTENSION_GL(glMapBuffer, OES);
|
||||
LOAD_EXTENSION_GL(glUnmapBuffer, OES);
|
||||
LOAD_EXTENSION_GL(glMapBufferRange, EXT);
|
||||
LOAD_EXTENSION_GL(glTexImage3D, OES);
|
||||
LOAD_EXTENSION_GL(glTexSubImage3D, OES);
|
||||
LOAD_EXTENSION_GL(glTexStorage2D, EXT);
|
||||
LOAD_EXTENSION_GL(glTexStorage3D, EXT);
|
||||
LOAD_EXTENSION_GL(glFenceSync, EXT);
|
||||
LOAD_EXTENSION_GL(glDeleteSync, EXT);
|
||||
LOAD_EXTENSION_GL(glClientWaitSync, EXT);
|
||||
LOAD_EXTENSION_GL(glWaitSync, EXT);
|
||||
LOAD_EXTENSION_GL(glBlitFramebuffer, EXT);
|
||||
LOAD_EXTENSION_GL(glRenderbufferStorageMultisample, EXT);
|
||||
LOAD_EXTENSION_GL(glInvalidateFramebuffer, EXT);
|
||||
}
|
||||
|
||||
~ContextGLAndroid()
|
||||
{
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
eglDestroyContext(display, context);
|
||||
eglDestroySurface(display, surface);
|
||||
|
||||
eglTerminate(display);
|
||||
|
||||
ANativeWindow_release(aNativeWindow);
|
||||
}
|
||||
|
||||
virtual void setCurrent()
|
||||
{
|
||||
bool result = eglMakeCurrent(display, surface, surface, context);
|
||||
AYAASSERT(result);
|
||||
}
|
||||
|
||||
virtual void swapBuffers()
|
||||
{
|
||||
bool result = eglSwapBuffers(display, surface);
|
||||
AYAASSERT(result);
|
||||
}
|
||||
|
||||
virtual unsigned int getMainFramebufferId()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool isMainFramebufferRetina()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void resizeWindow(int w, int h)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
|
||||
{
|
||||
return std::make_pair(surfaceWidth, surfaceHeight);
|
||||
}
|
||||
|
||||
private:
|
||||
ANativeWindow* aNativeWindow;
|
||||
EGLDisplay display;
|
||||
EGLSurface surface;
|
||||
EGLContext context;
|
||||
EGLint surfaceWidth;
|
||||
EGLint surfaceHeight;
|
||||
|
||||
static bool tryChooseConfig(EGLDisplay display, EGLConfig* config, int redBits, int greenBits, int blueBits, int depthBits, int swapInterval)
|
||||
{
|
||||
const EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_MIN_SWAP_INTERVAL, swapInterval, EGL_RED_SIZE, redBits, EGL_GREEN_SIZE,
|
||||
greenBits, EGL_BLUE_SIZE, blueBits, EGL_DEPTH_SIZE, depthBits, EGL_NONE};
|
||||
|
||||
FASTLOG4(FLog::Graphics, "Trying to choose EGL config r%d g%d b%d d%d", redBits, greenBits, blueBits, depthBits);
|
||||
|
||||
EGLint numConfigs = 0;
|
||||
return eglChooseConfig(display, attribs, config, 1, &numConfigs) && numConfigs > 0;
|
||||
}
|
||||
};
|
||||
|
||||
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
|
||||
{
|
||||
return new ContextGLAndroid(windowHandle);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
177
engine/gfx/src/API/GL/ContextGLMac.mm
Normal file
177
engine/gfx/src/API/GL/ContextGLMac.mm
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "ContextGL.hpp"
|
||||
#include "HeadersGL.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
#endif
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
#ifdef __APPLE__
|
||||
class ContextGLMacOS : public ContextGL
|
||||
{
|
||||
public:
|
||||
ContextGLMacOS(void* windowHandle)
|
||||
{
|
||||
// Handle both NSWindow and NSView (Qt widgets)
|
||||
NSView* view = (__bridge NSView*)windowHandle;
|
||||
if (!view) {
|
||||
printf("ERROR: Invalid window/view handle\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if it's a window or view
|
||||
NSWindow* window = nil;
|
||||
NSView* targetView = view;
|
||||
|
||||
if ([view isKindOfClass:[NSWindow class]]) {
|
||||
window = (NSWindow*)view;
|
||||
targetView = [window contentView];
|
||||
nsWindow = window;
|
||||
} else {
|
||||
// If it's a view, try to get its window
|
||||
window = [view window];
|
||||
targetView = view;
|
||||
nsWindow = window;
|
||||
}
|
||||
|
||||
if (!targetView) {
|
||||
printf("ERROR: Could not get target view\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Creating native macOS OpenGL context...\n");
|
||||
|
||||
// Create OpenGL pixel format
|
||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
NSOpenGLPFAStencilSize, 8,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAAlphaSize, 8,
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFANoRecovery,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
0
|
||||
};
|
||||
|
||||
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
||||
if (!pixelFormat) {
|
||||
printf("Failed to create OpenGL pixel format\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create OpenGL context
|
||||
glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
|
||||
if (!glContext) {
|
||||
printf("Failed to create OpenGL context\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the target view
|
||||
[glContext setView:targetView];
|
||||
|
||||
// Make context current
|
||||
[glContext makeCurrentContext];
|
||||
|
||||
printf("Context created and made current successfully\n");
|
||||
|
||||
// Test OpenGL
|
||||
const GLubyte* version = glGetString(GL_VERSION);
|
||||
if (version) {
|
||||
printf("OpenGL Version: %s\n", version);
|
||||
}
|
||||
|
||||
const GLubyte* vendor = glGetString(GL_VENDOR);
|
||||
if (vendor) {
|
||||
printf("OpenGL Vendor: %s\n", vendor);
|
||||
}
|
||||
|
||||
const GLubyte* renderer = glGetString(GL_RENDERER);
|
||||
if (renderer) {
|
||||
printf("OpenGL Renderer: %s\n", renderer);
|
||||
}
|
||||
}
|
||||
|
||||
~ContextGLMacOS()
|
||||
{
|
||||
if (glContext) {
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
}
|
||||
}
|
||||
|
||||
virtual void setCurrent() override
|
||||
{
|
||||
if (glContext) {
|
||||
[glContext makeCurrentContext];
|
||||
}
|
||||
}
|
||||
|
||||
virtual void swapBuffers() override
|
||||
{
|
||||
if (glContext) {
|
||||
[glContext flushBuffer];
|
||||
}
|
||||
}
|
||||
|
||||
virtual unsigned int getMainFramebufferId() override
|
||||
{
|
||||
return 0; // Default framebuffer on macOS
|
||||
}
|
||||
|
||||
virtual bool isMainFramebufferRetina() override
|
||||
{
|
||||
if (!nsWindow) return false;
|
||||
|
||||
NSScreen* screen = [nsWindow screen];
|
||||
if (!screen) return false;
|
||||
|
||||
return [screen backingScaleFactor] > 1.0;
|
||||
}
|
||||
|
||||
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) override
|
||||
{
|
||||
if (!nsWindow) {
|
||||
return std::make_pair(width, height);
|
||||
}
|
||||
|
||||
NSView* contentView = [nsWindow contentView];
|
||||
if (!contentView) {
|
||||
return std::make_pair(width, height);
|
||||
}
|
||||
|
||||
NSRect contentRect = [contentView bounds];
|
||||
NSRect backingRect = [contentView convertRectToBacking:contentRect];
|
||||
|
||||
return std::make_pair((unsigned int)backingRect.size.width,
|
||||
(unsigned int)backingRect.size.height);
|
||||
}
|
||||
|
||||
virtual void resizeWindow(int w, int h) override
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private:
|
||||
NSWindow* nsWindow;
|
||||
NSOpenGLContext* glContext;
|
||||
NSOpenGLPixelFormat* pixelFormat;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
|
||||
{
|
||||
return new ContextGLMacOS(windowHandle);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
146
engine/gfx/src/API/GL/ContextGLWin32.cpp
Normal file
146
engine/gfx/src/API/GL/ContextGLWin32.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "ContextGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <glad/gl.h>
|
||||
#include <glad/wgl.h>
|
||||
|
||||
#include "Utility/StandardOut.hpp"
|
||||
|
||||
|
||||
FASTFLAG(DebugGraphicsGL)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
class ContextGLWin32 : public ContextGL
|
||||
{
|
||||
public:
|
||||
ContextGLWin32(void* windowHandle)
|
||||
: hwnd(reinterpret_cast<HWND>(windowHandle))
|
||||
, hdc(GetDC(hwnd))
|
||||
, hglrc(NULL)
|
||||
{
|
||||
PIXELFORMATDESCRIPTOR pfd = {};
|
||||
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
pfd.cAlphaBits = 8;
|
||||
pfd.cDepthBits = 24;
|
||||
pfd.cStencilBits = 8;
|
||||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
|
||||
int pf = ChoosePixelFormat(hdc, &pfd);
|
||||
|
||||
if (!pf)
|
||||
throw Aya::runtime_error("Error choosing pixel format: %x", GetLastError());
|
||||
|
||||
if (!SetPixelFormat(hdc, pf, &pfd))
|
||||
throw Aya::runtime_error("Error setting pixel format: %x", GetLastError());
|
||||
|
||||
hglrc = wglCreateContext(hdc);
|
||||
|
||||
if (!hglrc)
|
||||
throw Aya::runtime_error("Error creating context: %x", GetLastError());
|
||||
|
||||
if (!wglMakeCurrent(hdc, hglrc))
|
||||
throw Aya::runtime_error("Error changing context: %x", GetLastError());
|
||||
|
||||
if (!gladLoaderLoadWGL(hdc))
|
||||
throw Aya::runtime_error("Failed to initialize GLAD WGL with hdc");
|
||||
|
||||
// Initialize GLAD
|
||||
if (!gladLoaderLoadGL())
|
||||
throw Aya::runtime_error("Failed to initialize GLAD");
|
||||
|
||||
if (GLAD_WGL_ARB_create_context)
|
||||
{
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
wglDeleteContext(hglrc);
|
||||
|
||||
const int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, 0, 0};
|
||||
|
||||
hglrc = wglCreateContextAttribsARB(hdc, NULL, attribs);
|
||||
|
||||
if (!hglrc)
|
||||
throw Aya::runtime_error("Error creating context: %x", GetLastError());
|
||||
|
||||
if (!wglMakeCurrent(hdc, hglrc))
|
||||
throw Aya::runtime_error("Error changing context: %x", GetLastError());
|
||||
|
||||
// Reinitialize GLAD with the new context
|
||||
if (!gladLoaderLoadGL())
|
||||
throw Aya::runtime_error("Failed to reinitialize GLAD");
|
||||
}
|
||||
|
||||
if (GLAD_WGL_EXT_swap_control)
|
||||
wglSwapIntervalEXT(0);
|
||||
}
|
||||
|
||||
~ContextGLWin32()
|
||||
{
|
||||
if (wglGetCurrentContext() == hglrc)
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
|
||||
wglDeleteContext(hglrc);
|
||||
}
|
||||
|
||||
virtual void setCurrent()
|
||||
{
|
||||
if (wglGetCurrentContext() != hglrc)
|
||||
{
|
||||
BOOL result = wglMakeCurrent(hdc, hglrc);
|
||||
AYAASSERT(result);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void swapBuffers()
|
||||
{
|
||||
AYAASSERT(wglGetCurrentContext() == hglrc);
|
||||
|
||||
SwapBuffers(hdc);
|
||||
}
|
||||
|
||||
virtual unsigned int getMainFramebufferId()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool isMainFramebufferRetina()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
|
||||
{
|
||||
RECT rect = {};
|
||||
GetClientRect(hwnd, &rect);
|
||||
|
||||
return std::make_pair(rect.right - rect.left, rect.bottom - rect.top);
|
||||
}
|
||||
|
||||
virtual void resizeWindow(int w, int h)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private:
|
||||
HWND hwnd;
|
||||
HDC hdc;
|
||||
HGLRC hglrc;
|
||||
};
|
||||
|
||||
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
|
||||
{
|
||||
return new ContextGLWin32(windowHandle);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
513
engine/gfx/src/API/GL/DeviceContextGL.cpp
Normal file
513
engine/gfx/src/API/GL/DeviceContextGL.cpp
Normal file
@@ -0,0 +1,513 @@
|
||||
#include "DeviceGL.hpp"
|
||||
|
||||
#include "GeometryGL.hpp"
|
||||
#include "ShaderGL.hpp"
|
||||
#include "TextureGL.hpp"
|
||||
#include "FramebufferGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
#ifdef AYA_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
FASTFLAGVARIABLE(GraphicsGLUseDiscard, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static const GLenum gCullModeGL[RasterizerState::Cull_Count] = {GL_NONE, GL_BACK, GL_FRONT};
|
||||
|
||||
struct BlendFuncGL
|
||||
{
|
||||
GLenum src, dst;
|
||||
};
|
||||
|
||||
static const GLenum gBlendFactorsGL[BlendState::Factor_Count] = {
|
||||
GL_ONE, GL_ZERO, GL_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA};
|
||||
|
||||
static const GLenum gDepthFuncGL[DepthState::Function_Count] = {GL_ALWAYS, GL_LESS, GL_LEQUAL};
|
||||
|
||||
DeviceContextGL::DeviceContextGL(DeviceGL* dev)
|
||||
: globalDataVersion(0)
|
||||
, device(dev)
|
||||
, defaultAnisotropy(1)
|
||||
, cachedProgram(0)
|
||||
, cachedRasterizerState(RasterizerState::Cull_None)
|
||||
, cachedBlendState(BlendState::Mode_None)
|
||||
, cachedDepthState(DepthState::Function_Always, false)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
|
||||
cachedTextures[i] = NULL;
|
||||
}
|
||||
|
||||
DeviceContextGL::~DeviceContextGL() {}
|
||||
|
||||
void DeviceContextGL::clearStates()
|
||||
{
|
||||
// Clear framebuffer cache
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Clear program cache
|
||||
cachedProgram = 0;
|
||||
glUseProgram(0);
|
||||
|
||||
// Clear texture cache
|
||||
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
|
||||
cachedTextures[i] = NULL;
|
||||
|
||||
// Clear states to invalid values to guarantee a cache miss on the next setup
|
||||
cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count);
|
||||
cachedBlendState = BlendState(BlendState::Mode_Count);
|
||||
cachedDepthState = DepthState(DepthState::Function_Count, false);
|
||||
|
||||
// Setup state we never touch once
|
||||
#ifndef GLES
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceContextGL::invalidateCachedProgram()
|
||||
{
|
||||
cachedProgram = 0;
|
||||
}
|
||||
|
||||
void DeviceContextGL::invalidateCachedTexture(Texture* texture)
|
||||
{
|
||||
for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextures); ++stage)
|
||||
if (cachedTextures[stage] == texture)
|
||||
cachedTextures[stage] = NULL;
|
||||
}
|
||||
|
||||
void DeviceContextGL::invalidateCachedTextureStage(unsigned int stage)
|
||||
{
|
||||
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
|
||||
cachedTextures[stage] = NULL;
|
||||
}
|
||||
|
||||
void DeviceContextGL::defineGlobalConstants(size_t dataSize)
|
||||
{
|
||||
AYAASSERT(globalData.empty());
|
||||
AYAASSERT(dataSize > 0);
|
||||
|
||||
globalData.resize(dataSize);
|
||||
}
|
||||
|
||||
void DeviceContextGL::setDefaultAnisotropy(unsigned int value)
|
||||
{
|
||||
defaultAnisotropy = value;
|
||||
}
|
||||
|
||||
void DeviceContextGL::updateGlobalConstants(const void* data, size_t dataSize)
|
||||
{
|
||||
AYAASSERT(dataSize == globalData.size());
|
||||
|
||||
memcpy(&globalData[0], data, dataSize);
|
||||
globalDataVersion++;
|
||||
}
|
||||
|
||||
void DeviceContextGL::bindFramebuffer(Framebuffer* buffer)
|
||||
{
|
||||
unsigned int drawId = static_cast<FramebufferGL*>(buffer)->getId();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, drawId);
|
||||
glViewport(0, 0, buffer->getWidth(), buffer->getHeight());
|
||||
|
||||
#ifndef GLES
|
||||
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
|
||||
|
||||
if (drawId == 0)
|
||||
{
|
||||
glDrawBuffer(GL_BACK);
|
||||
}
|
||||
else if (glDrawBuffers)
|
||||
{
|
||||
GLenum buffers[16];
|
||||
AYAASSERT(drawBuffers < ARRAYSIZE(buffers));
|
||||
|
||||
for (unsigned int i = 0; i < drawBuffers; ++i)
|
||||
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
|
||||
glDrawBuffers(drawBuffers, buffers);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceContextGL::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil)
|
||||
{
|
||||
unsigned int maskGl = 0;
|
||||
|
||||
if (mask & Buffer_Color)
|
||||
{
|
||||
// Need color writes for color clear to work
|
||||
setBlendState(BlendState(BlendState::Mode_None, BlendState::Color_All));
|
||||
|
||||
maskGl |= GL_COLOR_BUFFER_BIT;
|
||||
glClearColor(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
if (mask & Buffer_Depth)
|
||||
{
|
||||
// Need depth writes for depth clear to work
|
||||
setDepthState(DepthState(DepthState::Function_Always, true));
|
||||
|
||||
maskGl |= GL_DEPTH_BUFFER_BIT;
|
||||
glClearDepth(depth);
|
||||
}
|
||||
|
||||
if (mask & Buffer_Stencil)
|
||||
{
|
||||
// Need stencil writes for stencil clear to work
|
||||
glStencilMask(~0u);
|
||||
|
||||
maskGl |= GL_STENCIL_BUFFER_BIT;
|
||||
glClearStencil(stencil);
|
||||
}
|
||||
|
||||
AYAASSERT(maskGl);
|
||||
glClear(maskGl);
|
||||
}
|
||||
|
||||
void DeviceContextGL::copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset)
|
||||
{
|
||||
AYAASSERT(texture->getType() == Texture::Type_2D);
|
||||
AYAASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight());
|
||||
|
||||
invalidateCachedTextureStage(0);
|
||||
|
||||
GLint oldfb = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, static_cast<TextureGL*>(texture)->getId());
|
||||
|
||||
#ifndef GLES
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
#endif
|
||||
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, -xOffset, yOffset, texture->getWidth(), texture->getHeight());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
|
||||
}
|
||||
|
||||
void DeviceContextGL::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask)
|
||||
{
|
||||
AYAASSERT(msaaBuffer->getSamples() > 1);
|
||||
AYAASSERT(buffer->getSamples() == 1);
|
||||
AYAASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight());
|
||||
|
||||
GLint oldfb = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<FramebufferGL*>(msaaBuffer)->getId());
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
|
||||
|
||||
if (mask & Buffer_Color)
|
||||
{
|
||||
#ifndef GLES
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
#endif
|
||||
|
||||
glBlitFramebuffer(
|
||||
0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
|
||||
if (mask & (Buffer_Depth | Buffer_Stencil))
|
||||
{
|
||||
unsigned int maskGl = 0;
|
||||
|
||||
if (mask & Buffer_Depth)
|
||||
maskGl |= GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
if (mask & Buffer_Stencil)
|
||||
maskGl |= GL_STENCIL_BUFFER_BIT;
|
||||
|
||||
glBlitFramebuffer(0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), maskGl, GL_NEAREST);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
|
||||
}
|
||||
|
||||
void DeviceContextGL::discardFramebuffer(Framebuffer* buffer, unsigned int mask)
|
||||
{
|
||||
if (!device->getCapsGL().ext3)
|
||||
return;
|
||||
|
||||
if (!FFlag::GraphicsGLUseDiscard)
|
||||
return;
|
||||
|
||||
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
|
||||
|
||||
GLenum attachments[16];
|
||||
unsigned int index = 0;
|
||||
|
||||
if (mask & Buffer_Color)
|
||||
for (unsigned int i = 0; i < drawBuffers; ++i)
|
||||
attachments[index++] = GL_COLOR_ATTACHMENT0 + i;
|
||||
|
||||
if (mask & Buffer_Depth)
|
||||
attachments[index++] = GL_DEPTH_ATTACHMENT;
|
||||
|
||||
if (mask & Buffer_Stencil)
|
||||
attachments[index++] = GL_STENCIL_ATTACHMENT;
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
GLint oldfb = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
|
||||
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, index, attachments);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextGL::bindProgram(ShaderProgram* program)
|
||||
{
|
||||
static_cast<ShaderProgramGL*>(program)->bind(&globalData[0], globalDataVersion, &cachedProgram);
|
||||
}
|
||||
|
||||
void DeviceContextGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
|
||||
{
|
||||
cachedProgram->setWorldTransforms4x3(data, matrixCount);
|
||||
}
|
||||
|
||||
void DeviceContextGL::setConstant(int handle, const float* data, size_t vectorCount)
|
||||
{
|
||||
cachedProgram->setConstant(handle, data, vectorCount);
|
||||
}
|
||||
|
||||
void DeviceContextGL::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state)
|
||||
{
|
||||
SamplerState realState = (state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0)
|
||||
? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy)
|
||||
: state;
|
||||
|
||||
AYAASSERT(stage < device->getCaps().maxTextureUnits);
|
||||
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
|
||||
|
||||
static_cast<TextureGL*>(texture)->bind(stage, realState, &cachedTextures[stage]);
|
||||
}
|
||||
|
||||
void DeviceContextGL::setRasterizerState(const RasterizerState& state)
|
||||
{
|
||||
if (cachedRasterizerState != state)
|
||||
{
|
||||
cachedRasterizerState = state;
|
||||
|
||||
if (state.getCullMode() == RasterizerState::Cull_None)
|
||||
{
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(gCullModeGL[state.getCullMode()]);
|
||||
}
|
||||
|
||||
if (state.getDepthBias() == 0)
|
||||
{
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
else
|
||||
{
|
||||
float bias = static_cast<float>(state.getDepthBias());
|
||||
float slopeBias = bias / 32.f; // do we need explicit control over slope or just a better formula since these numbers are magic anyway?
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(slopeBias, bias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextGL::setBlendState(const BlendState& state)
|
||||
{
|
||||
if (cachedBlendState != state)
|
||||
{
|
||||
cachedBlendState = state;
|
||||
|
||||
unsigned int colorMask = state.getColorMask();
|
||||
|
||||
glColorMask((colorMask & BlendState::Color_R) != 0, (colorMask & BlendState::Color_G) != 0, (colorMask & BlendState::Color_B) != 0,
|
||||
(colorMask & BlendState::Color_A) != 0);
|
||||
|
||||
if (!state.blendingNeeded())
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
else
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
if (!state.separateAlphaBlend())
|
||||
glBlendFunc(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()]);
|
||||
else
|
||||
glBlendFuncSeparate(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()], gBlendFactorsGL[state.getAlphaSrc()],
|
||||
gBlendFactorsGL[state.getAlphaDst()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextGL::setDepthState(const DepthState& state)
|
||||
{
|
||||
if (cachedDepthState != state)
|
||||
{
|
||||
cachedDepthState = state;
|
||||
|
||||
if (state.getFunction() != DepthState::Function_None && state.getFunction() == DepthState::Function_Always && !state.getWrite())
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.getFunction() != DepthState::Function_None)
|
||||
{
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(gDepthFuncGL[state.getFunction()]);
|
||||
glDepthMask(state.getWrite());
|
||||
}
|
||||
}
|
||||
|
||||
switch (state.getStencilMode())
|
||||
{
|
||||
case DepthState::Stencil_None:
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_IsNotZero:
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_NOTEQUAL, 0, ~0u);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
break;
|
||||
|
||||
case DepthState::Stencil_UpdateZFail:
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 0, ~0u);
|
||||
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
|
||||
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
|
||||
break;
|
||||
case DepthState::Stencil_Increment:
|
||||
// disable writing to color & depth buffers
|
||||
// clear stencil buffer
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
|
||||
// enable face culling
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// enable stencil testing, always pass
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glStencilMask(~0u);
|
||||
|
||||
// (?) not sure what exactly this is supposed to do, added based on analysis of the framebuffer of the graphics pipeline in older clients
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// increase for back faces, decrease stencil bit for front faces
|
||||
|
||||
glCullFace(GL_FRONT);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
||||
glStencilFunc(GL_ALWAYS, 0, ~0u);
|
||||
break;
|
||||
case DepthState::Stencil_Decrement:
|
||||
glCullFace(GL_BACK);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP);
|
||||
glStencilFunc(GL_ALWAYS, 0, ~0u);
|
||||
break;
|
||||
case DepthState::Stencil_IsNotZeroReplace:
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_LESS, 0x01, ~0u);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceContextGL::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
|
||||
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
|
||||
{
|
||||
static_cast<GeometryGL*>(geometry)->draw(primitive, offset, count);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Markers:
|
||||
|
||||
void DeviceContextGL::pushDebugMarkerGroup(const char* text)
|
||||
{
|
||||
#ifdef AYA_PLATFORM_IOS
|
||||
if (device->getCapsGL().extDebugMarkers)
|
||||
{
|
||||
glPushGroupMarkerEXT(0, text);
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
;
|
||||
#else
|
||||
if (glPushDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
|
||||
{
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION_ARB, 0, -1, text);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceContextGL::popDebugMarkerGroup()
|
||||
{
|
||||
#ifdef AYA_PLATFORM_IOS
|
||||
if (device->getCapsGL().extDebugMarkers)
|
||||
{
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
;
|
||||
#else
|
||||
if (glPopDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
|
||||
{
|
||||
glPopDebugGroup();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceContextGL::setDebugMarker(const char* text)
|
||||
{
|
||||
#ifdef AYA_PLATFORM_IOS
|
||||
if (device->getCapsGL().extDebugMarkers)
|
||||
{
|
||||
glInsertEventMarkerEXT(0, text);
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
;
|
||||
#else
|
||||
if (glDebugMessageInsert) // Requires GL4.3, because ARB-version does not have marker enums for type
|
||||
{
|
||||
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, text);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
617
engine/gfx/src/API/GL/DeviceGL.cpp
Normal file
617
engine/gfx/src/API/GL/DeviceGL.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
#include "DeviceGL.hpp"
|
||||
|
||||
#include "GeometryGL.hpp"
|
||||
#include "ShaderGL.hpp"
|
||||
#include "TextureGL.hpp"
|
||||
#include "FramebufferGL.hpp"
|
||||
|
||||
#include "ContextGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
#include "ImGui.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(__linux) || defined(__APPLE__)
|
||||
// https://stackoverflow.com/questions/33257597/segmentation-fault-in-c-linux-but-not-in-windows
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
FASTFLAGVARIABLE(DebugGraphicsGL, false)
|
||||
|
||||
FASTFLAGVARIABLE(GraphicsGL3, true)
|
||||
|
||||
FASTFLAGVARIABLE(GraphicsGLReduceLatency, true)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static std::set<std::string> getExtensions()
|
||||
{
|
||||
std::set<std::string> result;
|
||||
|
||||
if (const char* extensionString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)))
|
||||
{
|
||||
std::istringstream ext(extensionString);
|
||||
|
||||
std::string str;
|
||||
while (ext >> str)
|
||||
result.insert(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef GLES
|
||||
if (FFlag::GraphicsGL3 && GLAD_GL_VERSION_3_0)
|
||||
{
|
||||
int extensionCount = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
|
||||
|
||||
for (int i = 0; i < extensionCount; ++i)
|
||||
result.insert(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef GLES
|
||||
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
|
||||
{
|
||||
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
|
||||
bool version3 = FFlag::GraphicsGL3 && strncmp(version, "OpenGL ES ", 10) == 0 && version[10] >= '3';
|
||||
|
||||
DeviceCapsGL caps;
|
||||
|
||||
GLint texSize;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
|
||||
|
||||
GLint stencilBits;
|
||||
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
|
||||
|
||||
caps.supportsFramebuffer = true;
|
||||
caps.supportsShaders = true;
|
||||
caps.supportsFFP = false;
|
||||
caps.supportsStencil = stencilBits >= 8;
|
||||
caps.supportsIndex32 = version3 || extensions.count("GL_OES_element_index_uint");
|
||||
|
||||
caps.supportsTextureDXT = false;
|
||||
caps.supportsTexturePVR = extensions.count("GL_IMG_texture_compression_pvrtc");
|
||||
caps.supportsTextureHalfFloat = version3 || extensions.count("GL_OES_texture_half_float");
|
||||
caps.supportsTexture3D = version3;
|
||||
caps.supportsTextureNPOT = version3;
|
||||
caps.supportsTextureETC1 = extensions.count("GL_OES_compressed_ETC1_RGB8_texture");
|
||||
|
||||
caps.supportsTexturePartialMipChain =
|
||||
version3 || extensions.count("GL_APPLE_texture_max_level") || (!FFlag::GraphicsGL3 && strstr(version, "OpenGL ES 3"));
|
||||
|
||||
caps.maxDrawBuffers = 1;
|
||||
|
||||
if (version3)
|
||||
{
|
||||
GLint samples = 1;
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &samples);
|
||||
|
||||
caps.maxSamples = samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps.maxSamples = 1;
|
||||
}
|
||||
|
||||
caps.maxTextureSize = texSize;
|
||||
caps.maxTextureUnits = version3 ? 16 : 8;
|
||||
|
||||
caps.colorOrderBGR = false;
|
||||
caps.needsHalfPixelOffset = false;
|
||||
caps.requiresRenderTargetFlipping = true;
|
||||
|
||||
caps.retina = context->isMainFramebufferRetina();
|
||||
|
||||
caps.ext3 = version3;
|
||||
|
||||
caps.extVertexArrayObject = version3 || (extensions.count("GL_OES_vertex_array_object") && !strstr(renderer, "Adreno"));
|
||||
caps.extTextureStorage = version3 || extensions.count("GL_EXT_texture_storage");
|
||||
caps.extMapBuffer = version3 || extensions.count("GL_OES_mapbuffer");
|
||||
caps.extMapBufferRange = version3 || extensions.count("GL_EXT_map_buffer_range");
|
||||
caps.extTimerQuery = false;
|
||||
caps.extDebugMarkers = extensions.count("GL_EXT_debug_marker");
|
||||
caps.extSync = version3;
|
||||
|
||||
return caps;
|
||||
}
|
||||
#else
|
||||
static DeviceCapsGL createDeviceCapsOld(ContextGL* context)
|
||||
{
|
||||
DeviceCapsGL caps;
|
||||
|
||||
GLint texSize = 0;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
|
||||
|
||||
GLint stencilBits = 0;
|
||||
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
|
||||
|
||||
caps.supportsFramebuffer = GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_framebuffer_object || GLAD_GL_EXT_framebuffer_object;
|
||||
caps.supportsShaders = GLAD_GL_VERSION_2_0 || (GLAD_GL_ARB_shading_language_100 && GLAD_GL_ARB_shader_objects && GLAD_GL_ARB_fragment_shader &&
|
||||
GLAD_GL_ARB_vertex_shader);
|
||||
caps.supportsFFP = false;
|
||||
caps.supportsStencil = GLAD_GL_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
|
||||
caps.supportsIndex32 = true;
|
||||
|
||||
caps.supportsTextureDXT = (GLAD_GL_VERSION_1_3 || GLAD_GL_ARB_texture_compression) && GLAD_GL_EXT_texture_compression_s3tc;
|
||||
caps.supportsTexturePVR = false;
|
||||
caps.supportsTextureHalfFloat = !!GLAD_GL_ARB_half_float_pixel;
|
||||
caps.supportsTexture3D = GLAD_GL_VERSION_1_2 || GLAD_GL_EXT_texture3D;
|
||||
caps.supportsTextureETC1 = false;
|
||||
|
||||
caps.supportsTexturePartialMipChain = true;
|
||||
|
||||
// According to http://aras-p.info/blog/2012/10/17/non-power-of-two-textures/, GL extensions lie
|
||||
caps.supportsTextureNPOT = GLAD_GL_ARB_texture_non_power_of_two && texSize >= 8192;
|
||||
|
||||
if (caps.supportsFramebuffer && (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_draw_buffers || GLAD_GL_ATI_draw_buffers))
|
||||
{
|
||||
GLint drawBuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
|
||||
|
||||
caps.maxDrawBuffers = drawBuffers;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps.maxDrawBuffers = 1;
|
||||
}
|
||||
|
||||
if (caps.supportsFramebuffer && (GLAD_GL_VERSION_3_3 || GLAD_GL_EXT_framebuffer_multisample))
|
||||
{
|
||||
GLint samples = 0;
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &samples);
|
||||
|
||||
caps.maxSamples = samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps.maxSamples = 1;
|
||||
}
|
||||
|
||||
caps.maxTextureSize = texSize;
|
||||
caps.maxTextureUnits = 16;
|
||||
|
||||
caps.colorOrderBGR = false;
|
||||
caps.needsHalfPixelOffset = false;
|
||||
caps.requiresRenderTargetFlipping = true;
|
||||
|
||||
caps.retina = context->isMainFramebufferRetina();
|
||||
|
||||
caps.ext3 = false;
|
||||
|
||||
caps.extVertexArrayObject = (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_vertex_array_object || GLAD_GL_APPLE_vertex_array_object);
|
||||
caps.extTextureStorage = GLAD_GL_ARB_texture_storage;
|
||||
caps.extMapBuffer = true;
|
||||
caps.extMapBufferRange = (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_map_buffer_range);
|
||||
caps.extTimerQuery = !!GLAD_GL_EXT_timer_query;
|
||||
caps.extDebugMarkers = false;
|
||||
caps.extSync = (GLAD_GL_VERSION_3_2 || GLAD_GL_ARB_sync);
|
||||
|
||||
#ifdef _WIN32
|
||||
const char* vendorString = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
|
||||
if (strstr(vendorString, "Intel"))
|
||||
{
|
||||
// Intel drivers don't store IB state as part of VAO (http://stackoverflow.com/questions/8973690/vao-and-element-array-buffer-state)
|
||||
caps.extVertexArrayObject = false;
|
||||
}
|
||||
|
||||
if (strstr(vendorString, "ATI Technologies"))
|
||||
{
|
||||
// Some AMD drivers can't create cubemaps with TS and can create 3D textures that they can't update afterwards...
|
||||
caps.extTextureStorage = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
|
||||
{
|
||||
// if (!FFlag::GraphicsGL3)
|
||||
// return createDeviceCapsOld(context);
|
||||
|
||||
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
|
||||
bool version3 = isdigit(version[0]) && version[0] >= '3';
|
||||
|
||||
DeviceCapsGL caps;
|
||||
|
||||
GLint texSize = 0;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
|
||||
|
||||
GLint stencilBits = 0;
|
||||
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
|
||||
|
||||
caps.supportsFramebuffer = version3 || (extensions.count("GL_ARB_framebuffer_object") || extensions.count("GL_EXT_framebuffer_object"));
|
||||
caps.supportsShaders = version3 || GLAD_GL_VERSION_2_0 ||
|
||||
// need this for Mac OpenGL 1.4 :-/
|
||||
(extensions.count("GL_ARB_shading_language_100") && extensions.count("GL_ARB_shader_objects") &&
|
||||
extensions.count("GL_ARB_fragment_shader") && extensions.count("GL_ARB_vertex_shader"));
|
||||
|
||||
caps.supportsFFP = false;
|
||||
caps.supportsStencil = GLAD_GL_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
|
||||
caps.supportsIndex32 = true;
|
||||
|
||||
caps.supportsTextureDXT = !!extensions.count("GL_EXT_texture_compression_s3tc");
|
||||
caps.supportsTexturePVR = false;
|
||||
caps.supportsTextureHalfFloat = version3;
|
||||
caps.supportsTexture3D = true;
|
||||
caps.supportsTextureETC1 = false;
|
||||
|
||||
caps.supportsTexturePartialMipChain = true;
|
||||
|
||||
caps.supportsTextureNPOT = version3;
|
||||
|
||||
if (version3)
|
||||
{
|
||||
GLint drawBuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
|
||||
|
||||
GLint samples = 0;
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &samples);
|
||||
|
||||
caps.maxDrawBuffers = drawBuffers;
|
||||
caps.maxSamples = samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps.maxDrawBuffers = 1;
|
||||
caps.maxSamples = 1;
|
||||
}
|
||||
|
||||
caps.maxTextureSize = texSize;
|
||||
caps.maxTextureUnits = 16;
|
||||
|
||||
caps.colorOrderBGR = false;
|
||||
caps.needsHalfPixelOffset = false;
|
||||
caps.requiresRenderTargetFlipping = true;
|
||||
|
||||
caps.retina = context->isMainFramebufferRetina();
|
||||
|
||||
caps.ext3 = version3;
|
||||
|
||||
caps.extVertexArrayObject = version3;
|
||||
caps.extTextureStorage = GLAD_GL_ARB_texture_storage || (version3 && extensions.count("GL_ARB_texture_storage"));
|
||||
caps.extMapBuffer = true;
|
||||
caps.extMapBufferRange = version3;
|
||||
caps.extTimerQuery = !!GLAD_GL_VERSION_3_3;
|
||||
caps.extDebugMarkers = false;
|
||||
caps.extSync = !!GLAD_GL_VERSION_3_2;
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static void GLAPIENTRY debugOutputGLARB(
|
||||
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam)
|
||||
{
|
||||
FASTLOGS(FLog::Graphics, "GL Debug: %s", message);
|
||||
}
|
||||
#endif
|
||||
|
||||
DeviceGL::DeviceGL(void* windowHandle, void* display, int w, int h)
|
||||
: frameTimeQueryId(0)
|
||||
, frameTimeQueryIssued(false)
|
||||
, frameEventQueryId(0)
|
||||
, frameEventQueryIssued(false)
|
||||
, gpuTime(0)
|
||||
{
|
||||
glContext.reset(ContextGL::create(windowHandle, display, w, h));
|
||||
|
||||
std::set<std::string> extensions = getExtensions();
|
||||
|
||||
caps = createDeviceCaps(glContext.get(), extensions);
|
||||
|
||||
FASTLOGS(FLog::Graphics, "GL Renderer: %s", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
|
||||
FASTLOGS(FLog::Graphics, "GL Version: %s", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
|
||||
FASTLOGS(FLog::Graphics, "GL Vendor: %s", reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
|
||||
|
||||
if (caps.supportsShaders)
|
||||
FASTLOGS(FLog::Graphics, "GLSL version: %s", reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
||||
|
||||
std::string extensionString;
|
||||
|
||||
for (auto& e : extensions)
|
||||
{
|
||||
extensionString += " ";
|
||||
extensionString += e;
|
||||
}
|
||||
|
||||
while (!extensionString.empty())
|
||||
{
|
||||
std::string::size_type space = extensionString.find_first_of(' ', 128);
|
||||
FASTLOGS(FLog::Graphics, "Ext:%s", extensionString.substr(0, space));
|
||||
extensionString.erase(0, space);
|
||||
}
|
||||
|
||||
caps.dumpToFLog(FLog::Graphics);
|
||||
|
||||
FASTLOG1(FLog::Graphics, "Caps: GL3 %d", caps.ext3);
|
||||
FASTLOG5(FLog::Graphics, "Caps: VAO %d TexStorage %d MapBuffer %d MapBufferRange %d TimerQuery %d", caps.extVertexArrayObject,
|
||||
caps.extTextureStorage, caps.extMapBuffer, caps.extMapBufferRange, caps.extTimerQuery);
|
||||
FASTLOG2(FLog::Graphics, "Caps: DebugMarkers %d Sync %d", caps.extDebugMarkers, caps.extSync);
|
||||
|
||||
immediateContext.reset(new DeviceContextGL(this));
|
||||
|
||||
mainFramebuffer.reset(new FramebufferGL(this, glContext->getMainFramebufferId()));
|
||||
|
||||
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(0, 0);
|
||||
|
||||
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
|
||||
|
||||
#ifndef GLES
|
||||
if (caps.extTimerQuery)
|
||||
{
|
||||
glGenQueries(1, &frameTimeQueryId);
|
||||
AYAASSERT(frameTimeQueryId);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
Profiler::gpuInit(0);
|
||||
#endif
|
||||
ImGui::gpuInit();
|
||||
}
|
||||
|
||||
DeviceGL::~DeviceGL()
|
||||
{
|
||||
glFinish();
|
||||
glContext.reset();
|
||||
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
Profiler::gpuShutdown();
|
||||
#endif
|
||||
ImGui::gpuShutdown();
|
||||
|
||||
immediateContext.reset();
|
||||
mainFramebuffer.reset();
|
||||
}
|
||||
|
||||
void DeviceGL::resize(int w, int h)
|
||||
{
|
||||
glContext->resizeWindow(w, h);
|
||||
}
|
||||
std::string DeviceGL::getFeatureLevel()
|
||||
{
|
||||
std::string glString = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
std::string featureLevel = "OpenGL ";
|
||||
if (!glString.empty())
|
||||
{
|
||||
size_t dotId = glString.find('.');
|
||||
|
||||
// this is based on GLEW implementation of determining GL version. Does it look unsafe? Dont worry, GLEW version is even worse;)
|
||||
if (dotId > 0 && dotId < (glString.length() - 1) && isdigit(glString[dotId - 1]) && isdigit(glString[dotId + 1]))
|
||||
{
|
||||
featureLevel += glString[dotId - 1];
|
||||
featureLevel += '.';
|
||||
featureLevel += glString[dotId + 1];
|
||||
return featureLevel;
|
||||
}
|
||||
}
|
||||
|
||||
featureLevel += "unknown";
|
||||
return featureLevel;
|
||||
}
|
||||
|
||||
bool DeviceGL::validate()
|
||||
{
|
||||
glContext->setCurrent();
|
||||
|
||||
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(mainFramebuffer->getWidth(), mainFramebuffer->getHeight());
|
||||
|
||||
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceContext* DeviceGL::beginFrame()
|
||||
{
|
||||
glContext->setCurrent();
|
||||
|
||||
#ifndef GLES
|
||||
if (frameTimeQueryId && !frameTimeQueryIssued)
|
||||
{
|
||||
glBeginQuery(GL_TIME_ELAPSED, frameTimeQueryId);
|
||||
}
|
||||
#endif
|
||||
|
||||
immediateContext->bindFramebuffer(mainFramebuffer.get());
|
||||
immediateContext->clearStates();
|
||||
|
||||
return immediateContext.get();
|
||||
}
|
||||
|
||||
void DeviceGL::endFrame()
|
||||
{
|
||||
#ifndef GLES
|
||||
if (frameTimeQueryId)
|
||||
{
|
||||
if (!frameTimeQueryIssued)
|
||||
{
|
||||
glEndQuery(GL_TIME_ELAPSED);
|
||||
|
||||
frameTimeQueryIssued = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int available = 0;
|
||||
glGetQueryObjectiv(frameTimeQueryId, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
if (available)
|
||||
{
|
||||
GLuint64EXT elapsed = 0;
|
||||
if (FFlag::GraphicsGL3)
|
||||
glGetQueryObjectui64v(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
|
||||
else
|
||||
glGetQueryObjectui64vEXT(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
|
||||
|
||||
gpuTime = static_cast<float>(elapsed) / 1e6f;
|
||||
|
||||
frameTimeQueryIssued = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FFlag::GraphicsGLReduceLatency && caps.extSync)
|
||||
{
|
||||
// Wait for last frame to finish on GPU
|
||||
if (frameEventQueryIssued)
|
||||
{
|
||||
int rc = glClientWaitSync(frameEventQueryId, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64(5e9));
|
||||
AYAASSERT(rc == GL_CONDITION_SATISFIED || rc == GL_ALREADY_SIGNALED);
|
||||
|
||||
glDeleteSync(frameEventQueryId);
|
||||
frameEventQueryId = 0;
|
||||
|
||||
frameEventQueryIssued = false;
|
||||
}
|
||||
|
||||
// Submit new query for frame wait
|
||||
if (!frameEventQueryIssued)
|
||||
{
|
||||
frameEventQueryId = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
AYAASSERT(frameEventQueryId);
|
||||
|
||||
frameEventQueryIssued = true;
|
||||
}
|
||||
}
|
||||
|
||||
glContext->swapBuffers();
|
||||
}
|
||||
|
||||
Framebuffer* DeviceGL::getMainFramebuffer()
|
||||
{
|
||||
return mainFramebuffer.get();
|
||||
}
|
||||
|
||||
void DeviceGL::defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants)
|
||||
{
|
||||
AYAASSERT(globalConstants.empty());
|
||||
AYAASSERT(!constants.empty());
|
||||
|
||||
globalConstants = constants;
|
||||
|
||||
immediateContext->defineGlobalConstants(dataSize);
|
||||
}
|
||||
|
||||
std::string DeviceGL::getShadingLanguage()
|
||||
{
|
||||
#ifdef GLES
|
||||
return caps.ext3 ? "glsles3" : "glsles";
|
||||
#else
|
||||
return caps.ext3 ? "glsl3" : "glsl3";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string DeviceGL::createShaderSource(
|
||||
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback)
|
||||
{
|
||||
// No preprocessor support
|
||||
return fileCallback(path);
|
||||
}
|
||||
|
||||
std::vector<char> DeviceGL::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint)
|
||||
{
|
||||
// No bytecode support
|
||||
AYAASSERT(entrypoint == "main");
|
||||
|
||||
return std::vector<char>(source.begin(), source.end());
|
||||
}
|
||||
|
||||
shared_ptr<VertexShader> DeviceGL::createVertexShader(const std::vector<char>& bytecode)
|
||||
{
|
||||
if (!caps.supportsShaders)
|
||||
throw Aya::runtime_error("No shader support");
|
||||
|
||||
std::string source(bytecode.begin(), bytecode.end());
|
||||
|
||||
return shared_ptr<VertexShader>(new VertexShaderGL(this, source));
|
||||
}
|
||||
|
||||
shared_ptr<FragmentShader> DeviceGL::createFragmentShader(const std::vector<char>& bytecode)
|
||||
{
|
||||
if (!caps.supportsShaders)
|
||||
throw Aya::runtime_error("No shader support");
|
||||
|
||||
std::string source(bytecode.begin(), bytecode.end());
|
||||
|
||||
return shared_ptr<FragmentShader>(new FragmentShaderGL(this, source));
|
||||
}
|
||||
|
||||
shared_ptr<ShaderProgram> DeviceGL::createShaderProgram(
|
||||
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
|
||||
{
|
||||
if (!caps.supportsShaders)
|
||||
throw Aya::runtime_error("No shader support");
|
||||
|
||||
return shared_ptr<ShaderProgram>(new ShaderProgramGL(this, vertexShader, fragmentShader));
|
||||
}
|
||||
|
||||
shared_ptr<ShaderProgram> DeviceGL::createShaderProgramFFP()
|
||||
{
|
||||
throw Aya::runtime_error("No FFP support");
|
||||
}
|
||||
|
||||
shared_ptr<VertexBuffer> DeviceGL::createVertexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
|
||||
{
|
||||
return shared_ptr<VertexBuffer>(new VertexBufferGL(this, elementSize, elementCount, usage));
|
||||
}
|
||||
|
||||
shared_ptr<IndexBuffer> DeviceGL::createIndexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
|
||||
{
|
||||
return shared_ptr<IndexBuffer>(new IndexBufferGL(this, elementSize, elementCount, usage));
|
||||
}
|
||||
|
||||
shared_ptr<VertexLayout> DeviceGL::createVertexLayout(const std::vector<VertexLayout::Element>& elements)
|
||||
{
|
||||
return shared_ptr<VertexLayout>(new VertexLayoutGL(this, elements));
|
||||
}
|
||||
|
||||
shared_ptr<Texture> DeviceGL::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, Texture::Usage usage)
|
||||
{
|
||||
return shared_ptr<Texture>(new TextureGL(this, type, format, width, height, depth, mipLevels, usage));
|
||||
}
|
||||
|
||||
shared_ptr<Renderbuffer> DeviceGL::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
|
||||
{
|
||||
return shared_ptr<Renderbuffer>(new RenderbufferGL(this, format, width, height, samples));
|
||||
}
|
||||
|
||||
shared_ptr<Geometry> DeviceGL::createGeometryImpl(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
{
|
||||
return shared_ptr<Geometry>(new GeometryGL(this, layout, vertexBuffers, indexBuffer, baseVertexIndex));
|
||||
}
|
||||
|
||||
shared_ptr<Framebuffer> DeviceGL::createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
{
|
||||
return shared_ptr<Framebuffer>(new FramebufferGL(this, color, depth));
|
||||
}
|
||||
|
||||
DeviceStats DeviceGL::getStatistics() const
|
||||
{
|
||||
DeviceStats result = {};
|
||||
|
||||
result.gpuFrameTime = gpuTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
178
engine/gfx/src/API/GL/DeviceGL.hpp
Normal file
178
engine/gfx/src/API/GL/DeviceGL.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Device.hpp"
|
||||
#include "Core/States.hpp"
|
||||
|
||||
typedef struct __GLsync* GLsync;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class FramebufferGL;
|
||||
class ShaderProgramGL;
|
||||
class TextureGL;
|
||||
class ContextGL;
|
||||
class DeviceGL;
|
||||
|
||||
|
||||
struct DeviceCapsGL : DeviceCaps
|
||||
{
|
||||
bool ext3;
|
||||
|
||||
bool extVertexArrayObject;
|
||||
bool extTextureStorage;
|
||||
bool extMapBuffer;
|
||||
bool extMapBufferRange;
|
||||
bool extTimerQuery;
|
||||
bool extDebugMarkers;
|
||||
bool extSync;
|
||||
};
|
||||
|
||||
class DeviceContextGL : public DeviceContext
|
||||
{
|
||||
public:
|
||||
DeviceContextGL(DeviceGL* dev);
|
||||
~DeviceContextGL();
|
||||
|
||||
void defineGlobalConstants(size_t dataSize);
|
||||
|
||||
void clearStates();
|
||||
|
||||
void invalidateCachedProgram();
|
||||
void invalidateCachedTexture(Texture* texture);
|
||||
void invalidateCachedTextureStage(unsigned int stage);
|
||||
|
||||
virtual void setDefaultAnisotropy(unsigned int value);
|
||||
|
||||
virtual void updateGlobalConstants(const void* data, size_t dataSize);
|
||||
|
||||
virtual void bindFramebuffer(Framebuffer* buffer);
|
||||
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil);
|
||||
|
||||
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset);
|
||||
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask);
|
||||
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask);
|
||||
|
||||
virtual void bindProgram(ShaderProgram* program);
|
||||
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount);
|
||||
virtual void setConstant(int handle, const float* data, size_t vectorCount);
|
||||
|
||||
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state);
|
||||
|
||||
virtual void setRasterizerState(const RasterizerState& state);
|
||||
virtual void setBlendState(const BlendState& state);
|
||||
virtual void setDepthState(const DepthState& state);
|
||||
|
||||
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
|
||||
unsigned int indexRangeEnd);
|
||||
|
||||
virtual void pushDebugMarkerGroup(const char* text);
|
||||
virtual void popDebugMarkerGroup();
|
||||
virtual void setDebugMarker(const char* text);
|
||||
|
||||
private:
|
||||
std::vector<char> globalData;
|
||||
unsigned int globalDataVersion;
|
||||
|
||||
unsigned int defaultAnisotropy;
|
||||
|
||||
ShaderProgramGL* cachedProgram;
|
||||
TextureGL* cachedTextures[16];
|
||||
|
||||
RasterizerState cachedRasterizerState;
|
||||
BlendState cachedBlendState;
|
||||
DepthState cachedDepthState;
|
||||
DeviceGL* device;
|
||||
};
|
||||
|
||||
class DeviceGL : public Device
|
||||
{
|
||||
public:
|
||||
DeviceGL(void* windowHandle, void* display, int w, int h);
|
||||
~DeviceGL();
|
||||
|
||||
virtual bool validate();
|
||||
virtual void resize(int w, int h);
|
||||
virtual DeviceContext* beginFrame();
|
||||
virtual void endFrame();
|
||||
|
||||
virtual Framebuffer* getMainFramebuffer();
|
||||
|
||||
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants);
|
||||
|
||||
virtual std::string getAPIName()
|
||||
{
|
||||
return "OpenGL";
|
||||
}
|
||||
virtual std::string getFeatureLevel();
|
||||
virtual std::string getShadingLanguage();
|
||||
virtual std::string createShaderSource(
|
||||
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback);
|
||||
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint);
|
||||
|
||||
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode);
|
||||
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode);
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgram(
|
||||
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgramFFP();
|
||||
|
||||
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
|
||||
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
|
||||
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements);
|
||||
|
||||
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, Texture::Usage usage);
|
||||
|
||||
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
|
||||
|
||||
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
|
||||
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
|
||||
|
||||
virtual shared_ptr<Framebuffer> createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
|
||||
|
||||
virtual const DeviceCaps& getCaps() const
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
virtual DeviceStats getStatistics() const;
|
||||
|
||||
DeviceContextGL* getImmediateContextGL()
|
||||
{
|
||||
return immediateContext.get();
|
||||
}
|
||||
|
||||
const DeviceCapsGL& getCapsGL() const
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
const std::vector<ShaderGlobalConstant>& getGlobalConstants() const
|
||||
{
|
||||
return globalConstants;
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceCapsGL caps;
|
||||
|
||||
scoped_ptr<DeviceContextGL> immediateContext;
|
||||
|
||||
scoped_ptr<FramebufferGL> mainFramebuffer;
|
||||
|
||||
std::vector<ShaderGlobalConstant> globalConstants;
|
||||
|
||||
unsigned int frameTimeQueryId;
|
||||
bool frameTimeQueryIssued;
|
||||
|
||||
GLsync frameEventQueryId;
|
||||
bool frameEventQueryIssued;
|
||||
|
||||
float gpuTime;
|
||||
|
||||
scoped_ptr<ContextGL> glContext;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
163
engine/gfx/src/API/GL/FramebufferGL.cpp
Normal file
163
engine/gfx/src/API/GL/FramebufferGL.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "FramebufferGL.hpp"
|
||||
|
||||
#include "TextureGL.hpp"
|
||||
#include "DeviceGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
RenderbufferGL::RenderbufferGL(Device* device, const shared_ptr<TextureGL>& container, unsigned int target, unsigned int samples)
|
||||
: Renderbuffer(device, container->getFormat(), container->getWidth(), container->getHeight(), samples)
|
||||
, owner(container)
|
||||
, target(target)
|
||||
, bufferId(0)
|
||||
{
|
||||
}
|
||||
|
||||
RenderbufferGL::RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
|
||||
: Renderbuffer(device, format, width, height, samples)
|
||||
, target(-1)
|
||||
, bufferId(0)
|
||||
{
|
||||
AYAASSERT(samples);
|
||||
if (samples > device->getCaps().maxSamples)
|
||||
throw Aya::runtime_error("Unsupported renderbuffer: too many samples (%d)", samples);
|
||||
|
||||
glGenRenderbuffers(1, &bufferId);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, bufferId);
|
||||
|
||||
if (samples > 1)
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, TextureGL::getInternalFormat(format), width, height);
|
||||
else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, TextureGL::getInternalFormat(format), width, height);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
RenderbufferGL::~RenderbufferGL()
|
||||
{
|
||||
if (bufferId)
|
||||
glDeleteRenderbuffers(1, &bufferId);
|
||||
}
|
||||
|
||||
FramebufferGL::FramebufferGL(Device* device, unsigned int id)
|
||||
: Framebuffer(device, 0, 0, 1)
|
||||
, id(id)
|
||||
{
|
||||
}
|
||||
|
||||
FramebufferGL::FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
: Framebuffer(device, 0, 0, 0)
|
||||
, id(0)
|
||||
, color(color)
|
||||
, depth(depth)
|
||||
{
|
||||
AYAASSERT(!color.empty());
|
||||
|
||||
if (color.size() > device->getCaps().maxDrawBuffers)
|
||||
throw Aya::runtime_error("Unsupported framebuffer configuration: too many buffers (%d)", (int)color.size());
|
||||
|
||||
glGenFramebuffers(1, &id);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, id);
|
||||
|
||||
for (size_t i = 0; i < color.size(); ++i)
|
||||
{
|
||||
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(color[i].get());
|
||||
AYAASSERT(buffer);
|
||||
AYAASSERT(!Texture::isFormatDepth(buffer->getFormat()));
|
||||
|
||||
if (buffer->getTextureId())
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, buffer->getTarget(), buffer->getTextureId(), 0);
|
||||
else
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, buffer->getBufferId());
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
width = buffer->getWidth();
|
||||
height = buffer->getHeight();
|
||||
samples = buffer->getSamples();
|
||||
}
|
||||
else
|
||||
{
|
||||
AYAASSERT(width == buffer->getWidth());
|
||||
AYAASSERT(height == buffer->getHeight());
|
||||
AYAASSERT(samples == buffer->getSamples());
|
||||
}
|
||||
}
|
||||
|
||||
if (depth)
|
||||
{
|
||||
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(depth.get());
|
||||
AYAASSERT(buffer->getTextureId() == 0);
|
||||
AYAASSERT(Texture::isFormatDepth(buffer->getFormat()));
|
||||
|
||||
AYAASSERT(width == buffer->getWidth());
|
||||
AYAASSERT(height == buffer->getHeight());
|
||||
AYAASSERT(samples == buffer->getSamples());
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
|
||||
}
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteFramebuffers(1, &id);
|
||||
|
||||
throw Aya::runtime_error("Unsupported framebuffer configuration: error %x", status);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
FramebufferGL::~FramebufferGL()
|
||||
{
|
||||
if (id)
|
||||
glDeleteFramebuffers(1, &id);
|
||||
}
|
||||
|
||||
void FramebufferGL::download(void* data, unsigned int size)
|
||||
{
|
||||
AYAASSERT(size == width * height * 4);
|
||||
|
||||
GLint oldfb = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, id);
|
||||
|
||||
#ifndef GLES
|
||||
glReadBuffer(id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);
|
||||
#endif
|
||||
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
|
||||
|
||||
std::vector<char> tempRow(width * 4);
|
||||
|
||||
for (unsigned int y = 0; y < height / 2; ++y)
|
||||
{
|
||||
char* dataRow = static_cast<char*>(data) + y * width * 4;
|
||||
char* dataOppositeRow = static_cast<char*>(data) + (height - 1 - y) * width * 4;
|
||||
|
||||
memcpy(&tempRow[0], dataRow, width * 4);
|
||||
memcpy(dataRow, dataOppositeRow, width * 4);
|
||||
memcpy(dataOppositeRow, &tempRow[0], width * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferGL::updateDimensions(unsigned int width, unsigned int height)
|
||||
{
|
||||
AYAASSERT(color.empty() && !depth);
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
67
engine/gfx/src/API/GL/FramebufferGL.hpp
Normal file
67
engine/gfx/src/API/GL/FramebufferGL.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Framebuffer.hpp"
|
||||
#include "TextureGL.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class RenderbufferGL : public Renderbuffer
|
||||
{
|
||||
public:
|
||||
RenderbufferGL(Device* device, const shared_ptr<TextureGL>& owner, unsigned int target, unsigned int samples = 1);
|
||||
RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
|
||||
~RenderbufferGL();
|
||||
|
||||
unsigned int getTextureId() const
|
||||
{
|
||||
return owner ? owner->getId() : 0;
|
||||
}
|
||||
unsigned int getTarget() const
|
||||
{
|
||||
return target;
|
||||
}
|
||||
unsigned int getBufferId() const
|
||||
{
|
||||
return bufferId;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int target; // TEXTURE_2D or TEXTURE_CUBE_MAP_POSITIVE_X, ...
|
||||
unsigned int bufferId;
|
||||
shared_ptr<TextureGL> owner;
|
||||
};
|
||||
|
||||
class FramebufferGL : public Framebuffer
|
||||
{
|
||||
public:
|
||||
FramebufferGL(Device* device, unsigned int id);
|
||||
FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
|
||||
~FramebufferGL();
|
||||
|
||||
virtual void download(void* data, unsigned int size);
|
||||
|
||||
void updateDimensions(unsigned int width, unsigned int height);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
unsigned int getDrawBuffers() const
|
||||
{
|
||||
return color.size();
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int id;
|
||||
|
||||
std::vector<shared_ptr<Renderbuffer>> color;
|
||||
shared_ptr<Renderbuffer> depth;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
339
engine/gfx/src/API/GL/GeometryGL.cpp
Normal file
339
engine/gfx/src/API/GL/GeometryGL.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include "DataModel/GameBasicSettings.hpp"
|
||||
|
||||
#include "GeometryGL.hpp"
|
||||
|
||||
#include "DeviceGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(FreakyModeEnabled, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static const GLenum gBufferUsageGL[GeometryBuffer::Usage_Count] = {GL_STATIC_DRAW, GL_DYNAMIC_DRAW};
|
||||
|
||||
static const GLenum gBufferLockGL[GeometryBuffer::Lock_Count] = {GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT};
|
||||
|
||||
struct VertexFormatGL
|
||||
{
|
||||
int size;
|
||||
GLenum type;
|
||||
bool normalized;
|
||||
};
|
||||
|
||||
static const VertexFormatGL gVertexFormatGL[VertexLayout::Format_Count] = {
|
||||
{1, GL_FLOAT, false},
|
||||
{2, GL_FLOAT, false},
|
||||
{3, GL_FLOAT, false},
|
||||
{4, GL_FLOAT, false},
|
||||
{2, GL_SHORT, false},
|
||||
{4, GL_SHORT, false},
|
||||
{4, GL_UNSIGNED_BYTE, false},
|
||||
{4, GL_UNSIGNED_BYTE, true},
|
||||
};
|
||||
|
||||
static const GLenum gGeometryPrimitiveGL[Geometry::Primitive_Count] = {GL_TRIANGLES, GL_LINES, GL_POINTS, GL_TRIANGLE_STRIP};
|
||||
|
||||
static unsigned int getVertexAttributeGL(VertexLayout::Semantic semantic, unsigned int index)
|
||||
{
|
||||
switch (semantic)
|
||||
{
|
||||
case VertexLayout::Semantic_Position:
|
||||
AYAASSERT(index == 0);
|
||||
return 0;
|
||||
|
||||
case VertexLayout::Semantic_Normal:
|
||||
AYAASSERT(index == 0);
|
||||
return 1;
|
||||
|
||||
case VertexLayout::Semantic_Color:
|
||||
AYAASSERT(index < 2);
|
||||
return 2 + index;
|
||||
|
||||
case VertexLayout::Semantic_Texture:
|
||||
AYAASSERT(index < 8);
|
||||
return 4 + index;
|
||||
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* VertexLayoutGL::getShaderAttributeName(unsigned int id)
|
||||
{
|
||||
static const char* table[] = {
|
||||
"vertex",
|
||||
"normal",
|
||||
"colour",
|
||||
"secondary_colour",
|
||||
"uv0",
|
||||
"uv1",
|
||||
"uv2",
|
||||
"uv3",
|
||||
"uv4",
|
||||
"uv5",
|
||||
"uv6",
|
||||
"uv7",
|
||||
};
|
||||
|
||||
return (id < ARRAYSIZE(table)) ? table[id] : NULL;
|
||||
}
|
||||
|
||||
VertexLayoutGL::VertexLayoutGL(Device* device, const std::vector<Element>& elements)
|
||||
: VertexLayout(device, elements)
|
||||
{
|
||||
}
|
||||
|
||||
VertexLayoutGL::~VertexLayoutGL() {}
|
||||
|
||||
template<typename Base>
|
||||
GeometryBufferGL<Base>::GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target)
|
||||
: Base(device, elementSize, elementCount, usage)
|
||||
, target(target)
|
||||
, id(0)
|
||||
, locked(0)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferGL<Base>::create()
|
||||
{
|
||||
glGenBuffers(1, &id);
|
||||
AYAASSERT(id);
|
||||
|
||||
glBindBuffer(target, id);
|
||||
glBufferData(target, this->elementSize * this->elementCount, NULL, gBufferUsageGL[this->usage]);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
GeometryBufferGL<Base>::~GeometryBufferGL()
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
|
||||
glDeleteBuffers(1, &id);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void* GeometryBufferGL<Base>::lock(GeometryBuffer::LockMode mode)
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
|
||||
unsigned int size = this->elementSize * this->elementCount;
|
||||
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
|
||||
|
||||
if (caps.extMapBuffer)
|
||||
{
|
||||
glBindBuffer(target, id);
|
||||
|
||||
if (caps.extMapBufferRange)
|
||||
{
|
||||
locked = glMapBufferRange(target, 0, size, gBufferLockGL[mode]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == GeometryBuffer::Lock_Discard)
|
||||
glBufferData(target, size, NULL, gBufferUsageGL[this->usage]);
|
||||
|
||||
locked = glMapBuffer(target, GL_WRITE_ONLY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
locked = new char[size];
|
||||
}
|
||||
|
||||
AYAASSERT(locked);
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferGL<Base>::unlock()
|
||||
{
|
||||
AYAASSERT(locked);
|
||||
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
|
||||
|
||||
if (caps.extMapBuffer)
|
||||
{
|
||||
glBindBuffer(target, id);
|
||||
glUnmapBuffer(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindBuffer(target, id);
|
||||
glBufferSubData(target, 0, this->elementSize * this->elementCount, locked);
|
||||
|
||||
delete[] static_cast<char*>(locked);
|
||||
}
|
||||
|
||||
locked = NULL;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
void GeometryBufferGL<Base>::upload(unsigned int offset, const void* data, unsigned int size)
|
||||
{
|
||||
AYAASSERT(!locked);
|
||||
AYAASSERT(offset + size <= this->elementSize * this->elementCount);
|
||||
|
||||
glBindBuffer(target, id);
|
||||
glBufferSubData(target, offset, size, data);
|
||||
}
|
||||
|
||||
VertexBufferGL::VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBufferGL<VertexBuffer>(device, elementSize, elementCount, usage, GL_ARRAY_BUFFER)
|
||||
{
|
||||
create();
|
||||
}
|
||||
|
||||
VertexBufferGL::~VertexBufferGL() {}
|
||||
|
||||
IndexBufferGL::IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBufferGL<IndexBuffer>(device, elementSize, elementCount, usage, GL_ELEMENT_ARRAY_BUFFER)
|
||||
{
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
if (elementSize != 2 && elementSize != 4)
|
||||
throw Aya::runtime_error("Invalid element size: %d", (int)elementSize);
|
||||
|
||||
if (elementSize == 4 && !caps.supportsIndex32)
|
||||
throw Aya::runtime_error("Unsupported element size: %d", (int)elementSize);
|
||||
|
||||
create();
|
||||
}
|
||||
|
||||
IndexBufferGL::~IndexBufferGL() {}
|
||||
|
||||
GeometryGL::GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
: Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex)
|
||||
, id(0)
|
||||
, indexElementSize(0)
|
||||
{
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
if (caps.extVertexArrayObject)
|
||||
{
|
||||
glGenVertexArrays(1, &id);
|
||||
AYAASSERT(id);
|
||||
|
||||
glBindVertexArray(id);
|
||||
|
||||
bindArrays();
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
// Cache indexElementSize to reduce cache misses in case we're using VAO
|
||||
indexElementSize = indexBuffer->getElementSize();
|
||||
}
|
||||
}
|
||||
|
||||
GeometryGL::~GeometryGL()
|
||||
{
|
||||
if (id)
|
||||
{
|
||||
glDeleteVertexArrays(1, &id);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryGL::draw(Primitive primitive, unsigned int offset, unsigned int count)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
|
||||
// Setup vertex/index buffers
|
||||
if (id)
|
||||
glBindVertexArray(id);
|
||||
else
|
||||
mask = bindArrays();
|
||||
|
||||
// Perform draw call
|
||||
if (indexBuffer)
|
||||
{
|
||||
if (mask == 0)
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, static_cast<IndexBufferGL*>(indexBuffer.get())->getId());
|
||||
}
|
||||
|
||||
GLenum indexElementType = indexElementSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
||||
|
||||
glDrawElements(Aya::GameBasicSettings::singleton().getWireframeRendering() ? GL_LINES : gGeometryPrimitiveGL[primitive], count,
|
||||
indexElementType, reinterpret_cast<void*>(offset * indexElementSize));
|
||||
}
|
||||
else
|
||||
glDrawArrays(gGeometryPrimitiveGL[primitive], offset, count);
|
||||
|
||||
if (id)
|
||||
glBindVertexArray(0);
|
||||
else
|
||||
unbindArrays(mask);
|
||||
}
|
||||
|
||||
unsigned int GeometryGL::bindArrays()
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
unsigned int vbId = 0;
|
||||
|
||||
const std::vector<VertexLayout::Element>& elements = layout->getElements();
|
||||
|
||||
for (size_t i = 0; i < elements.size(); ++i)
|
||||
{
|
||||
const VertexLayout::Element& e = elements[i];
|
||||
|
||||
int index = getVertexAttributeGL(e.semantic, e.semanticIndex);
|
||||
AYAASSERT(index >= 0);
|
||||
|
||||
AYAASSERT(e.stream < vertexBuffers.size());
|
||||
|
||||
VertexBufferGL* vb = static_cast<VertexBufferGL*>(vertexBuffers[e.stream].get());
|
||||
|
||||
AYAASSERT(e.offset < vb->getElementSize());
|
||||
|
||||
size_t stride = vb->getElementSize();
|
||||
size_t offset = e.offset + stride * baseVertexIndex;
|
||||
|
||||
const VertexFormatGL& formatGl = gVertexFormatGL[e.format];
|
||||
|
||||
if (vbId != vb->getId())
|
||||
{
|
||||
vbId = vb->getId();
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbId);
|
||||
}
|
||||
|
||||
glEnableVertexAttribArray(index);
|
||||
glVertexAttribPointer(index, formatGl.size, formatGl.type, formatGl.normalized, stride, reinterpret_cast<void*>(offset));
|
||||
|
||||
mask |= 1 << index;
|
||||
}
|
||||
|
||||
if (indexBuffer)
|
||||
{
|
||||
IndexBufferGL* ib = static_cast<IndexBufferGL*>(indexBuffer.get());
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->getId());
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
void GeometryGL::unbindArrays(unsigned int mask)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (mask & (1 << i))
|
||||
glDisableVertexAttribArray(i);
|
||||
}
|
||||
|
||||
// Instantiate GeometryBufferGL template
|
||||
template class GeometryBufferGL<VertexBuffer>;
|
||||
template class GeometryBufferGL<IndexBuffer>;
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
83
engine/gfx/src/API/GL/GeometryGL.hpp
Normal file
83
engine/gfx/src/API/GL/GeometryGL.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Geometry.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class VertexLayoutGL : public VertexLayout
|
||||
{
|
||||
public:
|
||||
static const char* getShaderAttributeName(unsigned int id);
|
||||
|
||||
VertexLayoutGL(Device* device, const std::vector<Element>& elements);
|
||||
~VertexLayoutGL();
|
||||
};
|
||||
|
||||
template<typename Base>
|
||||
class GeometryBufferGL : public Base
|
||||
{
|
||||
public:
|
||||
GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target);
|
||||
~GeometryBufferGL();
|
||||
|
||||
virtual void* lock(GeometryBuffer::LockMode mode);
|
||||
virtual void unlock();
|
||||
|
||||
virtual void upload(unsigned int offset, const void* data, unsigned int size);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
protected:
|
||||
void create();
|
||||
|
||||
private:
|
||||
unsigned int target;
|
||||
unsigned int id;
|
||||
|
||||
void* locked;
|
||||
};
|
||||
|
||||
class VertexBufferGL : public GeometryBufferGL<VertexBuffer>
|
||||
{
|
||||
public:
|
||||
VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
~VertexBufferGL();
|
||||
};
|
||||
|
||||
class IndexBufferGL : public GeometryBufferGL<IndexBuffer>
|
||||
{
|
||||
public:
|
||||
IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
~IndexBufferGL();
|
||||
};
|
||||
|
||||
class GeometryGL : public Geometry
|
||||
{
|
||||
public:
|
||||
GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
|
||||
~GeometryGL();
|
||||
|
||||
void draw(Primitive primitive, unsigned int offset, unsigned int count);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int id;
|
||||
unsigned int indexElementSize;
|
||||
|
||||
unsigned int bindArrays();
|
||||
void unbindArrays(unsigned int mask);
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
137
engine/gfx/src/API/GL/HeadersGL.hpp
Normal file
137
engine/gfx/src/API/GL/HeadersGL.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef RBX_PLATFORM_IOS
|
||||
#include <OpenGLES/ES2/gl.h>
|
||||
#include <OpenGLES/ES2/glext.h>
|
||||
#ifdef __OBJC__
|
||||
#include <OpenGLES/EAGL.h>
|
||||
#endif
|
||||
|
||||
#define GLES
|
||||
#elif defined(__ANDROID__)
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
#define GLES
|
||||
#else
|
||||
#include <glad/gl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
typedef struct __GLsync* GLsync;
|
||||
typedef uint64_t GLuint64;
|
||||
#endif
|
||||
|
||||
#ifdef GLES
|
||||
// Core GLES
|
||||
#define glClearDepth glClearDepthf
|
||||
|
||||
// OES_vertex_array_object
|
||||
GLvoid glBindVertexArray(GLuint array);
|
||||
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint* arrays);
|
||||
GLvoid glGenVertexArrays(GLsizei n, GLuint* arrays);
|
||||
|
||||
// OES_mapbuffer
|
||||
#define GL_WRITE_ONLY 0x88B9
|
||||
GLvoid* glMapBuffer(GLenum target, GLenum access);
|
||||
GLboolean glUnmapBuffer(GLenum target);
|
||||
|
||||
// EXT_map_buffer_range
|
||||
GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
#define GL_MAP_READ_BIT 0x0001
|
||||
#define GL_MAP_WRITE_BIT 0x0002
|
||||
#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
|
||||
#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
|
||||
#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
|
||||
#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
|
||||
|
||||
// EXT_texture_storage
|
||||
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
#define GL_LUMINANCE8 0x8040
|
||||
#define GL_LUMINANCE8_ALPHA8 0x8045
|
||||
|
||||
// GL_OES_packed_depth_stencil
|
||||
#define GL_DEPTH_STENCIL 0x84F9
|
||||
#define GL_UNSIGNED_INT_24_8 0x84FA
|
||||
#define GL_DEPTH24_STENCIL8 0x88F0
|
||||
|
||||
// OES_rgb8_rgba8
|
||||
#define GL_RGBA8 0x8058
|
||||
|
||||
// OES_texture_half_float
|
||||
#define GL_HALF_FLOAT 0x8D61
|
||||
|
||||
// EXT_color_buffer_half_float
|
||||
#define GL_RGBA16F 0x881A
|
||||
|
||||
// OES_texture_3D
|
||||
#define GL_TEXTURE_3D 0x806F
|
||||
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format,
|
||||
GLenum type, const GLvoid* pixels);
|
||||
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
|
||||
GLenum format, GLenum type, const GLvoid* pixels);
|
||||
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
|
||||
|
||||
// GLES 3.0 formats
|
||||
#define GL_RED 0x1903
|
||||
#define GL_RG 0x8227
|
||||
#define GL_R8 0x8229
|
||||
#define GL_RG8 0x822B
|
||||
#define GL_RG16 0x822C
|
||||
|
||||
// GLES 3.0 buffer objects
|
||||
#define GL_DYNAMIC_COPY 0x88EA
|
||||
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
||||
|
||||
// GLES 3.0 sync objects
|
||||
#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
|
||||
#define GL_ALREADY_SIGNALED 0x911A
|
||||
#define GL_TIMEOUT_EXPIRED 0x911B
|
||||
#define GL_CONDITION_SATISFIED 0x911C
|
||||
#define GL_WAIT_FAILED 0x911D
|
||||
#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
|
||||
GLsync glFenceSync(GLenum condition, GLbitfield flags);
|
||||
void glDeleteSync(GLsync sync);
|
||||
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
|
||||
// GLES 3.0 MSAA
|
||||
#define GL_READ_FRAMEBUFFER 0x8CA8
|
||||
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
||||
#define GL_MAX_SAMPLES 0x8D57
|
||||
GLvoid glBlitFramebuffer(
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
|
||||
// GLES 3.0 invalidate FBO
|
||||
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments);
|
||||
#endif
|
||||
|
||||
// EXT_texture_compression_s3tc
|
||||
#ifndef GL_EXT_texture_compression_s3tc
|
||||
#define GL_EXT_texture_compression_s3tc 1
|
||||
|
||||
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
|
||||
#endif
|
||||
|
||||
// IMG_texture_compression_pvrtc
|
||||
#ifndef GL_IMG_texture_compression_pvrtc
|
||||
#define GL_IMG_texture_compression_pvrtc 1
|
||||
|
||||
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
|
||||
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
|
||||
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
|
||||
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
|
||||
#endif
|
||||
|
||||
#ifndef GL_ETC1_RGB8_OES
|
||||
#define GL_ETC1_RGB8_OES (0x8D64)
|
||||
#endif
|
||||
|
||||
// APPLE_texture_max_level, GLES 3.0
|
||||
#ifndef GL_TEXTURE_MAX_LEVEL
|
||||
#define GL_TEXTURE_MAX_LEVEL 0x813D
|
||||
#endif
|
||||
563
engine/gfx/src/API/GL/ShaderGL.cpp
Normal file
563
engine/gfx/src/API/GL/ShaderGL.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
#include "ShaderGL.hpp"
|
||||
|
||||
#include "GeometryGL.hpp"
|
||||
#include "DeviceGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static int getShaderParameter(GLuint shader, GLenum pname)
|
||||
{
|
||||
GLint result;
|
||||
glGetShaderiv(shader, pname, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int getProgramParameter(GLuint program, GLenum pname)
|
||||
{
|
||||
GLint result;
|
||||
glGetProgramiv(program, pname, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename GetParameter, typename GetLog>
|
||||
static std::string getInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
|
||||
{
|
||||
int infoLength = getParameter(id, GL_INFO_LOG_LENGTH);
|
||||
|
||||
if (infoLength > 0)
|
||||
{
|
||||
std::vector<char> buffer(infoLength);
|
||||
|
||||
getLog(id, infoLength, NULL, &buffer[0]);
|
||||
|
||||
return std::string(&buffer[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename GetParameter, typename GetLog>
|
||||
static void dumpInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
|
||||
{
|
||||
std::string log = getInfoLog(id, getParameter, getLog);
|
||||
|
||||
// Intel and AMD drivers like to say "No errors" for every shader.
|
||||
if (strstr(log.c_str(), "warn") || strstr(log.c_str(), "Warn") || strstr(log.c_str(), "WARN"))
|
||||
{
|
||||
FASTLOG2(FLog::Graphics, "Shader %d has a non-empty infolog (length %d)", id, log.length());
|
||||
|
||||
ShaderProgram::dumpToFLog(log, FLog::Graphics);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int compileShader(const std::string& source, GLenum type)
|
||||
{
|
||||
unsigned int id = glCreateShader(type);
|
||||
AYAASSERT(id);
|
||||
|
||||
int length = source.length();
|
||||
const char* data = source.c_str();
|
||||
|
||||
glShaderSource(id, 1, &data, &length);
|
||||
|
||||
glCompileShader(id);
|
||||
|
||||
if (getShaderParameter(id, GL_COMPILE_STATUS) != 1)
|
||||
{
|
||||
std::string infoLog = getInfoLog(id, getShaderParameter, glGetShaderInfoLog);
|
||||
|
||||
glDeleteShader(id);
|
||||
|
||||
throw std::runtime_error(infoLog);
|
||||
}
|
||||
|
||||
dumpInfoLog(id, getShaderParameter, glGetShaderInfoLog);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static unsigned int parseAttribs(const std::string& source, const char* prefix)
|
||||
{
|
||||
std::set<std::string> attribs;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < source.length())
|
||||
{
|
||||
std::string::size_type start = source.find(prefix, offset);
|
||||
if (start == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string::size_type eol = source.find('\n', start);
|
||||
if (eol == std::string::npos)
|
||||
break;
|
||||
|
||||
offset = eol;
|
||||
|
||||
std::string::size_type semi = source.find(';', start);
|
||||
std::string::size_type space = source.find_last_of(' ', eol);
|
||||
|
||||
if ((start == 0 || source[start - 1] == '\n') && semi != std::string::npos && space != std::string::npos && space < semi)
|
||||
attribs.insert(source.substr(space + 1, semi - space - 1));
|
||||
}
|
||||
|
||||
unsigned int result = 0;
|
||||
|
||||
for (unsigned int attr = 0; attr < 16; ++attr)
|
||||
{
|
||||
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
|
||||
|
||||
if (name && attribs.count(name))
|
||||
result |= 1 << attr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void applyAttribs(unsigned int id, unsigned int attribMask)
|
||||
{
|
||||
for (unsigned int attr = 0; attr < 16; ++attr)
|
||||
if (attribMask & (1 << attr))
|
||||
{
|
||||
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
|
||||
AYAASSERT(name);
|
||||
glBindAttribLocation(id, attr, name);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<FragmentShaderGL::Sampler> parseSamplers(const std::string& source)
|
||||
{
|
||||
std::vector<FragmentShaderGL::Sampler> result;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < source.length())
|
||||
{
|
||||
std::string::size_type start = source.find("//$$", offset);
|
||||
if (start == std::string::npos)
|
||||
return result;
|
||||
|
||||
std::string::size_type equals = source.find('=', start);
|
||||
if (equals == std::string::npos)
|
||||
return result;
|
||||
|
||||
std::string::size_type end = source.find('\n', equals);
|
||||
if (end == std::string::npos)
|
||||
return result;
|
||||
|
||||
std::string name(source.begin() + start + 4, source.begin() + equals);
|
||||
std::string value(source.begin() + equals + 1, source.begin() + end);
|
||||
|
||||
if (!value.empty() && value[0] == 's')
|
||||
{
|
||||
FragmentShaderGL::Sampler entry = {name, atoi(value.c_str() + 1)};
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
|
||||
offset = end;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int getSamplerLocation(unsigned int id, const std::string& name)
|
||||
{
|
||||
int location;
|
||||
|
||||
if ((location = glGetUniformLocation(id, name.c_str())) >= 0)
|
||||
return location;
|
||||
|
||||
if ((location = glGetUniformLocation(id, ("xlu_" + name).c_str())) >= 0)
|
||||
return location;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned int applySamplers(unsigned int id, const std::vector<FragmentShaderGL::Sampler>& samplers)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
|
||||
glUseProgram(id);
|
||||
|
||||
for (size_t i = 0; i < samplers.size(); ++i)
|
||||
{
|
||||
int location = getSamplerLocation(id, samplers[i].name);
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
glUniform1i(location, samplers[i].slot);
|
||||
|
||||
mask |= 1 << samplers[i].slot;
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static ShaderProgramGL::Uniform::Type getUniformType(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_FLOAT:
|
||||
return ShaderProgramGL::Uniform::Type_Float;
|
||||
case GL_FLOAT_VEC2:
|
||||
return ShaderProgramGL::Uniform::Type_Float2;
|
||||
case GL_FLOAT_VEC3:
|
||||
return ShaderProgramGL::Uniform::Type_Float3;
|
||||
case GL_FLOAT_VEC4:
|
||||
return ShaderProgramGL::Uniform::Type_Float4;
|
||||
case GL_FLOAT_MAT4:
|
||||
return ShaderProgramGL::Uniform::Type_Float4x4;
|
||||
default:
|
||||
return ShaderProgramGL::Uniform::Type_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<std::string, ShaderProgramGL::Uniform> UniformTable;
|
||||
|
||||
static UniformTable extractUniforms(unsigned int id)
|
||||
{
|
||||
int uniformCount = getProgramParameter(id, GL_ACTIVE_UNIFORMS);
|
||||
int uniformMaxLength = getProgramParameter(id, GL_ACTIVE_UNIFORM_MAX_LENGTH);
|
||||
|
||||
std::vector<char> uniformName(uniformMaxLength + 1);
|
||||
|
||||
UniformTable result;
|
||||
|
||||
for (int i = 0; i < uniformCount; ++i)
|
||||
{
|
||||
GLint size;
|
||||
GLenum type;
|
||||
glGetActiveUniform(id, i, uniformMaxLength, NULL, &size, &type, &uniformName[0]);
|
||||
|
||||
ShaderProgramGL::Uniform::Type uniformType = getUniformType(type);
|
||||
|
||||
int location = glGetUniformLocation(id, &uniformName[0]);
|
||||
|
||||
if (location >= 0 && uniformType != ShaderProgramGL::Uniform::Type_Unknown)
|
||||
{
|
||||
ShaderProgramGL::Uniform uniform = {location, uniformType, (unsigned int)size, 0};
|
||||
|
||||
std::string name(&uniformName[0]);
|
||||
|
||||
// Remove xlu_ prefix for entrypoint-local uniforms
|
||||
if (name.compare(0, 4, "xlu_") == 0)
|
||||
name.erase(name.begin(), name.begin() + 4);
|
||||
|
||||
// Remove [0] from arrays
|
||||
std::string::size_type index = name.find('[');
|
||||
|
||||
if (index != std::string::npos)
|
||||
name.erase(name.begin() + index, name.end());
|
||||
|
||||
result[name] = uniform;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const ShaderProgramGL::Uniform* findUniform(const UniformTable& ctab, const std::string& name)
|
||||
{
|
||||
UniformTable::const_iterator it = ctab.find(name);
|
||||
|
||||
return (it == ctab.end()) ? NULL : &it->second;
|
||||
}
|
||||
|
||||
static inline void transposeMatrix(float* dest, const float* source, const float* lastColumn)
|
||||
{
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[4];
|
||||
dest[2] = source[8];
|
||||
dest[3] = lastColumn[0];
|
||||
dest[4] = source[1];
|
||||
dest[5] = source[5];
|
||||
dest[6] = source[9];
|
||||
dest[7] = lastColumn[1];
|
||||
dest[8] = source[2];
|
||||
dest[9] = source[6];
|
||||
dest[10] = source[10];
|
||||
dest[11] = lastColumn[2];
|
||||
dest[12] = source[3];
|
||||
dest[13] = source[7];
|
||||
dest[14] = source[11];
|
||||
dest[15] = lastColumn[3];
|
||||
}
|
||||
|
||||
VertexShaderGL::VertexShaderGL(Device* device, const std::string& source)
|
||||
: VertexShader(device)
|
||||
, id(0)
|
||||
, attribMask(0)
|
||||
{
|
||||
id = compileShader(source, GL_VERTEX_SHADER);
|
||||
|
||||
attribMask = parseAttribs(source, static_cast<DeviceGL*>(device)->getCapsGL().ext3 ? "in " : "attribute ");
|
||||
}
|
||||
|
||||
VertexShaderGL::~VertexShaderGL()
|
||||
{
|
||||
glDeleteShader(id);
|
||||
}
|
||||
|
||||
void VertexShaderGL::reloadBytecode(const std::vector<char>& bytecode)
|
||||
{
|
||||
throw std::runtime_error("Bytecode reloading is not supported");
|
||||
}
|
||||
|
||||
FragmentShaderGL::FragmentShaderGL(Device* device, const std::string& source)
|
||||
: FragmentShader(device)
|
||||
, id(0)
|
||||
{
|
||||
id = compileShader(source, GL_FRAGMENT_SHADER);
|
||||
|
||||
samplers = parseSamplers(source);
|
||||
}
|
||||
|
||||
FragmentShaderGL::~FragmentShaderGL()
|
||||
{
|
||||
glDeleteShader(id);
|
||||
}
|
||||
|
||||
void FragmentShaderGL::reloadBytecode(const std::vector<char>& bytecode)
|
||||
{
|
||||
throw std::runtime_error("Bytecode reloading is not supported");
|
||||
}
|
||||
|
||||
ShaderProgramGL::ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
|
||||
: ShaderProgram(device, vertexShader, fragmentShader)
|
||||
, id(0)
|
||||
, uniformWorldMatrix(-1)
|
||||
, uniformWorldMatrixArray(-1)
|
||||
, cachedGlobalVersion(0)
|
||||
, maxWorldTransforms(0)
|
||||
, samplerMask(0)
|
||||
{
|
||||
id = glCreateProgram();
|
||||
AYAASSERT(id);
|
||||
|
||||
glAttachShader(id, static_cast<VertexShaderGL*>(vertexShader.get())->getId());
|
||||
glAttachShader(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getId());
|
||||
|
||||
// Setup attribute locations
|
||||
applyAttribs(id, static_cast<VertexShaderGL*>(vertexShader.get())->getAttribMask());
|
||||
|
||||
glLinkProgram(id);
|
||||
|
||||
if (getProgramParameter(id, GL_LINK_STATUS) != 1)
|
||||
{
|
||||
std::string infoLog = getInfoLog(id, getProgramParameter, glGetProgramInfoLog);
|
||||
|
||||
glDeleteProgram(id);
|
||||
|
||||
throw std::runtime_error(infoLog);
|
||||
}
|
||||
|
||||
dumpInfoLog(id, getProgramParameter, glGetProgramInfoLog);
|
||||
|
||||
// Make sure the program uses correct sampler slot assignments
|
||||
// Note: this resets current program to 0!
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
|
||||
|
||||
samplerMask = applySamplers(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getSamplers());
|
||||
|
||||
// Populate constant tables
|
||||
const std::vector<ShaderGlobalConstant>& globalConstants = static_cast<DeviceGL*>(device)->getGlobalConstants();
|
||||
|
||||
UniformTable ctab = extractUniforms(id);
|
||||
|
||||
for (size_t i = 0; i < globalConstants.size(); ++i)
|
||||
if (const Uniform* uniform = findUniform(ctab, globalConstants[i].name))
|
||||
{
|
||||
Uniform u = *uniform;
|
||||
u.offset = globalConstants[i].offset;
|
||||
|
||||
globalUniforms.push_back(u);
|
||||
|
||||
ctab.erase(globalConstants[i].name);
|
||||
}
|
||||
|
||||
// Populate world matrix information
|
||||
if (const Uniform* uniform = findUniform(ctab, "WorldMatrix"))
|
||||
{
|
||||
AYAASSERT(uniform->type == Uniform::Type_Float4x4);
|
||||
|
||||
uniformWorldMatrix = uniform->location;
|
||||
maxWorldTransforms = 1;
|
||||
|
||||
ctab.erase("WorldMatrix");
|
||||
}
|
||||
|
||||
if (const Uniform* uniform = findUniform(ctab, "WorldMatrixArray"))
|
||||
{
|
||||
AYAASSERT(uniform->type == Uniform::Type_Float4);
|
||||
AYAASSERT(uniform->size % 3 == 0);
|
||||
|
||||
uniformWorldMatrixArray = uniform->location;
|
||||
maxWorldTransforms = uniform->size / 3;
|
||||
|
||||
ctab.erase("WorldMatrixArray");
|
||||
}
|
||||
|
||||
// Populate uniform information
|
||||
for (UniformTable::iterator it = ctab.begin(); it != ctab.end(); ++it)
|
||||
{
|
||||
uniforms.push_back(std::make_pair(it->first, it->second));
|
||||
}
|
||||
}
|
||||
|
||||
ShaderProgramGL::~ShaderProgramGL()
|
||||
{
|
||||
glDeleteProgram(id);
|
||||
|
||||
// Shader program destruction invalidates currently bound program id
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
|
||||
}
|
||||
|
||||
int ShaderProgramGL::getConstantHandle(const char* name) const
|
||||
{
|
||||
for (size_t i = 0; i < uniforms.size(); ++i)
|
||||
if (uniforms[i].first == name)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int ShaderProgramGL::getMaxWorldTransforms() const
|
||||
{
|
||||
return maxWorldTransforms;
|
||||
}
|
||||
|
||||
unsigned int ShaderProgramGL::getSamplerMask() const
|
||||
{
|
||||
return samplerMask;
|
||||
}
|
||||
|
||||
void ShaderProgramGL::bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache)
|
||||
{
|
||||
if (*cache != this)
|
||||
{
|
||||
*cache = this;
|
||||
|
||||
glUseProgram(id);
|
||||
}
|
||||
|
||||
if (cachedGlobalVersion != globalVersion)
|
||||
{
|
||||
cachedGlobalVersion = globalVersion;
|
||||
|
||||
for (size_t i = 0; i < globalUniforms.size(); ++i)
|
||||
{
|
||||
const Uniform& u = globalUniforms[i];
|
||||
|
||||
const float* data = reinterpret_cast<const float*>(static_cast<const char*>(globalData) + u.offset);
|
||||
|
||||
switch (u.type)
|
||||
{
|
||||
case Uniform::Type_Float:
|
||||
glUniform1fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float2:
|
||||
glUniform2fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float3:
|
||||
glUniform3fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float4:
|
||||
glUniform4fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float4x4:
|
||||
{
|
||||
float matrix[16];
|
||||
transposeMatrix(matrix, data, data + 12);
|
||||
|
||||
glUniformMatrix4fv(u.location, 1, false, matrix);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
|
||||
{
|
||||
if (matrixCount == 0)
|
||||
return;
|
||||
|
||||
if (uniformWorldMatrix >= 0)
|
||||
{
|
||||
static const float lastColumn[] = {0, 0, 0, 1};
|
||||
|
||||
float matrix[16];
|
||||
transposeMatrix(matrix, data, lastColumn);
|
||||
|
||||
glUniformMatrix4fv(uniformWorldMatrix, 1, false, matrix);
|
||||
}
|
||||
|
||||
if (uniformWorldMatrixArray >= 0)
|
||||
{
|
||||
AYAASSERT(matrixCount <= maxWorldTransforms);
|
||||
|
||||
glUniform4fv(uniformWorldMatrixArray, matrixCount * 3, data);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgramGL::setConstant(int handle, const float* data, size_t vectorCount)
|
||||
{
|
||||
if (handle < 0)
|
||||
return;
|
||||
|
||||
AYAASSERT(static_cast<size_t>(handle) < uniforms.size());
|
||||
|
||||
const Uniform& u = uniforms[handle].second;
|
||||
|
||||
switch (u.type)
|
||||
{
|
||||
case Uniform::Type_Float:
|
||||
AYAASSERT(vectorCount == 1);
|
||||
glUniform1fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float2:
|
||||
AYAASSERT(vectorCount == 1);
|
||||
glUniform2fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float3:
|
||||
AYAASSERT(vectorCount == 1);
|
||||
glUniform3fv(u.location, 1, data);
|
||||
break;
|
||||
|
||||
case Uniform::Type_Float4:
|
||||
AYAASSERT(vectorCount > 0 && vectorCount <= u.size);
|
||||
glUniform4fv(u.location, vectorCount, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
AYAASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
123
engine/gfx/src/API/GL/ShaderGL.hpp
Normal file
123
engine/gfx/src/API/GL/ShaderGL.hpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Shader.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class VertexShaderGL : public VertexShader
|
||||
{
|
||||
public:
|
||||
VertexShaderGL(Device* device, const std::string& source);
|
||||
~VertexShaderGL();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
unsigned int getAttribMask() const
|
||||
{
|
||||
return attribMask;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int id;
|
||||
unsigned int attribMask;
|
||||
};
|
||||
|
||||
class FragmentShaderGL : public FragmentShader
|
||||
{
|
||||
public:
|
||||
struct Sampler
|
||||
{
|
||||
std::string name;
|
||||
int slot;
|
||||
};
|
||||
|
||||
FragmentShaderGL(Device* device, const std::string& source);
|
||||
~FragmentShaderGL();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const std::vector<Sampler>& getSamplers() const
|
||||
{
|
||||
return samplers;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int id;
|
||||
std::vector<Sampler> samplers;
|
||||
};
|
||||
|
||||
class ShaderProgramGL : public ShaderProgram
|
||||
{
|
||||
public:
|
||||
struct Uniform
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Type_Unknown,
|
||||
|
||||
Type_Float,
|
||||
Type_Float2,
|
||||
Type_Float3,
|
||||
Type_Float4,
|
||||
Type_Float4x4,
|
||||
|
||||
Type_Count
|
||||
};
|
||||
|
||||
int location;
|
||||
Type type;
|
||||
unsigned int size;
|
||||
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
|
||||
~ShaderProgramGL();
|
||||
|
||||
virtual int getConstantHandle(const char* name) const;
|
||||
|
||||
virtual unsigned int getMaxWorldTransforms() const;
|
||||
virtual unsigned int getSamplerMask() const;
|
||||
|
||||
void bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache);
|
||||
|
||||
void setWorldTransforms4x3(const float* data, size_t matrixCount);
|
||||
void setConstant(int handle, const float* data, size_t vectorCount);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int id;
|
||||
|
||||
int uniformWorldMatrix;
|
||||
int uniformWorldMatrixArray;
|
||||
|
||||
unsigned int cachedGlobalVersion;
|
||||
|
||||
std::vector<Uniform> globalUniforms;
|
||||
std::vector<std::pair<std::string, Uniform>> uniforms;
|
||||
|
||||
unsigned int maxWorldTransforms;
|
||||
unsigned int samplerMask;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
545
engine/gfx/src/API/GL/TextureGL.cpp
Normal file
545
engine/gfx/src/API/GL/TextureGL.cpp
Normal file
@@ -0,0 +1,545 @@
|
||||
#include "TextureGL.hpp"
|
||||
|
||||
#include "DeviceGL.hpp"
|
||||
#include "FramebufferGL.hpp"
|
||||
|
||||
#include "HeadersGL.hpp"
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
FASTFLAG(GraphicsTextureCommitChanges)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
static const GLenum gTextureTargetGL[Texture::Type_Count] = {GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP};
|
||||
|
||||
struct TextureFormatGL
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum dataFormat;
|
||||
GLenum dataType;
|
||||
};
|
||||
|
||||
static const TextureFormatGL gTextureFormatGL[Texture::Format_Count] = {
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE},
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE},
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1},
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
|
||||
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT},
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT},
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, 0},
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, 0},
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, 0},
|
||||
{GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0},
|
||||
{GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0},
|
||||
{GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0},
|
||||
{GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0},
|
||||
{GL_ETC1_RGB8_OES, 0, 0},
|
||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT},
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},
|
||||
};
|
||||
|
||||
const TextureFormatGL& getTextureFormatGL(Texture::Format format, bool ext3)
|
||||
{
|
||||
if (!ext3 && format == Texture::Format_L8)
|
||||
{
|
||||
static const TextureFormatGL result = {GL_LUMINANCE8_EXT, GL_LUMINANCE, GL_UNSIGNED_BYTE};
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!ext3 && format == Texture::Format_LA8)
|
||||
{
|
||||
static const TextureFormatGL result = {GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE};
|
||||
return result;
|
||||
}
|
||||
|
||||
return gTextureFormatGL[format];
|
||||
}
|
||||
|
||||
static const GLenum gSamplerFilterMinGL[SamplerState::Filter_Count][2] = {
|
||||
{GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST}, {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR}, {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR}};
|
||||
|
||||
static const GLenum gSamplerFilterMagGL[SamplerState::Filter_Count] = {GL_NEAREST, GL_LINEAR, GL_LINEAR};
|
||||
|
||||
static const GLenum gSamplerAddressGL[SamplerState::Address_Count] = {GL_REPEAT, GL_CLAMP_TO_EDGE};
|
||||
|
||||
static bool doesFormatSupportPartialUpload(Texture::Format format)
|
||||
{
|
||||
return format != Texture::Format_ETC1;
|
||||
}
|
||||
|
||||
static unsigned int createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, const DeviceCapsGL& caps)
|
||||
{
|
||||
GLenum target = gTextureTargetGL[type];
|
||||
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
||||
|
||||
unsigned int id = 0;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(target, id);
|
||||
|
||||
if (caps.extTextureStorage)
|
||||
{
|
||||
if (type == Texture::Type_3D)
|
||||
{
|
||||
glTexStorage3D(target, mipLevels, formatGl.internalFormat, width, height, depth);
|
||||
}
|
||||
else if (type == Texture::Type_2DMultisampled)
|
||||
{
|
||||
glTexStorage2DMultisample(target, 4, formatGl.internalFormat, width, height, GL_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexStorage2D(target, mipLevels, formatGl.internalFormat, width, height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is this correct? There's a subtle difference here between desktop GL and GLES
|
||||
#ifdef GLES
|
||||
GLenum internalFormat = formatGl.dataFormat;
|
||||
#else
|
||||
GLenum internalFormat = formatGl.internalFormat;
|
||||
#endif
|
||||
|
||||
unsigned int faces = (type == Texture::Type_Cube) ? 6 : 1;
|
||||
|
||||
for (unsigned int index = 0; index < faces; ++index)
|
||||
{
|
||||
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
||||
|
||||
for (unsigned int mip = 0; mip < mipLevels; ++mip)
|
||||
{
|
||||
unsigned int mipWidth = Texture::getMipSide(width, mip);
|
||||
unsigned int mipHeight = Texture::getMipSide(height, mip);
|
||||
unsigned int mipDepth = Texture::getMipSide(depth, mip);
|
||||
|
||||
if (type == Texture::Type_3D)
|
||||
{
|
||||
glTexImage3D(faceTarget, mip, internalFormat, mipWidth, mipHeight, mipDepth, 0, formatGl.dataFormat, formatGl.dataType, NULL);
|
||||
}
|
||||
else if (Texture::isFormatCompressed(format))
|
||||
{
|
||||
unsigned int size = Texture::getImageSize(format, mipWidth, mipHeight);
|
||||
|
||||
// glCompressedTexImage does not accept NULL as a valid pointer
|
||||
std::vector<char> data(size);
|
||||
|
||||
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, mipWidth, mipHeight, 0, size, &data[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexImage2D(faceTarget, mip, internalFormat, mipWidth, mipHeight, 0, formatGl.dataFormat, formatGl.dataType, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (caps.supportsTexturePartialMipChain)
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipLevels - 1);
|
||||
else
|
||||
AYAASSERT(mipLevels == 1 || mipLevels == Texture::getMaxMipCount(width, height, depth));
|
||||
|
||||
glBindTexture(target, 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void uploadTexture(unsigned int id, Texture::Type type, Texture::Format format, unsigned int index, unsigned int mip,
|
||||
const TextureRegion& region, bool entireRegion, const void* data, const DeviceCapsGL& caps)
|
||||
{
|
||||
GLenum target = gTextureTargetGL[type];
|
||||
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
||||
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(target, id);
|
||||
|
||||
bool needsCustomAlignment = (!Texture::isFormatCompressed(format) && Texture::getFormatBits(format) < 32);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
|
||||
|
||||
if (type == Texture::Type_3D)
|
||||
{
|
||||
glTexSubImage3D(
|
||||
faceTarget, mip, region.x, region.y, region.z, region.width, region.height, region.depth, formatGl.dataFormat, formatGl.dataType, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Texture::isFormatCompressed(format))
|
||||
{
|
||||
unsigned int size = Texture::getImageSize(format, region.width, region.height) * region.depth;
|
||||
|
||||
if (entireRegion && !doesFormatSupportPartialUpload(format) && !caps.extTextureStorage)
|
||||
{
|
||||
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, region.width, region.height, 0, size, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
glCompressedTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.internalFormat, size, data);
|
||||
}
|
||||
}
|
||||
else if (type == Texture::Type_2DMultisampled)
|
||||
{
|
||||
glTexStorage2DMultisample(faceTarget, 4, formatGl.internalFormat, region.width, region.height, GL_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.dataFormat, formatGl.dataType, data);
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(target, 0);
|
||||
}
|
||||
|
||||
TextureGL::TextureGL(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
|
||||
: Texture(device, type, format, width, height, depth, mipLevels, usage)
|
||||
, id(0)
|
||||
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
|
||||
, external(false)
|
||||
, scratchId(0)
|
||||
, scratchOffset(0)
|
||||
, scratchSize(0)
|
||||
{
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
||||
|
||||
if (!formatGl.internalFormat)
|
||||
throw Aya::runtime_error("Unsupported format: %d (platform)", format);
|
||||
|
||||
if ((format == Format_RGBA16F) && !caps.supportsTextureHalfFloat)
|
||||
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
|
||||
|
||||
if ((format == Format_BC1 || format == Format_BC2 || format == Format_BC3) && !caps.supportsTextureDXT)
|
||||
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
|
||||
|
||||
if ((format == Format_PVRTC_RGB2 || format == Format_PVRTC_RGBA2 || format == Format_PVRTC_RGB4 || format == Format_PVRTC_RGBA4) &&
|
||||
!caps.supportsTexturePVR)
|
||||
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
|
||||
|
||||
if (format == Format_ETC1 && !caps.supportsTextureETC1)
|
||||
throw Aya::runtime_error("Unsupported format: %d (capse)", format);
|
||||
|
||||
if (!caps.supportsTextureNPOT && ((width & (width - 1)) || (height & (height - 1)) || (depth & (depth - 1))) &&
|
||||
(usage != Usage_Renderbuffer || mipLevels > 1))
|
||||
throw Aya::runtime_error("Unsupported dimensions: %dx%dx%d (NPOT)", width, height, depth);
|
||||
|
||||
if (!caps.supportsTexture3D && type == Type_3D)
|
||||
throw Aya::runtime_error("Unsupported type: 3D");
|
||||
|
||||
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && isFormatCompressed(format))
|
||||
throw Aya::runtime_error("Unsupported format: compressed formats are not supported for dynamic textures");
|
||||
|
||||
// createTexture poisons the binding on the first stage
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
||||
|
||||
id = createTexture(type, format, width, height, depth, mipLevels, caps);
|
||||
|
||||
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && caps.ext3)
|
||||
{
|
||||
scratchSize = getTextureSize(type, format, width, height, depth, mipLevels);
|
||||
|
||||
glGenBuffers(1, &scratchId);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, scratchSize, NULL, GL_DYNAMIC_COPY);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels,
|
||||
Usage usage, unsigned int id)
|
||||
: Texture(device, type, format, width, height, depth, mipLevels, usage)
|
||||
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
|
||||
, id(id)
|
||||
, external(true)
|
||||
, scratchId(0)
|
||||
, scratchOffset(0)
|
||||
, scratchSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
TextureGL::~TextureGL()
|
||||
{
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTexture(this);
|
||||
|
||||
if (scratchId)
|
||||
glDeleteBuffers(1, &scratchId);
|
||||
|
||||
if (!external)
|
||||
glDeleteTextures(1, &id);
|
||||
}
|
||||
|
||||
void TextureGL::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
|
||||
{
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
|
||||
unsigned int mipWidth = getMipSide(width, mip);
|
||||
unsigned int mipHeight = getMipSide(height, mip);
|
||||
unsigned int mipDepth = getMipSide(depth, mip);
|
||||
|
||||
AYAASSERT(mip < mipLevels);
|
||||
AYAASSERT(region.x + region.width <= mipWidth);
|
||||
AYAASSERT(region.y + region.height <= mipHeight);
|
||||
AYAASSERT(region.z + region.depth <= mipDepth);
|
||||
|
||||
AYAASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
|
||||
|
||||
if (supportsLocking())
|
||||
{
|
||||
unsigned int bpp = getFormatBits(format) / 8;
|
||||
|
||||
LockResult lr = lock(index, mip, region);
|
||||
|
||||
for (unsigned int z = 0; z < region.depth; ++z)
|
||||
for (unsigned int y = 0; y < region.height; ++y)
|
||||
{
|
||||
unsigned int sourceOffset = (region.width * region.height * z + region.width * y) * bpp;
|
||||
unsigned int targetOffset = lr.slicePitch * z + lr.rowPitch * y;
|
||||
|
||||
memcpy(static_cast<char*>(lr.data) + targetOffset, static_cast<const char*>(data) + sourceOffset, region.width * bpp);
|
||||
}
|
||||
|
||||
unlock(index, mip);
|
||||
}
|
||||
else
|
||||
{
|
||||
// uploadTexture poisons the binding on the first stage
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
||||
|
||||
bool entireRegion = (region.x == 0 && region.y == 0 && region.width == mipWidth && region.height == mipHeight);
|
||||
|
||||
uploadTexture(id, type, format, index, mip, region, entireRegion, data, caps);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GLES
|
||||
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
|
||||
{
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
GLenum target = gTextureTargetGL[type];
|
||||
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
||||
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
GLenum faceTarget = (type == Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
||||
|
||||
unsigned int mipWidth = getMipSide(width, mip);
|
||||
unsigned int mipHeight = getMipSide(height, mip);
|
||||
unsigned int mipDepth = getMipSide(depth, mip);
|
||||
|
||||
AYAASSERT(mip < mipLevels);
|
||||
AYAASSERT(size == getImageSize(format, mipWidth, mipHeight) * mipDepth);
|
||||
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(target, id);
|
||||
|
||||
bool needsCustomAlignment = (!isFormatCompressed(format) && getFormatBits(format) < 32);
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
|
||||
|
||||
if (isFormatCompressed(format))
|
||||
glGetCompressedTexImage(faceTarget, mip, data);
|
||||
else
|
||||
glGetTexImage(faceTarget, mip, formatGl.dataFormat, formatGl.dataType, data);
|
||||
|
||||
glBindTexture(target, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool TextureGL::supportsLocking() const
|
||||
{
|
||||
if (FFlag::GraphicsTextureCommitChanges)
|
||||
return scratchId != 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Texture::LockResult TextureGL::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
|
||||
{
|
||||
if (!scratchId)
|
||||
{
|
||||
Texture::LockResult result = {0, 0, 0};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
|
||||
unsigned int mipWidth = getMipSide(width, mip);
|
||||
unsigned int mipHeight = getMipSide(height, mip);
|
||||
unsigned int mipDepth = getMipSide(depth, mip);
|
||||
|
||||
AYAASSERT(mip < mipLevels);
|
||||
AYAASSERT(region.x + region.width <= mipWidth);
|
||||
AYAASSERT(region.y + region.height <= mipHeight);
|
||||
AYAASSERT(region.z + region.depth <= mipDepth);
|
||||
|
||||
unsigned int size = getImageSize(format, region.width, region.height) * region.depth;
|
||||
unsigned int flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
|
||||
if (scratchOffset + size > scratchSize)
|
||||
{
|
||||
commitChanges();
|
||||
|
||||
scratchOffset = 0;
|
||||
flags = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
||||
void* data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, scratchOffset, size, flags);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
Change change = {index, mip, region, scratchOffset};
|
||||
|
||||
scratchOffset += size;
|
||||
pendingChanges.push_back(change);
|
||||
|
||||
Texture::LockResult result = {data, getImageSize(format, region.width, 1), getImageSize(format, region.width, region.height)};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureGL::unlock(unsigned int index, unsigned int mip)
|
||||
{
|
||||
if (!scratchId)
|
||||
return;
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
shared_ptr<Renderbuffer> TextureGL::getRenderbuffer(unsigned int index, unsigned int mip)
|
||||
{
|
||||
AYAASSERT(usage == Usage_Renderbuffer);
|
||||
|
||||
AYAASSERT(mip == 0);
|
||||
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
|
||||
|
||||
weak_ptr<Renderbuffer>& slot = renderBuffers[std::make_pair(index, mip)];
|
||||
shared_ptr<Renderbuffer> rb = slot.lock();
|
||||
|
||||
if (!rb)
|
||||
{
|
||||
GLenum target = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : gTextureTargetGL[type];
|
||||
|
||||
rb.reset(new RenderbufferGL(device, shared_from_this(), target, getType() == Texture::Type_2DMultisampled ? 4 : 1));
|
||||
slot = rb;
|
||||
}
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
void TextureGL::commitChanges()
|
||||
{
|
||||
if (!FFlag::GraphicsTextureCommitChanges)
|
||||
return;
|
||||
|
||||
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
||||
|
||||
if (pendingChanges.empty())
|
||||
return;
|
||||
|
||||
// uploadTexture poisons the binding on the first stage
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
||||
|
||||
for (auto& change : pendingChanges)
|
||||
{
|
||||
AYAASSERT(change.scratchOffset < scratchOffset);
|
||||
|
||||
bool entireRegion = false; // this is only important for compressed formats that we don't support
|
||||
|
||||
uploadTexture(id, type, format, change.index, change.mip, change.region, entireRegion, reinterpret_cast<void*>(change.scratchOffset), caps);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
pendingChanges.clear();
|
||||
}
|
||||
|
||||
void TextureGL::generateMipmaps()
|
||||
{
|
||||
AYAASSERT(usage != Usage_Dynamic);
|
||||
|
||||
if (mipLevels <= 1)
|
||||
return;
|
||||
|
||||
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(gTextureTargetGL[type], id);
|
||||
|
||||
glGenerateMipmap(gTextureTargetGL[type]);
|
||||
|
||||
glBindTexture(gTextureTargetGL[type], 0);
|
||||
}
|
||||
|
||||
void TextureGL::bind(unsigned int stage, const SamplerState& state, TextureGL** cache)
|
||||
{
|
||||
if (*cache != this || cachedState != state)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + stage);
|
||||
|
||||
GLenum target = gTextureTargetGL[type];
|
||||
|
||||
if (*cache != this)
|
||||
{
|
||||
*cache = this;
|
||||
|
||||
glBindTexture(target, id);
|
||||
}
|
||||
|
||||
if (cachedState != state)
|
||||
{
|
||||
cachedState = state;
|
||||
|
||||
bool hasMips = mipLevels > 1;
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gSamplerFilterMinGL[state.getFilter()][hasMips]);
|
||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gSamplerFilterMagGL[state.getFilter()]);
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, gSamplerAddressGL[state.getAddress()]);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, gSamplerAddressGL[state.getAddress()]);
|
||||
|
||||
#ifndef GLES
|
||||
int anisotropy = state.getFilter() == SamplerState::Filter_Anisotropic ? state.getAnisotropy() : 1;
|
||||
|
||||
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int TextureGL::getTarget() const
|
||||
{
|
||||
return gTextureTargetGL[type];
|
||||
}
|
||||
|
||||
unsigned int TextureGL::getInternalFormat(Format format)
|
||||
{
|
||||
return gTextureFormatGL[format].internalFormat;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
75
engine/gfx/src/API/GL/TextureGL.hpp
Normal file
75
engine/gfx/src/API/GL/TextureGL.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Texture.hpp"
|
||||
#include "Core/States.hpp"
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class TextureGL
|
||||
: public Texture
|
||||
, public boost::enable_shared_from_this<TextureGL>
|
||||
{
|
||||
public:
|
||||
TextureGL(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
|
||||
TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels,
|
||||
Usage usage, unsigned int id);
|
||||
~TextureGL();
|
||||
|
||||
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size);
|
||||
|
||||
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size);
|
||||
|
||||
virtual bool supportsLocking() const;
|
||||
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region);
|
||||
virtual void unlock(unsigned int index, unsigned int mip);
|
||||
|
||||
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip);
|
||||
|
||||
virtual void commitChanges();
|
||||
virtual void generateMipmaps();
|
||||
|
||||
void bind(unsigned int stage, const SamplerState& state, TextureGL** cache);
|
||||
|
||||
unsigned int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
unsigned int getTarget() const;
|
||||
|
||||
static unsigned int getInternalFormat(Format format);
|
||||
|
||||
private:
|
||||
struct Change
|
||||
{
|
||||
unsigned int index;
|
||||
unsigned int mip;
|
||||
TextureRegion region;
|
||||
|
||||
unsigned int scratchOffset;
|
||||
};
|
||||
|
||||
unsigned int id;
|
||||
|
||||
SamplerState cachedState;
|
||||
|
||||
bool external;
|
||||
|
||||
unsigned int scratchId;
|
||||
unsigned int scratchOffset;
|
||||
unsigned int scratchSize;
|
||||
std::vector<Change> pendingChanges;
|
||||
|
||||
typedef std::map<std::pair<unsigned, unsigned>, weak_ptr<Renderbuffer>> RenderBufferMap;
|
||||
RenderBufferMap renderBuffers;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
175
engine/gfx/src/API/GL/glad/include/EGL/eglplatform.h
Normal file
175
engine/gfx/src/API/GL/glad/include/EGL/eglplatform.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifndef __eglplatform_h_
|
||||
#define __eglplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright 2007-2020 The Khronos Group Inc.
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* Platform-specific types and definitions for egl.h
|
||||
*
|
||||
* Adopters may modify khrplatform.h and this file to suit their platform.
|
||||
* You are encouraged to submit all modifications to the Khronos group so that
|
||||
* they can be included in future versions of this file. Please submit changes
|
||||
* by filing an issue or pull request on the public Khronos EGL Registry, at
|
||||
* https://www.github.com/KhronosGroup/EGL-Registry/
|
||||
*/
|
||||
|
||||
#include <KHR/khrplatform.h>
|
||||
|
||||
/* Macros used in EGL function prototype declarations.
|
||||
*
|
||||
* EGL functions should be prototyped as:
|
||||
*
|
||||
* EGLAPI return-type EGLAPIENTRY eglFunction(arguments);
|
||||
* typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);
|
||||
*
|
||||
* KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h
|
||||
*/
|
||||
|
||||
#ifndef EGLAPI
|
||||
#define EGLAPI KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
#ifndef EGLAPIENTRY
|
||||
#define EGLAPIENTRY KHRONOS_APIENTRY
|
||||
#endif
|
||||
#define EGLAPIENTRYP EGLAPIENTRY*
|
||||
|
||||
/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
|
||||
* are aliases of window-system-dependent types, such as X Display * or
|
||||
* Windows Device Context. They must be defined in platform-specific
|
||||
* code below. The EGL-prefixed versions of Native*Type are the same
|
||||
* types, renamed in EGL 1.3 so all types in the API start with "EGL".
|
||||
*
|
||||
* Khronos STRONGLY RECOMMENDS that you use the default definitions
|
||||
* provided below, since these changes affect both binary and source
|
||||
* portability of applications using EGL running on different EGL
|
||||
* implementations.
|
||||
*/
|
||||
|
||||
#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)
|
||||
|
||||
typedef void *EGLNativeDisplayType;
|
||||
typedef void *EGLNativePixmapType;
|
||||
typedef void *EGLNativeWindowType;
|
||||
|
||||
#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
typedef HDC EGLNativeDisplayType;
|
||||
typedef HBITMAP EGLNativePixmapType;
|
||||
typedef HWND EGLNativeWindowType;
|
||||
|
||||
#elif defined(__QNX__)
|
||||
|
||||
typedef khronos_uintptr_t EGLNativeDisplayType;
|
||||
typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
|
||||
typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
|
||||
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
|
||||
typedef int EGLNativeDisplayType;
|
||||
typedef int EGLNativePixmapType;
|
||||
typedef int EGLNativeWindowType;
|
||||
|
||||
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
|
||||
|
||||
typedef int EGLNativeDisplayType;
|
||||
typedef void *EGLNativePixmapType;
|
||||
typedef void *EGLNativeWindowType;
|
||||
|
||||
#elif defined(WL_EGL_PLATFORM)
|
||||
|
||||
typedef struct wl_display *EGLNativeDisplayType;
|
||||
typedef struct wl_egl_pixmap *EGLNativePixmapType;
|
||||
typedef struct wl_egl_window *EGLNativeWindowType;
|
||||
|
||||
#elif defined(__GBM__)
|
||||
|
||||
typedef struct gbm_device *EGLNativeDisplayType;
|
||||
typedef struct gbm_bo *EGLNativePixmapType;
|
||||
typedef void *EGLNativeWindowType;
|
||||
|
||||
#elif defined(__ANDROID__) || defined(ANDROID)
|
||||
|
||||
struct ANativeWindow;
|
||||
struct egl_native_pixmap_t;
|
||||
|
||||
typedef void* EGLNativeDisplayType;
|
||||
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
|
||||
typedef struct ANativeWindow* EGLNativeWindowType;
|
||||
|
||||
#elif defined(USE_OZONE)
|
||||
|
||||
typedef intptr_t EGLNativeDisplayType;
|
||||
typedef intptr_t EGLNativePixmapType;
|
||||
typedef intptr_t EGLNativeWindowType;
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
/* X11 (tentative) */
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
typedef Display *EGLNativeDisplayType;
|
||||
typedef Pixmap EGLNativePixmapType;
|
||||
typedef Window EGLNativeWindowType;
|
||||
|
||||
#elif defined(__unix__)
|
||||
|
||||
typedef void *EGLNativeDisplayType;
|
||||
typedef khronos_uintptr_t EGLNativePixmapType;
|
||||
typedef khronos_uintptr_t EGLNativeWindowType;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
typedef int EGLNativeDisplayType;
|
||||
typedef void *EGLNativePixmapType;
|
||||
typedef void *EGLNativeWindowType;
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
#include <kernel/image.h>
|
||||
|
||||
typedef void *EGLNativeDisplayType;
|
||||
typedef khronos_uintptr_t EGLNativePixmapType;
|
||||
typedef khronos_uintptr_t EGLNativeWindowType;
|
||||
|
||||
#elif defined(__Fuchsia__)
|
||||
|
||||
typedef void *EGLNativeDisplayType;
|
||||
typedef khronos_uintptr_t EGLNativePixmapType;
|
||||
typedef khronos_uintptr_t EGLNativeWindowType;
|
||||
|
||||
#else
|
||||
#error "Platform not recognized"
|
||||
#endif
|
||||
|
||||
/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
|
||||
typedef EGLNativeDisplayType NativeDisplayType;
|
||||
typedef EGLNativePixmapType NativePixmapType;
|
||||
typedef EGLNativeWindowType NativeWindowType;
|
||||
|
||||
|
||||
/* Define EGLint. This must be a signed integral type large enough to contain
|
||||
* all legal attribute names and values passed into and out of EGL, whether
|
||||
* their type is boolean, bitmask, enumerant (symbolic constant), integer,
|
||||
* handle, or other. While in general a 32-bit integer will suffice, if
|
||||
* handles are 64 bit types, then EGLint should be defined as a signed 64-bit
|
||||
* integer type.
|
||||
*/
|
||||
typedef khronos_int32_t EGLint;
|
||||
|
||||
|
||||
/* C++ / C typecast macros for special EGL handle values */
|
||||
#if defined(__cplusplus)
|
||||
#define EGL_CAST(type, value) (static_cast<type>(value))
|
||||
#else
|
||||
#define EGL_CAST(type, value) ((type) (value))
|
||||
#endif
|
||||
|
||||
#endif /* __eglplatform_h */
|
||||
311
engine/gfx/src/API/GL/glad/include/KHR/khrplatform.h
Normal file
311
engine/gfx/src/API/GL/glad/include/KHR/khrplatform.h
Normal file
@@ -0,0 +1,311 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
1651
engine/gfx/src/API/GL/glad/include/glad/egl.h
Normal file
1651
engine/gfx/src/API/GL/glad/include/glad/egl.h
Normal file
File diff suppressed because it is too large
Load Diff
15685
engine/gfx/src/API/GL/glad/include/glad/gl.h
Normal file
15685
engine/gfx/src/API/GL/glad/include/glad/gl.h
Normal file
File diff suppressed because one or more lines are too long
1179
engine/gfx/src/API/GL/glad/include/glad/glx.h
Normal file
1179
engine/gfx/src/API/GL/glad/include/glad/glx.h
Normal file
File diff suppressed because it is too large
Load Diff
1025
engine/gfx/src/API/GL/glad/include/glad/wgl.h
Normal file
1025
engine/gfx/src/API/GL/glad/include/glad/wgl.h
Normal file
File diff suppressed because it is too large
Load Diff
1217
engine/gfx/src/API/GL/glad/src/egl.c
Normal file
1217
engine/gfx/src/API/GL/glad/src/egl.c
Normal file
File diff suppressed because it is too large
Load Diff
11634
engine/gfx/src/API/GL/glad/src/gl.c
Normal file
11634
engine/gfx/src/API/GL/glad/src/gl.c
Normal file
File diff suppressed because it is too large
Load Diff
798
engine/gfx/src/API/GL/glad/src/glx.c
Normal file
798
engine/gfx/src/API/GL/glad/src/glx.c
Normal file
@@ -0,0 +1,798 @@
|
||||
/**
|
||||
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glad/glx.h>
|
||||
|
||||
#ifndef GLAD_IMPL_UTIL_C_
|
||||
#define GLAD_IMPL_UTIL_C_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define GLAD_IMPL_UTIL_SSCANF sscanf_s
|
||||
#else
|
||||
#define GLAD_IMPL_UTIL_SSCANF sscanf
|
||||
#endif
|
||||
|
||||
#endif /* GLAD_IMPL_UTIL_C_ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int GLAD_GLX_VERSION_1_0 = 0;
|
||||
int GLAD_GLX_VERSION_1_1 = 0;
|
||||
int GLAD_GLX_VERSION_1_2 = 0;
|
||||
int GLAD_GLX_VERSION_1_3 = 0;
|
||||
int GLAD_GLX_VERSION_1_4 = 0;
|
||||
int GLAD_GLX_3DFX_multisample = 0;
|
||||
int GLAD_GLX_AMD_gpu_association = 0;
|
||||
int GLAD_GLX_ARB_context_flush_control = 0;
|
||||
int GLAD_GLX_ARB_create_context = 0;
|
||||
int GLAD_GLX_ARB_create_context_no_error = 0;
|
||||
int GLAD_GLX_ARB_create_context_profile = 0;
|
||||
int GLAD_GLX_ARB_create_context_robustness = 0;
|
||||
int GLAD_GLX_ARB_fbconfig_float = 0;
|
||||
int GLAD_GLX_ARB_framebuffer_sRGB = 0;
|
||||
int GLAD_GLX_ARB_get_proc_address = 0;
|
||||
int GLAD_GLX_ARB_multisample = 0;
|
||||
int GLAD_GLX_ARB_robustness_application_isolation = 0;
|
||||
int GLAD_GLX_ARB_robustness_share_group_isolation = 0;
|
||||
int GLAD_GLX_ARB_vertex_buffer_object = 0;
|
||||
int GLAD_GLX_EXT_buffer_age = 0;
|
||||
int GLAD_GLX_EXT_context_priority = 0;
|
||||
int GLAD_GLX_EXT_create_context_es2_profile = 0;
|
||||
int GLAD_GLX_EXT_create_context_es_profile = 0;
|
||||
int GLAD_GLX_EXT_fbconfig_packed_float = 0;
|
||||
int GLAD_GLX_EXT_framebuffer_sRGB = 0;
|
||||
int GLAD_GLX_EXT_get_drawable_type = 0;
|
||||
int GLAD_GLX_EXT_import_context = 0;
|
||||
int GLAD_GLX_EXT_libglvnd = 0;
|
||||
int GLAD_GLX_EXT_no_config_context = 0;
|
||||
int GLAD_GLX_EXT_stereo_tree = 0;
|
||||
int GLAD_GLX_EXT_swap_control = 0;
|
||||
int GLAD_GLX_EXT_swap_control_tear = 0;
|
||||
int GLAD_GLX_EXT_texture_from_pixmap = 0;
|
||||
int GLAD_GLX_EXT_visual_info = 0;
|
||||
int GLAD_GLX_EXT_visual_rating = 0;
|
||||
int GLAD_GLX_INTEL_swap_event = 0;
|
||||
int GLAD_GLX_MESA_agp_offset = 0;
|
||||
int GLAD_GLX_MESA_copy_sub_buffer = 0;
|
||||
int GLAD_GLX_MESA_pixmap_colormap = 0;
|
||||
int GLAD_GLX_MESA_query_renderer = 0;
|
||||
int GLAD_GLX_MESA_release_buffers = 0;
|
||||
int GLAD_GLX_MESA_set_3dfx_mode = 0;
|
||||
int GLAD_GLX_MESA_swap_control = 0;
|
||||
int GLAD_GLX_NV_copy_buffer = 0;
|
||||
int GLAD_GLX_NV_copy_image = 0;
|
||||
int GLAD_GLX_NV_delay_before_swap = 0;
|
||||
int GLAD_GLX_NV_float_buffer = 0;
|
||||
int GLAD_GLX_NV_multigpu_context = 0;
|
||||
int GLAD_GLX_NV_multisample_coverage = 0;
|
||||
int GLAD_GLX_NV_present_video = 0;
|
||||
int GLAD_GLX_NV_robustness_video_memory_purge = 0;
|
||||
int GLAD_GLX_NV_swap_group = 0;
|
||||
int GLAD_GLX_NV_video_capture = 0;
|
||||
int GLAD_GLX_NV_video_out = 0;
|
||||
int GLAD_GLX_OML_swap_method = 0;
|
||||
int GLAD_GLX_OML_sync_control = 0;
|
||||
int GLAD_GLX_SGIS_blended_overlay = 0;
|
||||
int GLAD_GLX_SGIS_multisample = 0;
|
||||
int GLAD_GLX_SGIS_shared_multisample = 0;
|
||||
int GLAD_GLX_SGIX_fbconfig = 0;
|
||||
int GLAD_GLX_SGIX_hyperpipe = 0;
|
||||
int GLAD_GLX_SGIX_pbuffer = 0;
|
||||
int GLAD_GLX_SGIX_swap_barrier = 0;
|
||||
int GLAD_GLX_SGIX_swap_group = 0;
|
||||
int GLAD_GLX_SGIX_video_resize = 0;
|
||||
int GLAD_GLX_SGIX_visual_select_group = 0;
|
||||
int GLAD_GLX_SGI_cushion = 0;
|
||||
int GLAD_GLX_SGI_make_current_read = 0;
|
||||
int GLAD_GLX_SGI_swap_control = 0;
|
||||
int GLAD_GLX_SGI_video_sync = 0;
|
||||
int GLAD_GLX_SUN_get_transparent_index = 0;
|
||||
|
||||
|
||||
|
||||
PFNGLXBINDCHANNELTOWINDOWSGIXPROC glad_glXBindChannelToWindowSGIX = NULL;
|
||||
PFNGLXBINDHYPERPIPESGIXPROC glad_glXBindHyperpipeSGIX = NULL;
|
||||
PFNGLXBINDSWAPBARRIERNVPROC glad_glXBindSwapBarrierNV = NULL;
|
||||
PFNGLXBINDSWAPBARRIERSGIXPROC glad_glXBindSwapBarrierSGIX = NULL;
|
||||
PFNGLXBINDTEXIMAGEEXTPROC glad_glXBindTexImageEXT = NULL;
|
||||
PFNGLXBINDVIDEOCAPTUREDEVICENVPROC glad_glXBindVideoCaptureDeviceNV = NULL;
|
||||
PFNGLXBINDVIDEODEVICENVPROC glad_glXBindVideoDeviceNV = NULL;
|
||||
PFNGLXBINDVIDEOIMAGENVPROC glad_glXBindVideoImageNV = NULL;
|
||||
PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC glad_glXBlitContextFramebufferAMD = NULL;
|
||||
PFNGLXCHANNELRECTSGIXPROC glad_glXChannelRectSGIX = NULL;
|
||||
PFNGLXCHANNELRECTSYNCSGIXPROC glad_glXChannelRectSyncSGIX = NULL;
|
||||
PFNGLXCHOOSEFBCONFIGPROC glad_glXChooseFBConfig = NULL;
|
||||
PFNGLXCHOOSEFBCONFIGSGIXPROC glad_glXChooseFBConfigSGIX = NULL;
|
||||
PFNGLXCHOOSEVISUALPROC glad_glXChooseVisual = NULL;
|
||||
PFNGLXCOPYBUFFERSUBDATANVPROC glad_glXCopyBufferSubDataNV = NULL;
|
||||
PFNGLXCOPYCONTEXTPROC glad_glXCopyContext = NULL;
|
||||
PFNGLXCOPYIMAGESUBDATANVPROC glad_glXCopyImageSubDataNV = NULL;
|
||||
PFNGLXCOPYSUBBUFFERMESAPROC glad_glXCopySubBufferMESA = NULL;
|
||||
PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC glad_glXCreateAssociatedContextAMD = NULL;
|
||||
PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_glXCreateAssociatedContextAttribsAMD = NULL;
|
||||
PFNGLXCREATECONTEXTPROC glad_glXCreateContext = NULL;
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC glad_glXCreateContextAttribsARB = NULL;
|
||||
PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC glad_glXCreateContextWithConfigSGIX = NULL;
|
||||
PFNGLXCREATEGLXPBUFFERSGIXPROC glad_glXCreateGLXPbufferSGIX = NULL;
|
||||
PFNGLXCREATEGLXPIXMAPPROC glad_glXCreateGLXPixmap = NULL;
|
||||
PFNGLXCREATEGLXPIXMAPMESAPROC glad_glXCreateGLXPixmapMESA = NULL;
|
||||
PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC glad_glXCreateGLXPixmapWithConfigSGIX = NULL;
|
||||
PFNGLXCREATENEWCONTEXTPROC glad_glXCreateNewContext = NULL;
|
||||
PFNGLXCREATEPBUFFERPROC glad_glXCreatePbuffer = NULL;
|
||||
PFNGLXCREATEPIXMAPPROC glad_glXCreatePixmap = NULL;
|
||||
PFNGLXCREATEWINDOWPROC glad_glXCreateWindow = NULL;
|
||||
PFNGLXCUSHIONSGIPROC glad_glXCushionSGI = NULL;
|
||||
PFNGLXDELAYBEFORESWAPNVPROC glad_glXDelayBeforeSwapNV = NULL;
|
||||
PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC glad_glXDeleteAssociatedContextAMD = NULL;
|
||||
PFNGLXDESTROYCONTEXTPROC glad_glXDestroyContext = NULL;
|
||||
PFNGLXDESTROYGLXPBUFFERSGIXPROC glad_glXDestroyGLXPbufferSGIX = NULL;
|
||||
PFNGLXDESTROYGLXPIXMAPPROC glad_glXDestroyGLXPixmap = NULL;
|
||||
PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC glad_glXDestroyHyperpipeConfigSGIX = NULL;
|
||||
PFNGLXDESTROYPBUFFERPROC glad_glXDestroyPbuffer = NULL;
|
||||
PFNGLXDESTROYPIXMAPPROC glad_glXDestroyPixmap = NULL;
|
||||
PFNGLXDESTROYWINDOWPROC glad_glXDestroyWindow = NULL;
|
||||
PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_glXEnumerateVideoCaptureDevicesNV = NULL;
|
||||
PFNGLXENUMERATEVIDEODEVICESNVPROC glad_glXEnumerateVideoDevicesNV = NULL;
|
||||
PFNGLXFREECONTEXTEXTPROC glad_glXFreeContextEXT = NULL;
|
||||
PFNGLXGETAGPOFFSETMESAPROC glad_glXGetAGPOffsetMESA = NULL;
|
||||
PFNGLXGETCLIENTSTRINGPROC glad_glXGetClientString = NULL;
|
||||
PFNGLXGETCONFIGPROC glad_glXGetConfig = NULL;
|
||||
PFNGLXGETCONTEXTGPUIDAMDPROC glad_glXGetContextGPUIDAMD = NULL;
|
||||
PFNGLXGETCONTEXTIDEXTPROC glad_glXGetContextIDEXT = NULL;
|
||||
PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_glXGetCurrentAssociatedContextAMD = NULL;
|
||||
PFNGLXGETCURRENTCONTEXTPROC glad_glXGetCurrentContext = NULL;
|
||||
PFNGLXGETCURRENTDISPLAYPROC glad_glXGetCurrentDisplay = NULL;
|
||||
PFNGLXGETCURRENTDISPLAYEXTPROC glad_glXGetCurrentDisplayEXT = NULL;
|
||||
PFNGLXGETCURRENTDRAWABLEPROC glad_glXGetCurrentDrawable = NULL;
|
||||
PFNGLXGETCURRENTREADDRAWABLEPROC glad_glXGetCurrentReadDrawable = NULL;
|
||||
PFNGLXGETCURRENTREADDRAWABLESGIPROC glad_glXGetCurrentReadDrawableSGI = NULL;
|
||||
PFNGLXGETFBCONFIGATTRIBPROC glad_glXGetFBConfigAttrib = NULL;
|
||||
PFNGLXGETFBCONFIGATTRIBSGIXPROC glad_glXGetFBConfigAttribSGIX = NULL;
|
||||
PFNGLXGETFBCONFIGFROMVISUALSGIXPROC glad_glXGetFBConfigFromVisualSGIX = NULL;
|
||||
PFNGLXGETFBCONFIGSPROC glad_glXGetFBConfigs = NULL;
|
||||
PFNGLXGETGPUIDSAMDPROC glad_glXGetGPUIDsAMD = NULL;
|
||||
PFNGLXGETGPUINFOAMDPROC glad_glXGetGPUInfoAMD = NULL;
|
||||
PFNGLXGETMSCRATEOMLPROC glad_glXGetMscRateOML = NULL;
|
||||
PFNGLXGETPROCADDRESSPROC glad_glXGetProcAddress = NULL;
|
||||
PFNGLXGETPROCADDRESSARBPROC glad_glXGetProcAddressARB = NULL;
|
||||
PFNGLXGETSELECTEDEVENTPROC glad_glXGetSelectedEvent = NULL;
|
||||
PFNGLXGETSELECTEDEVENTSGIXPROC glad_glXGetSelectedEventSGIX = NULL;
|
||||
PFNGLXGETSWAPINTERVALMESAPROC glad_glXGetSwapIntervalMESA = NULL;
|
||||
PFNGLXGETSYNCVALUESOMLPROC glad_glXGetSyncValuesOML = NULL;
|
||||
PFNGLXGETTRANSPARENTINDEXSUNPROC glad_glXGetTransparentIndexSUN = NULL;
|
||||
PFNGLXGETVIDEODEVICENVPROC glad_glXGetVideoDeviceNV = NULL;
|
||||
PFNGLXGETVIDEOINFONVPROC glad_glXGetVideoInfoNV = NULL;
|
||||
PFNGLXGETVIDEOSYNCSGIPROC glad_glXGetVideoSyncSGI = NULL;
|
||||
PFNGLXGETVISUALFROMFBCONFIGPROC glad_glXGetVisualFromFBConfig = NULL;
|
||||
PFNGLXGETVISUALFROMFBCONFIGSGIXPROC glad_glXGetVisualFromFBConfigSGIX = NULL;
|
||||
PFNGLXHYPERPIPEATTRIBSGIXPROC glad_glXHyperpipeAttribSGIX = NULL;
|
||||
PFNGLXHYPERPIPECONFIGSGIXPROC glad_glXHyperpipeConfigSGIX = NULL;
|
||||
PFNGLXIMPORTCONTEXTEXTPROC glad_glXImportContextEXT = NULL;
|
||||
PFNGLXISDIRECTPROC glad_glXIsDirect = NULL;
|
||||
PFNGLXJOINSWAPGROUPNVPROC glad_glXJoinSwapGroupNV = NULL;
|
||||
PFNGLXJOINSWAPGROUPSGIXPROC glad_glXJoinSwapGroupSGIX = NULL;
|
||||
PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC glad_glXLockVideoCaptureDeviceNV = NULL;
|
||||
PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_glXMakeAssociatedContextCurrentAMD = NULL;
|
||||
PFNGLXMAKECONTEXTCURRENTPROC glad_glXMakeContextCurrent = NULL;
|
||||
PFNGLXMAKECURRENTPROC glad_glXMakeCurrent = NULL;
|
||||
PFNGLXMAKECURRENTREADSGIPROC glad_glXMakeCurrentReadSGI = NULL;
|
||||
PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC glad_glXNamedCopyBufferSubDataNV = NULL;
|
||||
PFNGLXQUERYCHANNELDELTASSGIXPROC glad_glXQueryChannelDeltasSGIX = NULL;
|
||||
PFNGLXQUERYCHANNELRECTSGIXPROC glad_glXQueryChannelRectSGIX = NULL;
|
||||
PFNGLXQUERYCONTEXTPROC glad_glXQueryContext = NULL;
|
||||
PFNGLXQUERYCONTEXTINFOEXTPROC glad_glXQueryContextInfoEXT = NULL;
|
||||
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glad_glXQueryCurrentRendererIntegerMESA = NULL;
|
||||
PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC glad_glXQueryCurrentRendererStringMESA = NULL;
|
||||
PFNGLXQUERYDRAWABLEPROC glad_glXQueryDrawable = NULL;
|
||||
PFNGLXQUERYEXTENSIONPROC glad_glXQueryExtension = NULL;
|
||||
PFNGLXQUERYEXTENSIONSSTRINGPROC glad_glXQueryExtensionsString = NULL;
|
||||
PFNGLXQUERYFRAMECOUNTNVPROC glad_glXQueryFrameCountNV = NULL;
|
||||
PFNGLXQUERYGLXPBUFFERSGIXPROC glad_glXQueryGLXPbufferSGIX = NULL;
|
||||
PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC glad_glXQueryHyperpipeAttribSGIX = NULL;
|
||||
PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC glad_glXQueryHyperpipeBestAttribSGIX = NULL;
|
||||
PFNGLXQUERYHYPERPIPECONFIGSGIXPROC glad_glXQueryHyperpipeConfigSGIX = NULL;
|
||||
PFNGLXQUERYHYPERPIPENETWORKSGIXPROC glad_glXQueryHyperpipeNetworkSGIX = NULL;
|
||||
PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC glad_glXQueryMaxSwapBarriersSGIX = NULL;
|
||||
PFNGLXQUERYMAXSWAPGROUPSNVPROC glad_glXQueryMaxSwapGroupsNV = NULL;
|
||||
PFNGLXQUERYRENDERERINTEGERMESAPROC glad_glXQueryRendererIntegerMESA = NULL;
|
||||
PFNGLXQUERYRENDERERSTRINGMESAPROC glad_glXQueryRendererStringMESA = NULL;
|
||||
PFNGLXQUERYSERVERSTRINGPROC glad_glXQueryServerString = NULL;
|
||||
PFNGLXQUERYSWAPGROUPNVPROC glad_glXQuerySwapGroupNV = NULL;
|
||||
PFNGLXQUERYVERSIONPROC glad_glXQueryVersion = NULL;
|
||||
PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC glad_glXQueryVideoCaptureDeviceNV = NULL;
|
||||
PFNGLXRELEASEBUFFERSMESAPROC glad_glXReleaseBuffersMESA = NULL;
|
||||
PFNGLXRELEASETEXIMAGEEXTPROC glad_glXReleaseTexImageEXT = NULL;
|
||||
PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC glad_glXReleaseVideoCaptureDeviceNV = NULL;
|
||||
PFNGLXRELEASEVIDEODEVICENVPROC glad_glXReleaseVideoDeviceNV = NULL;
|
||||
PFNGLXRELEASEVIDEOIMAGENVPROC glad_glXReleaseVideoImageNV = NULL;
|
||||
PFNGLXRESETFRAMECOUNTNVPROC glad_glXResetFrameCountNV = NULL;
|
||||
PFNGLXSELECTEVENTPROC glad_glXSelectEvent = NULL;
|
||||
PFNGLXSELECTEVENTSGIXPROC glad_glXSelectEventSGIX = NULL;
|
||||
PFNGLXSENDPBUFFERTOVIDEONVPROC glad_glXSendPbufferToVideoNV = NULL;
|
||||
PFNGLXSET3DFXMODEMESAPROC glad_glXSet3DfxModeMESA = NULL;
|
||||
PFNGLXSWAPBUFFERSPROC glad_glXSwapBuffers = NULL;
|
||||
PFNGLXSWAPBUFFERSMSCOMLPROC glad_glXSwapBuffersMscOML = NULL;
|
||||
PFNGLXSWAPINTERVALEXTPROC glad_glXSwapIntervalEXT = NULL;
|
||||
PFNGLXSWAPINTERVALMESAPROC glad_glXSwapIntervalMESA = NULL;
|
||||
PFNGLXSWAPINTERVALSGIPROC glad_glXSwapIntervalSGI = NULL;
|
||||
PFNGLXUSEXFONTPROC glad_glXUseXFont = NULL;
|
||||
PFNGLXWAITFORMSCOMLPROC glad_glXWaitForMscOML = NULL;
|
||||
PFNGLXWAITFORSBCOMLPROC glad_glXWaitForSbcOML = NULL;
|
||||
PFNGLXWAITGLPROC glad_glXWaitGL = NULL;
|
||||
PFNGLXWAITVIDEOSYNCSGIPROC glad_glXWaitVideoSyncSGI = NULL;
|
||||
PFNGLXWAITXPROC glad_glXWaitX = NULL;
|
||||
|
||||
|
||||
static void glad_glx_load_GLX_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_VERSION_1_0) return;
|
||||
glad_glXChooseVisual = (PFNGLXCHOOSEVISUALPROC) load(userptr, "glXChooseVisual");
|
||||
glad_glXCopyContext = (PFNGLXCOPYCONTEXTPROC) load(userptr, "glXCopyContext");
|
||||
glad_glXCreateContext = (PFNGLXCREATECONTEXTPROC) load(userptr, "glXCreateContext");
|
||||
glad_glXCreateGLXPixmap = (PFNGLXCREATEGLXPIXMAPPROC) load(userptr, "glXCreateGLXPixmap");
|
||||
glad_glXDestroyContext = (PFNGLXDESTROYCONTEXTPROC) load(userptr, "glXDestroyContext");
|
||||
glad_glXDestroyGLXPixmap = (PFNGLXDESTROYGLXPIXMAPPROC) load(userptr, "glXDestroyGLXPixmap");
|
||||
glad_glXGetConfig = (PFNGLXGETCONFIGPROC) load(userptr, "glXGetConfig");
|
||||
glad_glXGetCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) load(userptr, "glXGetCurrentContext");
|
||||
glad_glXGetCurrentDrawable = (PFNGLXGETCURRENTDRAWABLEPROC) load(userptr, "glXGetCurrentDrawable");
|
||||
glad_glXIsDirect = (PFNGLXISDIRECTPROC) load(userptr, "glXIsDirect");
|
||||
glad_glXMakeCurrent = (PFNGLXMAKECURRENTPROC) load(userptr, "glXMakeCurrent");
|
||||
glad_glXQueryExtension = (PFNGLXQUERYEXTENSIONPROC) load(userptr, "glXQueryExtension");
|
||||
glad_glXQueryVersion = (PFNGLXQUERYVERSIONPROC) load(userptr, "glXQueryVersion");
|
||||
glad_glXSwapBuffers = (PFNGLXSWAPBUFFERSPROC) load(userptr, "glXSwapBuffers");
|
||||
glad_glXUseXFont = (PFNGLXUSEXFONTPROC) load(userptr, "glXUseXFont");
|
||||
glad_glXWaitGL = (PFNGLXWAITGLPROC) load(userptr, "glXWaitGL");
|
||||
glad_glXWaitX = (PFNGLXWAITXPROC) load(userptr, "glXWaitX");
|
||||
}
|
||||
static void glad_glx_load_GLX_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_VERSION_1_1) return;
|
||||
glad_glXGetClientString = (PFNGLXGETCLIENTSTRINGPROC) load(userptr, "glXGetClientString");
|
||||
glad_glXQueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) load(userptr, "glXQueryExtensionsString");
|
||||
glad_glXQueryServerString = (PFNGLXQUERYSERVERSTRINGPROC) load(userptr, "glXQueryServerString");
|
||||
}
|
||||
static void glad_glx_load_GLX_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_VERSION_1_2) return;
|
||||
glad_glXGetCurrentDisplay = (PFNGLXGETCURRENTDISPLAYPROC) load(userptr, "glXGetCurrentDisplay");
|
||||
}
|
||||
static void glad_glx_load_GLX_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_VERSION_1_3) return;
|
||||
glad_glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) load(userptr, "glXChooseFBConfig");
|
||||
glad_glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) load(userptr, "glXCreateNewContext");
|
||||
glad_glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC) load(userptr, "glXCreatePbuffer");
|
||||
glad_glXCreatePixmap = (PFNGLXCREATEPIXMAPPROC) load(userptr, "glXCreatePixmap");
|
||||
glad_glXCreateWindow = (PFNGLXCREATEWINDOWPROC) load(userptr, "glXCreateWindow");
|
||||
glad_glXDestroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) load(userptr, "glXDestroyPbuffer");
|
||||
glad_glXDestroyPixmap = (PFNGLXDESTROYPIXMAPPROC) load(userptr, "glXDestroyPixmap");
|
||||
glad_glXDestroyWindow = (PFNGLXDESTROYWINDOWPROC) load(userptr, "glXDestroyWindow");
|
||||
glad_glXGetCurrentReadDrawable = (PFNGLXGETCURRENTREADDRAWABLEPROC) load(userptr, "glXGetCurrentReadDrawable");
|
||||
glad_glXGetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) load(userptr, "glXGetFBConfigAttrib");
|
||||
glad_glXGetFBConfigs = (PFNGLXGETFBCONFIGSPROC) load(userptr, "glXGetFBConfigs");
|
||||
glad_glXGetSelectedEvent = (PFNGLXGETSELECTEDEVENTPROC) load(userptr, "glXGetSelectedEvent");
|
||||
glad_glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) load(userptr, "glXGetVisualFromFBConfig");
|
||||
glad_glXMakeContextCurrent = (PFNGLXMAKECONTEXTCURRENTPROC) load(userptr, "glXMakeContextCurrent");
|
||||
glad_glXQueryContext = (PFNGLXQUERYCONTEXTPROC) load(userptr, "glXQueryContext");
|
||||
glad_glXQueryDrawable = (PFNGLXQUERYDRAWABLEPROC) load(userptr, "glXQueryDrawable");
|
||||
glad_glXSelectEvent = (PFNGLXSELECTEVENTPROC) load(userptr, "glXSelectEvent");
|
||||
}
|
||||
static void glad_glx_load_GLX_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_VERSION_1_4) return;
|
||||
glad_glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC) load(userptr, "glXGetProcAddress");
|
||||
}
|
||||
static void glad_glx_load_GLX_AMD_gpu_association( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_AMD_gpu_association) return;
|
||||
glad_glXBlitContextFramebufferAMD = (PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC) load(userptr, "glXBlitContextFramebufferAMD");
|
||||
glad_glXCreateAssociatedContextAMD = (PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXCreateAssociatedContextAMD");
|
||||
glad_glXCreateAssociatedContextAttribsAMD = (PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) load(userptr, "glXCreateAssociatedContextAttribsAMD");
|
||||
glad_glXDeleteAssociatedContextAMD = (PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXDeleteAssociatedContextAMD");
|
||||
glad_glXGetContextGPUIDAMD = (PFNGLXGETCONTEXTGPUIDAMDPROC) load(userptr, "glXGetContextGPUIDAMD");
|
||||
glad_glXGetCurrentAssociatedContextAMD = (PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXGetCurrentAssociatedContextAMD");
|
||||
glad_glXGetGPUIDsAMD = (PFNGLXGETGPUIDSAMDPROC) load(userptr, "glXGetGPUIDsAMD");
|
||||
glad_glXGetGPUInfoAMD = (PFNGLXGETGPUINFOAMDPROC) load(userptr, "glXGetGPUInfoAMD");
|
||||
glad_glXMakeAssociatedContextCurrentAMD = (PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) load(userptr, "glXMakeAssociatedContextCurrentAMD");
|
||||
}
|
||||
static void glad_glx_load_GLX_ARB_create_context( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_ARB_create_context) return;
|
||||
glad_glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) load(userptr, "glXCreateContextAttribsARB");
|
||||
}
|
||||
static void glad_glx_load_GLX_ARB_get_proc_address( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_ARB_get_proc_address) return;
|
||||
glad_glXGetProcAddressARB = (PFNGLXGETPROCADDRESSARBPROC) load(userptr, "glXGetProcAddressARB");
|
||||
}
|
||||
static void glad_glx_load_GLX_EXT_import_context( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_EXT_import_context) return;
|
||||
glad_glXFreeContextEXT = (PFNGLXFREECONTEXTEXTPROC) load(userptr, "glXFreeContextEXT");
|
||||
glad_glXGetContextIDEXT = (PFNGLXGETCONTEXTIDEXTPROC) load(userptr, "glXGetContextIDEXT");
|
||||
glad_glXGetCurrentDisplayEXT = (PFNGLXGETCURRENTDISPLAYEXTPROC) load(userptr, "glXGetCurrentDisplayEXT");
|
||||
glad_glXImportContextEXT = (PFNGLXIMPORTCONTEXTEXTPROC) load(userptr, "glXImportContextEXT");
|
||||
glad_glXQueryContextInfoEXT = (PFNGLXQUERYCONTEXTINFOEXTPROC) load(userptr, "glXQueryContextInfoEXT");
|
||||
}
|
||||
static void glad_glx_load_GLX_EXT_swap_control( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_EXT_swap_control) return;
|
||||
glad_glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) load(userptr, "glXSwapIntervalEXT");
|
||||
}
|
||||
static void glad_glx_load_GLX_EXT_texture_from_pixmap( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_EXT_texture_from_pixmap) return;
|
||||
glad_glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC) load(userptr, "glXBindTexImageEXT");
|
||||
glad_glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC) load(userptr, "glXReleaseTexImageEXT");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_agp_offset( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_agp_offset) return;
|
||||
glad_glXGetAGPOffsetMESA = (PFNGLXGETAGPOFFSETMESAPROC) load(userptr, "glXGetAGPOffsetMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_copy_sub_buffer( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_copy_sub_buffer) return;
|
||||
glad_glXCopySubBufferMESA = (PFNGLXCOPYSUBBUFFERMESAPROC) load(userptr, "glXCopySubBufferMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_pixmap_colormap( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_pixmap_colormap) return;
|
||||
glad_glXCreateGLXPixmapMESA = (PFNGLXCREATEGLXPIXMAPMESAPROC) load(userptr, "glXCreateGLXPixmapMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_query_renderer( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_query_renderer) return;
|
||||
glad_glXQueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) load(userptr, "glXQueryCurrentRendererIntegerMESA");
|
||||
glad_glXQueryCurrentRendererStringMESA = (PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC) load(userptr, "glXQueryCurrentRendererStringMESA");
|
||||
glad_glXQueryRendererIntegerMESA = (PFNGLXQUERYRENDERERINTEGERMESAPROC) load(userptr, "glXQueryRendererIntegerMESA");
|
||||
glad_glXQueryRendererStringMESA = (PFNGLXQUERYRENDERERSTRINGMESAPROC) load(userptr, "glXQueryRendererStringMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_release_buffers( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_release_buffers) return;
|
||||
glad_glXReleaseBuffersMESA = (PFNGLXRELEASEBUFFERSMESAPROC) load(userptr, "glXReleaseBuffersMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_set_3dfx_mode( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_set_3dfx_mode) return;
|
||||
glad_glXSet3DfxModeMESA = (PFNGLXSET3DFXMODEMESAPROC) load(userptr, "glXSet3DfxModeMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_MESA_swap_control( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_MESA_swap_control) return;
|
||||
glad_glXGetSwapIntervalMESA = (PFNGLXGETSWAPINTERVALMESAPROC) load(userptr, "glXGetSwapIntervalMESA");
|
||||
glad_glXSwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) load(userptr, "glXSwapIntervalMESA");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_copy_buffer( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_copy_buffer) return;
|
||||
glad_glXCopyBufferSubDataNV = (PFNGLXCOPYBUFFERSUBDATANVPROC) load(userptr, "glXCopyBufferSubDataNV");
|
||||
glad_glXNamedCopyBufferSubDataNV = (PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC) load(userptr, "glXNamedCopyBufferSubDataNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_copy_image( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_copy_image) return;
|
||||
glad_glXCopyImageSubDataNV = (PFNGLXCOPYIMAGESUBDATANVPROC) load(userptr, "glXCopyImageSubDataNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_delay_before_swap( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_delay_before_swap) return;
|
||||
glad_glXDelayBeforeSwapNV = (PFNGLXDELAYBEFORESWAPNVPROC) load(userptr, "glXDelayBeforeSwapNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_present_video( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_present_video) return;
|
||||
glad_glXBindVideoDeviceNV = (PFNGLXBINDVIDEODEVICENVPROC) load(userptr, "glXBindVideoDeviceNV");
|
||||
glad_glXEnumerateVideoDevicesNV = (PFNGLXENUMERATEVIDEODEVICESNVPROC) load(userptr, "glXEnumerateVideoDevicesNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_swap_group( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_swap_group) return;
|
||||
glad_glXBindSwapBarrierNV = (PFNGLXBINDSWAPBARRIERNVPROC) load(userptr, "glXBindSwapBarrierNV");
|
||||
glad_glXJoinSwapGroupNV = (PFNGLXJOINSWAPGROUPNVPROC) load(userptr, "glXJoinSwapGroupNV");
|
||||
glad_glXQueryFrameCountNV = (PFNGLXQUERYFRAMECOUNTNVPROC) load(userptr, "glXQueryFrameCountNV");
|
||||
glad_glXQueryMaxSwapGroupsNV = (PFNGLXQUERYMAXSWAPGROUPSNVPROC) load(userptr, "glXQueryMaxSwapGroupsNV");
|
||||
glad_glXQuerySwapGroupNV = (PFNGLXQUERYSWAPGROUPNVPROC) load(userptr, "glXQuerySwapGroupNV");
|
||||
glad_glXResetFrameCountNV = (PFNGLXRESETFRAMECOUNTNVPROC) load(userptr, "glXResetFrameCountNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_video_capture( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_video_capture) return;
|
||||
glad_glXBindVideoCaptureDeviceNV = (PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXBindVideoCaptureDeviceNV");
|
||||
glad_glXEnumerateVideoCaptureDevicesNV = (PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) load(userptr, "glXEnumerateVideoCaptureDevicesNV");
|
||||
glad_glXLockVideoCaptureDeviceNV = (PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXLockVideoCaptureDeviceNV");
|
||||
glad_glXQueryVideoCaptureDeviceNV = (PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXQueryVideoCaptureDeviceNV");
|
||||
glad_glXReleaseVideoCaptureDeviceNV = (PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXReleaseVideoCaptureDeviceNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_NV_video_out( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_NV_video_out) return;
|
||||
glad_glXBindVideoImageNV = (PFNGLXBINDVIDEOIMAGENVPROC) load(userptr, "glXBindVideoImageNV");
|
||||
glad_glXGetVideoDeviceNV = (PFNGLXGETVIDEODEVICENVPROC) load(userptr, "glXGetVideoDeviceNV");
|
||||
glad_glXGetVideoInfoNV = (PFNGLXGETVIDEOINFONVPROC) load(userptr, "glXGetVideoInfoNV");
|
||||
glad_glXReleaseVideoDeviceNV = (PFNGLXRELEASEVIDEODEVICENVPROC) load(userptr, "glXReleaseVideoDeviceNV");
|
||||
glad_glXReleaseVideoImageNV = (PFNGLXRELEASEVIDEOIMAGENVPROC) load(userptr, "glXReleaseVideoImageNV");
|
||||
glad_glXSendPbufferToVideoNV = (PFNGLXSENDPBUFFERTOVIDEONVPROC) load(userptr, "glXSendPbufferToVideoNV");
|
||||
}
|
||||
static void glad_glx_load_GLX_OML_sync_control( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_OML_sync_control) return;
|
||||
glad_glXGetMscRateOML = (PFNGLXGETMSCRATEOMLPROC) load(userptr, "glXGetMscRateOML");
|
||||
glad_glXGetSyncValuesOML = (PFNGLXGETSYNCVALUESOMLPROC) load(userptr, "glXGetSyncValuesOML");
|
||||
glad_glXSwapBuffersMscOML = (PFNGLXSWAPBUFFERSMSCOMLPROC) load(userptr, "glXSwapBuffersMscOML");
|
||||
glad_glXWaitForMscOML = (PFNGLXWAITFORMSCOMLPROC) load(userptr, "glXWaitForMscOML");
|
||||
glad_glXWaitForSbcOML = (PFNGLXWAITFORSBCOMLPROC) load(userptr, "glXWaitForSbcOML");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_fbconfig( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_fbconfig) return;
|
||||
glad_glXChooseFBConfigSGIX = (PFNGLXCHOOSEFBCONFIGSGIXPROC) load(userptr, "glXChooseFBConfigSGIX");
|
||||
glad_glXCreateContextWithConfigSGIX = (PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) load(userptr, "glXCreateContextWithConfigSGIX");
|
||||
glad_glXCreateGLXPixmapWithConfigSGIX = (PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) load(userptr, "glXCreateGLXPixmapWithConfigSGIX");
|
||||
glad_glXGetFBConfigAttribSGIX = (PFNGLXGETFBCONFIGATTRIBSGIXPROC) load(userptr, "glXGetFBConfigAttribSGIX");
|
||||
glad_glXGetFBConfigFromVisualSGIX = (PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) load(userptr, "glXGetFBConfigFromVisualSGIX");
|
||||
glad_glXGetVisualFromFBConfigSGIX = (PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) load(userptr, "glXGetVisualFromFBConfigSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_hyperpipe( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_hyperpipe) return;
|
||||
glad_glXBindHyperpipeSGIX = (PFNGLXBINDHYPERPIPESGIXPROC) load(userptr, "glXBindHyperpipeSGIX");
|
||||
glad_glXDestroyHyperpipeConfigSGIX = (PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) load(userptr, "glXDestroyHyperpipeConfigSGIX");
|
||||
glad_glXHyperpipeAttribSGIX = (PFNGLXHYPERPIPEATTRIBSGIXPROC) load(userptr, "glXHyperpipeAttribSGIX");
|
||||
glad_glXHyperpipeConfigSGIX = (PFNGLXHYPERPIPECONFIGSGIXPROC) load(userptr, "glXHyperpipeConfigSGIX");
|
||||
glad_glXQueryHyperpipeAttribSGIX = (PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) load(userptr, "glXQueryHyperpipeAttribSGIX");
|
||||
glad_glXQueryHyperpipeBestAttribSGIX = (PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) load(userptr, "glXQueryHyperpipeBestAttribSGIX");
|
||||
glad_glXQueryHyperpipeConfigSGIX = (PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) load(userptr, "glXQueryHyperpipeConfigSGIX");
|
||||
glad_glXQueryHyperpipeNetworkSGIX = (PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) load(userptr, "glXQueryHyperpipeNetworkSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_pbuffer( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_pbuffer) return;
|
||||
glad_glXCreateGLXPbufferSGIX = (PFNGLXCREATEGLXPBUFFERSGIXPROC) load(userptr, "glXCreateGLXPbufferSGIX");
|
||||
glad_glXDestroyGLXPbufferSGIX = (PFNGLXDESTROYGLXPBUFFERSGIXPROC) load(userptr, "glXDestroyGLXPbufferSGIX");
|
||||
glad_glXGetSelectedEventSGIX = (PFNGLXGETSELECTEDEVENTSGIXPROC) load(userptr, "glXGetSelectedEventSGIX");
|
||||
glad_glXQueryGLXPbufferSGIX = (PFNGLXQUERYGLXPBUFFERSGIXPROC) load(userptr, "glXQueryGLXPbufferSGIX");
|
||||
glad_glXSelectEventSGIX = (PFNGLXSELECTEVENTSGIXPROC) load(userptr, "glXSelectEventSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_swap_barrier( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_swap_barrier) return;
|
||||
glad_glXBindSwapBarrierSGIX = (PFNGLXBINDSWAPBARRIERSGIXPROC) load(userptr, "glXBindSwapBarrierSGIX");
|
||||
glad_glXQueryMaxSwapBarriersSGIX = (PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) load(userptr, "glXQueryMaxSwapBarriersSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_swap_group( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_swap_group) return;
|
||||
glad_glXJoinSwapGroupSGIX = (PFNGLXJOINSWAPGROUPSGIXPROC) load(userptr, "glXJoinSwapGroupSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGIX_video_resize( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGIX_video_resize) return;
|
||||
glad_glXBindChannelToWindowSGIX = (PFNGLXBINDCHANNELTOWINDOWSGIXPROC) load(userptr, "glXBindChannelToWindowSGIX");
|
||||
glad_glXChannelRectSGIX = (PFNGLXCHANNELRECTSGIXPROC) load(userptr, "glXChannelRectSGIX");
|
||||
glad_glXChannelRectSyncSGIX = (PFNGLXCHANNELRECTSYNCSGIXPROC) load(userptr, "glXChannelRectSyncSGIX");
|
||||
glad_glXQueryChannelDeltasSGIX = (PFNGLXQUERYCHANNELDELTASSGIXPROC) load(userptr, "glXQueryChannelDeltasSGIX");
|
||||
glad_glXQueryChannelRectSGIX = (PFNGLXQUERYCHANNELRECTSGIXPROC) load(userptr, "glXQueryChannelRectSGIX");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGI_cushion( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGI_cushion) return;
|
||||
glad_glXCushionSGI = (PFNGLXCUSHIONSGIPROC) load(userptr, "glXCushionSGI");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGI_make_current_read( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGI_make_current_read) return;
|
||||
glad_glXGetCurrentReadDrawableSGI = (PFNGLXGETCURRENTREADDRAWABLESGIPROC) load(userptr, "glXGetCurrentReadDrawableSGI");
|
||||
glad_glXMakeCurrentReadSGI = (PFNGLXMAKECURRENTREADSGIPROC) load(userptr, "glXMakeCurrentReadSGI");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGI_swap_control( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGI_swap_control) return;
|
||||
glad_glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) load(userptr, "glXSwapIntervalSGI");
|
||||
}
|
||||
static void glad_glx_load_GLX_SGI_video_sync( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SGI_video_sync) return;
|
||||
glad_glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC) load(userptr, "glXGetVideoSyncSGI");
|
||||
glad_glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC) load(userptr, "glXWaitVideoSyncSGI");
|
||||
}
|
||||
static void glad_glx_load_GLX_SUN_get_transparent_index( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GLX_SUN_get_transparent_index) return;
|
||||
glad_glXGetTransparentIndexSUN = (PFNGLXGETTRANSPARENTINDEXSUNPROC) load(userptr, "glXGetTransparentIndexSUN");
|
||||
}
|
||||
|
||||
|
||||
static void glad_glx_resolve_aliases(void) {
|
||||
}
|
||||
|
||||
static int glad_glx_has_extension(Display *display, int screen, const char *ext) {
|
||||
#ifndef GLX_VERSION_1_1
|
||||
GLAD_UNUSED(display);
|
||||
GLAD_UNUSED(screen);
|
||||
GLAD_UNUSED(ext);
|
||||
#else
|
||||
const char *terminator;
|
||||
const char *loc;
|
||||
const char *extensions;
|
||||
|
||||
if (glXQueryExtensionsString == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extensions = glXQueryExtensionsString(display, screen);
|
||||
|
||||
if(extensions == NULL || ext == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
loc = strstr(extensions, ext);
|
||||
if(loc == NULL)
|
||||
break;
|
||||
|
||||
terminator = loc + strlen(ext);
|
||||
if((loc == extensions || *(loc - 1) == ' ') &&
|
||||
(*terminator == ' ' || *terminator == '\0')) {
|
||||
return 1;
|
||||
}
|
||||
extensions = terminator;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GLADapiproc glad_glx_get_proc_from_userptr(void *userptr, const char* name) {
|
||||
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
|
||||
}
|
||||
|
||||
static int glad_glx_find_extensions(Display *display, int screen) {
|
||||
GLAD_GLX_3DFX_multisample = glad_glx_has_extension(display, screen, "GLX_3DFX_multisample");
|
||||
GLAD_GLX_AMD_gpu_association = glad_glx_has_extension(display, screen, "GLX_AMD_gpu_association");
|
||||
GLAD_GLX_ARB_context_flush_control = glad_glx_has_extension(display, screen, "GLX_ARB_context_flush_control");
|
||||
GLAD_GLX_ARB_create_context = glad_glx_has_extension(display, screen, "GLX_ARB_create_context");
|
||||
GLAD_GLX_ARB_create_context_no_error = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_no_error");
|
||||
GLAD_GLX_ARB_create_context_profile = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_profile");
|
||||
GLAD_GLX_ARB_create_context_robustness = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_robustness");
|
||||
GLAD_GLX_ARB_fbconfig_float = glad_glx_has_extension(display, screen, "GLX_ARB_fbconfig_float");
|
||||
GLAD_GLX_ARB_framebuffer_sRGB = glad_glx_has_extension(display, screen, "GLX_ARB_framebuffer_sRGB");
|
||||
GLAD_GLX_ARB_get_proc_address = glad_glx_has_extension(display, screen, "GLX_ARB_get_proc_address");
|
||||
GLAD_GLX_ARB_multisample = glad_glx_has_extension(display, screen, "GLX_ARB_multisample");
|
||||
GLAD_GLX_ARB_robustness_application_isolation = glad_glx_has_extension(display, screen, "GLX_ARB_robustness_application_isolation");
|
||||
GLAD_GLX_ARB_robustness_share_group_isolation = glad_glx_has_extension(display, screen, "GLX_ARB_robustness_share_group_isolation");
|
||||
GLAD_GLX_ARB_vertex_buffer_object = glad_glx_has_extension(display, screen, "GLX_ARB_vertex_buffer_object");
|
||||
GLAD_GLX_EXT_buffer_age = glad_glx_has_extension(display, screen, "GLX_EXT_buffer_age");
|
||||
GLAD_GLX_EXT_context_priority = glad_glx_has_extension(display, screen, "GLX_EXT_context_priority");
|
||||
GLAD_GLX_EXT_create_context_es2_profile = glad_glx_has_extension(display, screen, "GLX_EXT_create_context_es2_profile");
|
||||
GLAD_GLX_EXT_create_context_es_profile = glad_glx_has_extension(display, screen, "GLX_EXT_create_context_es_profile");
|
||||
GLAD_GLX_EXT_fbconfig_packed_float = glad_glx_has_extension(display, screen, "GLX_EXT_fbconfig_packed_float");
|
||||
GLAD_GLX_EXT_framebuffer_sRGB = glad_glx_has_extension(display, screen, "GLX_EXT_framebuffer_sRGB");
|
||||
GLAD_GLX_EXT_get_drawable_type = glad_glx_has_extension(display, screen, "GLX_EXT_get_drawable_type");
|
||||
GLAD_GLX_EXT_import_context = glad_glx_has_extension(display, screen, "GLX_EXT_import_context");
|
||||
GLAD_GLX_EXT_libglvnd = glad_glx_has_extension(display, screen, "GLX_EXT_libglvnd");
|
||||
GLAD_GLX_EXT_no_config_context = glad_glx_has_extension(display, screen, "GLX_EXT_no_config_context");
|
||||
GLAD_GLX_EXT_stereo_tree = glad_glx_has_extension(display, screen, "GLX_EXT_stereo_tree");
|
||||
GLAD_GLX_EXT_swap_control = glad_glx_has_extension(display, screen, "GLX_EXT_swap_control");
|
||||
GLAD_GLX_EXT_swap_control_tear = glad_glx_has_extension(display, screen, "GLX_EXT_swap_control_tear");
|
||||
GLAD_GLX_EXT_texture_from_pixmap = glad_glx_has_extension(display, screen, "GLX_EXT_texture_from_pixmap");
|
||||
GLAD_GLX_EXT_visual_info = glad_glx_has_extension(display, screen, "GLX_EXT_visual_info");
|
||||
GLAD_GLX_EXT_visual_rating = glad_glx_has_extension(display, screen, "GLX_EXT_visual_rating");
|
||||
GLAD_GLX_INTEL_swap_event = glad_glx_has_extension(display, screen, "GLX_INTEL_swap_event");
|
||||
GLAD_GLX_MESA_agp_offset = glad_glx_has_extension(display, screen, "GLX_MESA_agp_offset");
|
||||
GLAD_GLX_MESA_copy_sub_buffer = glad_glx_has_extension(display, screen, "GLX_MESA_copy_sub_buffer");
|
||||
GLAD_GLX_MESA_pixmap_colormap = glad_glx_has_extension(display, screen, "GLX_MESA_pixmap_colormap");
|
||||
GLAD_GLX_MESA_query_renderer = glad_glx_has_extension(display, screen, "GLX_MESA_query_renderer");
|
||||
GLAD_GLX_MESA_release_buffers = glad_glx_has_extension(display, screen, "GLX_MESA_release_buffers");
|
||||
GLAD_GLX_MESA_set_3dfx_mode = glad_glx_has_extension(display, screen, "GLX_MESA_set_3dfx_mode");
|
||||
GLAD_GLX_MESA_swap_control = glad_glx_has_extension(display, screen, "GLX_MESA_swap_control");
|
||||
GLAD_GLX_NV_copy_buffer = glad_glx_has_extension(display, screen, "GLX_NV_copy_buffer");
|
||||
GLAD_GLX_NV_copy_image = glad_glx_has_extension(display, screen, "GLX_NV_copy_image");
|
||||
GLAD_GLX_NV_delay_before_swap = glad_glx_has_extension(display, screen, "GLX_NV_delay_before_swap");
|
||||
GLAD_GLX_NV_float_buffer = glad_glx_has_extension(display, screen, "GLX_NV_float_buffer");
|
||||
GLAD_GLX_NV_multigpu_context = glad_glx_has_extension(display, screen, "GLX_NV_multigpu_context");
|
||||
GLAD_GLX_NV_multisample_coverage = glad_glx_has_extension(display, screen, "GLX_NV_multisample_coverage");
|
||||
GLAD_GLX_NV_present_video = glad_glx_has_extension(display, screen, "GLX_NV_present_video");
|
||||
GLAD_GLX_NV_robustness_video_memory_purge = glad_glx_has_extension(display, screen, "GLX_NV_robustness_video_memory_purge");
|
||||
GLAD_GLX_NV_swap_group = glad_glx_has_extension(display, screen, "GLX_NV_swap_group");
|
||||
GLAD_GLX_NV_video_capture = glad_glx_has_extension(display, screen, "GLX_NV_video_capture");
|
||||
GLAD_GLX_NV_video_out = glad_glx_has_extension(display, screen, "GLX_NV_video_out");
|
||||
GLAD_GLX_OML_swap_method = glad_glx_has_extension(display, screen, "GLX_OML_swap_method");
|
||||
GLAD_GLX_OML_sync_control = glad_glx_has_extension(display, screen, "GLX_OML_sync_control");
|
||||
GLAD_GLX_SGIS_blended_overlay = glad_glx_has_extension(display, screen, "GLX_SGIS_blended_overlay");
|
||||
GLAD_GLX_SGIS_multisample = glad_glx_has_extension(display, screen, "GLX_SGIS_multisample");
|
||||
GLAD_GLX_SGIS_shared_multisample = glad_glx_has_extension(display, screen, "GLX_SGIS_shared_multisample");
|
||||
GLAD_GLX_SGIX_fbconfig = glad_glx_has_extension(display, screen, "GLX_SGIX_fbconfig");
|
||||
GLAD_GLX_SGIX_hyperpipe = glad_glx_has_extension(display, screen, "GLX_SGIX_hyperpipe");
|
||||
GLAD_GLX_SGIX_pbuffer = glad_glx_has_extension(display, screen, "GLX_SGIX_pbuffer");
|
||||
GLAD_GLX_SGIX_swap_barrier = glad_glx_has_extension(display, screen, "GLX_SGIX_swap_barrier");
|
||||
GLAD_GLX_SGIX_swap_group = glad_glx_has_extension(display, screen, "GLX_SGIX_swap_group");
|
||||
GLAD_GLX_SGIX_video_resize = glad_glx_has_extension(display, screen, "GLX_SGIX_video_resize");
|
||||
GLAD_GLX_SGIX_visual_select_group = glad_glx_has_extension(display, screen, "GLX_SGIX_visual_select_group");
|
||||
GLAD_GLX_SGI_cushion = glad_glx_has_extension(display, screen, "GLX_SGI_cushion");
|
||||
GLAD_GLX_SGI_make_current_read = glad_glx_has_extension(display, screen, "GLX_SGI_make_current_read");
|
||||
GLAD_GLX_SGI_swap_control = glad_glx_has_extension(display, screen, "GLX_SGI_swap_control");
|
||||
GLAD_GLX_SGI_video_sync = glad_glx_has_extension(display, screen, "GLX_SGI_video_sync");
|
||||
GLAD_GLX_SUN_get_transparent_index = glad_glx_has_extension(display, screen, "GLX_SUN_get_transparent_index");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int glad_glx_find_core_glx(Display **display, int *screen) {
|
||||
int major = 0, minor = 0;
|
||||
if(*display == NULL) {
|
||||
#ifdef GLAD_GLX_NO_X11
|
||||
GLAD_UNUSED(screen);
|
||||
return 0;
|
||||
#else
|
||||
*display = XOpenDisplay(0);
|
||||
if (*display == NULL) {
|
||||
return 0;
|
||||
}
|
||||
*screen = XScreenNumberOfScreen(XDefaultScreenOfDisplay(*display));
|
||||
#endif
|
||||
}
|
||||
glXQueryVersion(*display, &major, &minor);
|
||||
GLAD_GLX_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
|
||||
GLAD_GLX_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;
|
||||
GLAD_GLX_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;
|
||||
GLAD_GLX_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;
|
||||
GLAD_GLX_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;
|
||||
return GLAD_MAKE_VERSION(major, minor);
|
||||
}
|
||||
|
||||
int gladLoadGLXUserPtr(Display *display, int screen, GLADuserptrloadfunc load, void *userptr) {
|
||||
int version;
|
||||
glXQueryVersion = (PFNGLXQUERYVERSIONPROC) load(userptr, "glXQueryVersion");
|
||||
if(glXQueryVersion == NULL) return 0;
|
||||
version = glad_glx_find_core_glx(&display, &screen);
|
||||
|
||||
glad_glx_load_GLX_VERSION_1_0(load, userptr);
|
||||
glad_glx_load_GLX_VERSION_1_1(load, userptr);
|
||||
glad_glx_load_GLX_VERSION_1_2(load, userptr);
|
||||
glad_glx_load_GLX_VERSION_1_3(load, userptr);
|
||||
glad_glx_load_GLX_VERSION_1_4(load, userptr);
|
||||
|
||||
if (!glad_glx_find_extensions(display, screen)) return 0;
|
||||
glad_glx_load_GLX_AMD_gpu_association(load, userptr);
|
||||
glad_glx_load_GLX_ARB_create_context(load, userptr);
|
||||
glad_glx_load_GLX_ARB_get_proc_address(load, userptr);
|
||||
glad_glx_load_GLX_EXT_import_context(load, userptr);
|
||||
glad_glx_load_GLX_EXT_swap_control(load, userptr);
|
||||
glad_glx_load_GLX_EXT_texture_from_pixmap(load, userptr);
|
||||
glad_glx_load_GLX_MESA_agp_offset(load, userptr);
|
||||
glad_glx_load_GLX_MESA_copy_sub_buffer(load, userptr);
|
||||
glad_glx_load_GLX_MESA_pixmap_colormap(load, userptr);
|
||||
glad_glx_load_GLX_MESA_query_renderer(load, userptr);
|
||||
glad_glx_load_GLX_MESA_release_buffers(load, userptr);
|
||||
glad_glx_load_GLX_MESA_set_3dfx_mode(load, userptr);
|
||||
glad_glx_load_GLX_MESA_swap_control(load, userptr);
|
||||
glad_glx_load_GLX_NV_copy_buffer(load, userptr);
|
||||
glad_glx_load_GLX_NV_copy_image(load, userptr);
|
||||
glad_glx_load_GLX_NV_delay_before_swap(load, userptr);
|
||||
glad_glx_load_GLX_NV_present_video(load, userptr);
|
||||
glad_glx_load_GLX_NV_swap_group(load, userptr);
|
||||
glad_glx_load_GLX_NV_video_capture(load, userptr);
|
||||
glad_glx_load_GLX_NV_video_out(load, userptr);
|
||||
glad_glx_load_GLX_OML_sync_control(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_fbconfig(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_hyperpipe(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_pbuffer(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_swap_barrier(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_swap_group(load, userptr);
|
||||
glad_glx_load_GLX_SGIX_video_resize(load, userptr);
|
||||
glad_glx_load_GLX_SGI_cushion(load, userptr);
|
||||
glad_glx_load_GLX_SGI_make_current_read(load, userptr);
|
||||
glad_glx_load_GLX_SGI_swap_control(load, userptr);
|
||||
glad_glx_load_GLX_SGI_video_sync(load, userptr);
|
||||
glad_glx_load_GLX_SUN_get_transparent_index(load, userptr);
|
||||
|
||||
glad_glx_resolve_aliases();
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
int gladLoadGLX(Display *display, int screen, GLADloadfunc load) {
|
||||
return gladLoadGLXUserPtr(display, screen, glad_glx_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef GLAD_GLX
|
||||
|
||||
#ifndef GLAD_LOADER_LIBRARY_C_
|
||||
#define GLAD_LOADER_LIBRARY_C_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if GLAD_PLATFORM_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
||||
static void* glad_get_dlopen_handle(const char *lib_names[], int length) {
|
||||
void *handle = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
#if GLAD_PLATFORM_WIN32
|
||||
#if GLAD_PLATFORM_UWP
|
||||
size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR);
|
||||
LPWSTR buffer = (LPWSTR) malloc(buffer_size);
|
||||
if (buffer != NULL) {
|
||||
int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size);
|
||||
if (ret != 0) {
|
||||
handle = (void*) LoadPackagedLibrary(buffer, 0);
|
||||
}
|
||||
free((void*) buffer);
|
||||
}
|
||||
#else
|
||||
handle = (void*) LoadLibraryA(lib_names[i]);
|
||||
#endif
|
||||
#else
|
||||
handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL);
|
||||
#endif
|
||||
if (handle != NULL) {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void glad_close_dlopen_handle(void* handle) {
|
||||
if (handle != NULL) {
|
||||
#if GLAD_PLATFORM_WIN32
|
||||
FreeLibrary((HMODULE) handle);
|
||||
#else
|
||||
dlclose(handle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static GLADapiproc glad_dlsym_handle(void* handle, const char *name) {
|
||||
if (handle == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if GLAD_PLATFORM_WIN32
|
||||
return (GLADapiproc) GetProcAddress((HMODULE) handle, name);
|
||||
#else
|
||||
return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* GLAD_LOADER_LIBRARY_C_ */
|
||||
|
||||
typedef void* (GLAD_API_PTR *GLADglxprocaddrfunc)(const char*);
|
||||
|
||||
static GLADapiproc glad_glx_get_proc(void *userptr, const char *name) {
|
||||
return GLAD_GNUC_EXTENSION ((GLADapiproc (*)(const char *name)) userptr)(name);
|
||||
}
|
||||
|
||||
static void* _glx_handle;
|
||||
|
||||
static void* glad_glx_dlopen_handle(void) {
|
||||
static const char *NAMES[] = {
|
||||
#if defined __CYGWIN__
|
||||
"libGL-1.so",
|
||||
#endif
|
||||
"libGL.so.1",
|
||||
"libGL.so"
|
||||
};
|
||||
|
||||
if (_glx_handle == NULL) {
|
||||
_glx_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0]));
|
||||
}
|
||||
|
||||
return _glx_handle;
|
||||
}
|
||||
|
||||
int gladLoaderLoadGLX(Display *display, int screen) {
|
||||
int version = 0;
|
||||
void *handle = NULL;
|
||||
int did_load = 0;
|
||||
GLADglxprocaddrfunc loader;
|
||||
|
||||
did_load = _glx_handle == NULL;
|
||||
handle = glad_glx_dlopen_handle();
|
||||
if (handle != NULL) {
|
||||
loader = (GLADglxprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB");
|
||||
if (loader != NULL) {
|
||||
version = gladLoadGLXUserPtr(display, screen, glad_glx_get_proc, GLAD_GNUC_EXTENSION (void*) loader);
|
||||
}
|
||||
|
||||
if (!version && did_load) {
|
||||
gladLoaderUnloadGLX();
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
void gladLoaderUnloadGLX() {
|
||||
if (_glx_handle != NULL) {
|
||||
glad_close_dlopen_handle(_glx_handle);
|
||||
_glx_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* GLAD_GLX */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
596
engine/gfx/src/API/GL/glad/src/wgl.c
Normal file
596
engine/gfx/src/API/GL/glad/src/wgl.c
Normal file
@@ -0,0 +1,596 @@
|
||||
/**
|
||||
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glad/wgl.h>
|
||||
|
||||
#ifndef GLAD_IMPL_UTIL_C_
|
||||
#define GLAD_IMPL_UTIL_C_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define GLAD_IMPL_UTIL_SSCANF sscanf_s
|
||||
#else
|
||||
#define GLAD_IMPL_UTIL_SSCANF sscanf
|
||||
#endif
|
||||
|
||||
#endif /* GLAD_IMPL_UTIL_C_ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int GLAD_WGL_VERSION_1_0 = 0;
|
||||
int GLAD_WGL_3DFX_multisample = 0;
|
||||
int GLAD_WGL_3DL_stereo_control = 0;
|
||||
int GLAD_WGL_AMD_gpu_association = 0;
|
||||
int GLAD_WGL_ARB_buffer_region = 0;
|
||||
int GLAD_WGL_ARB_context_flush_control = 0;
|
||||
int GLAD_WGL_ARB_create_context = 0;
|
||||
int GLAD_WGL_ARB_create_context_no_error = 0;
|
||||
int GLAD_WGL_ARB_create_context_profile = 0;
|
||||
int GLAD_WGL_ARB_create_context_robustness = 0;
|
||||
int GLAD_WGL_ARB_extensions_string = 0;
|
||||
int GLAD_WGL_ARB_framebuffer_sRGB = 0;
|
||||
int GLAD_WGL_ARB_make_current_read = 0;
|
||||
int GLAD_WGL_ARB_multisample = 0;
|
||||
int GLAD_WGL_ARB_pbuffer = 0;
|
||||
int GLAD_WGL_ARB_pixel_format = 0;
|
||||
int GLAD_WGL_ARB_pixel_format_float = 0;
|
||||
int GLAD_WGL_ARB_render_texture = 0;
|
||||
int GLAD_WGL_ARB_robustness_application_isolation = 0;
|
||||
int GLAD_WGL_ARB_robustness_share_group_isolation = 0;
|
||||
int GLAD_WGL_ATI_pixel_format_float = 0;
|
||||
int GLAD_WGL_ATI_render_texture_rectangle = 0;
|
||||
int GLAD_WGL_EXT_colorspace = 0;
|
||||
int GLAD_WGL_EXT_create_context_es2_profile = 0;
|
||||
int GLAD_WGL_EXT_create_context_es_profile = 0;
|
||||
int GLAD_WGL_EXT_depth_float = 0;
|
||||
int GLAD_WGL_EXT_display_color_table = 0;
|
||||
int GLAD_WGL_EXT_extensions_string = 0;
|
||||
int GLAD_WGL_EXT_framebuffer_sRGB = 0;
|
||||
int GLAD_WGL_EXT_make_current_read = 0;
|
||||
int GLAD_WGL_EXT_multisample = 0;
|
||||
int GLAD_WGL_EXT_pbuffer = 0;
|
||||
int GLAD_WGL_EXT_pixel_format = 0;
|
||||
int GLAD_WGL_EXT_pixel_format_packed_float = 0;
|
||||
int GLAD_WGL_EXT_swap_control = 0;
|
||||
int GLAD_WGL_EXT_swap_control_tear = 0;
|
||||
int GLAD_WGL_I3D_digital_video_control = 0;
|
||||
int GLAD_WGL_I3D_gamma = 0;
|
||||
int GLAD_WGL_I3D_genlock = 0;
|
||||
int GLAD_WGL_I3D_image_buffer = 0;
|
||||
int GLAD_WGL_I3D_swap_frame_lock = 0;
|
||||
int GLAD_WGL_I3D_swap_frame_usage = 0;
|
||||
int GLAD_WGL_NV_DX_interop = 0;
|
||||
int GLAD_WGL_NV_DX_interop2 = 0;
|
||||
int GLAD_WGL_NV_copy_image = 0;
|
||||
int GLAD_WGL_NV_delay_before_swap = 0;
|
||||
int GLAD_WGL_NV_float_buffer = 0;
|
||||
int GLAD_WGL_NV_gpu_affinity = 0;
|
||||
int GLAD_WGL_NV_multigpu_context = 0;
|
||||
int GLAD_WGL_NV_multisample_coverage = 0;
|
||||
int GLAD_WGL_NV_present_video = 0;
|
||||
int GLAD_WGL_NV_render_depth_texture = 0;
|
||||
int GLAD_WGL_NV_render_texture_rectangle = 0;
|
||||
int GLAD_WGL_NV_swap_group = 0;
|
||||
int GLAD_WGL_NV_vertex_array_range = 0;
|
||||
int GLAD_WGL_NV_video_capture = 0;
|
||||
int GLAD_WGL_NV_video_output = 0;
|
||||
int GLAD_WGL_OML_sync_control = 0;
|
||||
|
||||
|
||||
|
||||
PFNWGLALLOCATEMEMORYNVPROC glad_wglAllocateMemoryNV = NULL;
|
||||
PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC glad_wglAssociateImageBufferEventsI3D = NULL;
|
||||
PFNWGLBEGINFRAMETRACKINGI3DPROC glad_wglBeginFrameTrackingI3D = NULL;
|
||||
PFNWGLBINDDISPLAYCOLORTABLEEXTPROC glad_wglBindDisplayColorTableEXT = NULL;
|
||||
PFNWGLBINDSWAPBARRIERNVPROC glad_wglBindSwapBarrierNV = NULL;
|
||||
PFNWGLBINDTEXIMAGEARBPROC glad_wglBindTexImageARB = NULL;
|
||||
PFNWGLBINDVIDEOCAPTUREDEVICENVPROC glad_wglBindVideoCaptureDeviceNV = NULL;
|
||||
PFNWGLBINDVIDEODEVICENVPROC glad_wglBindVideoDeviceNV = NULL;
|
||||
PFNWGLBINDVIDEOIMAGENVPROC glad_wglBindVideoImageNV = NULL;
|
||||
PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC glad_wglBlitContextFramebufferAMD = NULL;
|
||||
PFNWGLCHOOSEPIXELFORMATARBPROC glad_wglChoosePixelFormatARB = NULL;
|
||||
PFNWGLCHOOSEPIXELFORMATEXTPROC glad_wglChoosePixelFormatEXT = NULL;
|
||||
PFNWGLCOPYIMAGESUBDATANVPROC glad_wglCopyImageSubDataNV = NULL;
|
||||
PFNWGLCREATEAFFINITYDCNVPROC glad_wglCreateAffinityDCNV = NULL;
|
||||
PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC glad_wglCreateAssociatedContextAMD = NULL;
|
||||
PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_wglCreateAssociatedContextAttribsAMD = NULL;
|
||||
PFNWGLCREATEBUFFERREGIONARBPROC glad_wglCreateBufferRegionARB = NULL;
|
||||
PFNWGLCREATECONTEXTATTRIBSARBPROC glad_wglCreateContextAttribsARB = NULL;
|
||||
PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC glad_wglCreateDisplayColorTableEXT = NULL;
|
||||
PFNWGLCREATEIMAGEBUFFERI3DPROC glad_wglCreateImageBufferI3D = NULL;
|
||||
PFNWGLCREATEPBUFFERARBPROC glad_wglCreatePbufferARB = NULL;
|
||||
PFNWGLCREATEPBUFFEREXTPROC glad_wglCreatePbufferEXT = NULL;
|
||||
PFNWGLDXCLOSEDEVICENVPROC glad_wglDXCloseDeviceNV = NULL;
|
||||
PFNWGLDXLOCKOBJECTSNVPROC glad_wglDXLockObjectsNV = NULL;
|
||||
PFNWGLDXOBJECTACCESSNVPROC glad_wglDXObjectAccessNV = NULL;
|
||||
PFNWGLDXOPENDEVICENVPROC glad_wglDXOpenDeviceNV = NULL;
|
||||
PFNWGLDXREGISTEROBJECTNVPROC glad_wglDXRegisterObjectNV = NULL;
|
||||
PFNWGLDXSETRESOURCESHAREHANDLENVPROC glad_wglDXSetResourceShareHandleNV = NULL;
|
||||
PFNWGLDXUNLOCKOBJECTSNVPROC glad_wglDXUnlockObjectsNV = NULL;
|
||||
PFNWGLDXUNREGISTEROBJECTNVPROC glad_wglDXUnregisterObjectNV = NULL;
|
||||
PFNWGLDELAYBEFORESWAPNVPROC glad_wglDelayBeforeSwapNV = NULL;
|
||||
PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC glad_wglDeleteAssociatedContextAMD = NULL;
|
||||
PFNWGLDELETEBUFFERREGIONARBPROC glad_wglDeleteBufferRegionARB = NULL;
|
||||
PFNWGLDELETEDCNVPROC glad_wglDeleteDCNV = NULL;
|
||||
PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC glad_wglDestroyDisplayColorTableEXT = NULL;
|
||||
PFNWGLDESTROYIMAGEBUFFERI3DPROC glad_wglDestroyImageBufferI3D = NULL;
|
||||
PFNWGLDESTROYPBUFFERARBPROC glad_wglDestroyPbufferARB = NULL;
|
||||
PFNWGLDESTROYPBUFFEREXTPROC glad_wglDestroyPbufferEXT = NULL;
|
||||
PFNWGLDISABLEFRAMELOCKI3DPROC glad_wglDisableFrameLockI3D = NULL;
|
||||
PFNWGLDISABLEGENLOCKI3DPROC glad_wglDisableGenlockI3D = NULL;
|
||||
PFNWGLENABLEFRAMELOCKI3DPROC glad_wglEnableFrameLockI3D = NULL;
|
||||
PFNWGLENABLEGENLOCKI3DPROC glad_wglEnableGenlockI3D = NULL;
|
||||
PFNWGLENDFRAMETRACKINGI3DPROC glad_wglEndFrameTrackingI3D = NULL;
|
||||
PFNWGLENUMGPUDEVICESNVPROC glad_wglEnumGpuDevicesNV = NULL;
|
||||
PFNWGLENUMGPUSFROMAFFINITYDCNVPROC glad_wglEnumGpusFromAffinityDCNV = NULL;
|
||||
PFNWGLENUMGPUSNVPROC glad_wglEnumGpusNV = NULL;
|
||||
PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_wglEnumerateVideoCaptureDevicesNV = NULL;
|
||||
PFNWGLENUMERATEVIDEODEVICESNVPROC glad_wglEnumerateVideoDevicesNV = NULL;
|
||||
PFNWGLFREEMEMORYNVPROC glad_wglFreeMemoryNV = NULL;
|
||||
PFNWGLGENLOCKSAMPLERATEI3DPROC glad_wglGenlockSampleRateI3D = NULL;
|
||||
PFNWGLGENLOCKSOURCEDELAYI3DPROC glad_wglGenlockSourceDelayI3D = NULL;
|
||||
PFNWGLGENLOCKSOURCEEDGEI3DPROC glad_wglGenlockSourceEdgeI3D = NULL;
|
||||
PFNWGLGENLOCKSOURCEI3DPROC glad_wglGenlockSourceI3D = NULL;
|
||||
PFNWGLGETCONTEXTGPUIDAMDPROC glad_wglGetContextGPUIDAMD = NULL;
|
||||
PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_wglGetCurrentAssociatedContextAMD = NULL;
|
||||
PFNWGLGETCURRENTREADDCARBPROC glad_wglGetCurrentReadDCARB = NULL;
|
||||
PFNWGLGETCURRENTREADDCEXTPROC glad_wglGetCurrentReadDCEXT = NULL;
|
||||
PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC glad_wglGetDigitalVideoParametersI3D = NULL;
|
||||
PFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB = NULL;
|
||||
PFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT = NULL;
|
||||
PFNWGLGETFRAMEUSAGEI3DPROC glad_wglGetFrameUsageI3D = NULL;
|
||||
PFNWGLGETGPUIDSAMDPROC glad_wglGetGPUIDsAMD = NULL;
|
||||
PFNWGLGETGPUINFOAMDPROC glad_wglGetGPUInfoAMD = NULL;
|
||||
PFNWGLGETGAMMATABLEI3DPROC glad_wglGetGammaTableI3D = NULL;
|
||||
PFNWGLGETGAMMATABLEPARAMETERSI3DPROC glad_wglGetGammaTableParametersI3D = NULL;
|
||||
PFNWGLGETGENLOCKSAMPLERATEI3DPROC glad_wglGetGenlockSampleRateI3D = NULL;
|
||||
PFNWGLGETGENLOCKSOURCEDELAYI3DPROC glad_wglGetGenlockSourceDelayI3D = NULL;
|
||||
PFNWGLGETGENLOCKSOURCEEDGEI3DPROC glad_wglGetGenlockSourceEdgeI3D = NULL;
|
||||
PFNWGLGETGENLOCKSOURCEI3DPROC glad_wglGetGenlockSourceI3D = NULL;
|
||||
PFNWGLGETMSCRATEOMLPROC glad_wglGetMscRateOML = NULL;
|
||||
PFNWGLGETPBUFFERDCARBPROC glad_wglGetPbufferDCARB = NULL;
|
||||
PFNWGLGETPBUFFERDCEXTPROC glad_wglGetPbufferDCEXT = NULL;
|
||||
PFNWGLGETPIXELFORMATATTRIBFVARBPROC glad_wglGetPixelFormatAttribfvARB = NULL;
|
||||
PFNWGLGETPIXELFORMATATTRIBFVEXTPROC glad_wglGetPixelFormatAttribfvEXT = NULL;
|
||||
PFNWGLGETPIXELFORMATATTRIBIVARBPROC glad_wglGetPixelFormatAttribivARB = NULL;
|
||||
PFNWGLGETPIXELFORMATATTRIBIVEXTPROC glad_wglGetPixelFormatAttribivEXT = NULL;
|
||||
PFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT = NULL;
|
||||
PFNWGLGETSYNCVALUESOMLPROC glad_wglGetSyncValuesOML = NULL;
|
||||
PFNWGLGETVIDEODEVICENVPROC glad_wglGetVideoDeviceNV = NULL;
|
||||
PFNWGLGETVIDEOINFONVPROC glad_wglGetVideoInfoNV = NULL;
|
||||
PFNWGLISENABLEDFRAMELOCKI3DPROC glad_wglIsEnabledFrameLockI3D = NULL;
|
||||
PFNWGLISENABLEDGENLOCKI3DPROC glad_wglIsEnabledGenlockI3D = NULL;
|
||||
PFNWGLJOINSWAPGROUPNVPROC glad_wglJoinSwapGroupNV = NULL;
|
||||
PFNWGLLOADDISPLAYCOLORTABLEEXTPROC glad_wglLoadDisplayColorTableEXT = NULL;
|
||||
PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC glad_wglLockVideoCaptureDeviceNV = NULL;
|
||||
PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_wglMakeAssociatedContextCurrentAMD = NULL;
|
||||
PFNWGLMAKECONTEXTCURRENTARBPROC glad_wglMakeContextCurrentARB = NULL;
|
||||
PFNWGLMAKECONTEXTCURRENTEXTPROC glad_wglMakeContextCurrentEXT = NULL;
|
||||
PFNWGLQUERYCURRENTCONTEXTNVPROC glad_wglQueryCurrentContextNV = NULL;
|
||||
PFNWGLQUERYFRAMECOUNTNVPROC glad_wglQueryFrameCountNV = NULL;
|
||||
PFNWGLQUERYFRAMELOCKMASTERI3DPROC glad_wglQueryFrameLockMasterI3D = NULL;
|
||||
PFNWGLQUERYFRAMETRACKINGI3DPROC glad_wglQueryFrameTrackingI3D = NULL;
|
||||
PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC glad_wglQueryGenlockMaxSourceDelayI3D = NULL;
|
||||
PFNWGLQUERYMAXSWAPGROUPSNVPROC glad_wglQueryMaxSwapGroupsNV = NULL;
|
||||
PFNWGLQUERYPBUFFERARBPROC glad_wglQueryPbufferARB = NULL;
|
||||
PFNWGLQUERYPBUFFEREXTPROC glad_wglQueryPbufferEXT = NULL;
|
||||
PFNWGLQUERYSWAPGROUPNVPROC glad_wglQuerySwapGroupNV = NULL;
|
||||
PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC glad_wglQueryVideoCaptureDeviceNV = NULL;
|
||||
PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC glad_wglReleaseImageBufferEventsI3D = NULL;
|
||||
PFNWGLRELEASEPBUFFERDCARBPROC glad_wglReleasePbufferDCARB = NULL;
|
||||
PFNWGLRELEASEPBUFFERDCEXTPROC glad_wglReleasePbufferDCEXT = NULL;
|
||||
PFNWGLRELEASETEXIMAGEARBPROC glad_wglReleaseTexImageARB = NULL;
|
||||
PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC glad_wglReleaseVideoCaptureDeviceNV = NULL;
|
||||
PFNWGLRELEASEVIDEODEVICENVPROC glad_wglReleaseVideoDeviceNV = NULL;
|
||||
PFNWGLRELEASEVIDEOIMAGENVPROC glad_wglReleaseVideoImageNV = NULL;
|
||||
PFNWGLRESETFRAMECOUNTNVPROC glad_wglResetFrameCountNV = NULL;
|
||||
PFNWGLRESTOREBUFFERREGIONARBPROC glad_wglRestoreBufferRegionARB = NULL;
|
||||
PFNWGLSAVEBUFFERREGIONARBPROC glad_wglSaveBufferRegionARB = NULL;
|
||||
PFNWGLSENDPBUFFERTOVIDEONVPROC glad_wglSendPbufferToVideoNV = NULL;
|
||||
PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC glad_wglSetDigitalVideoParametersI3D = NULL;
|
||||
PFNWGLSETGAMMATABLEI3DPROC glad_wglSetGammaTableI3D = NULL;
|
||||
PFNWGLSETGAMMATABLEPARAMETERSI3DPROC glad_wglSetGammaTableParametersI3D = NULL;
|
||||
PFNWGLSETPBUFFERATTRIBARBPROC glad_wglSetPbufferAttribARB = NULL;
|
||||
PFNWGLSETSTEREOEMITTERSTATE3DLPROC glad_wglSetStereoEmitterState3DL = NULL;
|
||||
PFNWGLSWAPBUFFERSMSCOMLPROC glad_wglSwapBuffersMscOML = NULL;
|
||||
PFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT = NULL;
|
||||
PFNWGLSWAPLAYERBUFFERSMSCOMLPROC glad_wglSwapLayerBuffersMscOML = NULL;
|
||||
PFNWGLWAITFORMSCOMLPROC glad_wglWaitForMscOML = NULL;
|
||||
PFNWGLWAITFORSBCOMLPROC glad_wglWaitForSbcOML = NULL;
|
||||
|
||||
|
||||
static void glad_wgl_load_WGL_3DL_stereo_control(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_3DL_stereo_control) return;
|
||||
glad_wglSetStereoEmitterState3DL = (PFNWGLSETSTEREOEMITTERSTATE3DLPROC) load(userptr, "wglSetStereoEmitterState3DL");
|
||||
}
|
||||
static void glad_wgl_load_WGL_AMD_gpu_association(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_AMD_gpu_association) return;
|
||||
glad_wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) load(userptr, "wglBlitContextFramebufferAMD");
|
||||
glad_wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglCreateAssociatedContextAMD");
|
||||
glad_wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) load(userptr, "wglCreateAssociatedContextAttribsAMD");
|
||||
glad_wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglDeleteAssociatedContextAMD");
|
||||
glad_wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC) load(userptr, "wglGetContextGPUIDAMD");
|
||||
glad_wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglGetCurrentAssociatedContextAMD");
|
||||
glad_wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC) load(userptr, "wglGetGPUIDsAMD");
|
||||
glad_wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC) load(userptr, "wglGetGPUInfoAMD");
|
||||
glad_wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) load(userptr, "wglMakeAssociatedContextCurrentAMD");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_buffer_region(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_buffer_region) return;
|
||||
glad_wglCreateBufferRegionARB = (PFNWGLCREATEBUFFERREGIONARBPROC) load(userptr, "wglCreateBufferRegionARB");
|
||||
glad_wglDeleteBufferRegionARB = (PFNWGLDELETEBUFFERREGIONARBPROC) load(userptr, "wglDeleteBufferRegionARB");
|
||||
glad_wglRestoreBufferRegionARB = (PFNWGLRESTOREBUFFERREGIONARBPROC) load(userptr, "wglRestoreBufferRegionARB");
|
||||
glad_wglSaveBufferRegionARB = (PFNWGLSAVEBUFFERREGIONARBPROC) load(userptr, "wglSaveBufferRegionARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_create_context(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_create_context) return;
|
||||
glad_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) load(userptr, "wglCreateContextAttribsARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_extensions_string(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_extensions_string) return;
|
||||
glad_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) load(userptr, "wglGetExtensionsStringARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_make_current_read(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_make_current_read) return;
|
||||
glad_wglGetCurrentReadDCARB = (PFNWGLGETCURRENTREADDCARBPROC) load(userptr, "wglGetCurrentReadDCARB");
|
||||
glad_wglMakeContextCurrentARB = (PFNWGLMAKECONTEXTCURRENTARBPROC) load(userptr, "wglMakeContextCurrentARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_pbuffer(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_pbuffer) return;
|
||||
glad_wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC) load(userptr, "wglCreatePbufferARB");
|
||||
glad_wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC) load(userptr, "wglDestroyPbufferARB");
|
||||
glad_wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC) load(userptr, "wglGetPbufferDCARB");
|
||||
glad_wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC) load(userptr, "wglQueryPbufferARB");
|
||||
glad_wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC) load(userptr, "wglReleasePbufferDCARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_pixel_format(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_pixel_format) return;
|
||||
glad_wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) load(userptr, "wglChoosePixelFormatARB");
|
||||
glad_wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC) load(userptr, "wglGetPixelFormatAttribfvARB");
|
||||
glad_wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) load(userptr, "wglGetPixelFormatAttribivARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_ARB_render_texture(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_ARB_render_texture) return;
|
||||
glad_wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC) load(userptr, "wglBindTexImageARB");
|
||||
glad_wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC) load(userptr, "wglReleaseTexImageARB");
|
||||
glad_wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC) load(userptr, "wglSetPbufferAttribARB");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_display_color_table(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_display_color_table) return;
|
||||
glad_wglBindDisplayColorTableEXT = (PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglBindDisplayColorTableEXT");
|
||||
glad_wglCreateDisplayColorTableEXT = (PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglCreateDisplayColorTableEXT");
|
||||
glad_wglDestroyDisplayColorTableEXT = (PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglDestroyDisplayColorTableEXT");
|
||||
glad_wglLoadDisplayColorTableEXT = (PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglLoadDisplayColorTableEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_extensions_string(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_extensions_string) return;
|
||||
glad_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) load(userptr, "wglGetExtensionsStringEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_make_current_read(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_make_current_read) return;
|
||||
glad_wglGetCurrentReadDCEXT = (PFNWGLGETCURRENTREADDCEXTPROC) load(userptr, "wglGetCurrentReadDCEXT");
|
||||
glad_wglMakeContextCurrentEXT = (PFNWGLMAKECONTEXTCURRENTEXTPROC) load(userptr, "wglMakeContextCurrentEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_pbuffer(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_pbuffer) return;
|
||||
glad_wglCreatePbufferEXT = (PFNWGLCREATEPBUFFEREXTPROC) load(userptr, "wglCreatePbufferEXT");
|
||||
glad_wglDestroyPbufferEXT = (PFNWGLDESTROYPBUFFEREXTPROC) load(userptr, "wglDestroyPbufferEXT");
|
||||
glad_wglGetPbufferDCEXT = (PFNWGLGETPBUFFERDCEXTPROC) load(userptr, "wglGetPbufferDCEXT");
|
||||
glad_wglQueryPbufferEXT = (PFNWGLQUERYPBUFFEREXTPROC) load(userptr, "wglQueryPbufferEXT");
|
||||
glad_wglReleasePbufferDCEXT = (PFNWGLRELEASEPBUFFERDCEXTPROC) load(userptr, "wglReleasePbufferDCEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_pixel_format(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_pixel_format) return;
|
||||
glad_wglChoosePixelFormatEXT = (PFNWGLCHOOSEPIXELFORMATEXTPROC) load(userptr, "wglChoosePixelFormatEXT");
|
||||
glad_wglGetPixelFormatAttribfvEXT = (PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) load(userptr, "wglGetPixelFormatAttribfvEXT");
|
||||
glad_wglGetPixelFormatAttribivEXT = (PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) load(userptr, "wglGetPixelFormatAttribivEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_EXT_swap_control(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_EXT_swap_control) return;
|
||||
glad_wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) load(userptr, "wglGetSwapIntervalEXT");
|
||||
glad_wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) load(userptr, "wglSwapIntervalEXT");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_digital_video_control(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_digital_video_control) return;
|
||||
glad_wglGetDigitalVideoParametersI3D = (PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) load(userptr, "wglGetDigitalVideoParametersI3D");
|
||||
glad_wglSetDigitalVideoParametersI3D = (PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) load(userptr, "wglSetDigitalVideoParametersI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_gamma(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_gamma) return;
|
||||
glad_wglGetGammaTableI3D = (PFNWGLGETGAMMATABLEI3DPROC) load(userptr, "wglGetGammaTableI3D");
|
||||
glad_wglGetGammaTableParametersI3D = (PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) load(userptr, "wglGetGammaTableParametersI3D");
|
||||
glad_wglSetGammaTableI3D = (PFNWGLSETGAMMATABLEI3DPROC) load(userptr, "wglSetGammaTableI3D");
|
||||
glad_wglSetGammaTableParametersI3D = (PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) load(userptr, "wglSetGammaTableParametersI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_genlock(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_genlock) return;
|
||||
glad_wglDisableGenlockI3D = (PFNWGLDISABLEGENLOCKI3DPROC) load(userptr, "wglDisableGenlockI3D");
|
||||
glad_wglEnableGenlockI3D = (PFNWGLENABLEGENLOCKI3DPROC) load(userptr, "wglEnableGenlockI3D");
|
||||
glad_wglGenlockSampleRateI3D = (PFNWGLGENLOCKSAMPLERATEI3DPROC) load(userptr, "wglGenlockSampleRateI3D");
|
||||
glad_wglGenlockSourceDelayI3D = (PFNWGLGENLOCKSOURCEDELAYI3DPROC) load(userptr, "wglGenlockSourceDelayI3D");
|
||||
glad_wglGenlockSourceEdgeI3D = (PFNWGLGENLOCKSOURCEEDGEI3DPROC) load(userptr, "wglGenlockSourceEdgeI3D");
|
||||
glad_wglGenlockSourceI3D = (PFNWGLGENLOCKSOURCEI3DPROC) load(userptr, "wglGenlockSourceI3D");
|
||||
glad_wglGetGenlockSampleRateI3D = (PFNWGLGETGENLOCKSAMPLERATEI3DPROC) load(userptr, "wglGetGenlockSampleRateI3D");
|
||||
glad_wglGetGenlockSourceDelayI3D = (PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) load(userptr, "wglGetGenlockSourceDelayI3D");
|
||||
glad_wglGetGenlockSourceEdgeI3D = (PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) load(userptr, "wglGetGenlockSourceEdgeI3D");
|
||||
glad_wglGetGenlockSourceI3D = (PFNWGLGETGENLOCKSOURCEI3DPROC) load(userptr, "wglGetGenlockSourceI3D");
|
||||
glad_wglIsEnabledGenlockI3D = (PFNWGLISENABLEDGENLOCKI3DPROC) load(userptr, "wglIsEnabledGenlockI3D");
|
||||
glad_wglQueryGenlockMaxSourceDelayI3D = (PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) load(userptr, "wglQueryGenlockMaxSourceDelayI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_image_buffer(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_image_buffer) return;
|
||||
glad_wglAssociateImageBufferEventsI3D = (PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) load(userptr, "wglAssociateImageBufferEventsI3D");
|
||||
glad_wglCreateImageBufferI3D = (PFNWGLCREATEIMAGEBUFFERI3DPROC) load(userptr, "wglCreateImageBufferI3D");
|
||||
glad_wglDestroyImageBufferI3D = (PFNWGLDESTROYIMAGEBUFFERI3DPROC) load(userptr, "wglDestroyImageBufferI3D");
|
||||
glad_wglReleaseImageBufferEventsI3D = (PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) load(userptr, "wglReleaseImageBufferEventsI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_swap_frame_lock(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_swap_frame_lock) return;
|
||||
glad_wglDisableFrameLockI3D = (PFNWGLDISABLEFRAMELOCKI3DPROC) load(userptr, "wglDisableFrameLockI3D");
|
||||
glad_wglEnableFrameLockI3D = (PFNWGLENABLEFRAMELOCKI3DPROC) load(userptr, "wglEnableFrameLockI3D");
|
||||
glad_wglIsEnabledFrameLockI3D = (PFNWGLISENABLEDFRAMELOCKI3DPROC) load(userptr, "wglIsEnabledFrameLockI3D");
|
||||
glad_wglQueryFrameLockMasterI3D = (PFNWGLQUERYFRAMELOCKMASTERI3DPROC) load(userptr, "wglQueryFrameLockMasterI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_I3D_swap_frame_usage(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_I3D_swap_frame_usage) return;
|
||||
glad_wglBeginFrameTrackingI3D = (PFNWGLBEGINFRAMETRACKINGI3DPROC) load(userptr, "wglBeginFrameTrackingI3D");
|
||||
glad_wglEndFrameTrackingI3D = (PFNWGLENDFRAMETRACKINGI3DPROC) load(userptr, "wglEndFrameTrackingI3D");
|
||||
glad_wglGetFrameUsageI3D = (PFNWGLGETFRAMEUSAGEI3DPROC) load(userptr, "wglGetFrameUsageI3D");
|
||||
glad_wglQueryFrameTrackingI3D = (PFNWGLQUERYFRAMETRACKINGI3DPROC) load(userptr, "wglQueryFrameTrackingI3D");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_DX_interop(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_DX_interop) return;
|
||||
glad_wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC) load(userptr, "wglDXCloseDeviceNV");
|
||||
glad_wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC) load(userptr, "wglDXLockObjectsNV");
|
||||
glad_wglDXObjectAccessNV = (PFNWGLDXOBJECTACCESSNVPROC) load(userptr, "wglDXObjectAccessNV");
|
||||
glad_wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC) load(userptr, "wglDXOpenDeviceNV");
|
||||
glad_wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC) load(userptr, "wglDXRegisterObjectNV");
|
||||
glad_wglDXSetResourceShareHandleNV = (PFNWGLDXSETRESOURCESHAREHANDLENVPROC) load(userptr, "wglDXSetResourceShareHandleNV");
|
||||
glad_wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC) load(userptr, "wglDXUnlockObjectsNV");
|
||||
glad_wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC) load(userptr, "wglDXUnregisterObjectNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_copy_image(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_copy_image) return;
|
||||
glad_wglCopyImageSubDataNV = (PFNWGLCOPYIMAGESUBDATANVPROC) load(userptr, "wglCopyImageSubDataNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_delay_before_swap(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_delay_before_swap) return;
|
||||
glad_wglDelayBeforeSwapNV = (PFNWGLDELAYBEFORESWAPNVPROC) load(userptr, "wglDelayBeforeSwapNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_gpu_affinity(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_gpu_affinity) return;
|
||||
glad_wglCreateAffinityDCNV = (PFNWGLCREATEAFFINITYDCNVPROC) load(userptr, "wglCreateAffinityDCNV");
|
||||
glad_wglDeleteDCNV = (PFNWGLDELETEDCNVPROC) load(userptr, "wglDeleteDCNV");
|
||||
glad_wglEnumGpuDevicesNV = (PFNWGLENUMGPUDEVICESNVPROC) load(userptr, "wglEnumGpuDevicesNV");
|
||||
glad_wglEnumGpusFromAffinityDCNV = (PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) load(userptr, "wglEnumGpusFromAffinityDCNV");
|
||||
glad_wglEnumGpusNV = (PFNWGLENUMGPUSNVPROC) load(userptr, "wglEnumGpusNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_present_video(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_present_video) return;
|
||||
glad_wglBindVideoDeviceNV = (PFNWGLBINDVIDEODEVICENVPROC) load(userptr, "wglBindVideoDeviceNV");
|
||||
glad_wglEnumerateVideoDevicesNV = (PFNWGLENUMERATEVIDEODEVICESNVPROC) load(userptr, "wglEnumerateVideoDevicesNV");
|
||||
glad_wglQueryCurrentContextNV = (PFNWGLQUERYCURRENTCONTEXTNVPROC) load(userptr, "wglQueryCurrentContextNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_swap_group(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_swap_group) return;
|
||||
glad_wglBindSwapBarrierNV = (PFNWGLBINDSWAPBARRIERNVPROC) load(userptr, "wglBindSwapBarrierNV");
|
||||
glad_wglJoinSwapGroupNV = (PFNWGLJOINSWAPGROUPNVPROC) load(userptr, "wglJoinSwapGroupNV");
|
||||
glad_wglQueryFrameCountNV = (PFNWGLQUERYFRAMECOUNTNVPROC) load(userptr, "wglQueryFrameCountNV");
|
||||
glad_wglQueryMaxSwapGroupsNV = (PFNWGLQUERYMAXSWAPGROUPSNVPROC) load(userptr, "wglQueryMaxSwapGroupsNV");
|
||||
glad_wglQuerySwapGroupNV = (PFNWGLQUERYSWAPGROUPNVPROC) load(userptr, "wglQuerySwapGroupNV");
|
||||
glad_wglResetFrameCountNV = (PFNWGLRESETFRAMECOUNTNVPROC) load(userptr, "wglResetFrameCountNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_vertex_array_range(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_vertex_array_range) return;
|
||||
glad_wglAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC) load(userptr, "wglAllocateMemoryNV");
|
||||
glad_wglFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC) load(userptr, "wglFreeMemoryNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_video_capture(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_video_capture) return;
|
||||
glad_wglBindVideoCaptureDeviceNV = (PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglBindVideoCaptureDeviceNV");
|
||||
glad_wglEnumerateVideoCaptureDevicesNV = (PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) load(userptr, "wglEnumerateVideoCaptureDevicesNV");
|
||||
glad_wglLockVideoCaptureDeviceNV = (PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglLockVideoCaptureDeviceNV");
|
||||
glad_wglQueryVideoCaptureDeviceNV = (PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglQueryVideoCaptureDeviceNV");
|
||||
glad_wglReleaseVideoCaptureDeviceNV = (PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglReleaseVideoCaptureDeviceNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_NV_video_output(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_NV_video_output) return;
|
||||
glad_wglBindVideoImageNV = (PFNWGLBINDVIDEOIMAGENVPROC) load(userptr, "wglBindVideoImageNV");
|
||||
glad_wglGetVideoDeviceNV = (PFNWGLGETVIDEODEVICENVPROC) load(userptr, "wglGetVideoDeviceNV");
|
||||
glad_wglGetVideoInfoNV = (PFNWGLGETVIDEOINFONVPROC) load(userptr, "wglGetVideoInfoNV");
|
||||
glad_wglReleaseVideoDeviceNV = (PFNWGLRELEASEVIDEODEVICENVPROC) load(userptr, "wglReleaseVideoDeviceNV");
|
||||
glad_wglReleaseVideoImageNV = (PFNWGLRELEASEVIDEOIMAGENVPROC) load(userptr, "wglReleaseVideoImageNV");
|
||||
glad_wglSendPbufferToVideoNV = (PFNWGLSENDPBUFFERTOVIDEONVPROC) load(userptr, "wglSendPbufferToVideoNV");
|
||||
}
|
||||
static void glad_wgl_load_WGL_OML_sync_control(GLADuserptrloadfunc load, void *userptr) {
|
||||
if(!GLAD_WGL_OML_sync_control) return;
|
||||
glad_wglGetMscRateOML = (PFNWGLGETMSCRATEOMLPROC) load(userptr, "wglGetMscRateOML");
|
||||
glad_wglGetSyncValuesOML = (PFNWGLGETSYNCVALUESOMLPROC) load(userptr, "wglGetSyncValuesOML");
|
||||
glad_wglSwapBuffersMscOML = (PFNWGLSWAPBUFFERSMSCOMLPROC) load(userptr, "wglSwapBuffersMscOML");
|
||||
glad_wglSwapLayerBuffersMscOML = (PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) load(userptr, "wglSwapLayerBuffersMscOML");
|
||||
glad_wglWaitForMscOML = (PFNWGLWAITFORMSCOMLPROC) load(userptr, "wglWaitForMscOML");
|
||||
glad_wglWaitForSbcOML = (PFNWGLWAITFORSBCOMLPROC) load(userptr, "wglWaitForSbcOML");
|
||||
}
|
||||
|
||||
|
||||
static void glad_wgl_resolve_aliases(void) {
|
||||
}
|
||||
|
||||
static int glad_wgl_has_extension(HDC hdc, const char *ext) {
|
||||
const char *terminator;
|
||||
const char *loc;
|
||||
const char *extensions;
|
||||
|
||||
if(wglGetExtensionsStringEXT == NULL && wglGetExtensionsStringARB == NULL)
|
||||
return 0;
|
||||
|
||||
if(wglGetExtensionsStringARB == NULL || hdc == INVALID_HANDLE_VALUE)
|
||||
extensions = wglGetExtensionsStringEXT();
|
||||
else
|
||||
extensions = wglGetExtensionsStringARB(hdc);
|
||||
|
||||
if(extensions == NULL || ext == NULL)
|
||||
return 0;
|
||||
|
||||
while(1) {
|
||||
loc = strstr(extensions, ext);
|
||||
if(loc == NULL)
|
||||
break;
|
||||
|
||||
terminator = loc + strlen(ext);
|
||||
if((loc == extensions || *(loc - 1) == ' ') &&
|
||||
(*terminator == ' ' || *terminator == '\0'))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
extensions = terminator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GLADapiproc glad_wgl_get_proc_from_userptr(void *userptr, const char* name) {
|
||||
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
|
||||
}
|
||||
|
||||
static int glad_wgl_find_extensions_wgl(HDC hdc) {
|
||||
GLAD_WGL_3DFX_multisample = glad_wgl_has_extension(hdc, "WGL_3DFX_multisample");
|
||||
GLAD_WGL_3DL_stereo_control = glad_wgl_has_extension(hdc, "WGL_3DL_stereo_control");
|
||||
GLAD_WGL_AMD_gpu_association = glad_wgl_has_extension(hdc, "WGL_AMD_gpu_association");
|
||||
GLAD_WGL_ARB_buffer_region = glad_wgl_has_extension(hdc, "WGL_ARB_buffer_region");
|
||||
GLAD_WGL_ARB_context_flush_control = glad_wgl_has_extension(hdc, "WGL_ARB_context_flush_control");
|
||||
GLAD_WGL_ARB_create_context = glad_wgl_has_extension(hdc, "WGL_ARB_create_context");
|
||||
GLAD_WGL_ARB_create_context_no_error = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_no_error");
|
||||
GLAD_WGL_ARB_create_context_profile = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_profile");
|
||||
GLAD_WGL_ARB_create_context_robustness = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_robustness");
|
||||
GLAD_WGL_ARB_extensions_string = glad_wgl_has_extension(hdc, "WGL_ARB_extensions_string");
|
||||
GLAD_WGL_ARB_framebuffer_sRGB = glad_wgl_has_extension(hdc, "WGL_ARB_framebuffer_sRGB");
|
||||
GLAD_WGL_ARB_make_current_read = glad_wgl_has_extension(hdc, "WGL_ARB_make_current_read");
|
||||
GLAD_WGL_ARB_multisample = glad_wgl_has_extension(hdc, "WGL_ARB_multisample");
|
||||
GLAD_WGL_ARB_pbuffer = glad_wgl_has_extension(hdc, "WGL_ARB_pbuffer");
|
||||
GLAD_WGL_ARB_pixel_format = glad_wgl_has_extension(hdc, "WGL_ARB_pixel_format");
|
||||
GLAD_WGL_ARB_pixel_format_float = glad_wgl_has_extension(hdc, "WGL_ARB_pixel_format_float");
|
||||
GLAD_WGL_ARB_render_texture = glad_wgl_has_extension(hdc, "WGL_ARB_render_texture");
|
||||
GLAD_WGL_ARB_robustness_application_isolation = glad_wgl_has_extension(hdc, "WGL_ARB_robustness_application_isolation");
|
||||
GLAD_WGL_ARB_robustness_share_group_isolation = glad_wgl_has_extension(hdc, "WGL_ARB_robustness_share_group_isolation");
|
||||
GLAD_WGL_ATI_pixel_format_float = glad_wgl_has_extension(hdc, "WGL_ATI_pixel_format_float");
|
||||
GLAD_WGL_ATI_render_texture_rectangle = glad_wgl_has_extension(hdc, "WGL_ATI_render_texture_rectangle");
|
||||
GLAD_WGL_EXT_colorspace = glad_wgl_has_extension(hdc, "WGL_EXT_colorspace");
|
||||
GLAD_WGL_EXT_create_context_es2_profile = glad_wgl_has_extension(hdc, "WGL_EXT_create_context_es2_profile");
|
||||
GLAD_WGL_EXT_create_context_es_profile = glad_wgl_has_extension(hdc, "WGL_EXT_create_context_es_profile");
|
||||
GLAD_WGL_EXT_depth_float = glad_wgl_has_extension(hdc, "WGL_EXT_depth_float");
|
||||
GLAD_WGL_EXT_display_color_table = glad_wgl_has_extension(hdc, "WGL_EXT_display_color_table");
|
||||
GLAD_WGL_EXT_extensions_string = glad_wgl_has_extension(hdc, "WGL_EXT_extensions_string");
|
||||
GLAD_WGL_EXT_framebuffer_sRGB = glad_wgl_has_extension(hdc, "WGL_EXT_framebuffer_sRGB");
|
||||
GLAD_WGL_EXT_make_current_read = glad_wgl_has_extension(hdc, "WGL_EXT_make_current_read");
|
||||
GLAD_WGL_EXT_multisample = glad_wgl_has_extension(hdc, "WGL_EXT_multisample");
|
||||
GLAD_WGL_EXT_pbuffer = glad_wgl_has_extension(hdc, "WGL_EXT_pbuffer");
|
||||
GLAD_WGL_EXT_pixel_format = glad_wgl_has_extension(hdc, "WGL_EXT_pixel_format");
|
||||
GLAD_WGL_EXT_pixel_format_packed_float = glad_wgl_has_extension(hdc, "WGL_EXT_pixel_format_packed_float");
|
||||
GLAD_WGL_EXT_swap_control = glad_wgl_has_extension(hdc, "WGL_EXT_swap_control");
|
||||
GLAD_WGL_EXT_swap_control_tear = glad_wgl_has_extension(hdc, "WGL_EXT_swap_control_tear");
|
||||
GLAD_WGL_I3D_digital_video_control = glad_wgl_has_extension(hdc, "WGL_I3D_digital_video_control");
|
||||
GLAD_WGL_I3D_gamma = glad_wgl_has_extension(hdc, "WGL_I3D_gamma");
|
||||
GLAD_WGL_I3D_genlock = glad_wgl_has_extension(hdc, "WGL_I3D_genlock");
|
||||
GLAD_WGL_I3D_image_buffer = glad_wgl_has_extension(hdc, "WGL_I3D_image_buffer");
|
||||
GLAD_WGL_I3D_swap_frame_lock = glad_wgl_has_extension(hdc, "WGL_I3D_swap_frame_lock");
|
||||
GLAD_WGL_I3D_swap_frame_usage = glad_wgl_has_extension(hdc, "WGL_I3D_swap_frame_usage");
|
||||
GLAD_WGL_NV_DX_interop = glad_wgl_has_extension(hdc, "WGL_NV_DX_interop");
|
||||
GLAD_WGL_NV_DX_interop2 = glad_wgl_has_extension(hdc, "WGL_NV_DX_interop2");
|
||||
GLAD_WGL_NV_copy_image = glad_wgl_has_extension(hdc, "WGL_NV_copy_image");
|
||||
GLAD_WGL_NV_delay_before_swap = glad_wgl_has_extension(hdc, "WGL_NV_delay_before_swap");
|
||||
GLAD_WGL_NV_float_buffer = glad_wgl_has_extension(hdc, "WGL_NV_float_buffer");
|
||||
GLAD_WGL_NV_gpu_affinity = glad_wgl_has_extension(hdc, "WGL_NV_gpu_affinity");
|
||||
GLAD_WGL_NV_multigpu_context = glad_wgl_has_extension(hdc, "WGL_NV_multigpu_context");
|
||||
GLAD_WGL_NV_multisample_coverage = glad_wgl_has_extension(hdc, "WGL_NV_multisample_coverage");
|
||||
GLAD_WGL_NV_present_video = glad_wgl_has_extension(hdc, "WGL_NV_present_video");
|
||||
GLAD_WGL_NV_render_depth_texture = glad_wgl_has_extension(hdc, "WGL_NV_render_depth_texture");
|
||||
GLAD_WGL_NV_render_texture_rectangle = glad_wgl_has_extension(hdc, "WGL_NV_render_texture_rectangle");
|
||||
GLAD_WGL_NV_swap_group = glad_wgl_has_extension(hdc, "WGL_NV_swap_group");
|
||||
GLAD_WGL_NV_vertex_array_range = glad_wgl_has_extension(hdc, "WGL_NV_vertex_array_range");
|
||||
GLAD_WGL_NV_video_capture = glad_wgl_has_extension(hdc, "WGL_NV_video_capture");
|
||||
GLAD_WGL_NV_video_output = glad_wgl_has_extension(hdc, "WGL_NV_video_output");
|
||||
GLAD_WGL_OML_sync_control = glad_wgl_has_extension(hdc, "WGL_OML_sync_control");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int glad_wgl_find_core_wgl(void) {
|
||||
int major = 1, minor = 0;
|
||||
GLAD_WGL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
|
||||
return GLAD_MAKE_VERSION(major, minor);
|
||||
}
|
||||
|
||||
int gladLoadWGLUserPtr(HDC hdc, GLADuserptrloadfunc load, void *userptr) {
|
||||
int version;
|
||||
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) load(userptr, "wglGetExtensionsStringARB");
|
||||
wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) load(userptr, "wglGetExtensionsStringEXT");
|
||||
if(wglGetExtensionsStringARB == NULL && wglGetExtensionsStringEXT == NULL) return 0;
|
||||
version = glad_wgl_find_core_wgl();
|
||||
|
||||
|
||||
if (!glad_wgl_find_extensions_wgl(hdc)) return 0;
|
||||
glad_wgl_load_WGL_3DL_stereo_control(load, userptr);
|
||||
glad_wgl_load_WGL_AMD_gpu_association(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_buffer_region(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_create_context(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_extensions_string(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_make_current_read(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_pbuffer(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_pixel_format(load, userptr);
|
||||
glad_wgl_load_WGL_ARB_render_texture(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_display_color_table(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_extensions_string(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_make_current_read(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_pbuffer(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_pixel_format(load, userptr);
|
||||
glad_wgl_load_WGL_EXT_swap_control(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_digital_video_control(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_gamma(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_genlock(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_image_buffer(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_swap_frame_lock(load, userptr);
|
||||
glad_wgl_load_WGL_I3D_swap_frame_usage(load, userptr);
|
||||
glad_wgl_load_WGL_NV_DX_interop(load, userptr);
|
||||
glad_wgl_load_WGL_NV_copy_image(load, userptr);
|
||||
glad_wgl_load_WGL_NV_delay_before_swap(load, userptr);
|
||||
glad_wgl_load_WGL_NV_gpu_affinity(load, userptr);
|
||||
glad_wgl_load_WGL_NV_present_video(load, userptr);
|
||||
glad_wgl_load_WGL_NV_swap_group(load, userptr);
|
||||
glad_wgl_load_WGL_NV_vertex_array_range(load, userptr);
|
||||
glad_wgl_load_WGL_NV_video_capture(load, userptr);
|
||||
glad_wgl_load_WGL_NV_video_output(load, userptr);
|
||||
glad_wgl_load_WGL_OML_sync_control(load, userptr);
|
||||
|
||||
glad_wgl_resolve_aliases();
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
int gladLoadWGL(HDC hdc, GLADloadfunc load) {
|
||||
return gladLoadWGLUserPtr(hdc, glad_wgl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
|
||||
}
|
||||
|
||||
|
||||
#ifdef GLAD_WGL
|
||||
|
||||
static GLADapiproc glad_wgl_get_proc(void *vuserptr, const char* name) {
|
||||
GLAD_UNUSED(vuserptr);
|
||||
return GLAD_GNUC_EXTENSION (GLADapiproc) wglGetProcAddress(name);
|
||||
}
|
||||
|
||||
int gladLoaderLoadWGL(HDC hdc) {
|
||||
return gladLoadWGLUserPtr(hdc, glad_wgl_get_proc, NULL);
|
||||
}
|
||||
|
||||
|
||||
#endif /* GLAD_WGL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
105
engine/gfx/src/Base/Adorn.cpp
Normal file
105
engine/gfx/src/Base/Adorn.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "Base/Adorn.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
template<typename Modifier>
|
||||
static void outlineRect2dImpl(Adorn* adorn, const Rect2D& rect, float thick, const Color4& color, const Modifier& modifier)
|
||||
{
|
||||
adorn->rect2d(Rect2D::xyxy(rect.x0() - thick, rect.y0() - thick, rect.x0(), rect.y1()), color, modifier);
|
||||
adorn->rect2d(Rect2D::xyxy(rect.x1(), rect.y0(), rect.x1() + thick, rect.y1() + thick), color, modifier);
|
||||
|
||||
adorn->rect2d(Rect2D::xyxy(rect.x0(), rect.y0() - thick, rect.x1() + thick, rect.y0()), color, modifier);
|
||||
adorn->rect2d(Rect2D::xyxy(rect.x0() - thick, rect.y1(), rect.x1(), rect.y1() + thick), color, modifier);
|
||||
}
|
||||
|
||||
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color)
|
||||
{
|
||||
outlineRect2dImpl(this, rect, thick, color, Rotation2D());
|
||||
}
|
||||
|
||||
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rotation2D& rotation)
|
||||
{
|
||||
outlineRect2dImpl(this, rect, thick, color, rotation);
|
||||
}
|
||||
|
||||
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rect2D& clipRect)
|
||||
{
|
||||
outlineRect2dImpl(this, rect, thick, color, clipRect);
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Color4& color)
|
||||
{
|
||||
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, Rotation2D());
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Color4& color, const Rotation2D& rotation)
|
||||
{
|
||||
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, rotation);
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Color4& color, const Rect2D& clipRect)
|
||||
{
|
||||
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, clipRect);
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color)
|
||||
{
|
||||
rect2d(rect, texul, texbr, color, Rotation2D());
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation)
|
||||
{
|
||||
if (!rotation.empty())
|
||||
{
|
||||
Vector2 x0y0 = rotation.rotate(rect.x0y0());
|
||||
Vector2 x1y0 = rotation.rotate(rect.x1y0());
|
||||
Vector2 x0y1 = rotation.rotate(rect.x0y1());
|
||||
Vector2 x1y1 = rotation.rotate(rect.x1y1());
|
||||
|
||||
rect2dImpl(x0y0, x1y0, x0y1, x1y1, texul, texbr, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect2dImpl(rect.x0y0(), rect.x1y0(), rect.x0y1(), rect.x1y1(), texul, texbr, color);
|
||||
}
|
||||
}
|
||||
|
||||
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect2D& clipRect)
|
||||
{
|
||||
Aya::Rect2D intersectRect = rect;
|
||||
|
||||
Vector2 lowerUV(texul.x, texul.y);
|
||||
Vector2 upperUV(texbr.x, texbr.y);
|
||||
|
||||
if (clipRect != rect)
|
||||
{
|
||||
intersectRect = clipRect.intersect(rect);
|
||||
|
||||
if (rect.width() != 0)
|
||||
{
|
||||
float uvwidth = upperUV.x - lowerUV.x;
|
||||
|
||||
lowerUV.x += (uvwidth * (intersectRect.x0() - rect.x0()) / rect.width());
|
||||
upperUV.x += (uvwidth * (intersectRect.x1() - rect.x1()) / rect.width());
|
||||
}
|
||||
|
||||
if (rect.height() != 0)
|
||||
{
|
||||
float uvheight = upperUV.y - lowerUV.y;
|
||||
|
||||
lowerUV.y += (uvheight * (intersectRect.y0() - rect.y0()) / rect.height());
|
||||
upperUV.y += (uvheight * (intersectRect.y1() - rect.y1()) / rect.height());
|
||||
}
|
||||
}
|
||||
|
||||
rect2dImpl(intersectRect.x0y0(), intersectRect.x1y0(), intersectRect.x0y1(), intersectRect.x1y1(), lowerUV, upperUV, color);
|
||||
}
|
||||
|
||||
Vector2 Adorn::drawFont2D(const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color, const Color4& outline,
|
||||
Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect, const Rotation2D& rotation)
|
||||
{
|
||||
return drawFont2DImpl(this, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
274
engine/gfx/src/Base/Adorn.hpp
Normal file
274
engine/gfx/src/Base/Adorn.hpp
Normal file
@@ -0,0 +1,274 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Base/Type.hpp"
|
||||
#include "Base/TextureProxyBase.hpp"
|
||||
|
||||
#include "Utility/Extents.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Utility/ContentId.hpp"
|
||||
#include "Utility/Rotation2d.hpp"
|
||||
#include "signal.hpp"
|
||||
#include "Declarations.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class I3DLinearFunc;
|
||||
class RenderCaps;
|
||||
class Camera;
|
||||
|
||||
struct Canvas
|
||||
{
|
||||
Canvas(Vector2 viewPort)
|
||||
: size(viewPort)
|
||||
{
|
||||
}
|
||||
Vector2 size;
|
||||
Vector2 toPixelSize(const Vector2& percent) const; // std screen is 100% wide and 75% tall
|
||||
int normalizedFontSize(int fontSize) const;
|
||||
};
|
||||
|
||||
// Aya::Adorn is a base class used to decorate other objects using 2D or 3D
|
||||
// basic shapes.
|
||||
class AyaBaseClass Adorn
|
||||
{
|
||||
public:
|
||||
enum Material
|
||||
{
|
||||
Material_Default,
|
||||
Material_NoLighting,
|
||||
Material_SelfLit,
|
||||
Material_SelfLitHighlight,
|
||||
Material_AALine,
|
||||
Material_Outline,
|
||||
|
||||
Material_Count
|
||||
};
|
||||
|
||||
Adorn()
|
||||
: ignoreTexture(false)
|
||||
, currentMaterial(Material_Default)
|
||||
{
|
||||
}
|
||||
|
||||
Canvas getCanvas() const
|
||||
{
|
||||
return getViewport().wh();
|
||||
}
|
||||
|
||||
virtual const Camera* getCamera() const = 0;
|
||||
|
||||
virtual ~Adorn() {}
|
||||
|
||||
virtual TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "") = 0;
|
||||
virtual TextureProxyBaseRef createTextureProxy(int width, int height) = 0;
|
||||
|
||||
// Listen to this signal if you need a hint about when to release your
|
||||
// TextureProxys.
|
||||
virtual Aya::signal<void()>& getUnbindResourcesSignal() = 0;
|
||||
|
||||
// Called to perform any preparations before the render pass begins.
|
||||
virtual void prepareRenderPass() {}
|
||||
|
||||
// Called to perform any cleanup after every 2D/3D render pass.
|
||||
virtual void finishRenderPass() {}
|
||||
|
||||
virtual void preSubmitPass() {}
|
||||
virtual void postSubmitPass() {}
|
||||
|
||||
virtual bool useFontSmoothScalling()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setMaterial(Material material_)
|
||||
{
|
||||
currentMaterial = material_;
|
||||
}
|
||||
|
||||
Material getMaterial() const
|
||||
{
|
||||
return currentMaterial;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Viewport
|
||||
// Returns the Adorn's viewport area.
|
||||
//
|
||||
// Note that this viewport doesn't always represent the area where the
|
||||
// game is being displayed.
|
||||
virtual Rect2D getViewport() const = 0;
|
||||
|
||||
// Hack - buffering the GuiRect here - ultimately need to clip the
|
||||
// "User Gui Space" with the Aya Gui stuff.
|
||||
void setUserGuiInset(const Vector4& value)
|
||||
{
|
||||
userGuiInset = value;
|
||||
}
|
||||
|
||||
Rect2D getUserGuiRect() const
|
||||
{
|
||||
Rect2D vp = getViewport();
|
||||
return Rect2D::xyxy(vp.x0() + userGuiInset.x, vp.y0() + userGuiInset.y, vp.x1() - userGuiInset.z, vp.y1() - userGuiInset.w);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Textures, Draw Rectangles, Lines
|
||||
|
||||
void setIgnoreTexture(bool ignore)
|
||||
{
|
||||
ignoreTexture = ignore;
|
||||
}
|
||||
bool getIgnoreTexture() const
|
||||
{
|
||||
return ignoreTexture;
|
||||
}
|
||||
|
||||
// Sets the texture used by the Adorn.
|
||||
virtual void setTexture(int id, const Aya::TextureProxyBaseRef& texture) = 0;
|
||||
|
||||
// Gets the size of the texture being used by the Adorn.
|
||||
virtual Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const = 0;
|
||||
|
||||
// Draws a line on the screen.
|
||||
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color) = 0;
|
||||
|
||||
// Draws a hollow rectangle on the screen.
|
||||
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color);
|
||||
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rotation2D& rotation);
|
||||
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rect2D& clipRect);
|
||||
|
||||
// Draws a solid rectangle on the screen.
|
||||
void rect2d(const Rect2D& rect, const Color4& color);
|
||||
void rect2d(const Rect2D& rect, const Color4& color, const Rotation2D& rotation);
|
||||
void rect2d(const Rect2D& rect, const Color4& color, const Rect2D& clipRect);
|
||||
|
||||
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color);
|
||||
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation);
|
||||
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect2D& clipRect);
|
||||
|
||||
// Rectangle drawing implementation
|
||||
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
|
||||
const Vector2& tex1, const Color4& color) = 0;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Draw Fonts
|
||||
|
||||
// Retrieves the boundaries (in pixels) of a string, if it were to be
|
||||
// drawn on screen.
|
||||
virtual Vector2 get2DStringBounds(
|
||||
const std::string& s, float size, Text::Font font = Text::FONT_LEGACY, const Vector2& availableSpace = Vector2::zero()) const = 0;
|
||||
|
||||
// Draws a string using this Adorn.
|
||||
Vector2 drawFont2D(const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color = Color3::black(),
|
||||
const Color4& outline = Color4::clear(), Text::Font font = Text::FONT_LEGACY, Text::XAlign xalign = Text::XALIGN_LEFT,
|
||||
Text::YAlign yalign = Text::YALIGN_TOP, const Vector2& availableSpace = Vector2::zero(),
|
||||
const Rect2D& clippingRect = Aya::Rect2D::xyxy(-1, -1, -1, -1), const Rotation2D& rotation = Rotation2D());
|
||||
|
||||
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation) = 0;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// 3D stuff - procedural
|
||||
|
||||
virtual void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color) = 0;
|
||||
|
||||
virtual void line3dAA(
|
||||
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop) = 0;
|
||||
|
||||
// Sets the adorn's coordinate frame.
|
||||
virtual void setObjectToWorldMatrix(const CoordinateFrame& c) = 0;
|
||||
|
||||
// Draws an axis aligned bounding box on the adorn.
|
||||
virtual void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f)) = 0;
|
||||
|
||||
// Draws a box on the adorn.
|
||||
void box(const Extents& extents, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
|
||||
{
|
||||
AABox aaBox(extents.min(), extents.max());
|
||||
box(aaBox, solidColor);
|
||||
}
|
||||
|
||||
virtual void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
|
||||
|
||||
// Draws a sphere on the adorn.
|
||||
virtual void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f)) = 0;
|
||||
|
||||
virtual void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
|
||||
|
||||
// Draws an explosion on the adorn.
|
||||
virtual void explosion(const Sphere& sphere) = 0;
|
||||
|
||||
virtual void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
|
||||
|
||||
// Draws a cylinder along the adorn's x axis.
|
||||
virtual void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true) = 0;
|
||||
|
||||
virtual void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
|
||||
|
||||
// Draws a ray from the adorn.
|
||||
virtual void ray(const RbxRay& ray, const Color4& color = Color3::orange()) = 0;
|
||||
|
||||
// Draws the x, y, z axis of the adorn.
|
||||
virtual void axes(
|
||||
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f) = 0;
|
||||
|
||||
|
||||
// Draws a quad on the adorn.
|
||||
//
|
||||
// v0 The first point to form the quad.
|
||||
// v1 The second point to form the quad.
|
||||
// v2 The third point to form the quad.
|
||||
// v3 The fourth point to form the quad.
|
||||
// color The color used to draw the quad.
|
||||
// v0tex UV coordinates used on the first polygon.
|
||||
// v2tex UV coordinates used on the second polygon.
|
||||
// opt The material options to use when drawing the quad.
|
||||
virtual void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
|
||||
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false) = 0;
|
||||
|
||||
// Draws a convex 3D polygon on the adorn.
|
||||
//
|
||||
// v The set of points that compose the polygon.
|
||||
// countv The number of points that compose the polygon.
|
||||
// color The color used when drawing the polygon.
|
||||
// opt The material options to use when drawing the polygon.
|
||||
virtual void convexPolygon(const Vector3* v, int countv, const Color4& color) = 0;
|
||||
|
||||
// Draws a convex 2D polygon on the adorn.
|
||||
//
|
||||
// v The set of points that compose the polygon.
|
||||
// countv The number of points that compose the polygon.
|
||||
// color The color used when drawing the polygon.
|
||||
// opt The material options to use when drawing the polygon.
|
||||
virtual void convexPolygon2d(const Vector2* v, int countv, const Color4& color) = 0;
|
||||
|
||||
// Evaluates extrusion, calling trajectory and profile func with domain [0..1].
|
||||
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[,
|
||||
// and evaluation for f(0) is used again for f(1).
|
||||
// Future: if closeTrajectory != closeProfile, that could indicate we need
|
||||
// caps at the end.
|
||||
virtual void extrusion(Aya::I3DLinearFunc* trajectory, int trajectorysegments, Aya::I3DLinearFunc* profile, int profilesegments,
|
||||
const Color4& color, bool closeTrajectory = true, bool closeProfile = true) = 0;
|
||||
|
||||
virtual bool isVisible(const Extents& extents, const CoordinateFrame& cframe)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static const int maximumZIndex = 10;
|
||||
|
||||
protected:
|
||||
Vector4 userGuiInset;
|
||||
bool ignoreTexture;
|
||||
Material currentMaterial;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
72
engine/gfx/src/Base/AdornBillboarder.cpp
Normal file
72
engine/gfx/src/Base/AdornBillboarder.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "Base/AdornBillboarder.hpp"
|
||||
#include "Base/ViewportBillboarder.hpp"
|
||||
#include "DataModel/Camera.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
AdornBillboarder::AdornBillboarder(Adorn* parent, const ViewportBillboarder& viewportBillboarder)
|
||||
: parent(parent)
|
||||
, viewport(viewportBillboarder.getViewport())
|
||||
, alwaysOnTop(viewportBillboarder.alwaysOnTop)
|
||||
{
|
||||
parent->setObjectToWorldMatrix(viewportBillboarder.getCoordinateFrame());
|
||||
}
|
||||
|
||||
AdornBillboarder::AdornBillboarder(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop)
|
||||
: parent(parent)
|
||||
, viewport(viewport)
|
||||
, alwaysOnTop(alwaysOnTop)
|
||||
{
|
||||
parent->setObjectToWorldMatrix(transform);
|
||||
}
|
||||
|
||||
Rect2D AdornBillboarder::getViewport() const
|
||||
{
|
||||
return viewport;
|
||||
}
|
||||
|
||||
void AdornBillboarder::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
|
||||
{
|
||||
Vector3 p03D = Vector3(p0, 0);
|
||||
Vector3 p13D = Vector3(p1, 0);
|
||||
|
||||
p03D.y *= -1;
|
||||
p13D.y *= -1;
|
||||
|
||||
parent->line3d(p03D, p13D, color);
|
||||
}
|
||||
|
||||
void AdornBillboarder::rect2dImpl(
|
||||
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
|
||||
{
|
||||
Vector3 px0y0(x0y0, 0);
|
||||
Vector3 px1y0(x1y0, 0);
|
||||
Vector3 px0y1(x0y1, 0);
|
||||
Vector3 px1y1(x1y1, 0);
|
||||
|
||||
px0y1.y *= -1;
|
||||
px1y1.y *= -1;
|
||||
px0y0.y *= -1;
|
||||
px1y0.y *= -1;
|
||||
|
||||
parent->quad(px0y0, px1y0, px0y1, px1y1, color, tex0, tex1, 0, alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornBillboarder::convexPolygon2d(const Vector2* v, int countv, const Color4& color)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Vector3* v3d = (Vector3*)_alloca(sizeof(Vector3) * countv);
|
||||
#else
|
||||
Vector3* v3d = (Vector3*)alloca(sizeof(Vector3) * countv);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < countv; ++i)
|
||||
{
|
||||
v3d[i] = Vector3(v[i].x, -v[i].y, 0); /*neg y : convert from ui space (0,0 top left) to math space */
|
||||
}
|
||||
|
||||
parent->convexPolygon(v3d, countv, color);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
172
engine/gfx/src/Base/AdornBillboarder.hpp
Normal file
172
engine/gfx/src/Base/AdornBillboarder.hpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#pragma once
|
||||
|
||||
#include "Base/Adorn.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class ViewportBillboarder;
|
||||
|
||||
class AdornBillboarder : public Adorn
|
||||
{
|
||||
Adorn* parent;
|
||||
Rect2D viewport;
|
||||
bool alwaysOnTop;
|
||||
|
||||
public:
|
||||
AdornBillboarder(Adorn* parent, const ViewportBillboarder& viewportBillboarder);
|
||||
AdornBillboarder(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop = false);
|
||||
|
||||
/*override*/ TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "")
|
||||
{
|
||||
return parent->createTextureProxy(id, waiting, bBlocking, context);
|
||||
};
|
||||
/*override*/ TextureProxyBaseRef createTextureProxy(int width, int height)
|
||||
{
|
||||
return parent->createTextureProxy(width, height);
|
||||
};
|
||||
/*override*/ Aya::signal<void()>& getUnbindResourcesSignal()
|
||||
{
|
||||
return parent->getUnbindResourcesSignal();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Viewport
|
||||
/*override*/ Rect2D getViewport() const;
|
||||
virtual const Camera* getCamera() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Textures, Draw Rectangles, Lines
|
||||
|
||||
/*override*/ void setTexture(int id, const Aya::TextureProxyBaseRef& texture)
|
||||
{
|
||||
parent->setTexture(id, texture);
|
||||
};
|
||||
|
||||
/*override*/ Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const
|
||||
{
|
||||
return parent->getTextureSize(texture);
|
||||
};
|
||||
|
||||
virtual bool useFontSmoothScalling()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
|
||||
|
||||
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
|
||||
const Vector2& tex1, const Color4& color);
|
||||
|
||||
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
|
||||
{
|
||||
return parent->get2DStringBounds(s, size, font, availableSpace);
|
||||
}
|
||||
|
||||
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation)
|
||||
{
|
||||
return parent->drawFont2DImpl(
|
||||
target, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// 3D stuff - procedural
|
||||
|
||||
/*override*/ void setObjectToWorldMatrix(const CoordinateFrame& c)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f))
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void explosion(const Sphere& sphere)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void ray(const RbxRay& ray, const Color4& color = Color3::orange())
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ virtual void line3dAA(
|
||||
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void axes(
|
||||
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
|
||||
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void convexPolygon2d(const Vector2* v, int countv, const Color4& color);
|
||||
|
||||
/*override*/ void convexPolygon(const Vector3* v, int countv, const Color4& color)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
// evaluates extrusion, calling trajectory and profile func with domain [0..1].
|
||||
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[, and evaluation for f(0) is used again for f(1).
|
||||
// future: if closeTrajectory != closeProfile, that could indicate we need caps at the end.
|
||||
/*override*/ void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
|
||||
bool closeTrajectory = true, bool closeProfile = true)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
};
|
||||
}; // namespace Aya
|
||||
29
engine/gfx/src/Base/AdornBillboarder2D.cpp
Normal file
29
engine/gfx/src/Base/AdornBillboarder2D.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Base/AdornBillboarder2D.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
AdornBillboarder2D::AdornBillboarder2D(Adorn* parent, const Rect2D& viewport, const Vector2& screenOffset)
|
||||
: parent(parent)
|
||||
, viewport(viewport)
|
||||
, screenOffset(screenOffset)
|
||||
{
|
||||
}
|
||||
|
||||
Rect2D AdornBillboarder2D::getViewport() const
|
||||
{
|
||||
return viewport;
|
||||
}
|
||||
|
||||
void AdornBillboarder2D::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
|
||||
{
|
||||
parent->line2d(p0 + screenOffset, p1 + screenOffset, color);
|
||||
}
|
||||
|
||||
void AdornBillboarder2D::rect2dImpl(
|
||||
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
|
||||
{
|
||||
parent->rect2dImpl(x0y0 + screenOffset, x1y0 + screenOffset, x0y1 + screenOffset, x1y1 + screenOffset, tex0, tex1, color);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
174
engine/gfx/src/Base/AdornBillboarder2D.hpp
Normal file
174
engine/gfx/src/Base/AdornBillboarder2D.hpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include "Base/Adorn.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class AdornBillboarder2D : public Adorn
|
||||
{
|
||||
protected:
|
||||
Adorn* parent;
|
||||
Rect2D viewport;
|
||||
Vector2 screenOffset;
|
||||
|
||||
public:
|
||||
AdornBillboarder2D(Adorn* parent, const Rect2D& viewport, const Vector2& screenOffset);
|
||||
|
||||
/*override*/ TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "")
|
||||
{
|
||||
return parent->createTextureProxy(id, waiting, bBlocking, context);
|
||||
};
|
||||
/*override*/ TextureProxyBaseRef createTextureProxy(int width, int height)
|
||||
{
|
||||
return parent->createTextureProxy(width, height);
|
||||
};
|
||||
/*override*/ Aya::signal<void()>& getUnbindResourcesSignal()
|
||||
{
|
||||
return parent->getUnbindResourcesSignal();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Viewport
|
||||
/*override*/ Rect2D getViewport() const;
|
||||
virtual const Camera* getCamera() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// Textures, Draw Rectangles, Lines
|
||||
|
||||
/*override*/ void setTexture(int id, const Aya::TextureProxyBaseRef& texture)
|
||||
{
|
||||
parent->setTexture(id, texture);
|
||||
};
|
||||
|
||||
/*override*/ Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const
|
||||
{
|
||||
return parent->getTextureSize(texture);
|
||||
};
|
||||
|
||||
virtual bool useFontSmoothScalling()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
|
||||
|
||||
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
|
||||
const Vector2& tex1, const Color4& color);
|
||||
|
||||
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
|
||||
{
|
||||
return parent->get2DStringBounds(s, size, font, availableSpace);
|
||||
}
|
||||
|
||||
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation)
|
||||
{
|
||||
return parent->drawFont2DImpl(
|
||||
target, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//
|
||||
// 3D stuff - procedural
|
||||
|
||||
/*override*/ void setObjectToWorldMatrix(const CoordinateFrame& c)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f))
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool drawFront)
|
||||
{
|
||||
throw std::runtime_error("Invalid ooperation");
|
||||
};
|
||||
|
||||
/*override*/ void explosion(const Sphere& sphere)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void ray(const RbxRay& ray, const Color4& color = Color3::orange())
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ virtual void line3dAA(
|
||||
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void axes(
|
||||
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
|
||||
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
/*override*/ void convexPolygon2d(const Vector2* v, int countv, const Color4& color)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
}
|
||||
|
||||
/*override*/ void convexPolygon(const Vector3* v, int countv, const Color4& color)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
|
||||
// evaluates extrusion, calling trajectory and profile func with domain [0..1].
|
||||
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[, and evaluation for f(0) is used again for f(1).
|
||||
// future: if closeTrajectory != closeProfile, that could indicate we need caps at the end.
|
||||
/*override*/ void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
|
||||
bool closeTrajectory = true, bool closeProfile = true)
|
||||
{
|
||||
throw std::runtime_error("Invalid operation");
|
||||
};
|
||||
};
|
||||
|
||||
}; // namespace Aya
|
||||
69
engine/gfx/src/Base/AdornSurface.cpp
Normal file
69
engine/gfx/src/Base/AdornSurface.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "Base/AdornSurface.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
AdornSurface::AdornSurface(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop)
|
||||
: parent(parent)
|
||||
, viewport(viewport)
|
||||
, alwaysOnTop(alwaysOnTop)
|
||||
{
|
||||
parent->setObjectToWorldMatrix(transform);
|
||||
}
|
||||
|
||||
Rect2D AdornSurface::getViewport() const
|
||||
{
|
||||
return viewport;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AdornSurface::setTexture(int id, const Aya::TextureProxyBaseRef& t)
|
||||
{
|
||||
return parent->setTexture(id, t);
|
||||
}
|
||||
|
||||
Rect2D AdornSurface::getTextureSize(const Aya::TextureProxyBaseRef& texture) const
|
||||
{
|
||||
return parent->getTextureSize(texture);
|
||||
}
|
||||
|
||||
void AdornSurface::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
|
||||
{
|
||||
Vector3 p03D = Vector3(p0, 0);
|
||||
Vector3 p13D = Vector3(p1, 0);
|
||||
|
||||
p03D.y *= -1;
|
||||
p13D.y *= -1;
|
||||
|
||||
parent->line3d(p03D, p13D, color);
|
||||
}
|
||||
|
||||
void AdornSurface::rect2dImpl(
|
||||
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
|
||||
{
|
||||
Vector3 px0y0(x0y0, 0);
|
||||
Vector3 px1y0(x1y0, 0);
|
||||
Vector3 px0y1(x0y1, 0);
|
||||
Vector3 px1y1(x1y1, 0);
|
||||
|
||||
px0y1.y *= -1;
|
||||
px1y1.y *= -1;
|
||||
px0y0.y *= -1;
|
||||
px1y0.y *= -1;
|
||||
|
||||
parent->quad(px0y0, px1y0, px0y1, px1y1, color, tex0, tex1, 0, alwaysOnTop);
|
||||
}
|
||||
|
||||
Vector2 AdornSurface::drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& pos2D, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation)
|
||||
{
|
||||
return parent->drawFont2DImpl(target, s, pos2D, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
|
||||
}
|
||||
|
||||
Vector2 AdornSurface::get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
|
||||
{
|
||||
return parent->get2DStringBounds(s, size, font, availableSpace);
|
||||
}
|
||||
} // namespace Aya
|
||||
127
engine/gfx/src/Base/AdornSurface.hpp
Normal file
127
engine/gfx/src/Base/AdornSurface.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "Base/Adorn.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Utility/UDim.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
class AdornSurface : public Adorn
|
||||
{
|
||||
Adorn* parent;
|
||||
Rect2D viewport;
|
||||
bool alwaysOnTop;
|
||||
|
||||
public:
|
||||
AdornSurface(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop = false);
|
||||
|
||||
virtual bool useFontSmoothScalling()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setTexture(int id, const Aya::TextureProxyBaseRef& texture);
|
||||
Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const;
|
||||
|
||||
void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
|
||||
|
||||
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
|
||||
const Vector2& tex1, const Color4& color);
|
||||
|
||||
Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const;
|
||||
Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& pos2D, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation);
|
||||
|
||||
const Camera* getCamera() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking, const std::string& context = "")
|
||||
{
|
||||
return parent->createTextureProxy(id, waiting, bBlocking, context);
|
||||
}
|
||||
TextureProxyBaseRef createTextureProxy(int width, int height)
|
||||
{
|
||||
return parent->createTextureProxy(width, height);
|
||||
};
|
||||
Aya::signal<void()>& getUnbindResourcesSignal()
|
||||
{
|
||||
return parent->getUnbindResourcesSignal();
|
||||
}
|
||||
Rect2D getViewport() const;
|
||||
|
||||
void setObjectToWorldMatrix(const CoordinateFrame& c)
|
||||
{
|
||||
;
|
||||
}
|
||||
void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
|
||||
{
|
||||
;
|
||||
}
|
||||
void line3dAA(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void box(const AABox& b, const Color4& solidColor)
|
||||
{
|
||||
;
|
||||
}
|
||||
void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void sphere(const Sphere& s, const Color4& solidColor)
|
||||
{
|
||||
;
|
||||
}
|
||||
void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void explosion(const Sphere& sphere)
|
||||
{
|
||||
;
|
||||
}
|
||||
void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap)
|
||||
{
|
||||
;
|
||||
}
|
||||
void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void ray(const RbxRay& ray, const Color4& color)
|
||||
{
|
||||
;
|
||||
}
|
||||
void axes(const Color4&, const Color4&, const Color4&, float)
|
||||
{
|
||||
;
|
||||
}
|
||||
void quad(
|
||||
const Vector3&, const Vector3&, const Vector3&, const Vector3&, const Color4&, const Vector2&, const Vector2&, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
;
|
||||
}
|
||||
void convexPolygon(const Vector3*, int, const Color4&)
|
||||
{
|
||||
;
|
||||
}
|
||||
void convexPolygon2d(const Vector2*, int, const Color4&)
|
||||
{
|
||||
;
|
||||
}
|
||||
void extrusion(Aya::I3DLinearFunc*, int, Aya::I3DLinearFunc*, int, const Color4&, bool, bool)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
45
engine/gfx/src/Base/AsyncResult.hpp
Normal file
45
engine/gfx/src/Base/AsyncResult.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/ContentProvider.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class AsyncResult
|
||||
{
|
||||
public:
|
||||
AsyncResult()
|
||||
: reqResult(Aya::AsyncHttpQueue::Succeeded){};
|
||||
|
||||
// make result always more restrictive only.
|
||||
// Succeeded < Waiting < Failed.
|
||||
void returnResult(Aya::AsyncHttpQueue::RequestResult reqResult)
|
||||
{
|
||||
switch (reqResult)
|
||||
{
|
||||
case Aya::AsyncHttpQueue::Succeeded:
|
||||
break;
|
||||
case Aya::AsyncHttpQueue::Waiting:
|
||||
if (this->reqResult == Aya::AsyncHttpQueue::Succeeded)
|
||||
{
|
||||
this->reqResult = reqResult;
|
||||
}
|
||||
break;
|
||||
case Aya::AsyncHttpQueue::Failed:
|
||||
this->reqResult = reqResult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void returnWaitingFor(const Aya::ContentId& id)
|
||||
{
|
||||
returnResult(Aya::AsyncHttpQueue::Waiting);
|
||||
waitingFor.push_back(id);
|
||||
}
|
||||
|
||||
Aya::AsyncHttpQueue::RequestResult reqResult;
|
||||
std::vector<Aya::ContentId> waitingFor;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
420
engine/gfx/src/Base/FileMeshData.cpp
Normal file
420
engine/gfx/src/Base/FileMeshData.cpp
Normal file
@@ -0,0 +1,420 @@
|
||||
#include "Base/FileMeshData.hpp"
|
||||
#include "Base/ObjLoader.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include "DenseHash.hpp"
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
struct MeshVertexHasher
|
||||
{
|
||||
bool operator()(const FileMeshVertexNormalTexture3d& l, const FileMeshVertexNormalTexture3d& r) const
|
||||
{
|
||||
return memcmp(&l, &r, sizeof(l)) == 0;
|
||||
}
|
||||
|
||||
size_t operator()(const FileMeshVertexNormalTexture3d& v) const
|
||||
{
|
||||
size_t result = 0;
|
||||
boost::hash_combine(result, v.vx);
|
||||
boost::hash_combine(result, v.vy);
|
||||
boost::hash_combine(result, v.vz);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
void optimizeMesh(FileMeshData& mesh)
|
||||
{
|
||||
std::vector<unsigned int> remap(mesh.vnts.size());
|
||||
|
||||
FileMeshVertexNormalTexture3d dummy = {};
|
||||
dummy.vx = FLT_MAX;
|
||||
|
||||
typedef DenseHashMap<FileMeshVertexNormalTexture3d, unsigned int, MeshVertexHasher, MeshVertexHasher> VertexMap;
|
||||
VertexMap vertexMap(dummy);
|
||||
|
||||
for (size_t i = 0; i < mesh.vnts.size(); ++i)
|
||||
{
|
||||
unsigned int& vi = vertexMap[mesh.vnts[i]];
|
||||
|
||||
if (vi == 0)
|
||||
vi = vertexMap.size();
|
||||
|
||||
remap[i] = vi - 1;
|
||||
}
|
||||
|
||||
std::vector<FileMeshVertexNormalTexture3d> newvnts(vertexMap.size());
|
||||
|
||||
for (size_t i = 0; i < mesh.vnts.size(); ++i)
|
||||
newvnts[remap[i]] = mesh.vnts[i];
|
||||
|
||||
mesh.vnts.swap(newvnts);
|
||||
|
||||
for (size_t i = 0; i < mesh.faces.size(); ++i)
|
||||
{
|
||||
FileMeshFace& face = mesh.faces[i];
|
||||
|
||||
face.a = remap[face.a];
|
||||
face.b = remap[face.b];
|
||||
face.c = remap[face.c];
|
||||
}
|
||||
}
|
||||
inline unsigned int atouFast(const char* value, const char** end)
|
||||
{
|
||||
const char* s = value;
|
||||
|
||||
// skip whitespace
|
||||
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
|
||||
s++;
|
||||
|
||||
// read integer part
|
||||
unsigned int result = 0;
|
||||
|
||||
while (static_cast<unsigned int>(*s - '0') < 10)
|
||||
{
|
||||
result = result * 10 + (*s - '0');
|
||||
s++;
|
||||
}
|
||||
|
||||
// done!
|
||||
*end = s;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double atofFast(const char* value, const char** end)
|
||||
{
|
||||
static const double digits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
static const double powers[] = {1e0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17,
|
||||
1e+18, 1e+19, 1e+20, 1e+21, 1e+22};
|
||||
|
||||
const char* s = value;
|
||||
|
||||
// skip whitespace
|
||||
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
|
||||
s++;
|
||||
|
||||
// read sign
|
||||
double sign = (*s == '-') ? -1 : 1;
|
||||
s += (*s == '-' || *s == '+');
|
||||
|
||||
// read integer part
|
||||
double result = 0;
|
||||
int power = 0;
|
||||
|
||||
while (static_cast<unsigned int>(*s - '0') < 10)
|
||||
{
|
||||
result = result * 10 + digits[*s - '0'];
|
||||
s++;
|
||||
}
|
||||
|
||||
// read fractional part
|
||||
if (*s == '.')
|
||||
{
|
||||
s++;
|
||||
|
||||
while (static_cast<unsigned int>(*s - '0') < 10)
|
||||
{
|
||||
result = result * 10 + digits[*s - '0'];
|
||||
s++;
|
||||
power--;
|
||||
}
|
||||
}
|
||||
|
||||
// read exponent part
|
||||
if ((*s | ' ') == 'e')
|
||||
{
|
||||
s++;
|
||||
|
||||
// read exponent sign
|
||||
int expsign = (*s == '-') ? -1 : 1;
|
||||
s += (*s == '-' || *s == '+');
|
||||
|
||||
// read exponent
|
||||
int exppower = 0;
|
||||
|
||||
while (static_cast<unsigned int>(*s - '0') < 10)
|
||||
{
|
||||
exppower = exppower * 10 + (*s - '0');
|
||||
s++;
|
||||
}
|
||||
|
||||
// done!
|
||||
power += expsign * exppower;
|
||||
}
|
||||
|
||||
// done!
|
||||
*end = s;
|
||||
|
||||
if (static_cast<unsigned int>(-power) < sizeof(powers) / sizeof(powers[0]))
|
||||
return sign * result / powers[-power];
|
||||
else if (static_cast<unsigned int>(power) < sizeof(powers) / sizeof(powers[0]))
|
||||
return sign * result * powers[power];
|
||||
else
|
||||
return sign * result * powf(10.0, power);
|
||||
}
|
||||
|
||||
inline const char* readToken(const char* data, char terminator)
|
||||
{
|
||||
while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n')
|
||||
++data;
|
||||
|
||||
if (*data != terminator)
|
||||
throw Aya::runtime_error("Error reading mesh data: expected %c", terminator);
|
||||
|
||||
return data + 1;
|
||||
}
|
||||
|
||||
inline const char* readFloatToken(const char* data, char terminator, float* output)
|
||||
{
|
||||
const char* end;
|
||||
double value = atofFast(data, &end);
|
||||
|
||||
if (*end != terminator)
|
||||
throw Aya::runtime_error("Error reading mesh data: expected %c", terminator);
|
||||
|
||||
*output = value;
|
||||
|
||||
return end + 1;
|
||||
}
|
||||
|
||||
shared_ptr<FileMeshData> readMeshFromV1(const std::string& data, size_t offset_, float scaler)
|
||||
{
|
||||
shared_ptr<FileMeshData> mesh(new FileMeshData());
|
||||
|
||||
const char* offset = data.c_str() + offset_;
|
||||
unsigned int num_faces = atouFast(offset, &offset);
|
||||
|
||||
mesh->vnts.reserve(num_faces * 3);
|
||||
mesh->faces.reserve(num_faces);
|
||||
|
||||
for (unsigned int i = 0; i < num_faces; i++)
|
||||
{
|
||||
for (int v = 0; v < 3; v++)
|
||||
{
|
||||
float vx, vy, vz, nx, ny, nz, tu, tv, tw;
|
||||
|
||||
offset = readToken(offset, '[');
|
||||
offset = readFloatToken(offset, ',', &vx);
|
||||
offset = readFloatToken(offset, ',', &vy);
|
||||
offset = readFloatToken(offset, ']', &vz);
|
||||
offset = readToken(offset, '[');
|
||||
offset = readFloatToken(offset, ',', &nx);
|
||||
offset = readFloatToken(offset, ',', &ny);
|
||||
offset = readFloatToken(offset, ']', &nz);
|
||||
offset = readToken(offset, '[');
|
||||
offset = readFloatToken(offset, ',', &tu);
|
||||
offset = readFloatToken(offset, ',', &tv);
|
||||
offset = readFloatToken(offset, ']', &tw);
|
||||
|
||||
G3D::Vector3 normal = G3D::Vector3(nx, ny, nz).unit();
|
||||
|
||||
if (!normal.isFinite())
|
||||
normal = G3D::Vector3::zero();
|
||||
|
||||
FileMeshVertexNormalTexture3d vtx = {vx * scaler, vy * scaler, vz * scaler, normal.x, normal.y, normal.z, tu, 1.f - tv, tw};
|
||||
|
||||
mesh->vnts.push_back(vtx);
|
||||
}
|
||||
|
||||
FileMeshFace face = {i * 3 + 0, i * 3 + 1, i * 3 + 2};
|
||||
|
||||
mesh->faces.push_back(face);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void readData(const std::string& data, size_t& offset, void* buffer, size_t size)
|
||||
{
|
||||
if (offset + size > data.size())
|
||||
throw std::runtime_error("Error reading mesh data: offset is out of bounds while reading");
|
||||
|
||||
memcpy(buffer, data.data() + offset, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
static shared_ptr<FileMeshData> readMeshFromV2(const std::string& data, size_t offset)
|
||||
{
|
||||
shared_ptr<FileMeshData> mesh(new FileMeshData());
|
||||
|
||||
FileMeshHeader header;
|
||||
readData(data, offset, &header, sizeof(header));
|
||||
|
||||
if (header.cbSize != sizeof(FileMeshHeader) ||
|
||||
(header.cbVerticesStride != sizeof(FileMeshVertexNormalTexture3d) &&
|
||||
header.cbVerticesStride != sizeof(FileMeshVertexNormalTexture3d) + 4) || // Check for both possible vertex sizes
|
||||
header.cbFaceStride != sizeof(FileMeshFace))
|
||||
{
|
||||
throw std::runtime_error("Error reading mesh data: incompatible stride");
|
||||
}
|
||||
|
||||
if (header.num_vertices == 0 || header.num_faces == 0)
|
||||
throw std::runtime_error("Error reading mesh data: empty mesh");
|
||||
|
||||
mesh->vnts.resize(header.num_vertices);
|
||||
|
||||
size_t vertexSize = sizeof(FileMeshVertexNormalTexture3d);
|
||||
size_t skipSize = (header.cbVerticesStride == vertexSize + 4) ? 4 : 0; // Determine whether to skip RGBA
|
||||
|
||||
for (size_t i = 0; i < header.num_vertices; ++i)
|
||||
{
|
||||
readData(data, offset, &mesh->vnts[i], vertexSize);
|
||||
offset += skipSize; // Skip RGBA data if necessary
|
||||
}
|
||||
|
||||
mesh->faces.resize(header.num_faces);
|
||||
readData(data, offset, &mesh->faces[0], header.num_faces * header.cbFaceStride);
|
||||
|
||||
if (offset != data.size())
|
||||
throw std::runtime_error("Error reading mesh data: unexpected data at end of file");
|
||||
|
||||
// Validate indices to avoid buffer overruns later
|
||||
for (auto& face : mesh->faces)
|
||||
if (face.a >= header.num_vertices || face.b >= header.num_vertices || face.c >= header.num_vertices)
|
||||
throw std::runtime_error("Error reading mesh data: index value out of range");
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
||||
FileMeshData* computeAABB(FileMeshData* mesh)
|
||||
{
|
||||
if (mesh->vnts.empty())
|
||||
{
|
||||
mesh->aabb = AABox(Vector3::zero());
|
||||
}
|
||||
else
|
||||
{
|
||||
AABox result = AABox(Vector3(mesh->vnts[0].vx, mesh->vnts[0].vy, mesh->vnts[0].vz));
|
||||
|
||||
for (size_t i = 1; i < mesh->vnts.size(); ++i)
|
||||
result.merge(Vector3(mesh->vnts[i].vx, mesh->vnts[i].vy, mesh->vnts[i].vz));
|
||||
|
||||
mesh->aabb = result;
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static shared_ptr<FileMeshData> readGenericMesh(const std::string& data)
|
||||
{
|
||||
shared_ptr<FileMeshData> mesh(new FileMeshData());
|
||||
|
||||
Assimp::Importer importer;
|
||||
const aiScene* scene =
|
||||
importer.ReadFileFromMemory(data.data(), data.size(), aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_CalcTangentSpace);
|
||||
|
||||
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
|
||||
{
|
||||
throw std::runtime_error("Failed to load mesh: " + std::string(importer.GetErrorString()));
|
||||
}
|
||||
|
||||
// Assuming we're only interested in the first mesh
|
||||
if (scene->mNumMeshes > 0)
|
||||
{
|
||||
const aiMesh* aiMesh = scene->mMeshes[0];
|
||||
|
||||
// Read vertices, normals, and texture coordinates
|
||||
for (unsigned int i = 0; i < aiMesh->mNumVertices; ++i)
|
||||
{
|
||||
FileMeshVertexNormalTexture3d vnt;
|
||||
|
||||
vnt.vx = aiMesh->mVertices[i].x;
|
||||
vnt.vy = aiMesh->mVertices[i].y;
|
||||
vnt.vz = aiMesh->mVertices[i].z;
|
||||
|
||||
if (aiMesh->HasNormals())
|
||||
{
|
||||
vnt.nx = aiMesh->mNormals[i].x;
|
||||
vnt.ny = aiMesh->mNormals[i].y;
|
||||
vnt.nz = aiMesh->mNormals[i].z;
|
||||
}
|
||||
|
||||
if (aiMesh->HasTextureCoords(0))
|
||||
{
|
||||
vnt.tu = aiMesh->mTextureCoords[0][i].x;
|
||||
vnt.tv = aiMesh->mTextureCoords[0][i].y;
|
||||
vnt.tw = aiMesh->mTextureCoords[0][i].z;
|
||||
}
|
||||
|
||||
mesh->vnts.push_back(vnt);
|
||||
}
|
||||
|
||||
// Read faces
|
||||
for (unsigned int i = 0; i < aiMesh->mNumFaces; ++i)
|
||||
{
|
||||
const aiFace& face = aiMesh->mFaces[i];
|
||||
if (face.mNumIndices == 3)
|
||||
{
|
||||
FileMeshFace meshFace;
|
||||
meshFace.a = face.mIndices[0];
|
||||
meshFace.b = face.mIndices[1];
|
||||
meshFace.c = face.mIndices[2];
|
||||
mesh->faces.push_back(meshFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("No meshes found in the imported file");
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
} // namespace Aya
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
shared_ptr<FileMeshData> ReadFileMesh(const std::string& data)
|
||||
{
|
||||
shared_ptr<FileMeshData> result;
|
||||
|
||||
std::string::size_type versionEnd = data.find('\n');
|
||||
if (versionEnd == std::string::npos)
|
||||
throw std::runtime_error("Error reading mesh data: unknown version");
|
||||
|
||||
if (data.compare(0, 12, "version 1.00") == 0)
|
||||
result = readMeshFromV1(data, versionEnd + 1, 0.5f);
|
||||
else if (data.compare(0, 12, "version 1.01") == 0)
|
||||
result = readMeshFromV1(data, versionEnd + 1, 1.0f);
|
||||
else if (data.compare(0, 12, "version 2.00") == 0)
|
||||
result = readMeshFromV2(data, versionEnd + 1);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
result = readGenericMesh(data);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw std::runtime_error("Error reading mesh data: unknown version");
|
||||
}
|
||||
}
|
||||
|
||||
optimizeMesh(*result);
|
||||
|
||||
computeAABB(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
void WriteFileMesh(std::ostream& f, const FileMeshData& data)
|
||||
{
|
||||
f << "version 2.00" << std::endl;
|
||||
|
||||
FileMeshHeader header;
|
||||
header.num_faces = data.faces.size();
|
||||
header.num_vertices = data.vnts.size();
|
||||
header.cbFaceStride = (unsigned char)sizeof(data.faces[0]);
|
||||
header.cbVerticesStride = (unsigned char)sizeof(data.vnts[0]);
|
||||
header.cbSize = sizeof(header);
|
||||
f.write(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
|
||||
f.write(reinterpret_cast<const char*>(&data.vnts[0]), sizeof(data.vnts[0]) * data.vnts.size());
|
||||
f.write(reinterpret_cast<const char*>(&data.faces[0]), sizeof(data.faces[0]) * data.faces.size());
|
||||
}
|
||||
} // namespace Aya
|
||||
24
engine/gfx/src/Base/FileMeshData.hpp
Normal file
24
engine/gfx/src/Base/FileMeshData.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "MeshFileStructs.hpp"
|
||||
|
||||
#include "Utility/Object.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
struct FileMeshData
|
||||
{
|
||||
std::vector<FileMeshVertexNormalTexture3d> vnts;
|
||||
std::vector<FileMeshFace> faces;
|
||||
AABox aabb;
|
||||
};
|
||||
|
||||
shared_ptr<FileMeshData> ReadFileMesh(const std::string& data);
|
||||
|
||||
// writes the newest version always.
|
||||
// remember: set ostream to binary!
|
||||
void WriteFileMesh(std::ostream& f, const FileMeshData& mesh);
|
||||
} // namespace Aya
|
||||
931
engine/gfx/src/Base/FrameRateManager.cpp
Normal file
931
engine/gfx/src/Base/FrameRateManager.cpp
Normal file
@@ -0,0 +1,931 @@
|
||||
|
||||
#include "Base/FrameRateManager.hpp"
|
||||
#include "Base/RenderCaps.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "FastLog.hpp"
|
||||
#include "AyaFormat.hpp"
|
||||
#include "TaskScheduler.hpp"
|
||||
|
||||
#include "Utility/Math.hpp"
|
||||
#include "SystemUtil.hpp"
|
||||
#include "DataModel/GameBasicSettings.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
LOGGROUP(FRM)
|
||||
|
||||
#ifdef AYA_TEST_BUILD
|
||||
FASTFLAGVARIABLE(DebugSSAOForce, true)
|
||||
#else
|
||||
FASTFLAGVARIABLE(DebugSSAOForce, false)
|
||||
#endif
|
||||
FASTINTVARIABLE(FRMRecomputeDistanceFrameDelay, 100)
|
||||
FASTINTVARIABLE(RenderGBufferMinQLvl, 20) // 14 for later
|
||||
|
||||
FASTFLAGVARIABLE(DebugSSAOForceDisable, false) // overrides DebugSSAOForce
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Tweakable section
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const int AveragingFrames = 40;
|
||||
static const int VarianceFrames = 20;
|
||||
|
||||
static const int LockStepDelayDown = 100; // Number of frames to wait after going a quality level down
|
||||
static const int LockStepDelayUp = 150; // Number of frames to wait after going a quality level up
|
||||
static const double RenderFraction = 0.625;
|
||||
static const double VarianceLimit = 5;
|
||||
|
||||
static const double MultiCoreRenderBottleneckFraction = 0.8;
|
||||
static const double MultiCorePrepareFraction = 0.3;
|
||||
|
||||
static const int SwitchCounterMax = 10;
|
||||
|
||||
static const int SettleDelay = 20; // Number of milliseconds that we consider level stable
|
||||
|
||||
// Fast backoff filter:
|
||||
// If during LockStepDelayDown your average frame length (averaged by FastBackoffFPSAve) is more than MaxFrameLen...
|
||||
// ... consecutively for WatchingFrames frames
|
||||
// you're going to be backed off to previous level
|
||||
// ... with StepLevel increased by FastBackoffStepLevelIncrement
|
||||
|
||||
#if defined(AYA_PLATFORM_IOS) || defined(__ANDROID__)
|
||||
static const double FastBackoffMaxFrameLen = 40; // 25 FPS
|
||||
#else
|
||||
static const double FastBackoffMaxFrameLen = 60; // 16.6 FPS
|
||||
#endif
|
||||
|
||||
static const int FastBackoffFPSAve = 10; // frames
|
||||
static const int FastBackoffWatchingFrames = 5; // frames
|
||||
|
||||
static const int SqDistanceBump = 50;
|
||||
|
||||
struct THROTTLE_LOCKSTEP
|
||||
{
|
||||
double framerate;
|
||||
float distance;
|
||||
int blockCount;
|
||||
float shadingDistance;
|
||||
int textureAnisotropy;
|
||||
SSAOLevel ssao;
|
||||
float lightGridRadius;
|
||||
bool lightAllowNonFixed;
|
||||
unsigned lightChunkBudget;
|
||||
int throttlingFactor; // Matches physics throttling table in World.cpp: - 0/8, 1/8, 1/4, 1/3, 1/2, 2/3, 3/4, 7/8, 15/16
|
||||
double StepHill;
|
||||
double MaxStepHill;
|
||||
};
|
||||
|
||||
inline float sqrf(float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
static THROTTLE_LOCKSTEP kLockstepTable60FPS[] = {
|
||||
|
||||
// Quality levels:
|
||||
{std::numeric_limits<double>::max(), 100000.0f, 1000000, 300, 8, ssaoFull, 512, true, 4, 0, 10, 10},
|
||||
{std::numeric_limits<double>::max(), 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 8, 5, 10}, // Level 1
|
||||
{80 /* 12 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 7, 5, 10}, // Level 2
|
||||
{66 /* 15 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 6, 5, 10}, // Level 3
|
||||
{50 /* 20 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 5, 5, 10}, // Level 4
|
||||
{
|
||||
42 /* 25 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
false,
|
||||
4,
|
||||
4,
|
||||
5,
|
||||
10,
|
||||
}, // Level 5
|
||||
{
|
||||
40 /* 25 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
false,
|
||||
4,
|
||||
3,
|
||||
5,
|
||||
10,
|
||||
}, // Level 6
|
||||
{
|
||||
35 /* 28 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
false,
|
||||
4,
|
||||
2,
|
||||
5,
|
||||
10,
|
||||
}, // Level 7
|
||||
{
|
||||
35 /* 28 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
false,
|
||||
4,
|
||||
1,
|
||||
5,
|
||||
10,
|
||||
}, // Level 8
|
||||
{
|
||||
35 /* 28 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
false,
|
||||
4,
|
||||
0,
|
||||
5,
|
||||
10,
|
||||
}, // Level 9
|
||||
{
|
||||
35 /* 28 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
5,
|
||||
10,
|
||||
}, // Level 10
|
||||
{
|
||||
35 /* 28 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
5,
|
||||
10,
|
||||
}, // Level 11
|
||||
{
|
||||
33 /* 30 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
}, // Level 12
|
||||
{
|
||||
30 /* 33 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
}, // Level 13
|
||||
{
|
||||
27 /* 37 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
}, // Level 14
|
||||
{
|
||||
25 /* 40 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
}, // Level 15
|
||||
{
|
||||
23 /* 43 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
4,
|
||||
8,
|
||||
}, // Level 16
|
||||
{
|
||||
20 /* 50 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
}, // Level 17
|
||||
{
|
||||
19 /* 60 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
}, // Level 18
|
||||
{
|
||||
19 /* 60 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
}, // Level 19
|
||||
{
|
||||
19 /* 60 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
}, // Level 20
|
||||
{
|
||||
19 /* 60 FPS */,
|
||||
100000.0f,
|
||||
100000,
|
||||
300,
|
||||
8,
|
||||
ssaoFull,
|
||||
512,
|
||||
true,
|
||||
4,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
} // Level 21
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Less tweakable, but still
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FrameRateManager::FrameRateManager(void)
|
||||
: mSettings(0)
|
||||
, mRenderCaps(0)
|
||||
, mBlockCullingEnabled(true)
|
||||
, mStableFramesCounter(0)
|
||||
, mThrottlingOn(false)
|
||||
, mCurrentQualityLevel(0)
|
||||
, frameTimeAverage(AveragingFrames)
|
||||
, renderTimeAverage(AveragingFrames)
|
||||
, prepareTimeAverage(AveragingFrames)
|
||||
, frameTimeVarianceAverage(VarianceFrames)
|
||||
, fastBackoffAverage(FastBackoffFPSAve)
|
||||
, mQualityDelayDown(LockStepDelayDown)
|
||||
, mQualityDelayUp(LockStepDelayDown)
|
||||
, mWasQualityUp(false)
|
||||
, mSwitchCounter(1)
|
||||
, mIsStable(false)
|
||||
, mBlockCounter(0)
|
||||
, mLastBlockCounter(0)
|
||||
, mAdjustmentOn(true)
|
||||
, mBadBackoffFrameCounter(0)
|
||||
, mRecomputeDistanceDelay(FInt::FRMRecomputeDistanceFrameDelay)
|
||||
, mAggressivePerformance(false)
|
||||
{
|
||||
AYAASSERT(CRenderSettings::QualityLevelMax ==
|
||||
ARRAYSIZE(kLockstepTable60FPS)); // If that fails, you probably added another quality level without syncing it with RenderSettings
|
||||
|
||||
LockstepTable = kLockstepTable60FPS;
|
||||
|
||||
AYAASSERT(LockStepDelayDown <= LockStepDelayUp);
|
||||
|
||||
// We need to have enough frames for averaging before we can step down again
|
||||
AYAASSERT(AveragingFrames + VarianceFrames <= LockStepDelayDown);
|
||||
|
||||
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
|
||||
mQualityCount[i] = 0;
|
||||
|
||||
// Sensible defaults for culling
|
||||
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
|
||||
mSqRenderDistance = mSqDistance;
|
||||
}
|
||||
|
||||
void FrameRateManager::configureFrameRateManager(CRenderSettings::FrameRateManagerMode mode, bool hasCharacter)
|
||||
{
|
||||
if (hasCharacter)
|
||||
{
|
||||
SetBlockCullingEnabled(mode == CRenderSettings::FrameRateManagerOff ? false : true);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBlockCullingEnabled(mode == CRenderSettings::FrameRateManagerOn ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
void FrameRateManager::setAggressivePerformance(bool value)
|
||||
{
|
||||
mAggressivePerformance = value;
|
||||
}
|
||||
|
||||
CRenderSettings::AntialiasingMode FrameRateManager::getAntialiasingMode()
|
||||
{
|
||||
switch (mSettings->getAntialiasingMode())
|
||||
{
|
||||
case CRenderSettings::AntialiasingAuto:
|
||||
// return mRenderCaps->getBestAntialiasingMode();
|
||||
return CRenderSettings::AntialiasingOff;
|
||||
// other settings simply override.
|
||||
default:
|
||||
return mSettings->getAntialiasingMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FrameRateManager::updateMaxSettings()
|
||||
{
|
||||
mSSAOSupported = mRenderCaps->getSupportsGBuffer();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// End of tweakable section
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
FrameRateManager::~FrameRateManager(void)
|
||||
{
|
||||
SendQualityLevelStats();
|
||||
}
|
||||
|
||||
void FrameRateManager::SendQualityLevelStats() {}
|
||||
|
||||
float FrameRateManager::GetAvarageQuality()
|
||||
{
|
||||
// compute average quality and send it to GA. Trying to keep the precision
|
||||
float floatCounts[CRenderSettings::QualityLevelMax];
|
||||
float freqSum = 0;
|
||||
for (unsigned i = 1; i < CRenderSettings::QualityLevelMax; ++i) // we ignore quality lvl = 0
|
||||
{
|
||||
floatCounts[i] = mQualityCount[i];
|
||||
freqSum += mQualityCount[i];
|
||||
}
|
||||
|
||||
// if there is less then 100 samples, there is really nothing to report
|
||||
if (freqSum > 100)
|
||||
{
|
||||
float avgQuality = 0;
|
||||
float freqSumInv = 1.0f / freqSum;
|
||||
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
|
||||
avgQuality += i * floatCounts[i] * freqSumInv;
|
||||
|
||||
return avgQuality;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
float FrameRateManager::GetTargetFrameTime(int level) const
|
||||
{
|
||||
return mAggressivePerformance ? 19.f : LockstepTable[level].framerate;
|
||||
}
|
||||
|
||||
void FrameRateManager::AddBlockQuota(int blocksInCluster, float sqDistanceToCamera, bool isInSpatialHash)
|
||||
{
|
||||
if (!mIsGatheringDistance)
|
||||
return;
|
||||
|
||||
mBlockCounter += blocksInCluster;
|
||||
if (mBlockCounter >= mBlockTarget)
|
||||
{
|
||||
// if cluster is in spatial hash, call order is done in roughly increasing camera distance so we can assume that the value is a valid cut-off
|
||||
// if cluster is not in spatial hash, calls are not ordered; we process all such clusters first, so if we ran out of blocks already, we have
|
||||
// to resort to the minimal culling distance for the current level
|
||||
if (isInSpatialHash)
|
||||
mSqDistance = std::max(
|
||||
LockstepTable[mCurrentQualityLevel].distance * LockstepTable[mCurrentQualityLevel].distance, sqDistanceToCamera + SqDistanceBump);
|
||||
else
|
||||
mSqDistance = LockstepTable[mCurrentQualityLevel].distance * LockstepTable[mCurrentQualityLevel].distance;
|
||||
|
||||
mIsGatheringDistance = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameRateManager::SubmitCurrentFrame(double frameTime, double renderTime, double prepareTime, double bonusTime)
|
||||
{
|
||||
updateMaxSettings(); // do this in a safe place. doesn't like being changed mid-frame?
|
||||
|
||||
UpdateStats(frameTime, renderTime, prepareTime);
|
||||
|
||||
// Use the distance from last frame for render distance this frame
|
||||
// If we were not gathering distance last frame they're the same
|
||||
// If we *were* then this is the cutoff distance where we reached the necessary block count
|
||||
mSqRenderDistance = mSqDistance;
|
||||
|
||||
if (mSettings->getEnableFRM())
|
||||
{
|
||||
if (mRecomputeDistanceDelay > 0)
|
||||
mRecomputeDistanceDelay--;
|
||||
else
|
||||
{
|
||||
FASTLOG1(FLog::FRM, "Recomputing gathering distance on level %u", mCurrentQualityLevel);
|
||||
// Temporarily unlock the culling distance for one frame
|
||||
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
|
||||
mRecomputeDistanceDelay = FInt::FRMRecomputeDistanceFrameDelay;
|
||||
mIsGatheringDistance = true;
|
||||
}
|
||||
|
||||
if (!mThrottlingOn)
|
||||
{
|
||||
// Initialize quality level for playing
|
||||
mThrottlingOn = true;
|
||||
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
|
||||
if (qualityLevel == CRenderSettings::QualityAuto)
|
||||
{
|
||||
int autoQualityLevel = mSettings->getAutoQualityLevel();
|
||||
mCurrentQualityLevel = std::max(1, std::min(autoQualityLevel, (int)CRenderSettings::QualityLevelMax - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentQualityLevel = qualityLevel;
|
||||
}
|
||||
|
||||
FASTLOG2(FLog::FRM, "Starting FRM, Quality setting: %u, starting level: %u", qualityLevel, mCurrentQualityLevel);
|
||||
UpdateQualitySettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
|
||||
|
||||
bool bAdjusmentOn = false;
|
||||
if (qualityLevel == CRenderSettings::QualityAuto)
|
||||
{
|
||||
bAdjusmentOn = mAdjustmentOn;
|
||||
}
|
||||
else if (qualityLevel != mCurrentQualityLevel)
|
||||
{
|
||||
mCurrentQualityLevel = qualityLevel;
|
||||
UpdateQualitySettings();
|
||||
}
|
||||
AdjustQuality(frameTime, renderTime, bAdjusmentOn, bonusTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mThrottlingOn = false;
|
||||
mCurrentQualityLevel = mSettings->getEditQualityLevel();
|
||||
UpdateQualitySettings();
|
||||
}
|
||||
|
||||
mLastBlockCounter = mBlockCounter;
|
||||
if (mIsGatheringDistance)
|
||||
mBlockCounter = 0;
|
||||
}
|
||||
|
||||
void FrameRateManager::StartCapturingMetrics()
|
||||
{
|
||||
memset(&mMetrics, 0, sizeof(mMetrics));
|
||||
mIsStable = false;
|
||||
mSettleTimer.reset();
|
||||
}
|
||||
|
||||
void FrameRateManager::UpdateStats(double frameTime, double renderTime, double prepareTime)
|
||||
{
|
||||
if (fabs(frameTime) < 0.001 || fabs(renderTime) < 0.001)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
frameTimeAverage.sample(frameTime);
|
||||
renderTimeAverage.sample(renderTime);
|
||||
prepareTimeAverage.sample(prepareTime);
|
||||
fastBackoffAverage.sample(frameTime);
|
||||
|
||||
mFPSCounter.Update(frameTime);
|
||||
|
||||
if (mCurrentQualityLevel > 0)
|
||||
{
|
||||
// prevent overflow (really unlike, but still)
|
||||
if (mQualityCount[mCurrentQualityLevel] == UINT_MAX - 1)
|
||||
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
|
||||
mQualityCount[i] /= 2;
|
||||
|
||||
++mQualityCount[mCurrentQualityLevel];
|
||||
}
|
||||
}
|
||||
|
||||
float FrameRateManager::GetTargetFrameTimeForNextLevel() const
|
||||
{
|
||||
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
|
||||
|
||||
return GetTargetFrameTime(mCurrentQualityLevel + 1);
|
||||
}
|
||||
|
||||
float FrameRateManager::GetTargetRenderTimeForNextLevel() const
|
||||
{
|
||||
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
|
||||
|
||||
return GetTargetFrameTime(mCurrentQualityLevel + 1) * MultiCoreRenderBottleneckFraction - LockstepTable[mCurrentQualityLevel + 1].StepHill;
|
||||
}
|
||||
|
||||
|
||||
void FrameRateManager::AdjustQuality(double frameTime, double renderTime, bool adjustmentOn, double bonusTime)
|
||||
{
|
||||
if (fabs(frameTime) < 0.001 || fabs(renderTime) < 0.001 || !adjustmentOn)
|
||||
return;
|
||||
|
||||
// Don't adjust until we have delayed enough
|
||||
if (mQualityDelayDown > 0)
|
||||
mQualityDelayDown--;
|
||||
|
||||
if (mQualityDelayUp > 0)
|
||||
mQualityDelayUp--;
|
||||
|
||||
Aya::WindowAverage<double, double>::Stats frameStats = frameTimeAverage.getStats();
|
||||
Aya::WindowAverage<double, double>::Stats renderStats = renderTimeAverage.getStats();
|
||||
Aya::WindowAverage<double, double>::Stats prepareStats = prepareTimeAverage.getStats();
|
||||
|
||||
frameTimeVarianceAverage.sample(frameStats.average);
|
||||
|
||||
frameStats.average -= bonusTime;
|
||||
renderStats.average -= bonusTime;
|
||||
prepareStats.average -= bonusTime;
|
||||
|
||||
|
||||
FASTLOG3F(FLog::FRM, "FRM status. Frame time average: %f, Delay up %f, Delay down %f", frameStats.average, (float)mQualityDelayUp,
|
||||
(float)mQualityDelayDown);
|
||||
|
||||
Aya::WindowAverage<double, double>::Stats fastBackoffStats = fastBackoffAverage.getStats();
|
||||
fastBackoffStats.average -= bonusTime;
|
||||
|
||||
if (fastBackoffStats.average > FastBackoffMaxFrameLen && FastBackoffMaxFrameLen > GetTargetFrameTime(mCurrentQualityLevel - 1))
|
||||
mBadBackoffFrameCounter++;
|
||||
else
|
||||
mBadBackoffFrameCounter = 0;
|
||||
|
||||
if (mBadBackoffFrameCounter >= FastBackoffWatchingFrames && mCurrentQualityLevel > 1)
|
||||
{
|
||||
FASTLOG(FLog::FRM, "FastBackoff, reducing quality");
|
||||
StepQuality(false, true);
|
||||
}
|
||||
|
||||
if (mQualityDelayDown > 0 && mQualityDelayUp > 0)
|
||||
return;
|
||||
|
||||
Aya::WindowAverage<double, double>::Stats frameAverageStats = frameTimeVarianceAverage.getStats();
|
||||
|
||||
if (frameAverageStats.variance > VarianceLimit)
|
||||
return;
|
||||
|
||||
bool bRenderLimited = renderStats.average > frameStats.average * RenderFraction;
|
||||
if (Aya::TaskScheduler::singleton().getThreadCount() > 1)
|
||||
bRenderLimited = renderStats.average > frameStats.average * MultiCoreRenderBottleneckFraction;
|
||||
|
||||
// Check for going down:
|
||||
if ((mQualityDelayDown == 0) && (mCurrentQualityLevel > 1) && (frameStats.average > GetTargetFrameTime(mCurrentQualityLevel)) && bRenderLimited)
|
||||
StepQuality(false, false);
|
||||
|
||||
// Check for going up:
|
||||
else if ((mQualityDelayUp == 0) && (mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1)) &&
|
||||
frameStats.average < GetTargetFrameTimeForNextLevel())
|
||||
{
|
||||
bool renderingHasRoom = renderStats.average < GetTargetRenderTimeForNextLevel();
|
||||
|
||||
if (Aya::TaskScheduler::singleton().getThreadCount() > 1)
|
||||
{
|
||||
renderingHasRoom = (renderStats.average < GetTargetRenderTimeForNextLevel()) &&
|
||||
prepareStats.average < GetTargetFrameTime(mCurrentQualityLevel + 1) * MultiCorePrepareFraction;
|
||||
}
|
||||
|
||||
if (renderingHasRoom)
|
||||
StepQuality(true, false);
|
||||
}
|
||||
|
||||
if (!mIsStable && mSettleTimer.delta().seconds() > SettleDelay)
|
||||
{
|
||||
mIsStable = true;
|
||||
|
||||
mMetrics.NumberOfSettles++;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameRateManager::StepQuality(bool stepUp, bool isBackOff)
|
||||
{
|
||||
int oldQualityLevel = mCurrentQualityLevel;
|
||||
|
||||
mCurrentQualityLevel += stepUp ? 1 : -1;
|
||||
FASTLOG2(FLog::FRM, "Stepping FRM quality, old: %u, new : %u", oldQualityLevel, mCurrentQualityLevel);
|
||||
|
||||
UpdateQualitySettings();
|
||||
frameTimeAverage.clear();
|
||||
renderTimeAverage.clear();
|
||||
|
||||
// Make delay for stepping down constant
|
||||
mQualityDelayDown = LockStepDelayDown;
|
||||
mBadBackoffFrameCounter = 0;
|
||||
|
||||
mQualityDelayUp = stepUp ? LockStepDelayUp : LockStepDelayUp * mSwitchCounter;
|
||||
|
||||
if (mCurrentQualityLevel > 1)
|
||||
{
|
||||
// If last step was down, make going up harder
|
||||
if (stepUp != mWasQualityUp && mSwitchCounter < SwitchCounterMax)
|
||||
{
|
||||
// If we're stepping down from higher level immediately, bump the step (within the allowed range, of course)
|
||||
if (!stepUp)
|
||||
{
|
||||
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
|
||||
int previousLevel = mCurrentQualityLevel + 1;
|
||||
|
||||
double StepHillAdd = isBackOff ? 0.1 : 1;
|
||||
LockstepTable[previousLevel].StepHill =
|
||||
std::min(LockstepTable[previousLevel].MaxStepHill, LockstepTable[previousLevel].StepHill + StepHillAdd);
|
||||
}
|
||||
|
||||
mSwitchCounter++;
|
||||
}
|
||||
else if (mSwitchCounter > 1)
|
||||
{
|
||||
mSwitchCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
mWasQualityUp = stepUp;
|
||||
|
||||
mSettings->setAutoQualityLevel(mCurrentQualityLevel);
|
||||
|
||||
mSettleTimer.reset();
|
||||
mIsStable = false;
|
||||
|
||||
// Accumulate number of switches here, average it on GetMetrics
|
||||
mMetrics.AverageSwitchesPerSettle++;
|
||||
}
|
||||
|
||||
FrameRateManager::Metrics FrameRateManager::GetMetrics()
|
||||
{
|
||||
Metrics result = mMetrics;
|
||||
|
||||
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
|
||||
result.AutoQuality = qualityLevel == CRenderSettings::QualityAuto;
|
||||
result.QualityLevel = Math::iRound(GetAvarageQuality());
|
||||
result.AverageFps = mFPSCounter.GetFPS();
|
||||
|
||||
if (result.NumberOfSettles != 0)
|
||||
result.AverageSwitchesPerSettle /= result.NumberOfSettles;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void FrameRateManager::UpdateQualitySettings()
|
||||
{
|
||||
const THROTTLE_LOCKSTEP& lockstep = LockstepTable[mCurrentQualityLevel];
|
||||
|
||||
if (mSettings->getFrameRateManagerMode() != CRenderSettings::FrameRateManagerOff)
|
||||
mBlockTarget = lockstep.blockCount;
|
||||
else
|
||||
mBlockTarget = LockstepTable[0].blockCount;
|
||||
|
||||
mIsGatheringDistance = true;
|
||||
|
||||
// Unlock the view distance but don't change rendering distance; we'll recompute it this frame
|
||||
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
|
||||
|
||||
mRecomputeDistanceDelay = FInt::FRMRecomputeDistanceFrameDelay;
|
||||
}
|
||||
|
||||
double FrameRateManager::getMetricValue(const std::string& metric)
|
||||
{
|
||||
if (metric == "FRM")
|
||||
return IsBlockCullingEnabled();
|
||||
else if (metric == "FRM Target")
|
||||
return GetVisibleBlockTarget();
|
||||
else if (metric == "FRM Visible")
|
||||
return GetVisibleBlockCounter();
|
||||
else if (metric == "FRM Distance")
|
||||
return sqrt(GetViewCullSqDistance());
|
||||
else if (metric == "FRM Quality")
|
||||
return GetQualityLevel();
|
||||
else if (metric == "FRM Auto Quality")
|
||||
return mSettings->getQualityLevel() == CRenderSettings::QualityAuto;
|
||||
else if (metric == "FRM Switch Counter")
|
||||
return mSwitchCounter;
|
||||
else if (metric == "FRM Step Hill")
|
||||
{
|
||||
// If Quality is not allowed to be adjusted, return -1
|
||||
if (mSettings->getQualityLevel() != CRenderSettings::QualityAuto)
|
||||
return -1;
|
||||
else
|
||||
return mCurrentQualityLevel + 1 < CRenderSettings::QualityLevelMax ? LockstepTable[mCurrentQualityLevel + 1].StepHill : 0;
|
||||
}
|
||||
else if (metric == "FRM Adjust Delay Up")
|
||||
return mQualityDelayUp;
|
||||
else if (metric == "FRM Adjust Delay Down")
|
||||
return mQualityDelayDown;
|
||||
else if (metric == "FRM Variance")
|
||||
return frameTimeVarianceAverage.getStats().variance;
|
||||
else if (metric == "FRM Backoff Counter")
|
||||
return mBadBackoffFrameCounter;
|
||||
else if (metric == "FRM Backoff Average")
|
||||
return fastBackoffAverage.getStats().average;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
double FrameRateManager::GetFrameTimeAverage()
|
||||
{
|
||||
return frameTimeAverage.getStats().average;
|
||||
}
|
||||
|
||||
double FrameRateManager::GetPrepareTimeAverage()
|
||||
{
|
||||
return prepareTimeAverage.getStats().average;
|
||||
}
|
||||
|
||||
double FrameRateManager::GetRenderTimeAverage()
|
||||
{
|
||||
return renderTimeAverage.getStats().average;
|
||||
}
|
||||
|
||||
const WindowAverage<double, double>& FrameRateManager::GetFrameTimeStats()
|
||||
{
|
||||
return frameTimeAverage;
|
||||
}
|
||||
|
||||
const WindowAverage<double, double>& FrameRateManager::GetRenderTimeStats()
|
||||
{
|
||||
return renderTimeAverage;
|
||||
}
|
||||
|
||||
float FrameRateManager::GetRenderCullSqDistance()
|
||||
{
|
||||
return mSqRenderDistance;
|
||||
}
|
||||
|
||||
float FrameRateManager::GetViewCullSqDistance()
|
||||
{
|
||||
return mSqDistance;
|
||||
}
|
||||
|
||||
float FrameRateManager::getShadingDistance() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].shadingDistance;
|
||||
}
|
||||
|
||||
int FrameRateManager::getPhysicsThrottling() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].throttlingFactor;
|
||||
}
|
||||
|
||||
float FrameRateManager::getShadingSqDistance() const
|
||||
{
|
||||
return sqrf(LockstepTable[mCurrentQualityLevel].shadingDistance);
|
||||
}
|
||||
|
||||
int FrameRateManager::getTextureAnisotropy() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].textureAnisotropy;
|
||||
}
|
||||
|
||||
float FrameRateManager::getLightGridRadius() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].lightGridRadius;
|
||||
}
|
||||
|
||||
bool FrameRateManager::getLightingNonFixedEnabled() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].lightAllowNonFixed;
|
||||
}
|
||||
|
||||
unsigned FrameRateManager::getLightingChunkBudget() const
|
||||
{
|
||||
return LockstepTable[mCurrentQualityLevel].lightChunkBudget;
|
||||
}
|
||||
|
||||
double FrameRateManager::GetMaxNextViewCullDistance()
|
||||
{
|
||||
return sqrt(mSqDistance) * 1.1;
|
||||
}
|
||||
|
||||
SSAOLevel FrameRateManager::getSSAOLevel()
|
||||
{
|
||||
if (FFlag::DebugSSAOForceDisable)
|
||||
return ssaoNone;
|
||||
|
||||
if (FFlag::DebugSSAOForce)
|
||||
return ssaoFull;
|
||||
|
||||
if (!mSSAOSupported)
|
||||
return ssaoNone;
|
||||
|
||||
return LockstepTable[mCurrentQualityLevel].ssao;
|
||||
}
|
||||
|
||||
void FrameRateManager::Configure(const RenderCaps* renderCaps, CRenderSettings* settings)
|
||||
{
|
||||
mSettings = settings;
|
||||
mRenderCaps = renderCaps;
|
||||
|
||||
updateMaxSettings();
|
||||
|
||||
UpdateQualitySettings();
|
||||
}
|
||||
|
||||
// returns overall particle throttle factor. Range ]0 .. 1] , 1 for full detail.
|
||||
double FrameRateManager::GetParticleThrottleFactor()
|
||||
{
|
||||
if (GetQualityLevel() == 0)
|
||||
return 1.0;
|
||||
|
||||
return std::max(0.0, std::min(1.0, (double)GetQualityLevel() / CRenderSettings::QualityLevelMax));
|
||||
}
|
||||
|
||||
bool FrameRateManager::getGBufferSetting()
|
||||
{
|
||||
return true;
|
||||
#if defined(AYA_PLATFORM_IOS) || defined(__ANDROID__)
|
||||
return false;
|
||||
#else
|
||||
return (isSSAOSupported() && GetQualityLevel() >= FInt::RenderGBufferMinQLvl);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FrameRateManager::PauseAutoAdjustment()
|
||||
{
|
||||
mAdjustmentOn = false;
|
||||
}
|
||||
|
||||
void FrameRateManager::ResumeAutoAdjustment()
|
||||
{
|
||||
mAdjustmentOn = true;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
249
engine/gfx/src/Base/FrameRateManager.hpp
Normal file
249
engine/gfx/src/Base/FrameRateManager.hpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // disable -D_SCL_SECURE_NO_WARNING in ublas.
|
||||
#include <boost/numeric/ublas/vector.hpp>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include "Base/RenderSettings.hpp"
|
||||
#include "RunningAverage.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class RenderCaps;
|
||||
class Log;
|
||||
|
||||
enum SSAOLevel
|
||||
{
|
||||
ssaoNone = 0,
|
||||
ssaoFullBlank,
|
||||
ssaoFull
|
||||
};
|
||||
|
||||
struct THROTTLE_LOCKSTEP;
|
||||
|
||||
class FrameRateManager
|
||||
{
|
||||
public:
|
||||
FrameRateManager(void);
|
||||
~FrameRateManager(void);
|
||||
|
||||
void configureFrameRateManager(CRenderSettings::FrameRateManagerMode mode, bool hasCharacter);
|
||||
void setAggressivePerformance(bool value);
|
||||
|
||||
struct Metrics
|
||||
{
|
||||
bool AutoQuality;
|
||||
int QualityLevel;
|
||||
int NumberOfSettles;
|
||||
double AverageSwitchesPerSettle;
|
||||
double AverageFps;
|
||||
};
|
||||
|
||||
// add to current frame counter.
|
||||
void AddBlockQuota(int blocksInCluster, float sqDistanceToCamera, bool isInSpatialHash);
|
||||
|
||||
bool getGBufferSetting();
|
||||
|
||||
SSAOLevel getSSAOLevel();
|
||||
bool isSSAOSupported()
|
||||
{
|
||||
return mSSAOSupported;
|
||||
}
|
||||
|
||||
float getShadingDistance() const;
|
||||
float getShadingSqDistance() const;
|
||||
int getTextureAnisotropy() const;
|
||||
|
||||
int getPhysicsThrottling() const;
|
||||
|
||||
float getLightGridRadius() const;
|
||||
bool getLightingNonFixedEnabled() const;
|
||||
unsigned getLightingChunkBudget() const;
|
||||
|
||||
void SubmitCurrentFrame(double frameTime, double renderTime, double prepareTime, double bonusTime);
|
||||
|
||||
// adjusts quality to try to fit rendering to this timespan.
|
||||
void ThrottleTo(double rendertime_ms);
|
||||
|
||||
double getMetricValue(const std::string& metric);
|
||||
|
||||
int GetRecomputeDistanceDelay()
|
||||
{
|
||||
return mRecomputeDistanceDelay;
|
||||
}
|
||||
|
||||
float GetViewCullSqDistance();
|
||||
float GetRenderCullSqDistance();
|
||||
|
||||
double GetMaxNextViewCullDistance(); // farthest cull distance possible in next frame.
|
||||
|
||||
int GetQualityLevel()
|
||||
{
|
||||
return mCurrentQualityLevel;
|
||||
}
|
||||
|
||||
bool IsBlockCullingEnabled()
|
||||
{
|
||||
return mBlockCullingEnabled;
|
||||
};
|
||||
void SetBlockCullingEnabled(bool enabled)
|
||||
{
|
||||
mBlockCullingEnabled = enabled;
|
||||
};
|
||||
|
||||
// supply the framerate manager with some special information that can be
|
||||
// used to formulate exceptions.
|
||||
void Configure(const RenderCaps* renderCaps, CRenderSettings* settings);
|
||||
|
||||
// after calling Configure, this gives our determination of the best
|
||||
// possible quality we can acheive with certain features, and with current settings
|
||||
CRenderSettings::AntialiasingMode getAntialiasingMode();
|
||||
void updateMaxSettings();
|
||||
|
||||
double GetVisibleBlockTarget() const
|
||||
{
|
||||
return mBlockTarget;
|
||||
}; // smoothed block target
|
||||
double GetVisibleBlockCounter() const
|
||||
{
|
||||
return mLastBlockCounter;
|
||||
};
|
||||
|
||||
float GetTargetFrameTimeForNextLevel() const;
|
||||
float GetTargetRenderTimeForNextLevel() const;
|
||||
|
||||
// counter that indicates how many frames have elapsed with the block count in a stable state.
|
||||
void ResetStableFramesCounter()
|
||||
{
|
||||
mStableFramesCounter = 0;
|
||||
};
|
||||
const int& GetStableFramesCounter()
|
||||
{
|
||||
return mStableFramesCounter;
|
||||
};
|
||||
|
||||
// returns overall particle throttle factor. Range ]0 .. 1] , 1 for full detail.
|
||||
double GetParticleThrottleFactor();
|
||||
|
||||
double GetRenderTimeAverage();
|
||||
double GetPrepareTimeAverage();
|
||||
double GetFrameTimeAverage();
|
||||
|
||||
const WindowAverage<double, double>& GetRenderTimeStats();
|
||||
const WindowAverage<double, double>& GetFrameTimeStats();
|
||||
|
||||
void StartCapturingMetrics();
|
||||
Metrics GetMetrics();
|
||||
|
||||
void PauseAutoAdjustment();
|
||||
void ResumeAutoAdjustment();
|
||||
|
||||
int GetQualityDelayUp() const
|
||||
{
|
||||
return mQualityDelayUp;
|
||||
}
|
||||
int GetQualityDelayDown() const
|
||||
{
|
||||
return mQualityDelayDown;
|
||||
}
|
||||
int GetBackoffCounter() const
|
||||
{
|
||||
return mBadBackoffFrameCounter;
|
||||
}
|
||||
double GetBackoffAverage() const
|
||||
{
|
||||
return fastBackoffAverage.getStats().average;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mSSAOSupported;
|
||||
|
||||
bool mAdjustmentOn;
|
||||
|
||||
CRenderSettings* mSettings;
|
||||
const RenderCaps* mRenderCaps;
|
||||
|
||||
bool mBlockCullingEnabled;
|
||||
bool mAggressivePerformance;
|
||||
|
||||
int mStableFramesCounter;
|
||||
|
||||
bool mThrottlingOn;
|
||||
|
||||
int mCurrentQualityLevel;
|
||||
unsigned mQualityCount[CRenderSettings::QualityLevelMax];
|
||||
|
||||
int mQualityDelayUp;
|
||||
int mQualityDelayDown;
|
||||
int mRecomputeDistanceDelay;
|
||||
|
||||
bool mWasQualityUp;
|
||||
int mSwitchCounter;
|
||||
|
||||
private:
|
||||
float mSqDistance;
|
||||
float mSqRenderDistance;
|
||||
|
||||
void UpdateStats(double frameTime, double renderTime, double prepareTime);
|
||||
void AdjustQuality(double frameTime, double renderTime, bool adjustmentOn, double bonusTime);
|
||||
void StepQuality(bool direction, bool isBackOff);
|
||||
void UpdateQualitySettings();
|
||||
void SendQualityLevelStats();
|
||||
float GetAvarageQuality();
|
||||
|
||||
float GetTargetFrameTime(int level) const;
|
||||
|
||||
Aya::WindowAverage<double, double> frameTimeAverage;
|
||||
Aya::WindowAverage<double, double> renderTimeAverage;
|
||||
Aya::WindowAverage<double, double> prepareTimeAverage;
|
||||
Aya::WindowAverage<double, double> frameTimeVarianceAverage;
|
||||
|
||||
Aya::WindowAverage<double, double> fastBackoffAverage;
|
||||
|
||||
int mBadBackoffFrameCounter;
|
||||
|
||||
Metrics mMetrics;
|
||||
Aya::Timer<Aya::Time::Fast> mSettleTimer;
|
||||
bool mIsStable;
|
||||
bool mIsGatheringDistance;
|
||||
int mBlockCounter;
|
||||
int mBlockTarget;
|
||||
int mLastBlockCounter;
|
||||
|
||||
class AvgFpsCounter
|
||||
{
|
||||
public:
|
||||
AvgFpsCounter()
|
||||
: timeSumSec(0)
|
||||
, frameCnt(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Update(double deltaTimeMs)
|
||||
{
|
||||
if (deltaTimeMs < 1000)
|
||||
{
|
||||
timeSumSec += deltaTimeMs * 0.001;
|
||||
++frameCnt;
|
||||
}
|
||||
}
|
||||
|
||||
double GetFPS()
|
||||
{
|
||||
return frameCnt ? 1.0 / (timeSumSec / frameCnt) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
double timeSumSec;
|
||||
unsigned frameCnt;
|
||||
};
|
||||
|
||||
AvgFpsCounter mFPSCounter;
|
||||
|
||||
THROTTLE_LOCKSTEP* LockstepTable;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
332
engine/gfx/src/Base/GfxPart.cpp
Normal file
332
engine/gfx/src/Base/GfxPart.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
#include "Base/GfxPart.hpp"
|
||||
|
||||
#include "DataModel/DataModelMesh.hpp"
|
||||
#include "DataModel/Decal.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/PrismInstance.hpp"
|
||||
#include "DataModel/PyramidInstance.hpp"
|
||||
#include "DataModel/BasicPartInstance.hpp"
|
||||
#include "DataModel/MeshPartInstance.hpp"
|
||||
#include "DataModel/ExtrudedPartInstance.hpp"
|
||||
#include "DataModel/PrismInstance.hpp"
|
||||
#include "DataModel/PyramidInstance.hpp"
|
||||
#include "DataModel/FileMesh.hpp"
|
||||
#include "DataModel/SpecialMesh.hpp"
|
||||
#include "DataModel/BlockMesh.hpp"
|
||||
#include "DataModel/CylinderMesh.hpp"
|
||||
#include "DataModel/PartOperation.hpp"
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
|
||||
#include "World/Primitive.hpp"
|
||||
#include "DataModel/PartCookie.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void updateCookie(Aya::PartInstance* part)
|
||||
{
|
||||
if (part)
|
||||
part->setCookie(Aya::PartCookie::compute(part));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
GfxBinding::~GfxBinding()
|
||||
{
|
||||
AYAASSERT(!isBound());
|
||||
}
|
||||
|
||||
// connects property change event listeners.
|
||||
void GfxBinding::bindProperties(const shared_ptr<Aya::PartInstance>& part)
|
||||
{
|
||||
updateCookie(part.get());
|
||||
connections.push_back(part->combinedSignal.connect(boost::bind(&GfxPart::onCombinedSignal, this, _1, _2)));
|
||||
part->visitChildren(boost::bind(&GfxPart::onChildAdded, this, _1));
|
||||
}
|
||||
|
||||
|
||||
void GfxBinding::zombify()
|
||||
{
|
||||
unbind();
|
||||
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void GfxBinding::unbind()
|
||||
{
|
||||
if (partInstance)
|
||||
{
|
||||
partInstance->setGfxPart(NULL);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < connections.size(); ++i)
|
||||
{
|
||||
connections[i].disconnect();
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
|
||||
bool GfxBinding::isBound()
|
||||
{
|
||||
return !connections.empty();
|
||||
}
|
||||
|
||||
void GfxBinding::onChildAdded(const shared_ptr<Instance>& child)
|
||||
{
|
||||
if (DataModelMesh* specShape = Instance::fastDynamicCast<DataModelMesh>(child.get()))
|
||||
{
|
||||
connections.push_back(specShape->propertyChangedSignal.connect(boost::bind(&GfxPart::onSpecialShapeChangedEx, this)));
|
||||
onSpecialShapeChangedEx();
|
||||
}
|
||||
else if (DecalTexture* tex = Instance::fastDynamicCast<DecalTexture>(child.get()))
|
||||
{
|
||||
connections.push_back(tex->propertyChangedSignal.connect(boost::bind(&GfxPart::onTexturePropertyChanged, this, _1)));
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (Decal* decal = Instance::fastDynamicCast<Decal>(child.get()))
|
||||
{
|
||||
connections.push_back(decal->propertyChangedSignal.connect(boost::bind(&GfxPart::onDecalPropertyChanged, this, _1)));
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxBinding::onChildRemoved(const shared_ptr<Instance>& child)
|
||||
{
|
||||
if (Instance::fastDynamicCast<DataModelMesh>(child.get()))
|
||||
{
|
||||
// todo: need a disconnect for propertyChangedEvents...
|
||||
onSpecialShapeChangedEx();
|
||||
}
|
||||
else if (Instance::fastDynamicCast<DecalTexture>(child.get()))
|
||||
{
|
||||
// todo: need a disconnect for propertyChangedEvents...
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (Instance::fastDynamicCast<Decal>(child.get()))
|
||||
{
|
||||
// todo: need a disconnect for propertyChangedEvents...
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
}
|
||||
|
||||
bool GfxBinding::isInWorkspace(Aya::Instance* part)
|
||||
{
|
||||
Instance* ws = Workspace::findWorkspace(part);
|
||||
return ws && part->isDescendantOf(ws);
|
||||
}
|
||||
|
||||
void GfxBinding::onAncestorChanged(const shared_ptr<Instance>& ancestor)
|
||||
{
|
||||
// Remove me from the scene if I am being removed from the Workspace
|
||||
if (partInstance && !isInWorkspace(partInstance.get()))
|
||||
{
|
||||
// will cause a delete on next updateEntity()
|
||||
zombify();
|
||||
}
|
||||
else
|
||||
{
|
||||
// part was removed from its ancestor (potentially deleted, or moved to a different cluster)
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GfxBinding::onSpecialShapeChangedEx()
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
onSpecialShapeChanged();
|
||||
}
|
||||
|
||||
|
||||
void GfxBinding::onPropertyChanged(const Reflection::PropertyDescriptor* descriptor)
|
||||
{
|
||||
if (*descriptor == PartInstance::prop_CFrame)
|
||||
{
|
||||
onCoordinateFrameChanged();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_Anchored)
|
||||
{
|
||||
// partInstance->getGfxPart()->onClumpChanged();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_Size)
|
||||
{
|
||||
onSizeChanged();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_Transparency || *descriptor == PartInstance::prop_LocalTransparencyModifier)
|
||||
{
|
||||
onTransparencyChanged();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_renderMaterial)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_Reflectance)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == BasicPartInstance::prop_shapeXml)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == ExtrudedPartInstance::prop_styleXml)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PrismInstance::prop_sidesXML)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
// else if (*descriptor==PrismInstance::prop_slices) {
|
||||
// invalidateEntity();
|
||||
// }
|
||||
else if (*descriptor == PyramidInstance::prop_sidesXML)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
// else if (*descriptor==PyramidInstance::prop_slices) {
|
||||
// invalidateEntity();
|
||||
// }
|
||||
else if (Surface::isSurfaceDescriptor(*descriptor))
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PartInstance::prop_Color)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PartOperation::desc_MeshData)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PartOperation::desc_UsePartColor)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == PartOperation::desc_FormFactor)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == MeshPartInstance::prop_meshPartId)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == MeshPartInstance::prop_meshPartTextureId)
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxBinding::onTexturePropertyChanged(const Reflection::PropertyDescriptor* descriptor)
|
||||
{
|
||||
if (*descriptor == FaceInstance::prop_Face)
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == DecalTexture::prop_Texture)
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == DecalTexture::prop_Specular)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == DecalTexture::prop_Shiny)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == DecalTexture::prop_StudsPerTileU)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == DecalTexture::prop_StudsPerTileV)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == DecalTexture::prop_Transparency || *descriptor == Decal::prop_LocalTransparencyModifier)
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void GfxBinding::onDecalPropertyChanged(const Reflection::PropertyDescriptor* descriptor)
|
||||
{
|
||||
if (*descriptor == FaceInstance::prop_Face)
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == Aya::Decal::prop_Texture)
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
else if (*descriptor == Aya::Decal::prop_Specular)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == Aya::Decal::prop_Shiny)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == Aya::Decal::prop_Transparency || *descriptor == Decal::prop_LocalTransparencyModifier)
|
||||
invalidateEntity();
|
||||
else if (*descriptor == Aya::Decal::prop_Color3)
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void GfxBinding::onCombinedSignal(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Instance::OUTFIT_CHANGED:
|
||||
onOutfitChanged();
|
||||
break;
|
||||
case Instance::HUMANOID_CHANGED:
|
||||
onHumanoidChanged();
|
||||
break;
|
||||
case Instance::CHILD_ADDED:
|
||||
onChildAdded(boost::polymorphic_downcast<const Instance::ChildAddedSignalData*>(data)->child);
|
||||
break;
|
||||
case Instance::CHILD_REMOVED:
|
||||
onChildRemoved(boost::polymorphic_downcast<const Instance::ChildRemovedSignalData*>(data)->child);
|
||||
break;
|
||||
case Instance::ANCESTRY_CHANGED:
|
||||
onAncestorChanged(boost::polymorphic_downcast<const Instance::AncestryChangedSignalData*>(data)->child);
|
||||
break;
|
||||
case Instance::PROPERTY_CHANGED:
|
||||
onPropertyChanged(boost::polymorphic_downcast<const Instance::PropertyChangedSignalData*>(data)->propertyDescriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GfxBinding::onOutfitChanged()
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void GfxBinding::onHumanoidChanged()
|
||||
{
|
||||
updateCookie(partInstance.get());
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void GfxBinding::cleanupStaleConnections()
|
||||
{
|
||||
for (size_t i = connections.size(); i != 0; --i)
|
||||
{
|
||||
if (!connections[i - 1].connected())
|
||||
{
|
||||
connections.erase(connections.begin() + (i - 1)); // remove dead connection.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*override*/ void GfxAttachment::unbind()
|
||||
{
|
||||
// nb: specifically ignore baseclass impl. we don't want to call setGfxPart.
|
||||
|
||||
for (size_t i = 0; i < connections.size(); ++i)
|
||||
{
|
||||
connections[i].disconnect();
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
147
engine/gfx/src/Base/GfxPart.hpp
Normal file
147
engine/gfx/src/Base/GfxPart.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "Utility/SpatialRegion.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "World/BasicSpatialHashPrimitive.hpp"
|
||||
#include "signal.hpp"
|
||||
#include "Reflection/Property.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartInstance;
|
||||
class AsyncResult;
|
||||
|
||||
class GfxBinding
|
||||
{
|
||||
protected:
|
||||
GfxBinding(const boost::shared_ptr<Aya::PartInstance>& part)
|
||||
: partInstance(part)
|
||||
{
|
||||
}
|
||||
|
||||
GfxBinding() {}
|
||||
|
||||
virtual ~GfxBinding();
|
||||
|
||||
public:
|
||||
Aya::PartInstance* getPartInstance()
|
||||
{
|
||||
return partInstance.get();
|
||||
};
|
||||
// unlinks from PartInstance.
|
||||
// will cause delete on next updateEntity();
|
||||
void zombify();
|
||||
|
||||
bool isBound();
|
||||
|
||||
// helper method. probably should be elsewhere.
|
||||
static bool isInWorkspace(Aya::Instance* part);
|
||||
|
||||
virtual void invalidateEntity() {};
|
||||
virtual void updateEntity(bool assetsUpdated = false) {};
|
||||
virtual void updateChunk(const SpatialRegion::Id& pos, bool isWaterChunk) {};
|
||||
virtual void onCoordinateFrameChanged() {};
|
||||
virtual void onSizeChanged()
|
||||
{
|
||||
invalidateEntity();
|
||||
};
|
||||
virtual void onTransparencyChanged()
|
||||
{
|
||||
invalidateEntity();
|
||||
};
|
||||
virtual void onSpecialShapeChanged()
|
||||
{
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
// disconnects all event listeners.
|
||||
virtual void unbind();
|
||||
void cleanupStaleConnections();
|
||||
|
||||
// meant to connect all listeners relevant to this instance.
|
||||
// basic implementation doesn't listen to much. overide and bind some more.
|
||||
// virtual void bind();
|
||||
|
||||
// helper: connects property change event listeners.
|
||||
void bindProperties(const shared_ptr<Aya::PartInstance>& part);
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<Aya::PartInstance> partInstance;
|
||||
std::vector<Aya::signals::connection> connections;
|
||||
|
||||
private:
|
||||
void onPropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
|
||||
void onAncestorChanged(const shared_ptr<Aya::Instance>& ancestor);
|
||||
void onChildAdded(const shared_ptr<Aya::Instance>& child);
|
||||
void onChildRemoved(const shared_ptr<Aya::Instance>& child);
|
||||
void onSpecialShapeChangedEx();
|
||||
|
||||
void onCombinedSignal(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data);
|
||||
void onHumanoidChanged();
|
||||
void onOutfitChanged();
|
||||
void onDecalPropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
|
||||
void onTexturePropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
|
||||
};
|
||||
|
||||
|
||||
// class used as a simple base class for linking PartInstances with graphics objects.
|
||||
class GfxPart
|
||||
: public GfxBinding
|
||||
, public Aya::BasicSpatialHashPrimitive
|
||||
{
|
||||
public:
|
||||
GfxPart(const boost::shared_ptr<Aya::PartInstance>& part)
|
||||
: GfxBinding(part)
|
||||
, lastFrustumVisibleFrameNumber(-1)
|
||||
{
|
||||
}
|
||||
|
||||
GfxPart()
|
||||
: lastFrustumVisibleFrameNumber(-1)
|
||||
{
|
||||
}
|
||||
|
||||
// accessors?
|
||||
int lastFrustumVisibleFrameNumber; // most recent frame where this object was within the view frustum
|
||||
|
||||
public:
|
||||
virtual void updateCoordinateFrame(bool recalcLocalBounds = false) {};
|
||||
virtual unsigned int getPartCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void onSleepingChanged(bool sleeping, PartInstance* part) {};
|
||||
virtual void onClumpChanged(PartInstance* part) {};
|
||||
|
||||
virtual Vector3 getCenter() const
|
||||
{
|
||||
return Vector3();
|
||||
}
|
||||
};
|
||||
|
||||
// serves to allow the gfx engine to have persistent gfxobject tracking the position of a part.
|
||||
class GfxAttachment : public GfxBinding
|
||||
{
|
||||
public:
|
||||
GfxAttachment(const boost::shared_ptr<Aya::PartInstance>& part)
|
||||
: GfxBinding(part)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GfxAttachment() {}
|
||||
|
||||
public:
|
||||
/*override*/ void unbind();
|
||||
|
||||
protected:
|
||||
virtual void onSleepingChanged(bool sleeping) = 0;
|
||||
|
||||
public:
|
||||
virtual void updateCoordinateFrame(bool recalcLocalBounds = false) = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
110
engine/gfx/src/Base/IAdornable.hpp
Normal file
110
engine/gfx/src/Base/IAdornable.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/IndexArray.hpp"
|
||||
#include "Utility/Selectable.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "SelectState.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class IAdornableCollector;
|
||||
class Adorn;
|
||||
class Camera;
|
||||
|
||||
class AyaInterface IAdornable : public Selectable
|
||||
{
|
||||
friend class IAdornableCollector;
|
||||
|
||||
private:
|
||||
int index2d;
|
||||
int index3d;
|
||||
int index3dSorted;
|
||||
int& indexFunc2d()
|
||||
{
|
||||
return index2d;
|
||||
}
|
||||
int& indexFunc3d()
|
||||
{
|
||||
return index3d;
|
||||
}
|
||||
int& indexFunc3dSorted()
|
||||
{
|
||||
return index3dSorted;
|
||||
}
|
||||
|
||||
IAdornableCollector* bucket;
|
||||
|
||||
protected:
|
||||
virtual bool shouldRender2d() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool shouldRender3dAdorn() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool shouldRender3dSortedAdorn() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
IAdornable()
|
||||
: bucket(NULL)
|
||||
, index2d(-1)
|
||||
, index3d(-1)
|
||||
, index3dSorted(-1)
|
||||
{
|
||||
}
|
||||
|
||||
~IAdornable();
|
||||
|
||||
void shouldRenderSetDirty(); // sets this IAdornable dirty
|
||||
float calculateDepth(const Camera* camera) const; // calculates the depth based on camera
|
||||
|
||||
virtual bool isVisible(const Rect2D& rect) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void renderBackground2d(Adorn* adorn) {}
|
||||
virtual void renderBackground2dContext(Adorn* adorn, const Instance* context)
|
||||
{
|
||||
renderBackground2d(adorn);
|
||||
}
|
||||
virtual void render2d(Adorn* adorn) {}
|
||||
virtual void render2dContext(Adorn* adorn, const Instance* context)
|
||||
{
|
||||
render2d(adorn);
|
||||
}
|
||||
virtual void render3dAdorn(Adorn* adorn) {}
|
||||
virtual void render3dSortedAdorn(Adorn* adorn) {}
|
||||
virtual void render3dSelect(Adorn* adorn, SelectState selectState) {}
|
||||
|
||||
virtual Vector3 render3dSortedPosition() const
|
||||
{
|
||||
return Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
virtual int getDisplayOrder() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct AdornableDepth
|
||||
{
|
||||
IAdornable* adornable;
|
||||
float depth;
|
||||
|
||||
bool operator<(const AdornableDepth& o) const
|
||||
{
|
||||
return depth > o.depth;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
210
engine/gfx/src/Base/IAdornableCollector.cpp
Normal file
210
engine/gfx/src/Base/IAdornableCollector.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
|
||||
|
||||
#include "Base/IAdornableCollector.hpp"
|
||||
#include "DataModel/Camera.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "FastLog.hpp"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
LOGGROUP(AdornableLifetime);
|
||||
DYNAMIC_FASTFLAGVARIABLE(DontReorderScreenGuisWhenDescendantRemoving, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
IAdornable::~IAdornable()
|
||||
{
|
||||
FASTLOG1(FLog::AdornableLifetime, "Destroying adornable %p", this);
|
||||
|
||||
if (bucket)
|
||||
bucket->onRenderableDescendantRemoving(this);
|
||||
}
|
||||
|
||||
void IAdornable::shouldRenderSetDirty()
|
||||
{
|
||||
if (bucket)
|
||||
{
|
||||
bucket->recomputeShouldRender(this);
|
||||
}
|
||||
}
|
||||
|
||||
float IAdornable::calculateDepth(const Camera* camera) const
|
||||
{
|
||||
return camera->dot(render3dSortedPosition());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
IAdornableCollector::~IAdornableCollector()
|
||||
{
|
||||
FASTLOG1(FLog::AdornableLifetime, "Adornable Collector %p deleted", this);
|
||||
FASTLOG3(
|
||||
FLog::AdornableLifetime, "Renderables 2D: %u 3D: %u 3DSorted: %u", renderable2ds.size(), renderable3ds.size(), renderable3dSorteds.size());
|
||||
|
||||
AYAASSERT(renderable2ds.size() == 0);
|
||||
AYAASSERT(renderable3ds.size() == 0);
|
||||
AYAASSERT(renderable3dSorteds.size() == 0);
|
||||
}
|
||||
|
||||
void IAdornableCollector::recomputeShouldRender(IAdornable* iR)
|
||||
{
|
||||
AYAASSERT(iR->bucket == this);
|
||||
|
||||
if (iR->shouldRender2d())
|
||||
{
|
||||
if (!renderable2ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 2D adorn %p", this, iR);
|
||||
renderable2ds.fastAppend(iR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (renderable2ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 2D adorn %p", this, iR);
|
||||
renderable2ds.fastRemove(iR);
|
||||
}
|
||||
}
|
||||
|
||||
if (iR->shouldRender3dAdorn())
|
||||
{
|
||||
if (!renderable3ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 3D adorn %p", this, iR);
|
||||
renderable3ds.fastAppend(iR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (renderable3ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3D adorn %p", this, iR);
|
||||
renderable3ds.fastRemove(iR);
|
||||
}
|
||||
}
|
||||
|
||||
if (iR->shouldRender3dSortedAdorn())
|
||||
{
|
||||
if (!renderable3dSorteds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 3DSort adorn %p", this, iR);
|
||||
renderable3dSorteds.fastAppend(iR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (renderable3dSorteds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3DSort adorn %p", this, iR);
|
||||
renderable3dSorteds.fastRemove(iR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IAdornableCollector::onRenderableDescendantAdded(IAdornable* iR)
|
||||
{
|
||||
AYAASSERT(iR->index2d == -1);
|
||||
AYAASSERT(iR->index3d == -1);
|
||||
AYAASSERT(iR->index3dSorted == -1);
|
||||
AYAASSERT(iR->bucket == NULL);
|
||||
|
||||
iR->bucket = this;
|
||||
|
||||
recomputeShouldRender(iR);
|
||||
}
|
||||
|
||||
|
||||
void IAdornableCollector::onRenderableDescendantRemoving(IAdornable* iR)
|
||||
{
|
||||
AYAASSERT(iR->bucket == this);
|
||||
|
||||
if (renderable2ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 2D adorn %p", this, iR);
|
||||
if (DFFlag::DontReorderScreenGuisWhenDescendantRemoving)
|
||||
{
|
||||
renderable2ds.remove(iR);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderable2ds.fastRemove(iR);
|
||||
}
|
||||
}
|
||||
|
||||
if (renderable3ds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3D adorn %p", this, iR);
|
||||
renderable3ds.fastRemove(iR);
|
||||
}
|
||||
|
||||
if (renderable3dSorteds.fastContains(iR))
|
||||
{
|
||||
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3DSort adorn %p", this, iR);
|
||||
renderable3dSorteds.fastRemove(iR);
|
||||
}
|
||||
|
||||
iR->bucket = NULL;
|
||||
|
||||
AYAASSERT(iR->index2d == -1);
|
||||
AYAASSERT(iR->index3d == -1);
|
||||
AYAASSERT(iR->index3dSorted == -1);
|
||||
}
|
||||
|
||||
|
||||
void IAdornableCollector::render2dItems(Adorn* adorn)
|
||||
{
|
||||
FASTLOG1(FLog::AdornRenderStats, "Rendering 2D Adorn Items, %u items", renderable2ds.size());
|
||||
|
||||
// Create a sorted list of renderables with their display order
|
||||
std::vector<std::pair<int, IAdornable*>> sortedRenderables;
|
||||
sortedRenderables.reserve(renderable2ds.size());
|
||||
|
||||
for (int i = 0; i < renderable2ds.size(); ++i)
|
||||
{
|
||||
IAdornable* adornable = renderable2ds[i];
|
||||
int displayOrder = adornable->getDisplayOrder();
|
||||
sortedRenderables.push_back(std::make_pair(displayOrder, adornable));
|
||||
}
|
||||
|
||||
// Sort by display order (lower values render first)
|
||||
std::sort(sortedRenderables.begin(), sortedRenderables.end(),
|
||||
[](const std::pair<int, IAdornable*>& a, const std::pair<int, IAdornable*>& b)
|
||||
{
|
||||
return a.first < b.first;
|
||||
});
|
||||
|
||||
// Render in sorted order
|
||||
for (size_t i = 0; i < sortedRenderables.size(); ++i)
|
||||
{
|
||||
sortedRenderables[i].second->render2d(adorn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IAdornableCollector::render3dAdornItems(Adorn* adorn)
|
||||
{
|
||||
FASTLOG1(FLog::AdornRenderStats, "Rendering 3D Adorn Items, %u items", renderable3ds.size());
|
||||
for (int i = 0; i < renderable3ds.size(); ++i)
|
||||
{
|
||||
renderable3ds[i]->render3dAdorn(adorn);
|
||||
}
|
||||
}
|
||||
|
||||
void IAdornableCollector::append3dSortedAdornItems(std::vector<AdornableDepth>& destination, const Camera* camera) const
|
||||
{
|
||||
FASTLOG1(FLog::AdornRenderStats, "Rendering 3DSort Adorn Items, %u items", renderable3dSorteds.size());
|
||||
for (int i = 0; i < renderable3dSorteds.size(); ++i)
|
||||
{
|
||||
AdornableDepth ad = {renderable3dSorteds[i], renderable3dSorteds[i]->calculateDepth(camera)};
|
||||
|
||||
destination.push_back(ad);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
39
engine/gfx/src/Base/IAdornableCollector.hpp
Normal file
39
engine/gfx/src/Base/IAdornableCollector.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Base/IAdornable.hpp"
|
||||
#include "Utility/IndexArray.hpp"
|
||||
|
||||
LOGGROUP(AdornRenderStats);
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class Adorn;
|
||||
|
||||
class AyaInterface IAdornableCollector
|
||||
{
|
||||
friend class IAdornable;
|
||||
|
||||
private:
|
||||
IndexArray<IAdornable, &IAdornable::indexFunc2d> renderable2ds;
|
||||
IndexArray<IAdornable, &IAdornable::indexFunc3d> renderable3ds;
|
||||
IndexArray<IAdornable, &IAdornable::indexFunc3dSorted> renderable3dSorteds;
|
||||
|
||||
public:
|
||||
void onRenderableDescendantAdded(IAdornable* iR);
|
||||
void onRenderableDescendantRemoving(IAdornable* iR);
|
||||
|
||||
void recomputeShouldRender(IAdornable* iR);
|
||||
|
||||
public:
|
||||
IAdornableCollector() {}
|
||||
|
||||
~IAdornableCollector();
|
||||
|
||||
void render2dItems(Adorn* adorn);
|
||||
void render3dAdornItems(Adorn* adorn);
|
||||
void append3dSortedAdornItems(std::vector<AdornableDepth>& destination, const Camera* camera) const;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
19
engine/gfx/src/Base/Image.hpp
Normal file
19
engine/gfx/src/Base/Image.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
virtual ~Image() {}
|
||||
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
virtual int getOriginalWidth() const = 0;
|
||||
virtual int getOriginalHeight() const = 0;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
36
engine/gfx/src/Base/MeshFileStructs.hpp
Normal file
36
engine/gfx/src/Base/MeshFileStructs.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// nb: keep backward/forward compatibility by only appending to these structs.
|
||||
// stride information will keep this working.
|
||||
struct FileMeshHeader
|
||||
{
|
||||
unsigned short cbSize;
|
||||
unsigned char cbVerticesStride;
|
||||
unsigned char cbFaceStride;
|
||||
// ---dword boundary-----
|
||||
unsigned int num_vertices;
|
||||
unsigned int num_faces;
|
||||
};
|
||||
|
||||
struct FileMeshVertexNormalTexture3d
|
||||
{
|
||||
float vx, vy, vz;
|
||||
float nx, ny, nz;
|
||||
float tu, tv, tw;
|
||||
};
|
||||
|
||||
struct FileMeshFace
|
||||
{
|
||||
unsigned int a;
|
||||
unsigned int b;
|
||||
unsigned int c;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace Aya
|
||||
20
engine/gfx/src/Base/MeshGen.hpp
Normal file
20
engine/gfx/src/Base/MeshGen.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class I3DLinearFunc
|
||||
{
|
||||
public:
|
||||
virtual Vector3 eval(float t) = 0;
|
||||
// first derivative.
|
||||
virtual Vector3 evalTangent(float t) = 0; // (tangent, normal, binormal, in that order, should form a right handed space)
|
||||
virtual Vector3 evalNormal(float t) = 0;
|
||||
virtual Vector3 evalBinormal(float t) = 0;
|
||||
|
||||
// string that encodes this function in a unique way.
|
||||
virtual std::string hashString() = 0;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
1140
engine/gfx/src/Base/ObjLoader.hpp
Normal file
1140
engine/gfx/src/Base/ObjLoader.hpp
Normal file
File diff suppressed because it is too large
Load Diff
66
engine/gfx/src/Base/Part.hpp
Normal file
66
engine/gfx/src/Base/Part.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/SurfaceType.hpp"
|
||||
#include "Utility/Vector6.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Color4.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
|
||||
// Simple description of a part suitable for drawing, etc. Build Instance on top of this.
|
||||
// Low level.
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
enum PartType
|
||||
{
|
||||
BALL_PART = 0,
|
||||
BLOCK_PART,
|
||||
CYLINDER_PART,
|
||||
TRUSS_PART,
|
||||
WEDGE_PART,
|
||||
PRISM_PART,
|
||||
PYRAMID_PART,
|
||||
PARALLELRAMP_PART,
|
||||
RIGHTANGLERAMP_PART,
|
||||
CORNERWEDGE_PART,
|
||||
MEGACLUSTER_PART,
|
||||
OPERATION_PART,
|
||||
MESH_PART
|
||||
};
|
||||
|
||||
class Part
|
||||
{
|
||||
public:
|
||||
// alpha order for simplification on dialogs
|
||||
|
||||
PartType type; // hash code hashes this block of data
|
||||
G3D::Vector3 gridSize;
|
||||
G3D::Color4 color;
|
||||
Vector6<SurfaceType> surfaceType;
|
||||
G3D::CoordinateFrame coordinateFrame;
|
||||
|
||||
Part() {}
|
||||
|
||||
Part(PartType _type, const G3D::Vector3& _gridSize, const G3D::Color4 _color, const G3D::CoordinateFrame& c)
|
||||
: type(_type)
|
||||
, gridSize(_gridSize)
|
||||
, color(_color)
|
||||
, surfaceType(NO_SURFACE)
|
||||
, coordinateFrame(c)
|
||||
{
|
||||
}
|
||||
|
||||
Part(PartType type, const G3D::Vector3& gridSize, const G3D::Color4 color, const Vector6<SurfaceType>& surfaceType, const G3D::CoordinateFrame& c)
|
||||
: type(type)
|
||||
, gridSize(gridSize)
|
||||
, color(color)
|
||||
, surfaceType(surfaceType)
|
||||
, coordinateFrame(c)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
328
engine/gfx/src/Base/PartIdentifier.cpp
Normal file
328
engine/gfx/src/Base/PartIdentifier.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
#include "Base/PartIdentifier.hpp"
|
||||
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
|
||||
#include "DataModel/DataModelMesh.hpp"
|
||||
#include "DataModel/FileMesh.hpp"
|
||||
#include "DataModel/Decal.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/CharacterAppearance.hpp"
|
||||
#include "DataModel/CharacterMesh.hpp"
|
||||
#include "DataModel/Accoutrement.hpp"
|
||||
#include "DataModel/MeshPartInstance.hpp"
|
||||
|
||||
#include "DataModel/PartCookie.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
static const Vector3 humanoidPartScalesR15[HumanoidIdentifier::PartType_Count] = {
|
||||
Vector3(2, 1.6, 1), // PartType_UpperTorso
|
||||
Vector3(2, 0.4, 1), // PartType_LowerTorso
|
||||
Vector3(1, 1.2, 1), // PartType_LowerArm
|
||||
Vector3(1, 1.4, 1), // PartType_UpperArm
|
||||
Vector3(1, 0.3, 1), // PartType_Hand
|
||||
Vector3(1, 1.5, 1), // PartType_UpperLeg
|
||||
Vector3(1, 1.5, 1), // PartType_LowerLeg
|
||||
Vector3(1, 0.3, 1) // PartType_Foot
|
||||
};
|
||||
|
||||
static const Vector3 humanoidPartScales[HumanoidIdentifier::PartType_Count] = {
|
||||
Vector3(1, 1, 1), // PartType_Head
|
||||
Vector3(2, 2, 1), // PartType_Torso
|
||||
Vector3(1, 2, 1), // PartType_Arm
|
||||
Vector3(1, 2, 1), // PartType_Leg
|
||||
Vector3(1, 1, 1) // PartType_Unknown
|
||||
};
|
||||
|
||||
// if the part is a humanoid, get further details with this.
|
||||
HumanoidIdentifier::HumanoidIdentifier(Aya::Humanoid* humanoid)
|
||||
: humanoid(humanoid)
|
||||
, head(0)
|
||||
, leftLeg(0)
|
||||
, rightLeg(0)
|
||||
, leftArm(0)
|
||||
, rightArm(0)
|
||||
, torso(0)
|
||||
, upperTorso(0)
|
||||
, lowerTorso(0)
|
||||
, rightUpperArm(0)
|
||||
, rightLowerArm(0)
|
||||
, rightHand(0)
|
||||
, leftUpperArm(0)
|
||||
, leftLowerArm(0)
|
||||
, leftHand(0)
|
||||
, rightUpperLeg(0)
|
||||
, rightLowerLeg(0)
|
||||
, rightFoot(0)
|
||||
, leftUpperLeg(0)
|
||||
, leftLowerLeg(0)
|
||||
, leftFoot(0)
|
||||
, leftLegMesh(0)
|
||||
, rightLegMesh(0)
|
||||
, leftArmMesh(0)
|
||||
, rightArmMesh(0)
|
||||
, torsoMesh(0)
|
||||
{
|
||||
if (!humanoid)
|
||||
return;
|
||||
|
||||
Instance* parent = humanoid->getParent();
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
const Instances& children = *parent->getChildren();
|
||||
|
||||
for (size_t i = 0; i < children.size(); ++i)
|
||||
{
|
||||
Instance* inst = children[i].get();
|
||||
|
||||
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(inst))
|
||||
{
|
||||
const std::string& name = part->getName();
|
||||
|
||||
if (name == "Head")
|
||||
head = part;
|
||||
else if (name == "Left Leg")
|
||||
leftLeg = part;
|
||||
else if (name == "Right Leg")
|
||||
rightLeg = part;
|
||||
else if (name == "Left Arm")
|
||||
leftArm = part;
|
||||
else if (name == "Right Arm")
|
||||
rightArm = part;
|
||||
else if (name == "Torso")
|
||||
torso = part;
|
||||
if (name == "UpperTorso")
|
||||
upperTorso = part;
|
||||
else if (name == "LowerTorso")
|
||||
lowerTorso = part;
|
||||
else if (name == "LeftUpperArm")
|
||||
leftUpperArm = part;
|
||||
else if (name == "LeftLowerArm")
|
||||
leftLowerArm = part;
|
||||
else if (name == "LeftHand")
|
||||
leftHand = part;
|
||||
else if (name == "RightUpperArm")
|
||||
rightUpperArm = part;
|
||||
else if (name == "RightLowerArm")
|
||||
rightLowerArm = part;
|
||||
else if (name == "RightHand")
|
||||
rightHand = part;
|
||||
else if (name == "LeftUpperLeg")
|
||||
leftUpperLeg = part;
|
||||
else if (name == "LeftLowerLeg")
|
||||
leftLowerLeg = part;
|
||||
else if (name == "LeftFoot")
|
||||
leftFoot = part;
|
||||
else if (name == "RightUpperLeg")
|
||||
rightUpperLeg = part;
|
||||
else if (name == "RightLowerLeg")
|
||||
rightLowerLeg = part;
|
||||
else if (name == "RightFoot")
|
||||
rightFoot = part;
|
||||
}
|
||||
else if (Clothing* c = Instance::fastDynamicCast<Clothing>(inst))
|
||||
{
|
||||
if (pants.isNull() && !c->outfit1.isNull())
|
||||
pants = c->outfit1;
|
||||
if (shirt.isNull() && !c->outfit2.isNull())
|
||||
shirt = c->outfit2;
|
||||
}
|
||||
else if (ShirtGraphic* s = Instance::fastDynamicCast<ShirtGraphic>(inst))
|
||||
{
|
||||
if (shirtGraphic.isNull() && !s->graphic.isNull())
|
||||
shirtGraphic = s->graphic;
|
||||
}
|
||||
else if (CharacterMesh* m = Instance::fastDynamicCast<CharacterMesh>(inst))
|
||||
{
|
||||
switch (m->getBodyPart())
|
||||
{
|
||||
case CharacterMesh::LEFTARM:
|
||||
leftArmMesh = m;
|
||||
break;
|
||||
case CharacterMesh::RIGHTARM:
|
||||
rightArmMesh = m;
|
||||
break;
|
||||
case CharacterMesh::LEFTLEG:
|
||||
leftLegMesh = m;
|
||||
break;
|
||||
case CharacterMesh::RIGHTLEG:
|
||||
rightLegMesh = m;
|
||||
break;
|
||||
case CharacterMesh::TORSO:
|
||||
torsoMesh = m;
|
||||
break;
|
||||
default:
|
||||
AYAASSERT(!"Unsupported body part type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Accoutrement* a = Instance::fastDynamicCast<Accoutrement>(inst))
|
||||
{
|
||||
accoutrements.push_back(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CharacterMesh* HumanoidIdentifier::getRelevantMesh(Aya::PartInstance* bodyPart) const
|
||||
{
|
||||
if (bodyPart == leftLeg || bodyPart == leftUpperLeg || bodyPart == leftLowerLeg || bodyPart == leftFoot)
|
||||
return leftLegMesh;
|
||||
if (bodyPart == rightLeg || bodyPart == rightUpperLeg || bodyPart == rightLowerLeg || bodyPart == rightFoot)
|
||||
return rightLegMesh;
|
||||
if (bodyPart == leftArm || bodyPart == leftUpperArm || bodyPart == leftLowerArm || bodyPart == leftHand)
|
||||
return leftArmMesh;
|
||||
if (bodyPart == rightArm || bodyPart == rightUpperArm || bodyPart == rightLowerArm || bodyPart == rightHand)
|
||||
return rightArmMesh;
|
||||
if (bodyPart == torso || bodyPart == upperTorso || bodyPart == lowerTorso)
|
||||
return torsoMesh;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HumanoidIdentifier::BodyPartType HumanoidIdentifier::getBodyPartType(Aya::PartInstance* bodyPart) const
|
||||
{
|
||||
if (humanoid->getUseR15())
|
||||
{
|
||||
if (bodyPart == leftUpperLeg || bodyPart == rightUpperLeg)
|
||||
return PartType_UpperLeg;
|
||||
if (bodyPart == leftUpperArm || bodyPart == rightUpperArm)
|
||||
return PartType_UpperArm;
|
||||
if (bodyPart == leftLowerLeg || bodyPart == rightLowerLeg)
|
||||
return PartType_LowerLeg;
|
||||
if (bodyPart == leftLowerArm || bodyPart == rightLowerArm)
|
||||
return PartType_LowerArm;
|
||||
if (bodyPart == leftHand || bodyPart == rightHand)
|
||||
return PartType_Hand;
|
||||
if (bodyPart == leftFoot || bodyPart == rightFoot)
|
||||
return PartType_Foot;
|
||||
if (bodyPart == upperTorso)
|
||||
return PartType_UpperTorso;
|
||||
if (bodyPart == lowerTorso)
|
||||
return PartType_LowerTorso;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bodyPart == leftLeg || bodyPart == rightLeg)
|
||||
return PartType_Leg;
|
||||
if (bodyPart == leftArm || bodyPart == rightArm)
|
||||
return PartType_Arm;
|
||||
if (bodyPart == torso)
|
||||
return PartType_Torso;
|
||||
if (bodyPart == head)
|
||||
return PartType_Head;
|
||||
}
|
||||
|
||||
return PartType_Unknown;
|
||||
}
|
||||
|
||||
Vector3 HumanoidIdentifier::getBodyPartScale(Aya::PartInstance* bodyPart) const
|
||||
{
|
||||
return humanoidPartScales[getBodyPartType(bodyPart)];
|
||||
}
|
||||
|
||||
bool HumanoidIdentifier::isPartHead(Aya::PartInstance* part) const
|
||||
{
|
||||
if (part != head)
|
||||
return false;
|
||||
|
||||
if (Aya::DataModelMesh* specialShape = Aya::getSpecialShape(part))
|
||||
{
|
||||
bool hasFace = (part->getCookie() & PartCookie::HAS_DECALS) != 0;
|
||||
|
||||
if (Aya::SpecialShape* shape = specialShape->fastDynamicCast<Aya::SpecialShape>())
|
||||
{
|
||||
// A real head or a file mesh - treat it as a head even if there is no face (might use a mesh texture)
|
||||
if (shape->getMeshType() == Aya::SpecialShape::HEAD_MESH || shape->getMeshType() == Aya::SpecialShape::FILE_MESH)
|
||||
return true;
|
||||
|
||||
// Probably one of the heads from the store - all character heads have faces
|
||||
if (shape->getMeshType() == Aya::SpecialShape::SPHERE_MESH)
|
||||
return hasFace;
|
||||
|
||||
// Unrecognized shape type - this is not a head from the store, so don't treat it as a head.
|
||||
return false;
|
||||
}
|
||||
else if (specialShape->fastDynamicCast<Aya::FileMesh>())
|
||||
{
|
||||
// A file mesh - treat it as a head even if there is no face (might use a mesh texture)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Probably one of the heads from the store - all character heads have faces
|
||||
return hasFace;
|
||||
}
|
||||
}
|
||||
|
||||
// No special shape - should be a simple part
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HumanoidIdentifier::isBodyPart(Aya::PartInstance* part) const
|
||||
{
|
||||
if (humanoid->getUseR15())
|
||||
return (leftUpperArm == part || leftUpperLeg == part || rightUpperArm == part || rightUpperLeg == part || upperTorso == part ||
|
||||
lowerTorso == part || rightLowerArm == part || leftLowerArm == part || leftHand == part || rightHand == part || leftFoot == part ||
|
||||
rightFoot == part || leftLowerLeg == part || rightLowerLeg == part || head == part);
|
||||
else if (part->getCookie() & PartCookie::IS_HUMANOID_PART)
|
||||
return (leftArm == part || leftLeg == part || rightArm == part || rightLeg == part || torso == part || head == part);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HumanoidIdentifier::isBodyPartComposited(Aya::PartInstance* part) const
|
||||
{
|
||||
bool noReflectance = part->getReflectance() <= 0.015f;
|
||||
|
||||
if (MeshPartInstance* meshPart = part->fastDynamicCast<MeshPartInstance>())
|
||||
return humanoid->getUseR15();
|
||||
|
||||
// Heads are always composited as long as they are not transparent
|
||||
if (part == head)
|
||||
return humanoid->getUseR15() ? false : (part->getTransparencyUi() <= 0 && isPartHead(part) && noReflectance);
|
||||
|
||||
// Body parts with special shapes are never composited to match the old behavior
|
||||
if (part->getCookie() & PartCookie::HAS_SPECIALSHAPE)
|
||||
return false;
|
||||
|
||||
// Non-block parts are never composited to match the old behavior
|
||||
if (part->getPartType() != BLOCK_PART)
|
||||
return false;
|
||||
|
||||
// Parts with meshes are always composited
|
||||
if (getRelevantMesh(part))
|
||||
return true;
|
||||
|
||||
// Arms are always composited if there is a shirt
|
||||
if ((part == leftArm || part == rightArm) && !shirt.isNull())
|
||||
return true;
|
||||
|
||||
// Legs are always composited if there are pants
|
||||
if ((part == leftLeg || part == rightLeg) && !pants.isNull())
|
||||
return true;
|
||||
|
||||
// Torso is always composited if there is a shirt, pants or a t-shirt
|
||||
if (part == torso && (!pants.isNull() || !shirt.isNull() || !shirtGraphic.isNull()))
|
||||
return true;
|
||||
|
||||
// Now we have a body part and we have a choice - we can composit it or skip compositing.
|
||||
// Compositing means that we lose materials; we also replace the body part with a prebaked mesh, so we lose the size information.
|
||||
// We also lose studs, but we care way less about those - so to improve batching, we'll composit plastic parts with expected size.
|
||||
return (part->getRenderMaterial() == PLASTIC_MATERIAL || part->getRenderMaterial() == SMOOTH_PLASTIC_MATERIAL) && noReflectance;
|
||||
}
|
||||
|
||||
bool HumanoidIdentifier::isPartComposited(Aya::PartInstance* part) const
|
||||
{
|
||||
if (isBodyPart(part))
|
||||
return isBodyPartComposited(part);
|
||||
|
||||
// We don't composit Accoutrements for R15
|
||||
// @mdolli
|
||||
if (Instance::isA<Accoutrement>(part->getParent()) && !this->humanoid->getUseR15())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace Aya
|
||||
100
engine/gfx/src/Base/PartIdentifier.hpp
Normal file
100
engine/gfx/src/Base/PartIdentifier.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utility/TextureId.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartInstance;
|
||||
class PartInstance;
|
||||
class Humanoid;
|
||||
class CharacterMesh;
|
||||
class Accoutrement;
|
||||
|
||||
// if the part is a humanoid, get further details with this.
|
||||
class HumanoidIdentifier
|
||||
{
|
||||
public:
|
||||
explicit HumanoidIdentifier(Aya::Humanoid* humanoid);
|
||||
|
||||
Humanoid* humanoid;
|
||||
|
||||
PartInstance* head;
|
||||
PartInstance* leftLeg;
|
||||
PartInstance* rightLeg;
|
||||
PartInstance* leftArm;
|
||||
PartInstance* rightArm;
|
||||
PartInstance* torso;
|
||||
|
||||
PartInstance* upperTorso;
|
||||
PartInstance* lowerTorso;
|
||||
PartInstance* leftUpperArm;
|
||||
PartInstance* leftLowerArm;
|
||||
PartInstance* leftHand;
|
||||
PartInstance* rightUpperArm;
|
||||
PartInstance* rightLowerArm;
|
||||
PartInstance* rightHand;
|
||||
PartInstance* leftUpperLeg;
|
||||
PartInstance* leftLowerLeg;
|
||||
PartInstance* leftFoot;
|
||||
PartInstance* rightUpperLeg;
|
||||
PartInstance* rightLowerLeg;
|
||||
PartInstance* rightFoot;
|
||||
|
||||
std::vector<Accoutrement*> accoutrements;
|
||||
|
||||
TextureId pants;
|
||||
TextureId shirt;
|
||||
TextureId shirtGraphic;
|
||||
|
||||
CharacterMesh* leftLegMesh;
|
||||
CharacterMesh* rightLegMesh;
|
||||
CharacterMesh* leftArmMesh;
|
||||
CharacterMesh* rightArmMesh;
|
||||
CharacterMesh* torsoMesh;
|
||||
|
||||
CharacterMesh* leftUpperLegMesh;
|
||||
CharacterMesh* leftLowerLegMesh;
|
||||
CharacterMesh* leftFootMesh;
|
||||
CharacterMesh* rightUpperLegMesh;
|
||||
CharacterMesh* rightLowerLegMesh;
|
||||
CharacterMesh* rightFootMesh;
|
||||
CharacterMesh* leftUpperArmMesh;
|
||||
CharacterMesh* leftLowerArmMesh;
|
||||
CharacterMesh* leftHandMesh;
|
||||
CharacterMesh* rightUpperArmMesh;
|
||||
CharacterMesh* rightLowerArmMesh;
|
||||
CharacterMesh* rightHandMesh;
|
||||
CharacterMesh* upperTorsoMesh;
|
||||
CharacterMesh* lowerTorsoMesh;
|
||||
|
||||
bool isBodyPart(Aya::PartInstance* part) const;
|
||||
bool isBodyPartComposited(Aya::PartInstance* part) const;
|
||||
bool isPartComposited(Aya::PartInstance* part) const;
|
||||
bool isPartHead(Aya::PartInstance* part) const;
|
||||
|
||||
// helper
|
||||
CharacterMesh* getRelevantMesh(Aya::PartInstance* bodyPart) const;
|
||||
|
||||
enum BodyPartType
|
||||
{
|
||||
PartType_Head,
|
||||
PartType_Torso,
|
||||
PartType_Arm,
|
||||
PartType_Leg,
|
||||
PartType_UpperTorso,
|
||||
PartType_LowerTorso,
|
||||
PartType_LowerArm,
|
||||
PartType_UpperArm,
|
||||
PartType_Hand,
|
||||
PartType_UpperLeg,
|
||||
PartType_LowerLeg,
|
||||
PartType_Foot,
|
||||
PartType_Unknown,
|
||||
PartType_Count
|
||||
};
|
||||
|
||||
BodyPartType getBodyPartType(Aya::PartInstance* bodyPart) const;
|
||||
Vector3 getBodyPartScale(Aya::PartInstance* bodyPart) const;
|
||||
};
|
||||
} // namespace Aya
|
||||
16
engine/gfx/src/Base/RenderCaps.cpp
Normal file
16
engine/gfx/src/Base/RenderCaps.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "Base/RenderCaps.hpp"
|
||||
#include "FastLog.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
RenderCaps::RenderCaps(std::string gfxCardName, size_t vidMemSize)
|
||||
: gfxCardName(gfxCardName)
|
||||
, vidMemSize(vidMemSize)
|
||||
, texturePowerOf2Only(false)
|
||||
, supportsGBuffer(false)
|
||||
, skinningBoneCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
58
engine/gfx/src/Base/RenderCaps.hpp
Normal file
58
engine/gfx/src/Base/RenderCaps.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "Base/RenderSettings.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class RenderCaps
|
||||
{
|
||||
size_t vidMemSize;
|
||||
std::string gfxCardName;
|
||||
bool texturePowerOf2Only;
|
||||
bool supportsGBuffer;
|
||||
|
||||
unsigned int skinningBoneCount;
|
||||
|
||||
public:
|
||||
RenderCaps(std::string gfxCardName, size_t vidMemSize);
|
||||
|
||||
void setTexturePowerOf2Only(bool b)
|
||||
{
|
||||
texturePowerOf2Only = b;
|
||||
}
|
||||
void setSupportsGBuffer(bool b)
|
||||
{
|
||||
supportsGBuffer = b;
|
||||
}
|
||||
void setSkinningBoneCount(unsigned int v)
|
||||
{
|
||||
skinningBoneCount = v;
|
||||
}
|
||||
|
||||
size_t getVidMemSize() const
|
||||
{
|
||||
return vidMemSize;
|
||||
}
|
||||
|
||||
bool getTexturePowerOf2Only() const
|
||||
{
|
||||
return texturePowerOf2Only;
|
||||
}
|
||||
const std::string& getGfxCardName() const
|
||||
{
|
||||
return gfxCardName;
|
||||
}
|
||||
|
||||
bool getSupportsGBuffer() const
|
||||
{
|
||||
return supportsGBuffer;
|
||||
}
|
||||
|
||||
unsigned int getSkinningBoneCount() const
|
||||
{
|
||||
return skinningBoneCount;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
63
engine/gfx/src/Base/RenderSettings.cpp
Normal file
63
engine/gfx/src/Base/RenderSettings.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "Base/RenderSettings.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const G3D::Vector2int16 CRenderSettings::minGameWindowSize = G3D::Vector2int16((G3D::int16)816, (G3D::int16)638);
|
||||
|
||||
CRenderSettings::GraphicsMode CRenderSettings::latchedGraphicsMode = CRenderSettings::UnknownGraphicsMode;
|
||||
|
||||
CRenderSettings::AASamples CRenderSettings::aaSamples(defaultAASamples);
|
||||
|
||||
const CRenderSettings::RESOLUTIONENTRY ResolutionTable[] = {{CRenderSettings::Resolution720x526, 720, 526},
|
||||
{CRenderSettings::Resolution800x600, 800, 600}, {CRenderSettings::Resolution1024x600, 1024, 600},
|
||||
{CRenderSettings::Resolution1024x768, 1024, 768}, {CRenderSettings::Resolution1280x720, 1280, 720},
|
||||
{CRenderSettings::Resolution1280x768, 1280, 768}, {CRenderSettings::Resolution1152x864, 1152, 864},
|
||||
{CRenderSettings::Resolution1280x800, 1280, 800}, {CRenderSettings::Resolution1360x768, 1360, 768},
|
||||
{CRenderSettings::Resolution1280x960, 1280, 960}, {CRenderSettings::Resolution1280x1024, 1280, 1024},
|
||||
{CRenderSettings::Resolution1440x900, 1440, 900}, {CRenderSettings::Resolution1600x900, 1600, 900},
|
||||
{CRenderSettings::Resolution1600x1024, 1600, 1024}, {CRenderSettings::Resolution1600x1200, 1600, 1200},
|
||||
{CRenderSettings::Resolution1680x1050, 1680, 1050}, {CRenderSettings::Resolution1920x1080, 1920, 1080},
|
||||
{CRenderSettings::Resolution1920x1200, 1920, 1200}};
|
||||
|
||||
CRenderSettings::CRenderSettings()
|
||||
#if !AYA_PLATFORM_IOS
|
||||
: fullscreenSize(G3D::Vector2int16(800, 600)) // these are just fail safes in case auto detect procedure fails.
|
||||
, windowSize(G3D::Vector2int16(800, 600)) //
|
||||
#else
|
||||
: fullscreenSize(G3D::Vector2int16(1024, 768)) // these are just fail safes in case auto detect procedure fails.
|
||||
, windowSize(G3D::Vector2int16(1024, 768)) //
|
||||
#endif
|
||||
, graphicsMode(AutoGraphicsMode)
|
||||
, qualityLevel(QualityAuto)
|
||||
, editQualityLevel(QualityAuto)
|
||||
, antialiasingMode(AntialiasingOff)
|
||||
, frameRateManagerMode(FrameRateManagerAuto)
|
||||
, showAggregation(false)
|
||||
, drawConnectors(false)
|
||||
, minCullDistance(50)
|
||||
, debugShowBoundingBoxes(false) // debug.
|
||||
, debugReloadAssets(false) // debug.
|
||||
, objExportMergeByMaterial(false)
|
||||
, eagerBulkExecution(false) // debug
|
||||
, enableFRM(true)
|
||||
, autoQualityLevel(1)
|
||||
, resolutionPreference(ResolutionAuto)
|
||||
, maxQualityLevel(QualityLevelMax)
|
||||
, textureCacheSize(1024 * 1024 * 32) // 32 MB
|
||||
, meshCacheSize(1024 * 1024 * 32) // 32 MB
|
||||
{
|
||||
}
|
||||
|
||||
const CRenderSettings::RESOLUTIONENTRY& CRenderSettings::getResolutionPreset(ResolutionPreset preset) const
|
||||
{
|
||||
AYAASSERT(preset < ResolutionMaxIndex);
|
||||
AYAASSERT(ResolutionTable[preset - 1].preset == preset);
|
||||
AYAASSERT(ResolutionMaxIndex == ARRAYSIZE(ResolutionTable) + 1);
|
||||
|
||||
return ResolutionTable[preset - 1];
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
279
engine/gfx/src/Base/RenderSettings.hpp
Normal file
279
engine/gfx/src/Base/RenderSettings.hpp
Normal file
@@ -0,0 +1,279 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DataModel/GameBasicSettings.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Utility/StandardOut.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class CRenderSettings
|
||||
{
|
||||
public:
|
||||
enum AASamples
|
||||
{
|
||||
NONE = 1,
|
||||
AA4 = 4,
|
||||
AA8 = 8,
|
||||
};
|
||||
|
||||
static const AASamples defaultAASamples = NONE;
|
||||
static const G3D::Vector2int16 defaultWindowSize;
|
||||
static const G3D::Vector2int16 defaultFullscreenSize();
|
||||
static const G3D::Vector2int16 minGameWindowSize;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UnknownGraphicsMode = 0,
|
||||
AutoGraphicsMode = 1,
|
||||
BGFX = 2,
|
||||
OpenGL,
|
||||
NoGraphics
|
||||
} GraphicsMode;
|
||||
|
||||
static GraphicsMode latchedGraphicsMode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AntialiasingAuto = 0,
|
||||
AntialiasingOn = 1,
|
||||
AntialiasingOff = 2
|
||||
} AntialiasingMode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FrameRateManagerAuto = 0,
|
||||
FrameRateManagerOn = 1,
|
||||
FrameRateManagerOff = 2
|
||||
} FrameRateManagerMode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
QualityAuto = 0,
|
||||
QualityLevel1,
|
||||
QualityLevel2,
|
||||
QualityLevel3,
|
||||
QualityLevel4,
|
||||
QualityLevel5,
|
||||
QualityLevel6,
|
||||
QualityLevel7,
|
||||
QualityLevel8,
|
||||
QualityLevel9,
|
||||
QualityLevel10,
|
||||
QualityLevel11,
|
||||
QualityLevel12,
|
||||
QualityLevel13,
|
||||
QualityLevel14,
|
||||
QualityLevel15,
|
||||
QualityLevel16,
|
||||
QualityLevel17,
|
||||
QualityLevel18,
|
||||
QualityLevel19,
|
||||
QualityLevel20,
|
||||
QualityLevel21,
|
||||
QualityLevelMax
|
||||
} QualityLevel;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ResolutionAuto,
|
||||
Resolution720x526,
|
||||
Resolution800x600,
|
||||
|
||||
Resolution1024x600,
|
||||
Resolution1024x768,
|
||||
|
||||
Resolution1280x720,
|
||||
Resolution1280x768,
|
||||
Resolution1152x864,
|
||||
Resolution1280x800,
|
||||
Resolution1360x768,
|
||||
Resolution1280x960,
|
||||
Resolution1280x1024,
|
||||
|
||||
Resolution1440x900,
|
||||
Resolution1600x900,
|
||||
Resolution1600x1024,
|
||||
Resolution1600x1200,
|
||||
Resolution1680x1050,
|
||||
|
||||
Resolution1920x1080,
|
||||
Resolution1920x1200,
|
||||
|
||||
ResolutionMaxIndex
|
||||
} ResolutionPreset;
|
||||
|
||||
struct RESOLUTIONENTRY
|
||||
{
|
||||
CRenderSettings::ResolutionPreset preset;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
protected:
|
||||
GraphicsMode graphicsMode;
|
||||
AntialiasingMode antialiasingMode;
|
||||
FrameRateManagerMode frameRateManagerMode;
|
||||
QualityLevel qualityLevel;
|
||||
QualityLevel editQualityLevel;
|
||||
|
||||
ResolutionPreset resolutionPreference;
|
||||
|
||||
int autoQualityLevel;
|
||||
int maxQualityLevel;
|
||||
int minCullDistance;
|
||||
bool debugShowBoundingBoxes;
|
||||
bool debugReloadAssets;
|
||||
bool enableFRM;
|
||||
bool objExportMergeByMaterial;
|
||||
|
||||
static AASamples aaSamples;
|
||||
|
||||
// filtered setting to use by app.
|
||||
G3D::Vector2int16 fullscreenSize;
|
||||
G3D::Vector2int16 windowSize;
|
||||
|
||||
bool showAggregation;
|
||||
|
||||
bool drawConnectors;
|
||||
|
||||
bool eagerBulkExecution;
|
||||
|
||||
// KB
|
||||
unsigned int textureCacheSize;
|
||||
unsigned int meshCacheSize;
|
||||
|
||||
public:
|
||||
CRenderSettings();
|
||||
|
||||
bool getShowAggregation() const
|
||||
{
|
||||
return showAggregation;
|
||||
}
|
||||
|
||||
static AASamples getAASamplesSafe()
|
||||
{
|
||||
return aaSamples;
|
||||
} // Thread-safe
|
||||
|
||||
GraphicsMode getGraphicsMode() const
|
||||
{
|
||||
return graphicsMode;
|
||||
}
|
||||
void setGraphicsMode(GraphicsMode value);
|
||||
|
||||
GraphicsMode getLatchedGraphicsMode()
|
||||
{
|
||||
if (latchedGraphicsMode == UnknownGraphicsMode)
|
||||
latchedGraphicsMode = getGraphicsMode();
|
||||
return latchedGraphicsMode;
|
||||
}
|
||||
|
||||
AASamples getAASamples() const
|
||||
{
|
||||
return aaSamples;
|
||||
}
|
||||
|
||||
G3D::Vector2int16 getFullscreenSize() const
|
||||
{
|
||||
return fullscreenSize;
|
||||
}
|
||||
G3D::Vector2int16 getWindowSize() const
|
||||
{
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
FrameRateManagerMode getFrameRateManagerMode() const
|
||||
{
|
||||
return frameRateManagerMode;
|
||||
}
|
||||
AntialiasingMode getAntialiasingMode() const
|
||||
{
|
||||
return antialiasingMode;
|
||||
}
|
||||
|
||||
QualityLevel getQualityLevel() const
|
||||
{
|
||||
return qualityLevel;
|
||||
}
|
||||
QualityLevel getEditQualityLevel() const
|
||||
{
|
||||
return editQualityLevel;
|
||||
}
|
||||
int getMaxQualityLevel()
|
||||
{
|
||||
return maxQualityLevel;
|
||||
}
|
||||
int getAutoQualityLevel() const
|
||||
{
|
||||
return autoQualityLevel;
|
||||
}
|
||||
|
||||
ResolutionPreset getResolutionPreference() const
|
||||
{
|
||||
return resolutionPreference;
|
||||
}
|
||||
const RESOLUTIONENTRY& getResolutionPreset(ResolutionPreset preset) const;
|
||||
|
||||
// FRM would like to report latest setting. Subclass is free to ignore it
|
||||
virtual void setAutoQualityLevel(int level) {}
|
||||
|
||||
float getMaxFrameRate()
|
||||
{
|
||||
float framerate[] = {30.f, 60.f, 75.f, 120.f, 144.f, 200.f, 240.f, 360.f, 9999999.f};
|
||||
|
||||
return framerate[Aya::GameBasicSettings::singleton().getMaxFramerate()];
|
||||
}
|
||||
float getMinFrameRate() const
|
||||
{
|
||||
return 30.0f;
|
||||
}
|
||||
|
||||
bool getDrawConnectors() const
|
||||
{
|
||||
return drawConnectors;
|
||||
}
|
||||
void setDrawConnectors(bool value)
|
||||
{
|
||||
drawConnectors = value;
|
||||
}
|
||||
|
||||
int getMinCullDistance() const
|
||||
{
|
||||
return minCullDistance;
|
||||
}
|
||||
bool getDebugShowBoundingBoxes() const
|
||||
{
|
||||
return debugShowBoundingBoxes;
|
||||
}
|
||||
bool getDebugReloadAssets() const
|
||||
{
|
||||
return debugReloadAssets;
|
||||
}
|
||||
bool getObjExportMergeByMaterial() const
|
||||
{
|
||||
return objExportMergeByMaterial;
|
||||
}
|
||||
bool getEnableFRM() const
|
||||
{
|
||||
return enableFRM;
|
||||
}
|
||||
|
||||
bool getEagerBulkExecution() const
|
||||
{
|
||||
return eagerBulkExecution;
|
||||
}
|
||||
|
||||
unsigned int getTextureCacheSize() const
|
||||
{
|
||||
return textureCacheSize;
|
||||
}
|
||||
unsigned int getMeshCacheSize() const
|
||||
{
|
||||
return meshCacheSize;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
32
engine/gfx/src/Base/RenderStats.cpp
Normal file
32
engine/gfx/src/Base/RenderStats.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#include "Base/RenderStats.hpp"
|
||||
#include "Utility/Profiling.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
RenderStats::RenderStats()
|
||||
: cpuRenderTotal(new Aya::Profiling::CodeProfiler("3D CPU Total"))
|
||||
|
||||
, culling(new Aya::Profiling::CodeProfiler("Culling"))
|
||||
, flip(new Aya::Profiling::CodeProfiler("Flipping Backbuffer"))
|
||||
, renderObjects(new Aya::Profiling::CodeProfiler("Render Objects"))
|
||||
, updateLighting(new Aya::Profiling::CodeProfiler("Update Lighting"))
|
||||
, adorn2D(new Aya::Profiling::CodeProfiler("Adorn 2D"))
|
||||
, adorn3D(new Aya::Profiling::CodeProfiler("Adorn 3D"))
|
||||
, visualEngineSceneUpdater(new Aya::Profiling::CodeProfiler("Visual Engine Scene Updater"))
|
||||
, finishRendering(new Aya::Profiling::CodeProfiler("Finish Rendering"))
|
||||
, renderTargetUpdate(new Aya::Profiling::CodeProfiler("RenderTarget Update"))
|
||||
|
||||
, frameRateManager(new Aya::Profiling::CodeProfiler("Frame Rate Manager"))
|
||||
|
||||
, textureCompositor(new Aya::Profiling::CodeProfiler("Texture Compositor"))
|
||||
, updateSceneGraph(new Aya::Profiling::CodeProfiler("Update SceneGraph"))
|
||||
, updateAllInvalidParts(new Aya::Profiling::CodeProfiler("updateAllInvalidParts"))
|
||||
, updateDynamicsAndAggregateStatics(new Aya::Profiling::CodeProfiler("updateDynamicsAndAggregateStatics"))
|
||||
, updateDynamicParts(new Aya::Profiling::CodeProfiler("updateDynamicParts"))
|
||||
{
|
||||
}
|
||||
|
||||
RenderStats::~RenderStats() {}
|
||||
103
engine/gfx/src/Base/RenderStats.hpp
Normal file
103
engine/gfx/src/Base/RenderStats.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include "boost/scoped_ptr.hpp"
|
||||
#include "Utility/Profiling.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace Profiling
|
||||
{
|
||||
class CodeProfiler;
|
||||
}
|
||||
|
||||
struct RenderPassStats
|
||||
{
|
||||
unsigned int batches;
|
||||
unsigned int faces;
|
||||
unsigned int vertices;
|
||||
unsigned int stateChanges;
|
||||
unsigned int passChanges;
|
||||
|
||||
RenderPassStats()
|
||||
: batches(0)
|
||||
, faces(0)
|
||||
, vertices(0)
|
||||
, stateChanges(0)
|
||||
, passChanges(0)
|
||||
{
|
||||
}
|
||||
|
||||
RenderPassStats& operator+=(const RenderPassStats& other)
|
||||
{
|
||||
batches += other.batches;
|
||||
faces += other.faces;
|
||||
vertices += other.vertices;
|
||||
stateChanges += other.stateChanges;
|
||||
passChanges += other.passChanges;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
RenderPassStats operator+(const RenderPassStats& other) const
|
||||
{
|
||||
RenderPassStats result = *this;
|
||||
result += other;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClusterStats
|
||||
{
|
||||
unsigned int clusters;
|
||||
unsigned int parts;
|
||||
|
||||
ClusterStats()
|
||||
: clusters(0)
|
||||
, parts(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RenderStats
|
||||
{
|
||||
public:
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> cpuRenderTotal;
|
||||
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> culling;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> flip;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> renderObjects;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateLighting;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> adorn2D;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> adorn3D;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> visualEngineSceneUpdater;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> finishRendering;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> renderTargetUpdate;
|
||||
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> frameRateManager;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> textureCompositor;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateSceneGraph;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateAllInvalidParts;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateDynamicsAndAggregateStatics;
|
||||
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateDynamicParts;
|
||||
|
||||
RenderPassStats passTotal;
|
||||
RenderPassStats passScene;
|
||||
RenderPassStats passShadow;
|
||||
RenderPassStats passUI;
|
||||
RenderPassStats pass3DAdorns;
|
||||
|
||||
ClusterStats clusterFast;
|
||||
ClusterStats clusterFastFW;
|
||||
ClusterStats clusterFastHumanoid;
|
||||
|
||||
ClusterStats lastFrameFast;
|
||||
unsigned lastFrameMegaClusterChunks;
|
||||
|
||||
RenderStats();
|
||||
~RenderStats();
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
30
engine/gfx/src/Base/TextureProxyBase.hpp
Normal file
30
engine/gfx/src/Base/TextureProxyBase.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "boost/enable_shared_from_this.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
typedef boost::shared_ptr<class TextureProxyBase> TextureProxyBaseRef;
|
||||
|
||||
class TextureProxyBase : public boost::enable_shared_from_this<TextureProxyBase>
|
||||
{
|
||||
private:
|
||||
typedef boost::enable_shared_from_this<TextureProxyBase> Super;
|
||||
|
||||
public:
|
||||
TextureProxyBase() {}
|
||||
virtual ~TextureProxyBase() {}
|
||||
|
||||
virtual G3D::Vector2 getOriginalSize() = 0;
|
||||
|
||||
static const unsigned int numStrips = 32;
|
||||
static float stripWidth()
|
||||
{
|
||||
return 1.0f / (float)numStrips;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
87
engine/gfx/src/Base/Type.hpp
Normal file
87
engine/gfx/src/Base/Type.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace Text
|
||||
{
|
||||
enum Font
|
||||
{
|
||||
FONT_LEGACY,
|
||||
FONT_ARIAL,
|
||||
FONT_ARIALBOLD,
|
||||
FONT_SOURCESANS,
|
||||
FONT_SOURCESANSBOLD,
|
||||
FONT_SOURCESANSLIGHT,
|
||||
FONT_SOURCESANSITALIC,
|
||||
FONT_BODONI,
|
||||
FONT_GARAMOND,
|
||||
FONT_CARTOON,
|
||||
FONT_CODE,
|
||||
FONT_HIGHWAY,
|
||||
FONT_SCIFI,
|
||||
FONT_ARCADE,
|
||||
FONT_FANTASY,
|
||||
FONT_ANTIQUE,
|
||||
FONT_SOURCESANSSEMIBOLD,
|
||||
FONT_GOTHAM,
|
||||
FONT_GOTHAMSEMIBOLD,
|
||||
FONT_GOTHAMBOLD,
|
||||
FONT_GOTHAMBLACK,
|
||||
FONT_AMATICSC,
|
||||
FONT_BANGERS,
|
||||
FONT_CREEPSTER,
|
||||
FONT_DENKONE,
|
||||
FONT_FONDAMENTO,
|
||||
FONT_FREDOKAONE,
|
||||
FONT_GRENZEGOTISCH,
|
||||
FONT_INDIEFLOWER,
|
||||
FONT_JOSEFINSANS,
|
||||
FONT_JURA,
|
||||
FONT_KALAM,
|
||||
FONT_LUCKIESTGUY,
|
||||
FONT_MERRIWEATHER,
|
||||
FONT_MICHROMA,
|
||||
FONT_NUNITO,
|
||||
FONT_OSWALD,
|
||||
FONT_PATRICKHAND,
|
||||
FONT_PERMANENTMARKER,
|
||||
FONT_ROBOTO,
|
||||
FONT_ROBOTOCONDENSED,
|
||||
FONT_ROBOTOMONO,
|
||||
FONT_SARPANCH,
|
||||
FONT_SPECIALELITE,
|
||||
FONT_TITILLIUMWEB,
|
||||
FONT_UBUNTU,
|
||||
FONT_NOTOSANS,
|
||||
FONT_NOTOSANSMEDIUM,
|
||||
FONT_NOTOSANSSEMIBOLD,
|
||||
FONT_NOTOSANSBOLD,
|
||||
FONT_NOTOSANSEXTRABOLD,
|
||||
FONT_NOTOSANSBLACK,
|
||||
FONT_NOTOSANSITALIC,
|
||||
FONT_NOTOSANSLIGHT,
|
||||
FONT_NOTOSANSEXTRALIGHT,
|
||||
FONT_NOTOSANSTHIN,
|
||||
FONT_CONSOLAS,
|
||||
FONT_COMICSANS,
|
||||
|
||||
FONT_LAST
|
||||
};
|
||||
// Font drawing params - copied from G3D
|
||||
enum XAlign
|
||||
{
|
||||
XALIGN_RIGHT,
|
||||
XALIGN_LEFT,
|
||||
XALIGN_CENTER
|
||||
};
|
||||
|
||||
enum YAlign
|
||||
{
|
||||
YALIGN_TOP,
|
||||
/*YALIGN_BASELINE,*/ YALIGN_CENTER,
|
||||
YALIGN_BOTTOM
|
||||
};
|
||||
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Aya
|
||||
68
engine/gfx/src/Base/Typesetter.hpp
Normal file
68
engine/gfx/src/Base/Typesetter.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Utility/Rotation2d.hpp"
|
||||
#include "Base/Type.hpp"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class Adorn;
|
||||
|
||||
namespace Graphics
|
||||
{
|
||||
class Texture;
|
||||
class TextureManager;
|
||||
class TextureAtlas;
|
||||
}; // namespace Graphics
|
||||
|
||||
// abstract base class
|
||||
class Typesetter
|
||||
{
|
||||
public:
|
||||
virtual ~Typesetter() {}
|
||||
|
||||
virtual Vector2 draw(Adorn* adorn, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Aya::Text::XAlign xalign = Aya::Text::XALIGN_LEFT, Aya::Text::YAlign yalign = Aya::Text::YALIGN_TOP,
|
||||
const Vector2& availableSpace = Vector2::zero(), const Rect2D& clippingRect = Rect2D::xyxy(-1, -1, -1, -1),
|
||||
const Rotation2D& rotation = Rotation2D()) const = 0;
|
||||
|
||||
|
||||
virtual int getCursorPositionInText(const std::string& s, const Aya::Vector2& pos2D, float size, Aya::Text::XAlign xalign,
|
||||
Aya::Text::YAlign yalign, const Aya::Vector2& availableSpace, const Rotation2D& rotation, Aya::Vector2 cursorPos) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
Useful for drawing centered text and boxes around text.
|
||||
*/
|
||||
virtual Vector2 measure(const std::string& s, float size, const Vector2& availableSpace = Vector2::zero(), bool* textFits = NULL) const = 0;
|
||||
|
||||
virtual void loadResources(Aya::Graphics::TextureManager* textureManager, Aya::Graphics::TextureAtlas* glyphAtlas) = 0;
|
||||
virtual void releaseResources() = 0;
|
||||
virtual const boost::shared_ptr<Graphics::Texture>& getTexture() const = 0;
|
||||
|
||||
static bool isCharNonWhitespace(char c)
|
||||
{
|
||||
return (c >= '!' && c <= '~');
|
||||
}
|
||||
static bool isCharWhitespace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t' || c == '\n');
|
||||
}
|
||||
static bool isCharSupported(char c)
|
||||
{
|
||||
return isCharNonWhitespace(c) || isCharWhitespace(c);
|
||||
}
|
||||
static bool isStringSupported(std::string& stringToCheck)
|
||||
{
|
||||
for (std::string::iterator iter = stringToCheck.begin(); iter != stringToCheck.end(); ++iter)
|
||||
{
|
||||
if (!isCharSupported(*iter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace Aya
|
||||
79
engine/gfx/src/Base/ViewBase.cpp
Normal file
79
engine/gfx/src/Base/ViewBase.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "Base/ViewBase.hpp"
|
||||
#include "time.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include "Utility/MachineIdUploader.hpp"
|
||||
#include "boost/functional/hash.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern void RenderView_InitModule();
|
||||
extern void RenderView_ShutdownModule();
|
||||
|
||||
static IViewBaseFactory** getFactory(CRenderSettings::GraphicsMode mode)
|
||||
{
|
||||
static IViewBaseFactory* s_rgFactories[6] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
if ((static_cast<size_t>(mode)) < ARRAYSIZE(s_rgFactories))
|
||||
{
|
||||
return s_rgFactories + static_cast<size_t>(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ViewBase* ViewBase::CreateView(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings)
|
||||
{
|
||||
IViewBaseFactory** ppfactory = getFactory(mode);
|
||||
|
||||
// did you call Aya::ViewBase::InitPluginModules?
|
||||
AYAASSERT(ppfactory && *ppfactory);
|
||||
if (ppfactory && *ppfactory)
|
||||
{
|
||||
return (*ppfactory)->Create(mode, context, renderSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewBase::RegisterFactory(CRenderSettings::GraphicsMode mode, IViewBaseFactory* factory)
|
||||
{
|
||||
IViewBaseFactory** ppfactory = getFactory(mode);
|
||||
|
||||
AYAASSERT(ppfactory);
|
||||
if (ppfactory)
|
||||
{
|
||||
*ppfactory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewBase::render(IMetric* metric, double timeRenderJob)
|
||||
{
|
||||
if (timeRenderJob == 0.0)
|
||||
timeRenderJob = Time::nowFastSec();
|
||||
renderPrepare(metric);
|
||||
renderPerform(timeRenderJob);
|
||||
}
|
||||
|
||||
void ViewBase::InitPluginModules()
|
||||
{
|
||||
RenderView_InitModule();
|
||||
}
|
||||
|
||||
void ViewBase::ShutdownPluginModules()
|
||||
{
|
||||
RenderView_ShutdownModule();
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> ViewBase::setFrameDataCallback(const boost::function<void(void*)>& callback)
|
||||
{
|
||||
return std::make_pair(0, 0);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
122
engine/gfx/src/Base/ViewBase.hpp
Normal file
122
engine/gfx/src/Base/ViewBase.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "Declarations.hpp"
|
||||
#include "Base/RenderSettings.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace Graphics
|
||||
{
|
||||
class VisualEngine;
|
||||
}
|
||||
|
||||
class DataModel;
|
||||
class ViewBase;
|
||||
class FrameRateManager;
|
||||
class CRenderSettings;
|
||||
class RenderStats;
|
||||
class AyaInterface IMetric;
|
||||
class Instance;
|
||||
|
||||
enum ExporterFormat
|
||||
{
|
||||
ExporterFormat_Obj,
|
||||
ExporterFormat_NumFormats
|
||||
};
|
||||
|
||||
enum ExporterSaveType
|
||||
{
|
||||
ExporterSaveType_Everything,
|
||||
ExporterSaveType_Selection,
|
||||
ExporterSaveType_NumSaveTypes
|
||||
};
|
||||
|
||||
struct OSContext
|
||||
{
|
||||
void* hWnd;
|
||||
void* display;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
// insert OS specific stuff here.
|
||||
OSContext()
|
||||
: hWnd(0)
|
||||
, display(0)
|
||||
, width(640)
|
||||
, height(480)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class IViewBaseFactory
|
||||
{
|
||||
public:
|
||||
virtual ViewBase* Create(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings) = 0;
|
||||
};
|
||||
|
||||
class ViewBase
|
||||
{
|
||||
friend class Visit;
|
||||
|
||||
public:
|
||||
static ViewBase* CreateView(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings);
|
||||
|
||||
static void RegisterFactory(CRenderSettings::GraphicsMode mode, IViewBaseFactory* factory);
|
||||
|
||||
// need this because we are statically linking.
|
||||
static void InitPluginModules();
|
||||
|
||||
// it is bad form to need this. phase out please.
|
||||
static void ShutdownPluginModules();
|
||||
|
||||
virtual void initResources() = 0;
|
||||
virtual void bindWorkspace(boost::shared_ptr<Aya::DataModel> dataModel) = 0;
|
||||
|
||||
virtual void render(IMetric* metric, double timeJobStart);
|
||||
virtual void renderPrepare(IMetric* metric) = 0;
|
||||
virtual void renderPerform(double timeJobStart) = 0;
|
||||
|
||||
virtual void onResize(int cx, int cy) = 0;
|
||||
virtual void buildGui(bool buildInGameGui = true) = 0;
|
||||
|
||||
virtual void renderThumb(unsigned char* data, int width, int height, bool crop, bool allowDolly) = 0;
|
||||
|
||||
virtual void garbageCollect() {}
|
||||
|
||||
virtual Instance* getWorkspace() = 0;
|
||||
virtual RenderStats& getRenderStats() = 0;
|
||||
|
||||
virtual DataModel* getDataModel() = 0;
|
||||
virtual Graphics::VisualEngine* getVisualEngine() = 0;
|
||||
|
||||
// use for pulling debug info only, please.
|
||||
virtual FrameRateManager* getFrameRateManager()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual double getMetricValue(const std::string& s)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
virtual bool exportScene(const std::string& filePath, ExporterSaveType saveType, ExporterFormat format) = 0;
|
||||
virtual bool exportSceneThumbJSON(ExporterSaveType saveType, ExporterFormat format, bool encodeBase64, std::string& strOut) = 0;
|
||||
|
||||
virtual void queueAssetReload(const std::string& filePath) {};
|
||||
virtual void immediateAssetReload(const std::string& filePath) = 0;
|
||||
|
||||
virtual void suspendView() = 0;
|
||||
virtual void resumeView() = 0;
|
||||
|
||||
virtual std::pair<unsigned, unsigned> setFrameDataCallback(const boost::function<void(void*)>& callback);
|
||||
|
||||
virtual ~ViewBase() {}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
150
engine/gfx/src/Base/ViewportBillboarder.cpp
Normal file
150
engine/gfx/src/Base/ViewportBillboarder.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "Base/ViewportBillboarder.hpp"
|
||||
#include "DataModel/Filters.hpp"
|
||||
#include "DataModel/Camera.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "World/ContactManager.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
ViewportBillboarder::ViewportBillboarder()
|
||||
: guiScreenSize(NULL)
|
||||
, alwaysOnTop(false)
|
||||
, screenOffset2D(Vector2::zero())
|
||||
{
|
||||
}
|
||||
ViewportBillboarder::ViewportBillboarder(const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset,
|
||||
const Vector2& billboardSizeRelativeOffset, const UDim2& billboardSize, const Vector2* guiScreenSize)
|
||||
: partExtentRelativeOffset(partExtentRelativeOffset)
|
||||
, partStudsOffset(partStudsOffset)
|
||||
, billboardSizeRelativeOffset(billboardSizeRelativeOffset)
|
||||
, billboardSize(billboardSize)
|
||||
, guiScreenSize(guiScreenSize)
|
||||
, alwaysOnTop(false)
|
||||
, screenOffset2D(Vector2::zero())
|
||||
{
|
||||
}
|
||||
|
||||
Vector2 ViewportBillboarder::getScreenOffset(const Rect2D& parentviewport, const Aya::Camera& camera, const CoordinateFrame& desiredModelView)
|
||||
{
|
||||
const CoordinateFrame& cameraFrame = camera.coordinateFrame();
|
||||
|
||||
Vector3 projectedTranslation = camera.project((cameraFrame * desiredModelView).translation);
|
||||
|
||||
return Math::roundVector2(projectedTranslation.xy());
|
||||
}
|
||||
|
||||
void ViewportBillboarder::update(const Rect2D& parentviewport, const Camera& camera, Vector3 partSize, CoordinateFrame partCFrame)
|
||||
{
|
||||
Vector3 halfSize = partSize / 2;
|
||||
|
||||
Extents cameraSpaceExtents;
|
||||
|
||||
// calculate extents in camera space
|
||||
for (int a = 0; a < 8; a++)
|
||||
{
|
||||
// go through all permutations (use bits of counter)
|
||||
Vector3 extent((a & 4) ? halfSize.x : -halfSize.x, (a & 2) ? halfSize.y : -halfSize.y, (a & 1) ? halfSize.z : -halfSize.z);
|
||||
Vector3 pextent = camera.coordinateFrame().pointToObjectSpace(partCFrame.pointToWorldSpace(extent));
|
||||
cameraSpaceExtents.expandToContain(pextent);
|
||||
}
|
||||
|
||||
Vector3 relativeOffsetInCameraSpace = (partExtentRelativeOffset + Vector3::one()) * 0.5f * cameraSpaceExtents.size() + cameraSpaceExtents.min();
|
||||
Vector3 billboardCenterInCameraSpace = relativeOffsetInCameraSpace + partStudsOffset;
|
||||
|
||||
Vector3 billboardCenterInWorldSpace = camera.coordinateFrame().pointToWorldSpace(billboardCenterInCameraSpace);
|
||||
Vector3 billboardCenterInProjSpace = camera.project(billboardCenterInWorldSpace);
|
||||
|
||||
if (billboardCenterInProjSpace.z > 0 && billboardCenterInProjSpace.z < 1000)
|
||||
{
|
||||
visibleAndValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
visibleAndValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float pixelsPerStud = billboardCenterInProjSpace.z;
|
||||
Vector2 studsPerPixel(1.f / pixelsPerStud, 1.f / pixelsPerStud); // undo the perspective effect of finding extents in screenspace.
|
||||
// -1 : to UI coordinates (0,0 upper left, )
|
||||
Vector2 billboardSizeInStuds = (billboardSize * (Vector2::one() * pixelsPerStud)) * studsPerPixel;
|
||||
if (guiScreenSize)
|
||||
viewport = Rect2D(*guiScreenSize);
|
||||
else
|
||||
viewport = Rect2D(billboardSizeInStuds * pixelsPerStud);
|
||||
|
||||
Vector2 UIToCameraSpaceScaler(billboardSizeInStuds / viewport.wh());
|
||||
|
||||
billboardCenterInCameraSpace += Vector3(billboardSizeRelativeOffset * billboardSizeInStuds, 0);
|
||||
|
||||
CoordinateFrame desiredModelView;
|
||||
desiredModelView.translation = billboardCenterInCameraSpace - Vector3(billboardSizeInStuds.x * 0.5f, billboardSizeInStuds.y * -0.5f, 0);
|
||||
desiredModelView.rotation.set(UIToCameraSpaceScaler.x, 0, 0, 0, UIToCameraSpaceScaler.y, 0, 0, 0, 1);
|
||||
|
||||
if (alwaysOnTop)
|
||||
screenOffset2D = getScreenOffset(parentviewport, camera, desiredModelView);
|
||||
|
||||
cframe = camera.coordinateFrame() * desiredModelView;
|
||||
}
|
||||
|
||||
bool ViewportBillboarder::hitTest(
|
||||
const Vector2int16& mousePosition, const Vector2int16& windowSize, Aya::Workspace* workspace, Vector2& billboardMousePosition)
|
||||
{
|
||||
const Aya::Camera& camera = *(workspace->getConstCamera());
|
||||
|
||||
Vector3 x0y0Screen, x1y1Screen;
|
||||
|
||||
if (alwaysOnTop)
|
||||
{
|
||||
x0y0Screen = Vector3(screenOffset2D, 0);
|
||||
x1y1Screen = Vector3(screenOffset2D + viewport.wh(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 x0y0World = cframe.pointToWorldSpace(Vector3(viewport.x0(), -viewport.y0(), 0));
|
||||
Vector3 x1y1World = cframe.pointToWorldSpace(Vector3(viewport.x1(), -viewport.y1(), 0));
|
||||
|
||||
x0y0Screen = camera.project(x0y0World);
|
||||
x1y1Screen = camera.project(x1y1World);
|
||||
}
|
||||
|
||||
Extents extents;
|
||||
extents.expandToContain(Vector3(x0y0Screen.xy(), 0));
|
||||
extents.expandToContain(Vector3(x1y1Screen.xy(), 0));
|
||||
|
||||
if (extents.contains(Vector3(mousePosition.x, mousePosition.y, 0)))
|
||||
{
|
||||
billboardMousePosition = Vector2((mousePosition.x - x0y0Screen.x) / (x1y1Screen.x - x0y0Screen.x) * viewport.width(),
|
||||
(mousePosition.y - x0y0Screen.y) / (x1y1Screen.y - x0y0Screen.y) * viewport.height());
|
||||
|
||||
if (alwaysOnTop)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ContactManager& contactManager = *workspace->getWorld()->getContactManager();
|
||||
RbxRay unitRay = camera.worldRay(mousePosition.x, mousePosition.y);
|
||||
RbxRay searchRay = RbxRay::fromOriginAndDirection(unitRay.origin(), unitRay.direction() * 2048);
|
||||
Vector3 partHitPointWorld;
|
||||
FilterInvisibleNonColliding invisibleNonCollidingObjectsFilter; // invisible & non-colliding parts don't block mouse-clicks
|
||||
if (contactManager.getHit(searchRay, NULL, &invisibleNonCollidingObjectsFilter, partHitPointWorld) == NULL)
|
||||
{
|
||||
// We didn't hit anything
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 hitPointWorld = cframe.pointToWorldSpace(Vector3(billboardMousePosition.x, billboardMousePosition.y, 0));
|
||||
Vector3 hitPointScreen = camera.project(hitPointWorld);
|
||||
|
||||
Vector3 partHitPointScreen = camera.project(partHitPointWorld);
|
||||
if (partHitPointScreen.z < hitPointScreen.z)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
58
engine/gfx/src/Base/ViewportBillboarder.hpp
Normal file
58
engine/gfx/src/Base/ViewportBillboarder.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Utility/UDim.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class ViewportBillboarder
|
||||
{
|
||||
private:
|
||||
CoordinateFrame cframe;
|
||||
Rect2D viewport;
|
||||
bool visibleAndValid;
|
||||
Vector2 screenOffset2D;
|
||||
|
||||
Vector2 getScreenOffset(const Rect2D& parentviewport, const Aya::Camera& camera, const CoordinateFrame& desiredModelView);
|
||||
|
||||
public:
|
||||
Vector3 partExtentRelativeOffset;
|
||||
Vector3 partStudsOffset;
|
||||
Vector2 billboardSizeRelativeOffset;
|
||||
UDim2 billboardSize;
|
||||
const Vector2* guiScreenSize;
|
||||
bool alwaysOnTop;
|
||||
|
||||
ViewportBillboarder();
|
||||
ViewportBillboarder(const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset, const Vector2& billboardSizeRelativeOffset,
|
||||
const UDim2& billboardSize, // studs* x + pixels
|
||||
const Vector2* guiScreenSize // null for pixel-exact.
|
||||
);
|
||||
|
||||
void update(const Rect2D& parentviewport, const Camera& camera, Vector3 partSize, CoordinateFrame partCFrame);
|
||||
|
||||
bool hitTest(const Vector2int16& mousePosition, const Vector2int16& windowSize, Aya::Workspace* workspace, Vector2& billboardMousePosition);
|
||||
|
||||
const Vector2& getScreenOffset() const
|
||||
{
|
||||
return screenOffset2D;
|
||||
}
|
||||
|
||||
bool isVisibleAndValid() const
|
||||
{
|
||||
return visibleAndValid;
|
||||
}
|
||||
|
||||
const Rect2D& getViewport() const
|
||||
{
|
||||
return viewport;
|
||||
}
|
||||
|
||||
const CoordinateFrame& getCoordinateFrame() const
|
||||
{
|
||||
return cframe;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Aya
|
||||
119
engine/gfx/src/Core/Device.cpp
Normal file
119
engine/gfx/src/Core/Device.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "Core/Device.hpp"
|
||||
#include "Utility/StandardOut.hpp"
|
||||
|
||||
LOGVARIABLE(Graphics, 0)
|
||||
FASTFLAGVARIABLE(DebugGraphicsCrashOnLeaks, false) // d9mz - Oh well
|
||||
|
||||
FASTFLAGVARIABLE(GraphicsDebugMarkersEnable, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
DeviceContext::DeviceContext() {}
|
||||
|
||||
DeviceContext::~DeviceContext() {}
|
||||
|
||||
void DeviceContext::draw(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
|
||||
unsigned int indexRangeEnd)
|
||||
{
|
||||
drawImpl(geometry, primitive, offset, count, indexRangeBegin, indexRangeEnd);
|
||||
}
|
||||
|
||||
void DeviceContext::draw(const GeometryBatch& geometryBatch)
|
||||
{
|
||||
drawImpl(geometryBatch.getGeometry(), geometryBatch.getPrimitive(), geometryBatch.getOffset(), geometryBatch.getCount(),
|
||||
geometryBatch.getIndexRangeBegin(), geometryBatch.getIndexRangeEnd());
|
||||
}
|
||||
|
||||
void DeviceCaps::dumpToFLog(int channel) const
|
||||
{
|
||||
FASTLOG2(channel, "Caps: Shaders %d FFP %d", supportsShaders, supportsFFP);
|
||||
FASTLOG5(channel, "Caps: Framebuffer %d (%d MRT, %d MSAA) Stencil %d 32bIdx %d", supportsFramebuffer, maxDrawBuffers, maxSamples, supportsStencil,
|
||||
supportsIndex32);
|
||||
FASTLOG4(channel, "Caps: Texture: DXT %d PVR %d ETC1 %d Half %d", supportsTextureDXT, supportsTexturePVR, supportsTextureETC1,
|
||||
supportsTextureHalfFloat);
|
||||
FASTLOG3(channel, "Caps: Texture: 3D %d NPOT %d PartialMips %d", supportsTexture3D, supportsTextureNPOT, supportsTexturePartialMipChain);
|
||||
FASTLOG2(channel, "Caps: Texture: Size %d Units %d", maxTextureSize, maxTextureUnits);
|
||||
FASTLOG3(channel, "Caps: ColorBGR %d HalfPixelOffset %d RTFlip %d", colorOrderBGR, needsHalfPixelOffset, requiresRenderTargetFlipping);
|
||||
FASTLOG1(channel, "Caps: Retina %d", retina);
|
||||
}
|
||||
|
||||
Device::Device()
|
||||
: resourceListHead(NULL)
|
||||
, resourceListTail(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
if (resourceListHead)
|
||||
{
|
||||
FASTLOG(FLog::Graphics, "ERROR: Not all resources are destroyed!");
|
||||
|
||||
unsigned int index = 0;
|
||||
|
||||
for (Resource* cur = resourceListHead; cur; cur = cur->next, index++)
|
||||
{
|
||||
FASTLOG2(FLog::Graphics, "Leak %d: resource %p", index, cur);
|
||||
FASTLOGS(FLog::Graphics, "Resource type %s", typeid(*cur).name());
|
||||
|
||||
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Leak %d: resource %p", index, cur);
|
||||
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Resource type %s", typeid(*cur).name());
|
||||
if (!cur->getDebugName().empty())
|
||||
{
|
||||
// FASTLOGS(FLog::Graphics, "Resource name %s", cur->getDebugName());
|
||||
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Resource name %s", cur->getDebugName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::DebugGraphicsCrashOnLeaks)
|
||||
AYACRASH();
|
||||
else
|
||||
AYAASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Geometry> Device::createGeometry(const shared_ptr<VertexLayout>& layout, const shared_ptr<VertexBuffer>& vertexBuffer,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
{
|
||||
std::vector<shared_ptr<VertexBuffer>> vertexBuffers;
|
||||
vertexBuffers.push_back(vertexBuffer);
|
||||
|
||||
return createGeometryImpl(layout, vertexBuffers, indexBuffer, baseVertexIndex);
|
||||
}
|
||||
|
||||
shared_ptr<Geometry> Device::createGeometry(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
{
|
||||
return createGeometryImpl(layout, vertexBuffers, indexBuffer, baseVertexIndex);
|
||||
}
|
||||
|
||||
shared_ptr<Framebuffer> Device::createFramebuffer(const shared_ptr<Renderbuffer>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
{
|
||||
std::vector<shared_ptr<Renderbuffer>> colors;
|
||||
colors.push_back(color);
|
||||
|
||||
return createFramebufferImpl(colors, depth);
|
||||
}
|
||||
|
||||
shared_ptr<Framebuffer> Device::createFramebuffer(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
|
||||
{
|
||||
return createFramebufferImpl(color, depth);
|
||||
}
|
||||
|
||||
void Device::fireDeviceLost()
|
||||
{
|
||||
for (Resource* cur = resourceListTail; cur; cur = cur->prev)
|
||||
cur->onDeviceLost();
|
||||
}
|
||||
|
||||
void Device::fireDeviceRestored()
|
||||
{
|
||||
for (Resource* cur = resourceListHead; cur; cur = cur->next)
|
||||
cur->onDeviceRestored();
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
195
engine/gfx/src/Core/Device.hpp
Normal file
195
engine/gfx/src/Core/Device.hpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/Geometry.hpp"
|
||||
#include "Core/Shader.hpp"
|
||||
#include "Core/Texture.hpp"
|
||||
#include "Core/Pix.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class RasterizerState;
|
||||
class BlendState;
|
||||
class DepthState;
|
||||
class SamplerState;
|
||||
|
||||
class Framebuffer;
|
||||
|
||||
class DeviceContext
|
||||
{
|
||||
public:
|
||||
enum BufferMask
|
||||
{
|
||||
Buffer_Color = 1 << 0,
|
||||
Buffer_Depth = 1 << 1,
|
||||
Buffer_Stencil = 1 << 2
|
||||
};
|
||||
|
||||
virtual ~DeviceContext();
|
||||
|
||||
virtual void setDefaultAnisotropy(unsigned int value) = 0;
|
||||
|
||||
virtual void updateGlobalConstants(const void* data, size_t dataSize) = 0;
|
||||
|
||||
virtual void bindFramebuffer(Framebuffer* buffer) = 0;
|
||||
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil) = 0;
|
||||
|
||||
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset) = 0;
|
||||
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask) = 0;
|
||||
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask) = 0;
|
||||
|
||||
virtual void bindProgram(ShaderProgram* program) = 0;
|
||||
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount) = 0;
|
||||
virtual void setConstant(int handle, const float* data, size_t vectorCount) = 0;
|
||||
|
||||
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state) = 0;
|
||||
|
||||
virtual void setRasterizerState(const RasterizerState& state) = 0;
|
||||
virtual void setBlendState(const BlendState& state) = 0;
|
||||
virtual void setDepthState(const DepthState& state) = 0;
|
||||
|
||||
void draw(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
|
||||
unsigned int indexRangeEnd);
|
||||
void draw(const GeometryBatch& geometryBatch);
|
||||
|
||||
virtual void pushDebugMarkerGroup(const char* text) = 0;
|
||||
virtual void popDebugMarkerGroup() = 0;
|
||||
virtual void setDebugMarker(const char* text) = 0;
|
||||
|
||||
protected:
|
||||
DeviceContext();
|
||||
|
||||
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
|
||||
unsigned int indexRangeEnd) = 0;
|
||||
};
|
||||
|
||||
struct DeviceCaps
|
||||
{
|
||||
bool supportsFramebuffer;
|
||||
bool supportsShaders;
|
||||
bool supportsFFP;
|
||||
bool supportsStencil;
|
||||
bool supportsIndex32;
|
||||
|
||||
bool supportsTextureDXT;
|
||||
bool supportsTexturePVR;
|
||||
bool supportsTextureHalfFloat;
|
||||
bool supportsTexture3D;
|
||||
bool supportsTextureNPOT;
|
||||
bool supportsTextureETC1;
|
||||
|
||||
bool supportsTexturePartialMipChain;
|
||||
|
||||
unsigned int maxDrawBuffers;
|
||||
unsigned int maxSamples;
|
||||
unsigned int maxTextureSize;
|
||||
unsigned int maxTextureUnits;
|
||||
|
||||
bool colorOrderBGR;
|
||||
bool needsHalfPixelOffset;
|
||||
bool requiresRenderTargetFlipping;
|
||||
|
||||
bool retina;
|
||||
|
||||
void dumpToFLog(int channel) const;
|
||||
};
|
||||
|
||||
struct DeviceStats
|
||||
{
|
||||
float gpuFrameTime;
|
||||
};
|
||||
|
||||
typedef boost::function<void(void*)> DeviceFrameDataCallback;
|
||||
|
||||
class Device
|
||||
{
|
||||
friend class Resource;
|
||||
|
||||
public:
|
||||
enum API
|
||||
{
|
||||
API_BGFX,
|
||||
API_OpenGL
|
||||
};
|
||||
|
||||
static Device* create(API api, void* windowHandle, void* display, int w, int h);
|
||||
|
||||
virtual ~Device();
|
||||
|
||||
virtual bool validate() = 0;
|
||||
virtual void resize(int w, int h) = 0;
|
||||
virtual DeviceContext* beginFrame() = 0;
|
||||
virtual void endFrame() = 0;
|
||||
|
||||
virtual Framebuffer* getMainFramebuffer() = 0;
|
||||
|
||||
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants) = 0;
|
||||
|
||||
virtual std::string getAPIName() = 0;
|
||||
virtual std::string getFeatureLevel() = 0;
|
||||
virtual std::string getShadingLanguage() = 0;
|
||||
virtual std::string createShaderSource(
|
||||
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback) = 0;
|
||||
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint) = 0;
|
||||
|
||||
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode) = 0;
|
||||
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode) = 0;
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgram(
|
||||
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader) = 0;
|
||||
virtual shared_ptr<ShaderProgram> createShaderProgramFFP() = 0;
|
||||
|
||||
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) = 0;
|
||||
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) = 0;
|
||||
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements) = 0;
|
||||
|
||||
shared_ptr<Geometry> createGeometry(const shared_ptr<VertexLayout>& layout, const shared_ptr<VertexBuffer>& vertexBuffer,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex = 0);
|
||||
shared_ptr<Geometry> createGeometry(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex = 0);
|
||||
|
||||
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
|
||||
unsigned int mipLevels, Texture::Usage usage) = 0;
|
||||
|
||||
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples) = 0;
|
||||
|
||||
shared_ptr<Framebuffer> createFramebuffer(
|
||||
const shared_ptr<Renderbuffer>& color, const shared_ptr<Renderbuffer>& depth = shared_ptr<Renderbuffer>());
|
||||
shared_ptr<Framebuffer> createFramebuffer(
|
||||
const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth = shared_ptr<Renderbuffer>());
|
||||
|
||||
virtual const DeviceCaps& getCaps() const = 0;
|
||||
|
||||
virtual DeviceStats getStatistics() const = 0;
|
||||
|
||||
virtual void suspend()
|
||||
{
|
||||
AYAASSERT(false); /* only DX11-Durango*/
|
||||
}
|
||||
virtual void resume()
|
||||
{
|
||||
AYAASSERT(false); /* only DX11-Durango*/
|
||||
}
|
||||
|
||||
protected:
|
||||
Resource* resourceListHead;
|
||||
Resource* resourceListTail;
|
||||
|
||||
Device();
|
||||
|
||||
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
|
||||
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex) = 0;
|
||||
virtual shared_ptr<Framebuffer> createFramebufferImpl(
|
||||
const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth) = 0;
|
||||
|
||||
void fireDeviceLost();
|
||||
void fireDeviceRestored();
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
20
engine/gfx/src/Core/DeviceCreate.cpp
Normal file
20
engine/gfx/src/Core/DeviceCreate.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "API/BGFX/DeviceBGFX.hpp"
|
||||
#include "API/GL/DeviceGL.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
Device* Device::create(API api, void* windowHandle, void* display, int w, int h)
|
||||
{
|
||||
if (api == API_BGFX)
|
||||
return new DeviceBGFX(windowHandle, display);
|
||||
if (api == API_OpenGL)
|
||||
return new DeviceGL(windowHandle, display, w, h);
|
||||
|
||||
throw Aya::runtime_error("Unsupported API: %d", api);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
36
engine/gfx/src/Core/Framebuffer.cpp
Normal file
36
engine/gfx/src/Core/Framebuffer.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "Core/Framebuffer.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
Renderbuffer::Renderbuffer(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
|
||||
: Resource(device)
|
||||
, format(format)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, samples(samples)
|
||||
{
|
||||
AYAPROFILER_COUNTER_ADD("memory/gpu/renderbuffer", Texture::getImageSize(format, width, height) * samples);
|
||||
}
|
||||
|
||||
Renderbuffer::~Renderbuffer()
|
||||
{
|
||||
AYAPROFILER_COUNTER_SUB("memory/gpu/renderbuffer", Texture::getImageSize(format, width, height) * samples);
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(Device* device, unsigned int width, unsigned int height, unsigned int samples)
|
||||
: Resource(device)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, samples(samples)
|
||||
{
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer() {}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
72
engine/gfx/src/Core/Framebuffer.hpp
Normal file
72
engine/gfx/src/Core/Framebuffer.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "Resource.hpp"
|
||||
#include "Texture.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class Renderbuffer : public Resource
|
||||
{
|
||||
public:
|
||||
~Renderbuffer();
|
||||
|
||||
Texture::Format getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
unsigned int getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
unsigned int getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
unsigned int getSamples() const
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
protected:
|
||||
Renderbuffer(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
|
||||
|
||||
Texture::Format format;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int samples;
|
||||
};
|
||||
|
||||
class Framebuffer : public Resource
|
||||
{
|
||||
public:
|
||||
~Framebuffer();
|
||||
|
||||
virtual void download(void* data, unsigned int size) = 0;
|
||||
|
||||
unsigned int getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
unsigned int getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
unsigned int getSamples() const
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
protected:
|
||||
Framebuffer(Device* device, unsigned int width, unsigned int height, unsigned int samples);
|
||||
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int samples;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
112
engine/gfx/src/Core/Geometry.cpp
Normal file
112
engine/gfx/src/Core/Geometry.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "Core/Geometry.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
VertexLayout::Element::Element(unsigned int stream, unsigned int offset, Format format, Semantic semantic, unsigned int semanticIndex)
|
||||
: stream(stream)
|
||||
, offset(offset)
|
||||
, format(format)
|
||||
, semantic(semantic)
|
||||
, semanticIndex(semanticIndex)
|
||||
{
|
||||
}
|
||||
|
||||
VertexLayout::VertexLayout(Device* device, const std::vector<Element>& elements)
|
||||
: Resource(device)
|
||||
, elements(elements)
|
||||
{
|
||||
}
|
||||
|
||||
VertexLayout::~VertexLayout() {}
|
||||
|
||||
GeometryBuffer::GeometryBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: Resource(device)
|
||||
, elementSize(elementSize)
|
||||
, elementCount(elementCount)
|
||||
, usage(usage)
|
||||
{
|
||||
}
|
||||
|
||||
GeometryBuffer::~GeometryBuffer() {}
|
||||
|
||||
VertexBuffer::VertexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBuffer(device, elementSize, elementCount, usage)
|
||||
{
|
||||
AYAPROFILER_COUNTER_ADD("memory/gpu/vertexbuffer", elementSize * elementCount);
|
||||
}
|
||||
|
||||
VertexBuffer::~VertexBuffer()
|
||||
{
|
||||
AYAPROFILER_COUNTER_SUB("memory/gpu/vertexbuffer", elementSize * elementCount);
|
||||
}
|
||||
|
||||
IndexBuffer::IndexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
|
||||
: GeometryBuffer(device, elementSize, elementCount, usage)
|
||||
{
|
||||
AYAPROFILER_COUNTER_ADD("memory/gpu/indexbuffer", elementSize * elementCount);
|
||||
}
|
||||
|
||||
IndexBuffer::~IndexBuffer()
|
||||
{
|
||||
AYAPROFILER_COUNTER_SUB("memory/gpu/indexbuffer", elementSize * elementCount);
|
||||
}
|
||||
|
||||
Geometry::Geometry(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
|
||||
: Resource(device)
|
||||
, layout(layout)
|
||||
, vertexBuffers(vertexBuffers)
|
||||
, indexBuffer(indexBuffer)
|
||||
, baseVertexIndex(baseVertexIndex)
|
||||
{
|
||||
}
|
||||
|
||||
Geometry::~Geometry() {}
|
||||
|
||||
inline bool isCountValid(Geometry::Primitive primitive, unsigned int count)
|
||||
{
|
||||
switch (primitive)
|
||||
{
|
||||
case Geometry::Primitive_Triangles:
|
||||
return count % 3 == 0;
|
||||
case Geometry::Primitive_Lines:
|
||||
return count % 2 == 0;
|
||||
case Geometry::Primitive_Points:
|
||||
return true;
|
||||
case Geometry::Primitive_TriangleStrip:
|
||||
return count != 1 && count != 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GeometryBatch::GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int count, unsigned int indexRangeSize)
|
||||
: geometry(geometry)
|
||||
, primitive(primitive)
|
||||
, offset(0)
|
||||
, count(count)
|
||||
, indexRangeBegin(0)
|
||||
, indexRangeEnd(indexRangeSize)
|
||||
{
|
||||
AYAASSERT(geometry && isCountValid(primitive, count));
|
||||
}
|
||||
|
||||
GeometryBatch::GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
|
||||
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
|
||||
: geometry(geometry)
|
||||
, primitive(primitive)
|
||||
, offset(offset)
|
||||
, count(count)
|
||||
, indexRangeBegin(indexRangeBegin)
|
||||
, indexRangeEnd(indexRangeEnd)
|
||||
{
|
||||
AYAASSERT(geometry && isCountValid(primitive, count) && indexRangeBegin <= indexRangeEnd);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
201
engine/gfx/src/Core/Geometry.hpp
Normal file
201
engine/gfx/src/Core/Geometry.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#pragma once
|
||||
|
||||
#include "Resource.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class VertexLayout : public Resource
|
||||
{
|
||||
public:
|
||||
enum Semantic
|
||||
{
|
||||
Semantic_Position,
|
||||
Semantic_Normal,
|
||||
Semantic_Color,
|
||||
Semantic_Texture,
|
||||
|
||||
Semantic_Count
|
||||
};
|
||||
|
||||
enum Format
|
||||
{
|
||||
Format_Float1,
|
||||
Format_Float2,
|
||||
Format_Float3,
|
||||
Format_Float4,
|
||||
Format_Short2,
|
||||
Format_Short4,
|
||||
Format_UByte4,
|
||||
Format_Color,
|
||||
|
||||
Format_Count
|
||||
};
|
||||
|
||||
struct Element
|
||||
{
|
||||
unsigned int stream;
|
||||
unsigned int offset;
|
||||
|
||||
Format format;
|
||||
|
||||
Semantic semantic;
|
||||
unsigned int semanticIndex;
|
||||
|
||||
Element(unsigned int stream, unsigned int offset, Format format, Semantic semantic, unsigned int semanticIndex = 0);
|
||||
};
|
||||
|
||||
~VertexLayout();
|
||||
|
||||
const std::vector<Element>& getElements() const
|
||||
{
|
||||
return elements;
|
||||
}
|
||||
|
||||
protected:
|
||||
VertexLayout(Device* device, const std::vector<Element>& elements);
|
||||
|
||||
std::vector<Element> elements;
|
||||
};
|
||||
|
||||
class GeometryBuffer : public Resource
|
||||
{
|
||||
public:
|
||||
enum Usage
|
||||
{
|
||||
Usage_Static,
|
||||
Usage_Dynamic,
|
||||
|
||||
Usage_Count
|
||||
};
|
||||
|
||||
enum LockMode
|
||||
{
|
||||
Lock_Normal,
|
||||
Lock_Discard,
|
||||
|
||||
Lock_Count
|
||||
};
|
||||
|
||||
~GeometryBuffer();
|
||||
|
||||
virtual void* lock(LockMode mode = Lock_Normal) = 0;
|
||||
virtual void unlock() = 0;
|
||||
|
||||
virtual void upload(unsigned int offset, const void* data, unsigned int size) = 0;
|
||||
|
||||
size_t getElementSize() const
|
||||
{
|
||||
return elementSize;
|
||||
}
|
||||
size_t getElementCount() const
|
||||
{
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
GeometryBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
|
||||
size_t elementSize;
|
||||
size_t elementCount;
|
||||
Usage usage;
|
||||
};
|
||||
|
||||
class VertexBuffer : public GeometryBuffer
|
||||
{
|
||||
public:
|
||||
~VertexBuffer();
|
||||
|
||||
protected:
|
||||
VertexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
};
|
||||
|
||||
class IndexBuffer : public GeometryBuffer
|
||||
{
|
||||
public:
|
||||
~IndexBuffer();
|
||||
|
||||
protected:
|
||||
IndexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
|
||||
};
|
||||
|
||||
class Geometry : public Resource
|
||||
{
|
||||
public:
|
||||
enum Primitive
|
||||
{
|
||||
Primitive_Triangles,
|
||||
Primitive_Lines,
|
||||
Primitive_Points,
|
||||
Primitive_TriangleStrip,
|
||||
|
||||
Primitive_Count
|
||||
};
|
||||
|
||||
~Geometry();
|
||||
|
||||
std::vector<shared_ptr<VertexBuffer>> getVertexBuffers() { return vertexBuffers; }
|
||||
shared_ptr<IndexBuffer> getIndexBuffer() { return indexBuffer; }
|
||||
shared_ptr<VertexLayout> getVertexLayout() { return layout; }
|
||||
|
||||
protected:
|
||||
Geometry(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
|
||||
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
|
||||
|
||||
shared_ptr<VertexLayout> layout;
|
||||
std::vector<shared_ptr<VertexBuffer>> vertexBuffers;
|
||||
shared_ptr<IndexBuffer> indexBuffer;
|
||||
unsigned int baseVertexIndex;
|
||||
};
|
||||
|
||||
class GeometryBatch
|
||||
{
|
||||
public:
|
||||
GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int count, unsigned int indexRangeSize);
|
||||
GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
|
||||
unsigned int indexRangeBegin, unsigned int indexRangeEnd);
|
||||
|
||||
Geometry* getGeometry() const
|
||||
{
|
||||
return geometry.get();
|
||||
}
|
||||
|
||||
Geometry::Primitive getPrimitive() const
|
||||
{
|
||||
return primitive;
|
||||
}
|
||||
unsigned int getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
unsigned int getCount() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned int getIndexRangeBegin() const
|
||||
{
|
||||
return indexRangeBegin;
|
||||
}
|
||||
unsigned int getIndexRangeEnd() const
|
||||
{
|
||||
return indexRangeEnd;
|
||||
}
|
||||
|
||||
private:
|
||||
shared_ptr<Geometry> geometry;
|
||||
|
||||
Geometry::Primitive primitive;
|
||||
unsigned int offset;
|
||||
unsigned int count;
|
||||
|
||||
unsigned int indexRangeBegin;
|
||||
unsigned int indexRangeEnd;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
66
engine/gfx/src/Core/Pix.cpp
Normal file
66
engine/gfx/src/Core/Pix.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "Core/Pix.hpp"
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
#if PIX_ENABLED
|
||||
|
||||
#include "Core/Device.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "FastLog.hpp"
|
||||
|
||||
FASTFLAG(GraphicsDebugMarkersEnable)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
|
||||
void PixMarker(DeviceContext* ctx, const char* fmt, ...)
|
||||
{
|
||||
if (FFlag::GraphicsDebugMarkersEnable)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
|
||||
char buffer[512];
|
||||
vsnprintf(buffer, 512, fmt, va);
|
||||
buffer[511] = 0;
|
||||
ctx->setDebugMarker(buffer);
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
}
|
||||
|
||||
PixScope::PixScope(DeviceContext* ctx, const char* fmt, ...)
|
||||
{
|
||||
devctx = ctx;
|
||||
if (FFlag::GraphicsDebugMarkersEnable)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
|
||||
char buffer[512];
|
||||
vsnprintf(buffer, 512, fmt, va);
|
||||
buffer[511] = 0;
|
||||
ctx->pushDebugMarkerGroup(buffer);
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
}
|
||||
|
||||
PixScope::~PixScope()
|
||||
{
|
||||
if (FFlag::GraphicsDebugMarkersEnable)
|
||||
{
|
||||
devctx->popDebugMarkerGroup();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
|
||||
#else
|
||||
static int dummy0001;
|
||||
#endif
|
||||
40
engine/gfx/src/Core/Pix.hpp
Normal file
40
engine/gfx/src/Core/Pix.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#define PIX_ENABLED 1
|
||||
|
||||
#if PIX_ENABLED
|
||||
|
||||
|
||||
#define PIX_CONCAT(a, b) PIX_CONCAT2(a, b)
|
||||
#define PIX_CONCAT2(a, b) PIX_CONCAT3(a, b)
|
||||
#define PIX_CONCAT3(a, b) a##b
|
||||
|
||||
#define PIX_MARKER(ctx, ...) Aya::Graphics::PixMarker(ctx, __VA_ARGS__)
|
||||
#define PIX_SCOPE(ctx, ...) Aya::Graphics::PixScope PIX_CONCAT(pixScopeVar, __LINE__)(ctx, __VA_ARGS__)
|
||||
|
||||
// PIX support, D3D-only
|
||||
// pix.cpp is in Core/D3D9
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class DeviceContext;
|
||||
void PixMarker(DeviceContext* ctx, const char* fmt, ...);
|
||||
struct PixScope
|
||||
{
|
||||
PixScope(DeviceContext* ctx, const char* fmt, ...);
|
||||
~PixScope();
|
||||
DeviceContext* devctx;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
|
||||
#else
|
||||
|
||||
#define PIX_MARKER(...)
|
||||
#define PIX_SCOPE(...)
|
||||
|
||||
#endif
|
||||
61
engine/gfx/src/Core/Resource.cpp
Normal file
61
engine/gfx/src/Core/Resource.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Core/Resource.hpp"
|
||||
|
||||
#include "Core/Device.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
Resource::Resource(Device* device)
|
||||
: device(device)
|
||||
, prev(NULL)
|
||||
, next(NULL)
|
||||
{
|
||||
if (device->resourceListHead)
|
||||
{
|
||||
AYAASSERT(device->resourceListTail);
|
||||
|
||||
prev = device->resourceListTail;
|
||||
device->resourceListTail->next = this;
|
||||
device->resourceListTail = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
AYAASSERT(!device->resourceListTail);
|
||||
|
||||
device->resourceListHead = this;
|
||||
device->resourceListTail = this;
|
||||
}
|
||||
}
|
||||
|
||||
Resource::~Resource()
|
||||
{
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
{
|
||||
AYAASSERT(device->resourceListHead == this);
|
||||
device->resourceListHead = next;
|
||||
}
|
||||
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
else
|
||||
{
|
||||
AYAASSERT(device->resourceListTail == this);
|
||||
device->resourceListTail = prev;
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::onDeviceLost() {}
|
||||
|
||||
void Resource::onDeviceRestored() {}
|
||||
|
||||
void Resource::setDebugName(const std::string& value)
|
||||
{
|
||||
debugName = value;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
40
engine/gfx/src/Core/Resource.hpp
Normal file
40
engine/gfx/src/Core/Resource.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class Device;
|
||||
|
||||
class Resource : boost::noncopyable
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
public:
|
||||
explicit Resource(Device* device);
|
||||
virtual ~Resource();
|
||||
|
||||
virtual void onDeviceLost();
|
||||
virtual void onDeviceRestored();
|
||||
|
||||
const std::string& getDebugName() const
|
||||
{
|
||||
return debugName;
|
||||
}
|
||||
void setDebugName(const std::string& value);
|
||||
|
||||
protected:
|
||||
Device* device;
|
||||
|
||||
Resource* prev;
|
||||
Resource* next;
|
||||
|
||||
std::string debugName;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
55
engine/gfx/src/Core/Shader.cpp
Normal file
55
engine/gfx/src/Core/Shader.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "Core/Shader.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
LOGGROUP(Graphics)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
VertexShader::VertexShader(Device* device)
|
||||
: Resource(device)
|
||||
{
|
||||
}
|
||||
|
||||
VertexShader::~VertexShader() {}
|
||||
|
||||
FragmentShader::FragmentShader(Device* device)
|
||||
: Resource(device)
|
||||
{
|
||||
}
|
||||
|
||||
FragmentShader::~FragmentShader() {}
|
||||
|
||||
ShaderProgram::ShaderProgram(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
|
||||
: Resource(device)
|
||||
, vertexShader(vertexShader)
|
||||
, fragmentShader(fragmentShader)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderProgram::~ShaderProgram() {}
|
||||
|
||||
void ShaderProgram::dumpToFLog(const std::string& text, int channel)
|
||||
{
|
||||
std::vector<std::string> messages;
|
||||
boost::split(messages, text, boost::is_from_range('\n', '\n'));
|
||||
|
||||
while (!messages.empty() && messages.back().empty())
|
||||
messages.pop_back();
|
||||
|
||||
for (size_t i = 0; i < messages.size(); ++i)
|
||||
FASTLOGS(channel, "%s", messages[i]);
|
||||
}
|
||||
|
||||
ShaderGlobalConstant::ShaderGlobalConstant(const char* name, unsigned int offset, unsigned int size)
|
||||
: name(name)
|
||||
, offset(offset)
|
||||
, size(size)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
64
engine/gfx/src/Core/Shader.hpp
Normal file
64
engine/gfx/src/Core/Shader.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "Resource.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class VertexShader : public Resource
|
||||
{
|
||||
public:
|
||||
~VertexShader();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode) = 0;
|
||||
|
||||
protected:
|
||||
VertexShader(Device* device);
|
||||
};
|
||||
|
||||
class FragmentShader : public Resource
|
||||
{
|
||||
public:
|
||||
~FragmentShader();
|
||||
|
||||
virtual void reloadBytecode(const std::vector<char>& bytecode) = 0;
|
||||
|
||||
protected:
|
||||
FragmentShader(Device* device);
|
||||
};
|
||||
|
||||
class ShaderProgram : public Resource
|
||||
{
|
||||
public:
|
||||
~ShaderProgram();
|
||||
|
||||
virtual int getConstantHandle(const char* name) const = 0;
|
||||
|
||||
virtual unsigned int getMaxWorldTransforms() const = 0;
|
||||
virtual unsigned int getSamplerMask() const = 0;
|
||||
|
||||
static void dumpToFLog(const std::string& text, int channel);
|
||||
|
||||
protected:
|
||||
ShaderProgram(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
|
||||
|
||||
shared_ptr<VertexShader> vertexShader;
|
||||
shared_ptr<FragmentShader> fragmentShader;
|
||||
};
|
||||
|
||||
struct ShaderGlobalConstant
|
||||
{
|
||||
const char* name;
|
||||
unsigned int offset;
|
||||
unsigned int size;
|
||||
|
||||
ShaderGlobalConstant(const char* name, unsigned int offset, unsigned int size);
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
9
engine/gfx/src/Core/States.cpp
Normal file
9
engine/gfx/src/Core/States.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "Core/States.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
}
|
||||
} // namespace Aya
|
||||
339
engine/gfx/src/Core/States.hpp
Normal file
339
engine/gfx/src/Core/States.hpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct StateHasher
|
||||
{
|
||||
std::size_t operator()(const T& state) const
|
||||
{
|
||||
return state.getHashId();
|
||||
}
|
||||
};
|
||||
|
||||
class RasterizerState
|
||||
{
|
||||
public:
|
||||
enum CullMode
|
||||
{
|
||||
Cull_None,
|
||||
Cull_Back,
|
||||
Cull_Front,
|
||||
|
||||
Cull_Count
|
||||
};
|
||||
|
||||
RasterizerState(CullMode cullMode, int depthBias = 0)
|
||||
: cullMode(cullMode)
|
||||
, depthBias(depthBias)
|
||||
{
|
||||
}
|
||||
|
||||
CullMode getCullMode() const
|
||||
{
|
||||
return cullMode;
|
||||
}
|
||||
int getDepthBias() const
|
||||
{
|
||||
return depthBias;
|
||||
}
|
||||
|
||||
bool operator==(const RasterizerState& other) const
|
||||
{
|
||||
return cullMode == other.cullMode && depthBias == other.depthBias;
|
||||
}
|
||||
|
||||
bool operator!=(const RasterizerState& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size_t getHashId() const
|
||||
{
|
||||
size_t seed = 0;
|
||||
boost::hash_combine(seed, cullMode);
|
||||
boost::hash_combine(seed, depthBias);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private:
|
||||
CullMode cullMode;
|
||||
int depthBias;
|
||||
};
|
||||
|
||||
class BlendState
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
Mode_None,
|
||||
Mode_Additive,
|
||||
Mode_Multiplicative,
|
||||
Mode_AlphaBlend,
|
||||
Mode_PremultipliedAlphaBlend,
|
||||
Mode_Count
|
||||
};
|
||||
|
||||
enum Factor
|
||||
{
|
||||
Factor_One,
|
||||
Factor_Zero,
|
||||
Factor_DstColor,
|
||||
Factor_SrcAlpha,
|
||||
Factor_InvSrcAlpha,
|
||||
Factor_DstAlpha,
|
||||
Factor_InvDstAlpha,
|
||||
Factor_Count
|
||||
};
|
||||
|
||||
enum ColorMask
|
||||
{
|
||||
Color_None = 0,
|
||||
Color_R = 1 << 0,
|
||||
Color_G = 1 << 1,
|
||||
Color_B = 1 << 2,
|
||||
Color_A = 1 << 3,
|
||||
Color_All = Color_R | Color_G | Color_B | Color_A
|
||||
};
|
||||
|
||||
BlendState(Mode mode, unsigned int colorMask = Color_All)
|
||||
{
|
||||
struct Translation
|
||||
{
|
||||
Factor src;
|
||||
Factor dst;
|
||||
};
|
||||
|
||||
static const Translation translation[Mode_Count] = {
|
||||
{Factor_One, Factor_Zero},
|
||||
{Factor_One, Factor_One},
|
||||
{Factor_DstColor, Factor_Zero},
|
||||
{Factor_SrcAlpha, Factor_InvSrcAlpha},
|
||||
{Factor_One, Factor_InvSrcAlpha},
|
||||
};
|
||||
|
||||
construct(translation[mode].src, translation[mode].dst, translation[mode].src, translation[mode].dst, colorMask);
|
||||
}
|
||||
|
||||
BlendState(Factor colorSrcIn, Factor colorDstIn, Factor alphaSrcIn, Factor alphaDstIn, unsigned int colorMask = Color_All)
|
||||
{
|
||||
construct(colorSrcIn, colorDstIn, alphaSrcIn, alphaDstIn, colorMask);
|
||||
}
|
||||
|
||||
BlendState(Factor src, Factor dst, unsigned int colorMask = Color_All)
|
||||
{
|
||||
construct(src, dst, src, dst, colorMask);
|
||||
}
|
||||
|
||||
bool blendingNeeded() const
|
||||
{
|
||||
return colorSrc != Factor_One || colorDst != Factor_Zero || alphaSrc != Factor_One || alphaDst != Factor_Zero;
|
||||
}
|
||||
bool separateAlphaBlend() const
|
||||
{
|
||||
return colorSrc != alphaSrc || colorDst != alphaDst;
|
||||
}
|
||||
unsigned int getColorMask() const
|
||||
{
|
||||
return colorMask;
|
||||
}
|
||||
|
||||
unsigned int getColorSrc() const
|
||||
{
|
||||
return colorSrc;
|
||||
}
|
||||
unsigned int getColorDst() const
|
||||
{
|
||||
return colorDst;
|
||||
}
|
||||
unsigned int getAlphaSrc() const
|
||||
{
|
||||
return alphaSrc;
|
||||
}
|
||||
unsigned int getAlphaDst() const
|
||||
{
|
||||
return alphaDst;
|
||||
}
|
||||
|
||||
bool operator==(const BlendState& other) const
|
||||
{
|
||||
return colorSrc == other.colorSrc && colorDst == other.colorDst && alphaSrc == other.alphaSrc && alphaDst == other.alphaDst &&
|
||||
colorMask == other.colorMask;
|
||||
}
|
||||
|
||||
bool operator!=(const BlendState& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size_t getHashId() const
|
||||
{
|
||||
size_t seed = 0;
|
||||
boost::hash_combine(seed, colorSrc);
|
||||
boost::hash_combine(seed, colorDst);
|
||||
boost::hash_combine(seed, alphaSrc);
|
||||
boost::hash_combine(seed, alphaDst);
|
||||
boost::hash_combine(seed, colorMask);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private:
|
||||
void construct(Factor colorSrcIn, Factor colorDstIn, Factor alphaSrcIn, Factor alphaDstIn, unsigned int colorMask)
|
||||
{
|
||||
this->colorSrc = colorSrcIn;
|
||||
this->colorDst = colorDstIn;
|
||||
this->alphaSrc = alphaSrcIn;
|
||||
this->alphaDst = alphaDstIn;
|
||||
this->colorMask = colorMask;
|
||||
}
|
||||
|
||||
unsigned int colorMask;
|
||||
|
||||
Factor colorSrc;
|
||||
Factor colorDst;
|
||||
Factor alphaSrc;
|
||||
Factor alphaDst;
|
||||
};
|
||||
|
||||
class DepthState
|
||||
{
|
||||
public:
|
||||
enum Function
|
||||
{
|
||||
Function_Always,
|
||||
Function_Less,
|
||||
Function_LessEqual,
|
||||
Function_None,
|
||||
|
||||
Function_Count
|
||||
};
|
||||
|
||||
enum StencilMode
|
||||
{
|
||||
Stencil_None,
|
||||
Stencil_IsNotZero,
|
||||
Stencil_UpdateZFail,
|
||||
Stencil_Increment,
|
||||
Stencil_Decrement,
|
||||
Stencil_IsNotZeroReplace,
|
||||
|
||||
Stencil_Count
|
||||
};
|
||||
|
||||
DepthState(Function function, bool write, StencilMode stencilMode = Stencil_None)
|
||||
: function(function)
|
||||
, write(write)
|
||||
, stencilMode(stencilMode)
|
||||
{
|
||||
}
|
||||
|
||||
Function getFunction() const
|
||||
{
|
||||
return function;
|
||||
}
|
||||
bool getWrite() const
|
||||
{
|
||||
return write;
|
||||
}
|
||||
StencilMode getStencilMode() const
|
||||
{
|
||||
return stencilMode;
|
||||
}
|
||||
|
||||
bool operator==(const DepthState& other) const
|
||||
{
|
||||
return function == other.function && write == other.write && stencilMode == other.stencilMode;
|
||||
}
|
||||
|
||||
bool operator!=(const DepthState& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size_t getHashId() const
|
||||
{
|
||||
size_t seed = 0;
|
||||
boost::hash_combine(seed, function);
|
||||
boost::hash_combine(seed, write);
|
||||
boost::hash_combine(seed, stencilMode);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private:
|
||||
Function function;
|
||||
bool write;
|
||||
StencilMode stencilMode;
|
||||
};
|
||||
|
||||
class SamplerState
|
||||
{
|
||||
public:
|
||||
enum Filter
|
||||
{
|
||||
Filter_Point,
|
||||
Filter_Linear,
|
||||
Filter_Anisotropic,
|
||||
|
||||
Filter_Count
|
||||
};
|
||||
|
||||
enum Address
|
||||
{
|
||||
Address_Wrap,
|
||||
Address_Clamp,
|
||||
|
||||
Address_Count
|
||||
};
|
||||
|
||||
SamplerState(Filter filter, Address address = Address_Wrap, unsigned int anisotropy = 0)
|
||||
: filter(filter)
|
||||
, address(address)
|
||||
, anisotropy(anisotropy)
|
||||
{
|
||||
}
|
||||
|
||||
Filter getFilter() const
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
Address getAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
unsigned int getAnisotropy() const
|
||||
{
|
||||
return anisotropy;
|
||||
}
|
||||
|
||||
bool operator==(const SamplerState& other) const
|
||||
{
|
||||
return filter == other.filter && address == other.address && anisotropy == other.anisotropy;
|
||||
}
|
||||
|
||||
bool operator!=(const SamplerState& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size_t getHashId() const
|
||||
{
|
||||
size_t seed = 0;
|
||||
boost::hash_combine(seed, filter);
|
||||
boost::hash_combine(seed, address);
|
||||
boost::hash_combine(seed, anisotropy);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private:
|
||||
Filter filter;
|
||||
Address address;
|
||||
unsigned int anisotropy;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
172
engine/gfx/src/Core/Texture.cpp
Normal file
172
engine/gfx/src/Core/Texture.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "Core/Texture.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
FASTFLAGVARIABLE(GraphicsTextureCommitChanges, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
struct FormatDescription
|
||||
{
|
||||
unsigned char bpp;
|
||||
bool compressed;
|
||||
bool depth;
|
||||
};
|
||||
|
||||
static const FormatDescription gTextureFormats[Texture::Format_Count] = {
|
||||
{8, false, false},
|
||||
{16, false, false},
|
||||
{16, false, false},
|
||||
{32, false, false},
|
||||
{32, false, false},
|
||||
{64, false, false},
|
||||
{4, true, false},
|
||||
{8, true, false},
|
||||
{8, true, false},
|
||||
{2, true, false},
|
||||
{2, true, false},
|
||||
{4, true, false},
|
||||
{4, true, false},
|
||||
{4, true, false},
|
||||
{16, false, true},
|
||||
{32, false, true},
|
||||
};
|
||||
|
||||
TextureRegion::TextureRegion()
|
||||
: x(0)
|
||||
, y(0)
|
||||
, z(0)
|
||||
, width(0)
|
||||
, height(0)
|
||||
, depth(0)
|
||||
{
|
||||
}
|
||||
|
||||
TextureRegion::TextureRegion(unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth)
|
||||
: x(x)
|
||||
, y(y)
|
||||
, z(z)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, depth(depth)
|
||||
{
|
||||
}
|
||||
|
||||
TextureRegion::TextureRegion(unsigned int x, unsigned int y, unsigned int width, unsigned int height)
|
||||
: x(x)
|
||||
, y(y)
|
||||
, z(0)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, depth(1)
|
||||
{
|
||||
}
|
||||
|
||||
Texture::Texture(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
|
||||
: Resource(device)
|
||||
, type(type)
|
||||
, format(format)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, depth(depth)
|
||||
, mipLevels(mipLevels)
|
||||
, usage(usage)
|
||||
{
|
||||
AYAASSERT(width > 0 && height > 0 && depth > 0);
|
||||
AYAASSERT(mipLevels > 0 && mipLevels <= getMaxMipCount(width, height, depth));
|
||||
AYAASSERT(type == Type_3D || depth == 1);
|
||||
|
||||
if (usage != Usage_Renderbuffer)
|
||||
{
|
||||
AYAPROFILER_COUNTER_ADD("memory/gpu/texture", getTextureSize(type, format, width, height, depth, mipLevels));
|
||||
}
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (usage != Usage_Renderbuffer)
|
||||
{
|
||||
AYAPROFILER_COUNTER_SUB("memory/gpu/texture", getTextureSize(type, format, width, height, depth, mipLevels));
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::isFormatCompressed(Format format)
|
||||
{
|
||||
return gTextureFormats[format].compressed;
|
||||
}
|
||||
|
||||
bool Texture::isFormatDepth(Format format)
|
||||
{
|
||||
return gTextureFormats[format].depth;
|
||||
}
|
||||
|
||||
unsigned int Texture::getFormatBits(Format format)
|
||||
{
|
||||
return gTextureFormats[format].bpp;
|
||||
}
|
||||
|
||||
unsigned int Texture::getImageSize(Format format, unsigned int width, unsigned int height)
|
||||
{
|
||||
const FormatDescription& desc = gTextureFormats[format];
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case Format_BC1:
|
||||
case Format_BC2:
|
||||
case Format_BC3:
|
||||
case Format_ETC1:
|
||||
return ((width + 3) / 4) * ((height + 3) / 4) * (desc.bpp * 16 / 8);
|
||||
|
||||
case Format_PVRTC_RGB2:
|
||||
case Format_PVRTC_RGBA2:
|
||||
return (std::max(width, 16u) * std::max(height, 8u) * 2 + 7) / 8;
|
||||
|
||||
case Format_PVRTC_RGB4:
|
||||
case Format_PVRTC_RGBA4:
|
||||
return (std::max(width, 8u) * std::max(height, 8u) * 4 + 7) / 8;
|
||||
|
||||
default:
|
||||
AYAASSERT(!desc.compressed);
|
||||
return width * height * (desc.bpp / 8);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Texture::getTextureSize(Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
|
||||
for (unsigned int mip = 0; mip < mipLevels; ++mip)
|
||||
result += Texture::getImageSize(format, Texture::getMipSide(width, mip), Texture::getMipSide(height, mip)) * Texture::getMipSide(depth, mip);
|
||||
|
||||
return result * (type == Texture::Type_Cube ? 6 : 1);
|
||||
}
|
||||
|
||||
unsigned int Texture::getMipSide(unsigned int value, unsigned int mip)
|
||||
{
|
||||
unsigned int result = value >> mip;
|
||||
|
||||
return result ? result : 1;
|
||||
}
|
||||
|
||||
unsigned int Texture::getMaxMipCount(unsigned int width, unsigned int height, unsigned int depth)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
|
||||
while (width || height || depth)
|
||||
{
|
||||
result++;
|
||||
|
||||
width /= 2;
|
||||
height /= 2;
|
||||
depth /= 2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
149
engine/gfx/src/Core/Texture.hpp
Normal file
149
engine/gfx/src/Core/Texture.hpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "Resource.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class Renderbuffer;
|
||||
|
||||
struct TextureRegion
|
||||
{
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int z;
|
||||
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int depth;
|
||||
|
||||
TextureRegion();
|
||||
TextureRegion(unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth);
|
||||
TextureRegion(unsigned int x, unsigned int y, unsigned int width, unsigned int height);
|
||||
};
|
||||
|
||||
class Texture : public Resource
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Type_2D,
|
||||
Type_2DMultisampled,
|
||||
Type_3D,
|
||||
Type_Cube,
|
||||
|
||||
Type_Count
|
||||
};
|
||||
|
||||
enum Usage
|
||||
{
|
||||
Usage_Static,
|
||||
Usage_Dynamic,
|
||||
Usage_Renderbuffer,
|
||||
|
||||
Usage_Count
|
||||
};
|
||||
|
||||
enum Format
|
||||
{
|
||||
Format_L8,
|
||||
Format_LA8,
|
||||
Format_RGB5A1,
|
||||
Format_RGBA8,
|
||||
Format_RG16,
|
||||
Format_RGBA16F,
|
||||
Format_BC1,
|
||||
Format_BC2,
|
||||
Format_BC3,
|
||||
Format_PVRTC_RGB2,
|
||||
Format_PVRTC_RGBA2,
|
||||
Format_PVRTC_RGB4,
|
||||
Format_PVRTC_RGBA4,
|
||||
Format_ETC1,
|
||||
Format_D16,
|
||||
Format_D24S8,
|
||||
|
||||
Format_Count
|
||||
};
|
||||
|
||||
struct LockResult
|
||||
{
|
||||
void* data;
|
||||
unsigned int rowPitch;
|
||||
unsigned int slicePitch;
|
||||
};
|
||||
|
||||
~Texture();
|
||||
|
||||
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size) = 0;
|
||||
|
||||
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size) = 0;
|
||||
|
||||
virtual bool supportsLocking() const = 0;
|
||||
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region) = 0;
|
||||
virtual void unlock(unsigned int index, unsigned int mip) = 0;
|
||||
|
||||
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip) = 0;
|
||||
|
||||
virtual void commitChanges() = 0;
|
||||
virtual void generateMipmaps() = 0;
|
||||
|
||||
Type getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
Format getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
unsigned int getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
unsigned int getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
unsigned int getDepth() const
|
||||
{
|
||||
return depth;
|
||||
}
|
||||
|
||||
unsigned int getMipLevels() const
|
||||
{
|
||||
return mipLevels;
|
||||
}
|
||||
|
||||
Usage getUsage() const
|
||||
{
|
||||
return usage;
|
||||
}
|
||||
|
||||
static bool isFormatCompressed(Format format);
|
||||
static bool isFormatDepth(Format format);
|
||||
static unsigned int getFormatBits(Format format);
|
||||
static unsigned int getImageSize(Format format, unsigned int width, unsigned int height);
|
||||
|
||||
static unsigned int getTextureSize(Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels);
|
||||
|
||||
static unsigned int getMipSide(unsigned int value, unsigned int mip);
|
||||
static unsigned int getMaxMipCount(unsigned int width, unsigned int height, unsigned int depth);
|
||||
|
||||
protected:
|
||||
Texture(
|
||||
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
|
||||
|
||||
Type type;
|
||||
Format format;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int depth;
|
||||
unsigned int mipLevels;
|
||||
Usage usage;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
869
engine/gfx/src/Render/AdornRender.cpp
Normal file
869
engine/gfx/src/Render/AdornRender.cpp
Normal file
@@ -0,0 +1,869 @@
|
||||
|
||||
#include "Render/AdornRender.hpp"
|
||||
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
|
||||
#include "Render/VisualEngine.hpp"
|
||||
#include "Render/VertexStreamer.hpp"
|
||||
#include "Base/Typesetter.hpp"
|
||||
#include "Render/TextureManager.hpp"
|
||||
#include "Render/ShaderManager.hpp"
|
||||
#include "Render/TextureRef.hpp"
|
||||
|
||||
#include "Base/MeshGen.hpp"
|
||||
#include "Base/FrameRateManager.hpp"
|
||||
#include "Base/RenderStats.hpp"
|
||||
|
||||
#include "Core/Texture.hpp"
|
||||
#include "Core/Device.hpp"
|
||||
#include "Core/States.hpp"
|
||||
#include "Core/Shader.hpp"
|
||||
|
||||
#include "Utility/IndexBox.hpp"
|
||||
#include "Utility/Rotation2d.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define alloca _alloca
|
||||
#endif
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
struct AdornVertex
|
||||
{
|
||||
Vector3 position;
|
||||
Vector2 uv;
|
||||
Vector3 normal;
|
||||
|
||||
AdornVertex() {}
|
||||
|
||||
AdornVertex(const Vector3& position, const Vector3& normal)
|
||||
: position(position)
|
||||
, normal(normal)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static GeometryBatch* createBatch(
|
||||
Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<AdornVertex>& vertices, const std::vector<unsigned short>& indices)
|
||||
{
|
||||
shared_ptr<VertexBuffer> vbuf = device->createVertexBuffer(sizeof(AdornVertex), vertices.size(), GeometryBuffer::Usage_Static);
|
||||
|
||||
vbuf->upload(0, &vertices[0], vertices.size() * sizeof(AdornVertex));
|
||||
|
||||
shared_ptr<IndexBuffer> ibuf = device->createIndexBuffer(sizeof(unsigned short), indices.size(), GeometryBuffer::Usage_Static);
|
||||
|
||||
ibuf->upload(0, &indices[0], indices.size() * sizeof(unsigned short));
|
||||
|
||||
return new GeometryBatch(device->createGeometry(layout, vbuf, ibuf), Geometry::Primitive_Triangles, indices.size(), vertices.size());
|
||||
}
|
||||
|
||||
static GeometryBatch* createBox(Device* device, const shared_ptr<VertexLayout>& layout)
|
||||
{
|
||||
IndexBox box(-Vector3::one(), Vector3::one());
|
||||
|
||||
std::vector<AdornVertex> vertices;
|
||||
std::vector<unsigned short> indices;
|
||||
|
||||
for (int face = 0; face < 6; ++face)
|
||||
{
|
||||
Vector3 n = box.getFaceNormal(face);
|
||||
Vector3 v0, v1, v2, v3;
|
||||
box.getFaceCorners(face, v0, v1, v2, v3);
|
||||
|
||||
vertices.push_back(AdornVertex(v0, n));
|
||||
vertices.push_back(AdornVertex(v1, n));
|
||||
vertices.push_back(AdornVertex(v2, n));
|
||||
vertices.push_back(AdornVertex(v3, n));
|
||||
|
||||
indices.push_back(face * 4 + 0);
|
||||
indices.push_back(face * 4 + 1);
|
||||
indices.push_back(face * 4 + 2);
|
||||
|
||||
indices.push_back(face * 4 + 0);
|
||||
indices.push_back(face * 4 + 2);
|
||||
indices.push_back(face * 4 + 3);
|
||||
}
|
||||
|
||||
return createBatch(device, layout, vertices, indices);
|
||||
}
|
||||
|
||||
static GeometryBatch* createCylinderX(Device* device, const shared_ptr<VertexLayout>& layout, int sides, bool zeroNormalBottom)
|
||||
{
|
||||
std::vector<AdornVertex> vertices;
|
||||
std::vector<unsigned short> indices;
|
||||
|
||||
RotationAngle increment(360.f / sides);
|
||||
RotationAngle current;
|
||||
|
||||
// center vertices for caps
|
||||
vertices.push_back(AdornVertex(Vector3(-1, 0, 0), zeroNormalBottom ? Vector3::zero() : Vector3(-1, 0, 0)));
|
||||
vertices.push_back(AdornVertex(Vector3(+1, 0, 0), zeroNormalBottom ? Vector3::zero() : Vector3(+1, 0, 0)));
|
||||
|
||||
size_t vertexOffset = vertices.size();
|
||||
size_t verticesPerSide = 4;
|
||||
|
||||
for (int side = 0; side < sides; ++side)
|
||||
{
|
||||
Vector3 vcur(0, current.getSin(), current.getCos());
|
||||
|
||||
vertices.push_back(AdornVertex(vcur - Vector3(1, 0, 0), vcur));
|
||||
vertices.push_back(AdornVertex(vcur + Vector3(1, 0, 0), vcur));
|
||||
|
||||
vertices.push_back(AdornVertex(vcur - Vector3(1, 0, 0), zeroNormalBottom ? vcur : Vector3(-1, 0, 0)));
|
||||
vertices.push_back(AdornVertex(vcur + Vector3(1, 0, 0), zeroNormalBottom ? vcur : Vector3(+1, 0, 0)));
|
||||
|
||||
current = current.combine(increment);
|
||||
}
|
||||
|
||||
for (int side = 0; side < sides; ++side)
|
||||
{
|
||||
int side0 = side;
|
||||
int side1 = (side + 1) % sides;
|
||||
|
||||
// side quad
|
||||
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 0));
|
||||
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 1));
|
||||
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 1));
|
||||
|
||||
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 0));
|
||||
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 1));
|
||||
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 0));
|
||||
|
||||
// caps
|
||||
indices.push_back(0);
|
||||
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 2));
|
||||
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 2));
|
||||
|
||||
indices.push_back(1);
|
||||
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 3));
|
||||
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 3));
|
||||
}
|
||||
|
||||
return createBatch(device, layout, vertices, indices);
|
||||
}
|
||||
|
||||
static GeometryBatch* createSphere(Device* device, const shared_ptr<VertexLayout>& layout)
|
||||
{
|
||||
const int sidesU = 18;
|
||||
const int sidesV = 9;
|
||||
|
||||
std::vector<AdornVertex> vertices;
|
||||
std::vector<unsigned short> indices;
|
||||
|
||||
RotationAngle incrementU(360.f / sidesU);
|
||||
RotationAngle incrementV(180.f / sidesV);
|
||||
|
||||
Vector3 avg;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
RotationAngle u = (i & 1) ? incrementU : RotationAngle();
|
||||
RotationAngle v = (i & 2) ? incrementV : RotationAngle();
|
||||
|
||||
Vector3 pos(v.getSin() * u.getCos(), v.getSin() * u.getSin(), v.getCos());
|
||||
|
||||
avg += pos;
|
||||
}
|
||||
|
||||
float radius = 1 / (avg / 4).length();
|
||||
|
||||
RotationAngle currentU;
|
||||
|
||||
for (int u = 0; u < sidesU; ++u)
|
||||
{
|
||||
RotationAngle currentV;
|
||||
|
||||
for (int v = 0; v <= sidesV; ++v)
|
||||
{
|
||||
Vector3 pos(currentV.getSin() * currentU.getCos(), currentV.getSin() * currentU.getSin(), currentV.getCos());
|
||||
|
||||
vertices.push_back(AdornVertex(pos * radius, pos));
|
||||
|
||||
currentV = currentV.combine(incrementV);
|
||||
}
|
||||
|
||||
currentU = currentU.combine(incrementU);
|
||||
}
|
||||
|
||||
for (int u = 0; u < sidesU; ++u)
|
||||
{
|
||||
int u0 = u;
|
||||
int u1 = (u + 1) % sidesU;
|
||||
|
||||
for (int v = 0; v < sidesV; ++v)
|
||||
{
|
||||
int v0 = v;
|
||||
int v1 = v + 1;
|
||||
|
||||
indices.push_back(u0 * (sidesV + 1) + v0);
|
||||
indices.push_back(u1 * (sidesV + 1) + v1);
|
||||
indices.push_back(u1 * (sidesV + 1) + v0);
|
||||
|
||||
indices.push_back(u0 * (sidesV + 1) + v0);
|
||||
indices.push_back(u0 * (sidesV + 1) + v1);
|
||||
indices.push_back(u1 * (sidesV + 1) + v1);
|
||||
}
|
||||
}
|
||||
|
||||
return createBatch(device, layout, vertices, indices);
|
||||
}
|
||||
|
||||
static GeometryBatch* createCone(Device* device, const shared_ptr<VertexLayout>& layout)
|
||||
{
|
||||
const int sides = 12;
|
||||
|
||||
std::vector<AdornVertex> vertices;
|
||||
std::vector<unsigned short> indices;
|
||||
|
||||
RotationAngle increment(360.f / sides);
|
||||
RotationAngle current;
|
||||
|
||||
vertices.push_back(AdornVertex(Vector3(0, 0, 0), Vector3(-1, 0, 0)));
|
||||
|
||||
for (int side = 0; side < sides; ++side)
|
||||
{
|
||||
RotationAngle next = (side + 1 < sides) ? current.combine(increment) : RotationAngle();
|
||||
|
||||
Vector3 vcur(0, current.getSin(), current.getCos());
|
||||
Vector3 vnext(0, next.getSin(), next.getCos());
|
||||
Vector3 apex(1, 0, 0);
|
||||
|
||||
size_t vertexOffset = vertices.size();
|
||||
|
||||
vertices.push_back(AdornVertex(vcur, vcur));
|
||||
vertices.push_back(AdornVertex(vnext, vnext));
|
||||
vertices.push_back(AdornVertex(apex, (vcur + vnext).unit()));
|
||||
|
||||
vertices.push_back(AdornVertex(vcur, Vector3(-1, 0, 0)));
|
||||
vertices.push_back(AdornVertex(vnext, Vector3(-1, 0, 0)));
|
||||
|
||||
// side
|
||||
indices.push_back((unsigned short)(vertexOffset + 0));
|
||||
indices.push_back((unsigned short)(vertexOffset + 2));
|
||||
indices.push_back((unsigned short)(vertexOffset + 1));
|
||||
|
||||
// base
|
||||
indices.push_back(0);
|
||||
indices.push_back((unsigned short)(vertexOffset + 3));
|
||||
indices.push_back((unsigned short)(vertexOffset + 4));
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return createBatch(device, layout, vertices, indices);
|
||||
}
|
||||
|
||||
static GeometryBatch* createTorus(Device* device, const shared_ptr<VertexLayout>& layout)
|
||||
{
|
||||
const int sides = 12;
|
||||
|
||||
std::vector<AdornVertex> vertices;
|
||||
std::vector<unsigned short> indices;
|
||||
|
||||
RotationAngle increment(360.f / sides);
|
||||
RotationAngle current;
|
||||
|
||||
vertices.push_back(AdornVertex(Vector3(0, 0, 0), Vector3(-1, 0, 0)));
|
||||
|
||||
for (int side = 0; side < sides; ++side)
|
||||
{
|
||||
RotationAngle next = (side + 1 < sides) ? current.combine(increment) : RotationAngle();
|
||||
|
||||
Vector3 vcur(0, current.getSin(), current.getCos());
|
||||
Vector3 vnext(0, next.getSin(), next.getCos());
|
||||
Vector3 apex(1, 0, 0);
|
||||
|
||||
size_t vertexOffset = vertices.size();
|
||||
|
||||
vertices.push_back(AdornVertex(vcur, vcur));
|
||||
vertices.push_back(AdornVertex(vnext, vnext));
|
||||
vertices.push_back(AdornVertex(apex, (vcur + vnext).unit()));
|
||||
|
||||
vertices.push_back(AdornVertex(vcur, Vector3(-1, 0, 0)));
|
||||
vertices.push_back(AdornVertex(vnext, Vector3(-1, 0, 0)));
|
||||
|
||||
// side
|
||||
indices.push_back((unsigned short)(vertexOffset + 0));
|
||||
indices.push_back((unsigned short)(vertexOffset + 2));
|
||||
indices.push_back((unsigned short)(vertexOffset + 1));
|
||||
|
||||
// base
|
||||
indices.push_back(0);
|
||||
indices.push_back((unsigned short)(vertexOffset + 3));
|
||||
indices.push_back((unsigned short)(vertexOffset + 4));
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return createBatch(device, layout, vertices, indices);
|
||||
}
|
||||
|
||||
|
||||
AdornRender::AdornMaterial::AdornMaterial()
|
||||
: colorHandle(-1)
|
||||
, pixelInfoHandle(-1)
|
||||
{
|
||||
}
|
||||
|
||||
AdornRender::AdornMaterial::AdornMaterial(const shared_ptr<ShaderProgram>& program)
|
||||
: program(program)
|
||||
, colorHandle(program ? program->getConstantHandle("Color") : -1)
|
||||
, pixelInfoHandle(program ? program->getConstantHandle("PixelInfo") : -1)
|
||||
{
|
||||
}
|
||||
|
||||
AdornRender::AdornRender(VisualEngine* visualEngine, const DataModel* dataModel)
|
||||
: visualEngine(visualEngine)
|
||||
, dataModel(dataModel)
|
||||
, currentTextureType(BatchTextureType_Color)
|
||||
{
|
||||
std::vector<VertexLayout::Element> elements;
|
||||
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, position), VertexLayout::Format_Float3, VertexLayout::Semantic_Position));
|
||||
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, uv), VertexLayout::Format_Float2, VertexLayout::Semantic_Texture));
|
||||
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, normal), VertexLayout::Format_Float3, VertexLayout::Semantic_Normal));
|
||||
|
||||
shared_ptr<VertexLayout> layout = visualEngine->getDevice()->createVertexLayout(elements);
|
||||
|
||||
batchBox.reset(createBox(visualEngine->getDevice(), layout));
|
||||
batchCylinderX.reset(createCylinderX(visualEngine->getDevice(), layout, 12, false));
|
||||
batchSphere.reset(createSphere(visualEngine->getDevice(), layout));
|
||||
batchCone.reset(createCone(visualEngine->getDevice(), layout));
|
||||
batchAALineCylinderX.reset(createCylinderX(visualEngine->getDevice(), layout, 12, true));
|
||||
|
||||
materials[Material_Default] = visualEngine->getShaderManager()->getProgramOrFFP("AdornLightingVS", "AdornFS");
|
||||
materials[Material_NoLighting] = visualEngine->getShaderManager()->getProgramOrFFP("AdornVS", "AdornFS");
|
||||
materials[Material_SelfLit] = visualEngine->getShaderManager()->getProgramOrFFP("AdornSelfLitVS", "AdornFS");
|
||||
materials[Material_SelfLitHighlight] = visualEngine->getShaderManager()->getProgramOrFFP("AdornSelfLitHighlightVS", "AdornFS");
|
||||
materials[Material_AALine] = visualEngine->getShaderManager()->getProgramOrFFP("AdornAALineVS", "AdornAALineFS");
|
||||
materials[Material_Outline] = visualEngine->getShaderManager()->getProgramOrFFP("AdornOutlineVS", "AdornOutlineFS");
|
||||
}
|
||||
|
||||
Aya::Rect2D AdornRender::getViewport() const
|
||||
{
|
||||
return Rect2D::xywh(0, 0, visualEngine->getViewWidth(), visualEngine->getViewHeight());
|
||||
}
|
||||
|
||||
const Camera* AdornRender::getCamera() const
|
||||
{
|
||||
return dataModel->getWorkspace()->getConstCamera();
|
||||
}
|
||||
|
||||
void AdornRender::setTexture(int id, const Aya::TextureProxyBaseRef& t)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
currentTexture = boost::polymorphic_downcast<TextureProxy*>(t.get())->getTexture();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTexture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Rect2D AdornRender::getTextureSize(const Aya::TextureProxyBaseRef& texture) const
|
||||
{
|
||||
return Aya::Rect2D(texture->getOriginalSize());
|
||||
}
|
||||
|
||||
void AdornRender::rect2dImpl(
|
||||
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
|
||||
{
|
||||
Vector2 px0y0(x0y0);
|
||||
Vector2 px1y0(x1y0);
|
||||
Vector2 px0y1(x0y1);
|
||||
Vector2 px1y1(x1y1);
|
||||
|
||||
float height = currentHeight;
|
||||
|
||||
px0y0.y = height - px0y0.y;
|
||||
px1y0.y = height - px1y0.y;
|
||||
px0y1.y = height - px0y1.y;
|
||||
px1y1.y = height - px1y1.y;
|
||||
|
||||
visualEngine->getVertexStreamer()->rectBlt(currentTexture, color, px0y0, px1y0, px0y1, px1y1, tex0, tex1, currentTextureType, getIgnoreTexture());
|
||||
}
|
||||
|
||||
void AdornRender::line2d(const Aya::Vector2& p0, const Aya::Vector2& p1, const Aya::Color4& color)
|
||||
{
|
||||
visualEngine->getVertexStreamer()->line(p0.x, currentHeight - p0.y, p1.x, currentHeight - p1.y, &color[0]);
|
||||
}
|
||||
|
||||
Vector2 AdornRender::drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation)
|
||||
{
|
||||
const shared_ptr<Typesetter>& typesetter = visualEngine->getTypesetter(font);
|
||||
|
||||
if (!typesetter)
|
||||
return Vector2::zero();
|
||||
|
||||
const shared_ptr<Texture>& tex = typesetter->getTexture();
|
||||
|
||||
shared_ptr<Texture> oldTexture = tex;
|
||||
oldTexture.swap(currentTexture);
|
||||
currentTextureType = BatchTextureType_Font;
|
||||
|
||||
Vector2 result = typesetter->draw(target, s, position, size, autoScale, color, outline, xalign, yalign, availableSpace, clippingRect, rotation);
|
||||
|
||||
currentTextureType = BatchTextureType_Color;
|
||||
oldTexture.swap(currentTexture);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector2 AdornRender::get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
|
||||
{
|
||||
const shared_ptr<Typesetter>& typesetter = visualEngine->getTypesetter(font);
|
||||
|
||||
if (!typesetter)
|
||||
return Vector2::zero();
|
||||
|
||||
return typesetter->measure(s, size, availableSpace, NULL);
|
||||
}
|
||||
|
||||
TextureProxyBaseRef AdornRender::createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking, const std::string& context)
|
||||
{
|
||||
TextureRef texture = visualEngine->getTextureManager()->load(id, TextureManager::Fallback_None, context);
|
||||
|
||||
waiting = (texture.getStatus() == TextureRef::Status_Waiting);
|
||||
|
||||
if (texture.getStatus() == TextureRef::Status_Loaded)
|
||||
{
|
||||
return TextureProxyBaseRef(new TextureProxy(texture));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TextureProxyBaseRef();
|
||||
}
|
||||
}
|
||||
|
||||
TextureProxyBaseRef AdornRender::createTextureProxy(int width, int height)
|
||||
{
|
||||
return TextureProxyBaseRef(new TextureProxy(
|
||||
TextureRef(visualEngine->getDevice()->createTexture(Texture::Type_2D, Texture::Format_RGBA8, width, height, 1, 1, Texture::Usage_Static))));
|
||||
}
|
||||
|
||||
Aya::signal<void()>& AdornRender::getUnbindResourcesSignal()
|
||||
{
|
||||
return unbindResourcesSignal;
|
||||
}
|
||||
|
||||
void AdornRender::setObjectToWorldMatrix(const CoordinateFrame& c)
|
||||
{
|
||||
currentCFrame = c;
|
||||
}
|
||||
|
||||
static const float kSqrt3 = 1.7320508f;
|
||||
|
||||
void AdornRender::box(const AABox& box, const Color4& solidColor)
|
||||
{
|
||||
Vector3 center = currentCFrame.pointToWorldSpace(box.center());
|
||||
Vector3 extent = box.extent();
|
||||
|
||||
submitMesh(*batchBox, Material_NoLighting, center, currentCFrame.rotation, extent * 0.5f, solidColor, Sphere(center, extent.max() / 2 * kSqrt3));
|
||||
}
|
||||
|
||||
void AdornRender::box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
submitMesh(*batchBox, Material_NoLighting, cFrame.translation, cFrame.rotation, size, color, Sphere(cFrame.translation, size.max() / 2 * kSqrt3),
|
||||
0.0f, zIndex, alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornRender::cylinder(
|
||||
const CoordinateFrame& cFrame, const float radius, const float height, const Color4& color, const int zIndex, const bool alwaysOnTop)
|
||||
{
|
||||
submitMesh(*batchCylinderX, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(height / 2, radius, radius), color,
|
||||
Sphere(cFrame.translation, std::max(radius, height / 2) * kSqrt3), 0.0f, zIndex, alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornRender::cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap)
|
||||
{
|
||||
Vector3 center = currentCFrame.translation;
|
||||
|
||||
submitMesh(*batchCylinderX, getMaterial(), center, currentCFrame.rotation, Vector3(length / 2, radius, radius), solidColor,
|
||||
Sphere(center, std::max(radius, length / 2) * kSqrt3));
|
||||
}
|
||||
|
||||
void AdornRender::sphere(const Sphere& sphere, const Color4& solidColor)
|
||||
{
|
||||
Vector3 center = currentCFrame.pointToWorldSpace(sphere.center);
|
||||
float radius = sphere.radius;
|
||||
|
||||
submitMesh(*batchSphere, getMaterial(), center, currentCFrame.rotation, Vector3(radius, radius, radius), solidColor, Sphere(center, radius));
|
||||
}
|
||||
|
||||
void AdornRender::sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
submitMesh(*batchSphere, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(radius, radius, radius), color,
|
||||
Sphere(cFrame.translation, radius), 0.0f, zIndex, alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornRender::extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
|
||||
bool closeTrajectory, bool closeProfile)
|
||||
{
|
||||
float radius = profile->eval(0).length();
|
||||
|
||||
for (int i = 0; i < trajectorysegments; ++i)
|
||||
{
|
||||
Vector3 from = currentCFrame.pointToWorldSpace(trajectory->eval(static_cast<float>(i) / trajectorysegments));
|
||||
Vector3 to = currentCFrame.pointToWorldSpace(
|
||||
trajectory->eval((i + 1 == trajectorysegments && closeTrajectory) ? 0.f : static_cast<float>(i + 1) / trajectorysegments));
|
||||
|
||||
Vector3 axisX = (to - from).unit();
|
||||
Vector3 axisY = (G3D::abs(axisX.y) < 0.7f ? Vector3(0, 1, 0) : Vector3(1, 0, 0)).unitCross(axisX);
|
||||
Vector3 axisZ = axisX.unitCross(axisY);
|
||||
|
||||
Matrix3 rotation;
|
||||
rotation.setColumn(0, axisX);
|
||||
rotation.setColumn(1, axisY);
|
||||
rotation.setColumn(2, axisZ);
|
||||
|
||||
Vector3 center = (from + to) / 2;
|
||||
float length = (to - from).length();
|
||||
|
||||
submitMesh(*batchCylinderX, getMaterial(), center, rotation, Vector3(length / 2, radius, radius), color, Sphere(center, length / 2));
|
||||
}
|
||||
}
|
||||
|
||||
void AdornRender::axes(const Color4& xColor, const Color4& yColor, const Color4& zColor, float scale) {}
|
||||
|
||||
void AdornRender::cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
submitMesh(*batchCone, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(height, radius, radius), color,
|
||||
Sphere(cFrame.translation, std::max(radius, height) * kSqrt3), 0.0f, zIndex, alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornRender::ray(const RbxRay& ray, const Color4& color)
|
||||
{
|
||||
float length = ray.direction().length();
|
||||
|
||||
Sphere worldBounds(currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() / 2), length / 2);
|
||||
|
||||
const float axisStalkLength = 1.f;
|
||||
const float axisHeadLength = 0.3f;
|
||||
const float axisLength = axisStalkLength + axisHeadLength;
|
||||
const float axisHeadBaseRadius = 0.075f;
|
||||
const float axisStalkRadius = axisHeadBaseRadius / 3;
|
||||
|
||||
float scale = length / axisLength;
|
||||
|
||||
Vector3 stalkCenter = currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() / 2 * (axisStalkLength / axisLength));
|
||||
float stalkScaleX = axisStalkLength / 2 * scale;
|
||||
float stalkScaleYZ = axisStalkRadius * scale;
|
||||
|
||||
Vector3 coneOrigin = currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() * (axisStalkLength / axisLength));
|
||||
float coneScaleX = axisHeadLength * scale;
|
||||
float coneScaleYZ = axisHeadBaseRadius * scale;
|
||||
|
||||
Vector3 axisX = currentCFrame.vectorToWorldSpace(ray.direction().unit());
|
||||
Vector3 axisY = (G3D::abs(axisX.y) < 0.7f ? Vector3(0, 1, 0) : Vector3(1, 0, 0)).unitCross(axisX);
|
||||
Vector3 axisZ = axisX.unitCross(axisY);
|
||||
|
||||
Matrix3 rotation;
|
||||
rotation.setColumn(0, axisX);
|
||||
rotation.setColumn(1, axisY);
|
||||
rotation.setColumn(2, axisZ);
|
||||
|
||||
submitMesh(*batchCylinderX, getMaterial(), stalkCenter, rotation, Vector3(stalkScaleX, stalkScaleYZ, stalkScaleYZ), color, worldBounds);
|
||||
submitMesh(*batchCone, getMaterial(), coneOrigin, rotation, Vector3(coneScaleX, coneScaleYZ, coneScaleYZ), color, worldBounds);
|
||||
}
|
||||
|
||||
void AdornRender::line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
|
||||
{
|
||||
const Vector3 p0 = currentCFrame.vectorToWorldSpace(startPoint) + currentCFrame.translation;
|
||||
const Vector3 p1 = currentCFrame.vectorToWorldSpace(endPoint) + currentCFrame.translation;
|
||||
visualEngine->getVertexStreamer()->line3d(p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, &color[0]);
|
||||
}
|
||||
|
||||
void AdornRender::line3dAA(
|
||||
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
// these points cant be behind the camera position, lets project them on infinite plane defined by cam near plane
|
||||
const RenderCamera camera = visualEngine->getCamera();
|
||||
Vector3 camHeading = camera.getViewMatrix().upper3x3().row(2);
|
||||
Plane pl = Plane(camHeading, camera.getPosition() + camHeading * -0.5f);
|
||||
|
||||
float distanceStart = pl.distance(startPoint);
|
||||
float distanceEnd = pl.distance(endPoint);
|
||||
if (distanceStart > 0 && distanceEnd > 0)
|
||||
return; // nothing to do here
|
||||
|
||||
Vector3 startP = startPoint;
|
||||
if (distanceStart > 0)
|
||||
{
|
||||
Vector3 dir = (endPoint - startPoint).direction();
|
||||
RbxRay ray = RbxRay(startPoint, dir);
|
||||
startP = ray.intersectionPlane(pl);
|
||||
}
|
||||
|
||||
Vector3 endP = endPoint;
|
||||
if (distanceEnd > 0)
|
||||
{
|
||||
Vector3 dir = (startPoint - endPoint).direction();
|
||||
RbxRay ray = RbxRay(endPoint, dir);
|
||||
endP = ray.intersectionPlane(pl);
|
||||
}
|
||||
|
||||
Vector3 startToEnd = endP - startP;
|
||||
|
||||
Vector3 center = startP + startToEnd * 0.5f;
|
||||
Vector3 scale = Vector3(startToEnd.length() * 0.5f, 1, 1);
|
||||
|
||||
Matrix3 rotMat = Matrix3::identity();
|
||||
float lngth = fabs(startToEnd.direction().unit().dot(Vector3(1, 0, 0)));
|
||||
if (lngth < 1)
|
||||
{
|
||||
Vector3 rotAxis = startToEnd.direction().cross(Vector3(1, 0, 0));
|
||||
float rotAngle = -acos(startToEnd.direction().dot(Vector3(1, 0, 0)));
|
||||
rotMat = Matrix3::fromAxisAngle(rotAxis, rotAngle);
|
||||
}
|
||||
|
||||
submitMesh(*batchAALineCylinderX, Material_AALine, center, rotMat, scale, color, Sphere(center, scale.max() / 2 * kSqrt3), thickness, zIndex,
|
||||
alwaysOnTop);
|
||||
}
|
||||
|
||||
void AdornRender::quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color, const Vector2& v0tex,
|
||||
const Vector2& v2tex, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
if ((v2 - v0).isZero())
|
||||
return;
|
||||
|
||||
visualEngine->getVertexStreamer()->spriteBlt3D(currentTexture, &color[0], currentTextureType, currentCFrame.pointToWorldSpace(v0),
|
||||
currentCFrame.pointToWorldSpace(v1), currentCFrame.pointToWorldSpace(v2), currentCFrame.pointToWorldSpace(v3), v0tex, v2tex, zIndex,
|
||||
alwaysOnTop && zIndex >= 0);
|
||||
}
|
||||
|
||||
void AdornRender::convexPolygon2d(const Vector2* v, int countv, const Color4& color)
|
||||
{
|
||||
// swap from UI convention (0,0 top left), to GFX convention (0,0 bottom left)
|
||||
Vector2* vertices = (Vector2*)alloca(sizeof(Vector2) * countv);
|
||||
for (int i = 0; i < countv; ++i)
|
||||
{
|
||||
vertices[i] = Vector2(v[i].x, currentHeight - v[i].y);
|
||||
}
|
||||
|
||||
short* indices = (short*)alloca(sizeof(short) * (countv - 2) * 3);
|
||||
short start = 0;
|
||||
int icount = 0;
|
||||
for (int i = 1; i < countv - 1; ++i)
|
||||
{
|
||||
indices[icount++] = start;
|
||||
indices[icount++] = i;
|
||||
indices[icount++] = i + 1;
|
||||
}
|
||||
AYAASSERT(icount == (countv - 2) * 3);
|
||||
AYAASSERT(icount <= 0xFFFF);
|
||||
visualEngine->getVertexStreamer()->triangleList2d(color, vertices, countv, indices, icount);
|
||||
}
|
||||
|
||||
void AdornRender::convexPolygon(const Vector3* v, int countv, const Color4& color)
|
||||
{
|
||||
short* indices = (short*)alloca(sizeof(short) * (countv - 2) * 3);
|
||||
short start = 0;
|
||||
int icount = 0;
|
||||
for (int i = 1; i < countv - 1; ++i)
|
||||
{
|
||||
indices[icount++] = start;
|
||||
indices[icount++] = i;
|
||||
indices[icount++] = i + 1;
|
||||
}
|
||||
AYAASSERT(icount == (countv - 2) * 3);
|
||||
AYAASSERT(icount <= 0xFFFF);
|
||||
visualEngine->getVertexStreamer()->triangleList(color, currentCFrame, v, countv, indices, icount);
|
||||
}
|
||||
|
||||
bool AdornRender::isVisible(const Extents& extents, const CoordinateFrame& cframe)
|
||||
{
|
||||
return visualEngine->getCameraCullFrm().isVisible(extents, cframe);
|
||||
}
|
||||
|
||||
AdornRender::~AdornRender()
|
||||
{
|
||||
unbindResourcesSignal();
|
||||
}
|
||||
|
||||
void AdornRender::explosion(const Sphere& sphere) {}
|
||||
|
||||
void AdornRender::preSubmitPass()
|
||||
{
|
||||
currentHeight = visualEngine->getViewHeight();
|
||||
}
|
||||
|
||||
void AdornRender::postSubmitPass()
|
||||
{
|
||||
currentTexture.reset();
|
||||
}
|
||||
|
||||
struct AdornMeshMaterialComparator
|
||||
{
|
||||
bool operator()(const AdornMesh& lhs, const AdornMesh& rhs) const
|
||||
{
|
||||
return (lhs.material != rhs.material) ? lhs.material < rhs.material : memcmp(&lhs.color, &rhs.color, sizeof(lhs.color)) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct AdornMeshDistanceComparator
|
||||
{
|
||||
bool operator()(const AdornMesh& lhs, const AdornMesh& rhs) const
|
||||
{
|
||||
return lhs.distanceKey > rhs.distanceKey;
|
||||
}
|
||||
};
|
||||
|
||||
void AdornRender::prepareRenderPass()
|
||||
{
|
||||
std::sort(meshesOpaque.begin(), meshesOpaque.end(), AdornMeshMaterialComparator());
|
||||
std::sort(meshesTransparent.begin(), meshesTransparent.end(), AdornMeshDistanceComparator());
|
||||
for (unsigned i = 0; i < Adorn::maximumZIndex + 1; ++i)
|
||||
std::sort(meshesNoDepthTest[i].begin(), meshesNoDepthTest[i].end(), AdornMeshDistanceComparator());
|
||||
}
|
||||
|
||||
void AdornRender::finishRenderPass()
|
||||
{
|
||||
meshesOpaque.clear();
|
||||
meshesTransparent.clear();
|
||||
|
||||
for (unsigned i = 0; i < Adorn::maximumZIndex + 1; ++i)
|
||||
meshesNoDepthTest[i].clear();
|
||||
|
||||
visualEngine->getVertexStreamer()->cleanUpFrameData();
|
||||
}
|
||||
|
||||
void AdornRender::render(DeviceContext* context, RenderPassStats& stats)
|
||||
{
|
||||
AYAPROFILER_SCOPE("Render", "Adorns");
|
||||
AYAPROFILER_SCOPE("GPU", "Adorns");
|
||||
|
||||
shared_ptr<Texture> defaultTexture = visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White);
|
||||
AYAASSERT(defaultTexture);
|
||||
|
||||
PIX_SCOPE(context, "AR::Adorns");
|
||||
|
||||
context->bindTexture(0, defaultTexture.get(), SamplerState::Filter_Linear);
|
||||
|
||||
context->setDepthState(DepthState(DepthState::Function_LessEqual, true));
|
||||
context->setRasterizerState(RasterizerState::Cull_Back);
|
||||
context->setBlendState(BlendState::Mode_None);
|
||||
|
||||
renderMeshes(context, meshesOpaque, stats);
|
||||
|
||||
context->setBlendState(BlendState::Mode_AlphaBlend);
|
||||
|
||||
renderMeshes(context, meshesTransparent, stats);
|
||||
}
|
||||
|
||||
void AdornRender::renderNoDepth(DeviceContext* context, RenderPassStats& stats, int renderIndex)
|
||||
{
|
||||
shared_ptr<Texture> defaultTexture = visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White);
|
||||
AYAASSERT(defaultTexture);
|
||||
|
||||
PIX_SCOPE(context, "AR::Adorns");
|
||||
|
||||
context->bindTexture(0, defaultTexture.get(), SamplerState::Filter_Linear);
|
||||
|
||||
context->setRasterizerState(RasterizerState::Cull_Back);
|
||||
context->setBlendState(BlendState::Mode_AlphaBlend);
|
||||
|
||||
renderMeshes(context, meshesNoDepthTest[renderIndex], stats);
|
||||
}
|
||||
|
||||
void AdornRender::submitMesh(const GeometryBatch& batch, Material material, const Vector3& translation, const Matrix3& rotation, const Vector3& scale,
|
||||
const Color4& color, const Sphere& worldBounds, float thickness, int zIndex, bool alwaysOnTop)
|
||||
{
|
||||
const RenderCamera& camera = visualEngine->getCamera();
|
||||
|
||||
// Skip meshes if we don't know how to render them
|
||||
if (materials[material].colorHandle < 0)
|
||||
return;
|
||||
|
||||
// Visibility culling
|
||||
if (!camera.isVisible(worldBounds))
|
||||
return;
|
||||
|
||||
// FRM distance culling
|
||||
float sqDistance = (visualEngine->getCamera().getPosition() - worldBounds.center).squaredLength();
|
||||
|
||||
FrameRateManager* frm = visualEngine->getFrameRateManager();
|
||||
|
||||
if (sqDistance > frm->GetRenderCullSqDistance())
|
||||
return;
|
||||
|
||||
frm->AddBlockQuota(/* blockCount= */ 5, sqDistance, /* isInSpatialHash= */ false);
|
||||
|
||||
CoordinateFrame cframe(rotation * Matrix3::fromDiagonal(scale), translation);
|
||||
|
||||
AdornMesh mesh = {material, &batch, 0, thickness, zIndex, alwaysOnTop, color, cframe};
|
||||
|
||||
if (zIndex >= 0 && zIndex <= Adorn::maximumZIndex)
|
||||
{
|
||||
mesh.distanceKey = (visualEngine->getCamera().getPosition() - translation).squaredLength();
|
||||
meshesNoDepthTest[zIndex].push_back(mesh);
|
||||
}
|
||||
else if (color.a < 1 || material == Material_AALine)
|
||||
{
|
||||
mesh.distanceKey = (visualEngine->getCamera().getPosition() - translation).squaredLength();
|
||||
meshesTransparent.push_back(mesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
meshesOpaque.push_back(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
void AdornRender::renderMeshes(DeviceContext* context, const std::vector<AdornMesh>& meshes, RenderPassStats& stats)
|
||||
{
|
||||
Material currentMaterial = Material_Default;
|
||||
Color4 currentColor;
|
||||
|
||||
for (size_t i = 0; i < meshes.size(); ++i)
|
||||
{
|
||||
const AdornMesh& mesh = meshes[i];
|
||||
|
||||
if (mesh.zIndex >= 0)
|
||||
context->setDepthState(DepthState(mesh.alwaysOnTop ? DepthState::Function_Always : DepthState::Function_LessEqual, false));
|
||||
|
||||
if (i == 0 || currentMaterial != mesh.material)
|
||||
{
|
||||
currentMaterial = mesh.material;
|
||||
currentColor = mesh.color;
|
||||
|
||||
context->bindProgram(materials[currentMaterial].program.get());
|
||||
context->setConstant(materials[currentMaterial].colorHandle, ¤tColor.r, 1);
|
||||
|
||||
stats.passChanges++;
|
||||
}
|
||||
else if (currentColor != mesh.color)
|
||||
{
|
||||
currentColor = mesh.color;
|
||||
|
||||
context->setConstant(materials[currentMaterial].colorHandle, ¤tColor.r, 1);
|
||||
}
|
||||
|
||||
if (mesh.lineThickness > 0)
|
||||
{
|
||||
Vector2 screenSize = Vector2(visualEngine->getViewWidth(), visualEngine->getViewHeight());
|
||||
float pixelScale = tanf(dataModel->getWorkspace()->getCamera()->getFieldOfView() * 0.5f) / screenSize.y;
|
||||
Vector4 pixelInfo = Vector4(pixelScale, screenSize.x, screenSize.y / screenSize.x, mesh.lineThickness);
|
||||
|
||||
context->setConstant(materials[currentMaterial].pixelInfoHandle, &pixelInfo.x, 1);
|
||||
}
|
||||
|
||||
Matrix4 transform(mesh.cframe);
|
||||
|
||||
context->setWorldTransforms4x3(transform[0], 1);
|
||||
context->draw(*mesh.batch);
|
||||
|
||||
stats.batches++;
|
||||
stats.faces += mesh.batch->getCount() / 3;
|
||||
stats.vertices += mesh.batch->getIndexRangeEnd() - mesh.batch->getIndexRangeBegin();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
178
engine/gfx/src/Render/AdornRender.hpp
Normal file
178
engine/gfx/src/Render/AdornRender.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
|
||||
#include "Base/Adorn.hpp"
|
||||
#include "Base/TextureProxyBase.hpp"
|
||||
#include "Render/Image.hpp"
|
||||
|
||||
#include "Render/TextureRef.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "VertexStreamer.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class DataModel;
|
||||
struct RenderPassStats;
|
||||
} // namespace Aya
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class VisualEngine;
|
||||
class Texture;
|
||||
class ShaderProgram;
|
||||
class GeometryBatch;
|
||||
class DeviceContext;
|
||||
|
||||
struct AdornMesh
|
||||
{
|
||||
Adorn::Material material;
|
||||
const GeometryBatch* batch;
|
||||
|
||||
float distanceKey;
|
||||
float lineThickness; // this is only AA-line specific, but not worth creating special mesh type for it
|
||||
|
||||
int zIndex;
|
||||
bool alwaysOnTop;
|
||||
|
||||
Color4 color;
|
||||
|
||||
CoordinateFrame cframe;
|
||||
};
|
||||
|
||||
|
||||
class TextureProxy : public Aya::TextureProxyBase
|
||||
{
|
||||
public:
|
||||
TextureProxy(const TextureRef& texture)
|
||||
: texture(texture)
|
||||
{
|
||||
}
|
||||
|
||||
const shared_ptr<Texture>& getTexture() const
|
||||
{
|
||||
return texture.getTexture();
|
||||
}
|
||||
|
||||
virtual G3D::Vector2 getOriginalSize()
|
||||
{
|
||||
const ImageInfo& info = texture.getInfo();
|
||||
|
||||
return G3D::Vector2(info.width, info.height);
|
||||
}
|
||||
|
||||
private:
|
||||
TextureRef texture;
|
||||
};
|
||||
|
||||
|
||||
class AdornRender : public Adorn
|
||||
{
|
||||
public:
|
||||
AdornRender(VisualEngine* visualEngine, const DataModel* dataModel);
|
||||
virtual ~AdornRender();
|
||||
|
||||
virtual void prepareRenderPass();
|
||||
virtual void finishRenderPass();
|
||||
|
||||
void preSubmitPass();
|
||||
void postSubmitPass();
|
||||
|
||||
void render(DeviceContext* context, RenderPassStats& stats);
|
||||
void renderNoDepth(DeviceContext* context, RenderPassStats& stats, int renderIndex);
|
||||
|
||||
virtual Aya::Rect2D getViewport() const;
|
||||
virtual const Camera* getCamera() const;
|
||||
|
||||
TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "");
|
||||
TextureProxyBaseRef createTextureProxy(int width, int height);
|
||||
|
||||
virtual void setTexture(int id, const Aya::TextureProxyBaseRef& t);
|
||||
virtual Rect2D getTextureSize(const TextureProxyBaseRef& texture) const;
|
||||
|
||||
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
|
||||
const Vector2& tex1, const Color4& color);
|
||||
|
||||
virtual void line2d(const Aya::Vector2& p0, const Aya::Vector2& p1, const Aya::Color4& color);
|
||||
|
||||
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const;
|
||||
|
||||
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
|
||||
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
|
||||
const Rotation2D& rotation);
|
||||
|
||||
virtual void setObjectToWorldMatrix(const CoordinateFrame& c);
|
||||
|
||||
virtual void box(const AABox& box, const Color4& solidColor);
|
||||
virtual void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop);
|
||||
virtual void sphere(const Sphere& sphere, const Color4& solidColor);
|
||||
virtual void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop);
|
||||
virtual void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap);
|
||||
virtual void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop);
|
||||
virtual void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop);
|
||||
|
||||
virtual void ray(const RbxRay& ray, const Color4& color);
|
||||
virtual void axes(const Color4& xColor, const Color4& yColor, const Color4& zColor, float scale);
|
||||
virtual void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
|
||||
bool closeTrajectory, bool closeProfile);
|
||||
|
||||
virtual void explosion(const Sphere& sphere);
|
||||
|
||||
virtual void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color);
|
||||
virtual void line3dAA(
|
||||
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop);
|
||||
|
||||
virtual void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color, const Vector2& v0tex,
|
||||
const Vector2& v2tex, int zIndex, bool alwaysOnTop);
|
||||
|
||||
virtual void convexPolygon2d(const Vector2* v, int countv, const Color4& color);
|
||||
|
||||
virtual void convexPolygon(const Vector3* v, int countv, const Color4& color);
|
||||
|
||||
virtual bool isVisible(const Extents& extents, const CoordinateFrame& cframe);
|
||||
|
||||
private:
|
||||
struct AdornMaterial
|
||||
{
|
||||
shared_ptr<ShaderProgram> program;
|
||||
int colorHandle;
|
||||
int pixelInfoHandle;
|
||||
|
||||
AdornMaterial();
|
||||
AdornMaterial(const shared_ptr<ShaderProgram>& program);
|
||||
};
|
||||
|
||||
VisualEngine* visualEngine;
|
||||
const DataModel* dataModel;
|
||||
|
||||
Aya::signal<void()> unbindResourcesSignal;
|
||||
|
||||
float currentHeight;
|
||||
CoordinateFrame currentCFrame;
|
||||
shared_ptr<Texture> currentTexture;
|
||||
BatchTextureType currentTextureType;
|
||||
|
||||
Aya::signal<void()>& getUnbindResourcesSignal();
|
||||
|
||||
std::vector<AdornMesh> meshesOpaque;
|
||||
std::vector<AdornMesh> meshesTransparent;
|
||||
std::vector<AdornMesh> meshesNoDepthTest[Adorn::maximumZIndex + 1];
|
||||
|
||||
scoped_ptr<GeometryBatch> batchBox;
|
||||
scoped_ptr<GeometryBatch> batchCylinderX;
|
||||
scoped_ptr<GeometryBatch> batchSphere;
|
||||
scoped_ptr<GeometryBatch> batchCone;
|
||||
scoped_ptr<GeometryBatch> batchTorus;
|
||||
scoped_ptr<GeometryBatch> batchAALineCylinderX;
|
||||
|
||||
AdornMaterial materials[Material_Count];
|
||||
|
||||
void submitMesh(const GeometryBatch& batch, Material material, const Vector3& translation, const Matrix3& rotation, const Vector3& scale,
|
||||
const Color4& color, const Sphere& worldBounds, float thickness = 0, const int zIndex = -1, const bool alwaysOnTop = false);
|
||||
|
||||
void renderMeshes(DeviceContext* context, const std::vector<AdornMesh>& meshes, RenderPassStats& stats);
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
67
engine/gfx/src/Render/CullableSceneNode.cpp
Normal file
67
engine/gfx/src/Render/CullableSceneNode.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
#include "Render/CullableSceneNode.hpp"
|
||||
|
||||
#include "Render/SceneManager.hpp"
|
||||
#include "Render/Util.hpp"
|
||||
#include "Render/SpatialHashedScene.hpp"
|
||||
|
||||
#include "Render/VisualEngine.hpp"
|
||||
#include "Base/FrameRateManager.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
CullableSceneNode::CullableSceneNode(VisualEngine* visualEngine, CullMode cullMode, unsigned int flags)
|
||||
: visualEngine(visualEngine)
|
||||
, cullMode(cullMode)
|
||||
, flags(flags)
|
||||
, blockCount(1)
|
||||
, sqDistanceToFocus(0)
|
||||
{
|
||||
}
|
||||
|
||||
CullableSceneNode::~CullableSceneNode()
|
||||
{
|
||||
visualEngine->getSceneManager()->getSpatialHashedScene()->internalRemoveChild(this);
|
||||
}
|
||||
|
||||
bool CullableSceneNode::updateIsCulledByFRM()
|
||||
{
|
||||
if (worldBounds.isNull())
|
||||
return true;
|
||||
|
||||
const Vector3& focusPosition = visualEngine->getSceneManager()->getPointOfInterest();
|
||||
|
||||
sqDistanceToFocus = G3D::ClosestSqDistanceToAABB(focusPosition, worldBounds.center(), worldBounds.size() * 0.5f);
|
||||
|
||||
// count blocks and update farplane regardless of cullability.
|
||||
if (sqDistanceToFocus > 1e-3f)
|
||||
visualEngine->getSceneManager()->processSqPartDistance(sqDistanceToFocus);
|
||||
|
||||
Aya::FrameRateManager* frm = visualEngine->getFrameRateManager();
|
||||
|
||||
frm->AddBlockQuota(blockCount, sqDistanceToFocus, IsInSpatialHash());
|
||||
|
||||
// We don't do distance-cull on huge objects
|
||||
if (cullMode == CullMode_SpatialHash && !IsInSpatialHash())
|
||||
return false;
|
||||
else
|
||||
return sqDistanceToFocus > frm->GetRenderCullSqDistance();
|
||||
}
|
||||
|
||||
void CullableSceneNode::updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass) {}
|
||||
|
||||
void CullableSceneNode::updateWorldBounds(const Extents& aabb)
|
||||
{
|
||||
worldBounds = aabb;
|
||||
|
||||
if (aabb.isNull())
|
||||
visualEngine->getSceneManager()->getSpatialHashedScene()->internalRemoveChild(this);
|
||||
else
|
||||
visualEngine->getSceneManager()->getSpatialHashedScene()->internalUpdateChild(this);
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
129
engine/gfx/src/Render/CullableSceneNode.hpp
Normal file
129
engine/gfx/src/Render/CullableSceneNode.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "Base/GfxPart.hpp"
|
||||
#include "Utility/Extents.hpp"
|
||||
|
||||
#include "RenderQueue.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class Contact;
|
||||
}
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class RenderCamera;
|
||||
class RenderQueue;
|
||||
class VisualEngine;
|
||||
|
||||
class SpatialHashNode;
|
||||
|
||||
class CullableSceneNode : public Aya::GfxPart
|
||||
{
|
||||
public:
|
||||
static const bool hasGetFirstContact = false; // simulate __if_exists
|
||||
static Aya::Contact* getContact(CullableSceneNode* p0, CullableSceneNode* p1)
|
||||
{
|
||||
AYAASSERT(0);
|
||||
return NULL;
|
||||
} // shouldn't be called if hasGetFirstContact is false
|
||||
static Aya::Contact* getFirstContact()
|
||||
{
|
||||
AYAASSERT(NULL);
|
||||
return NULL;
|
||||
}
|
||||
static Aya::Contact* getNextContact(Aya::Contact* prev)
|
||||
{
|
||||
AYAASSERT(NULL);
|
||||
return NULL;
|
||||
}
|
||||
static int getNumContacts()
|
||||
{
|
||||
AYAASSERT(NULL);
|
||||
return 0;
|
||||
}
|
||||
static CullableSceneNode* getContactOther(int id)
|
||||
{
|
||||
AYAASSERT(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool requestFixed() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const Extents& getFastFuzzyExtents()
|
||||
{
|
||||
return worldBounds;
|
||||
}
|
||||
|
||||
public:
|
||||
enum CullMode
|
||||
{
|
||||
CullMode_None,
|
||||
CullMode_Simple,
|
||||
CullMode_SpatialHash,
|
||||
};
|
||||
|
||||
enum Flags
|
||||
{
|
||||
Flags_LightObject = 1 << 0,
|
||||
Flags_ShadowCaster = 1 << 1,
|
||||
};
|
||||
|
||||
CullableSceneNode(VisualEngine* visualEngine, CullMode cullMode, unsigned int flags);
|
||||
~CullableSceneNode();
|
||||
|
||||
void setBlockCount(int blockCount)
|
||||
{
|
||||
this->blockCount = blockCount;
|
||||
}
|
||||
|
||||
virtual void updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass);
|
||||
|
||||
float getSqDistanceToFocus() const
|
||||
{
|
||||
return sqDistanceToFocus;
|
||||
}
|
||||
|
||||
const Extents& getWorldBounds() const
|
||||
{
|
||||
return worldBounds;
|
||||
}
|
||||
|
||||
void updateWorldBounds(const Extents& aabb);
|
||||
|
||||
VisualEngine* getVisualEngine() const
|
||||
{
|
||||
return visualEngine;
|
||||
}
|
||||
|
||||
unsigned int getFlags() const
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool updateIsCulledByFRM();
|
||||
|
||||
Vector3 getCenter() const override
|
||||
{
|
||||
return worldBounds.center();
|
||||
}
|
||||
|
||||
private:
|
||||
VisualEngine* visualEngine;
|
||||
|
||||
CullMode cullMode;
|
||||
unsigned int flags;
|
||||
|
||||
int blockCount;
|
||||
float sqDistanceToFocus;
|
||||
|
||||
Extents worldBounds;
|
||||
};
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
351
engine/gfx/src/Render/CustomEmitter.cpp
Normal file
351
engine/gfx/src/Render/CustomEmitter.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
|
||||
|
||||
#include "Render/CustomEmitter.hpp"
|
||||
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/CustomParticleEmitter.hpp"
|
||||
|
||||
#include "Render/SceneUpdater.hpp"
|
||||
#include "Render/Util.hpp"
|
||||
|
||||
#include "Render/Emitter.hpp"
|
||||
#include "Render/TextureManager.hpp"
|
||||
|
||||
#include "Render/VisualEngine.hpp"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
CustomEmitter::CustomEmitter(VisualEngine* visualEngine)
|
||||
: Super(visualEngine, CullMode_SpatialHash)
|
||||
, dirtyFlags(0)
|
||||
, enabled(false)
|
||||
, localBox(Vector3::zero(), Vector3::zero())
|
||||
, requestedEmitCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
CustomEmitter::~CustomEmitter()
|
||||
{
|
||||
unbind();
|
||||
|
||||
// notify scene updater about destruction so that the pointer to CustomEmitter is no longer stored
|
||||
getVisualEngine()->getSceneUpdater()->notifyDestroyed(this);
|
||||
}
|
||||
|
||||
void CustomEmitter::onSleepingChangedEx(bool sleeping)
|
||||
{
|
||||
if (sleeping)
|
||||
{
|
||||
getVisualEngine()->getSceneUpdater()->notifySleeping(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
getVisualEngine()->getSceneUpdater()->notifyAwake(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::updateCoordinateFrame(bool recalcLocalBounds)
|
||||
{
|
||||
CoordinateFrame frame = part ? part->calcRenderingCoordinateFrame() : CoordinateFrame();
|
||||
|
||||
if ((dirtyFlags & kDirty_Transform) == 0 && transform.fuzzyEq(frame))
|
||||
{
|
||||
return; // Nothing to update
|
||||
}
|
||||
|
||||
transform = frame;
|
||||
if (emitter)
|
||||
{
|
||||
emitter->transform() = transform;
|
||||
localBox = emitter->computeBBox();
|
||||
updateWorldBounds(localBox.toWorldSpace(transform));
|
||||
|
||||
if (part)
|
||||
emitter->setVelocity(part->getVelocity());
|
||||
else
|
||||
emitter->setVelocity(Velocity());
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::onCombinedSignalEx(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Instance::PROPERTY_CHANGED:
|
||||
onPropertyChangedEx(boost::polymorphic_downcast<const Instance::PropertyChangedSignalData*>(data)->propertyDescriptor);
|
||||
break;
|
||||
case Instance::ANCESTRY_CHANGED:
|
||||
onAncestorChangedEx();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::onAncestorChangedEx()
|
||||
{
|
||||
shared_ptr<Instance> effectCopy = effect;
|
||||
|
||||
// Remove me from the scene if I am being removed from the Workspace
|
||||
if (!isInWorkspace(effectCopy.get()))
|
||||
{
|
||||
// will cause a delete on next updateEntity()
|
||||
zombify();
|
||||
}
|
||||
else
|
||||
{
|
||||
unbind();
|
||||
|
||||
Aya::PartInstance* parent = Aya::Instance::fastDynamicCast<Aya::PartInstance>(effectCopy->getParent());
|
||||
shared_ptr<Aya::PartInstance> part = shared_from(parent);
|
||||
|
||||
bind(part, effectCopy);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::bind(const shared_ptr<Aya::PartInstance>& part, const shared_ptr<Aya::Instance>& instance)
|
||||
{
|
||||
AYAASSERT(!this->part && !this->effect);
|
||||
this->part = part;
|
||||
this->effect = shared_from(instance->fastDynamicCast<Aya::CustomParticleEmitter>());
|
||||
|
||||
//
|
||||
|
||||
if (part)
|
||||
{
|
||||
connections.push_back(part->onDemandWrite()->sleepingChangedSignal.connect(boost::bind(&CustomEmitter::onSleepingChangedEx, this, _1)));
|
||||
|
||||
// we just connected, so sync up the state.
|
||||
onSleepingChangedEx(part->getSleeping());
|
||||
}
|
||||
|
||||
if (effect)
|
||||
{
|
||||
connections.push_back(this->effect->onEmitRequested.connect(boost::bind(&CustomEmitter::requestEmit, this, _1)));
|
||||
connections.push_back(instance->combinedSignal.connect(boost::bind(&CustomEmitter::onCombinedSignalEx, this, _1, _2)));
|
||||
|
||||
if (part)
|
||||
{
|
||||
connections.push_back(part->propertyChangedSignal.connect(boost::bind(&CustomEmitter::onParentSize, this, _1)));
|
||||
}
|
||||
}
|
||||
|
||||
dirtyFlags = kDirty_All;
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void CustomEmitter::unbind()
|
||||
{
|
||||
Super::unbind();
|
||||
part.reset();
|
||||
effect.reset();
|
||||
}
|
||||
|
||||
void CustomEmitter::requestEmit(int particleCount)
|
||||
{
|
||||
requestedEmitCount += particleCount;
|
||||
|
||||
dirtyFlags |= kDirty_Fast;
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void CustomEmitter::invalidateEntity()
|
||||
{
|
||||
if ((dirtyFlags & kDirty_Invalidate) == 0)
|
||||
{
|
||||
dirtyFlags |= kDirty_Invalidate; // prevent repeated submissions
|
||||
getVisualEngine()->getSceneUpdater()->queueInvalidatePart(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::updateEntity(bool assetsUpdated)
|
||||
{
|
||||
if (connections.empty()) // zombified.
|
||||
{
|
||||
getVisualEngine()->getSceneUpdater()->destroyAttachment(this);
|
||||
return;
|
||||
}
|
||||
|
||||
applyAllSettings();
|
||||
updateCoordinateFrame(true);
|
||||
|
||||
dirtyFlags = kDirty_None;
|
||||
}
|
||||
|
||||
void CustomEmitter::updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass)
|
||||
{
|
||||
if (!enabled && emitter && !emitter->pcount())
|
||||
emitter.reset();
|
||||
|
||||
if (emitter)
|
||||
{
|
||||
emitter->draw(queue);
|
||||
}
|
||||
|
||||
// Render bounding box
|
||||
if (getVisualEngine()->getSettings()->getDebugShowBoundingBoxes())
|
||||
debugRenderBoundingBox();
|
||||
}
|
||||
|
||||
static inline Vector2 v2(const NumberRange& r)
|
||||
{
|
||||
return Vector2(r.min, r.max);
|
||||
}
|
||||
|
||||
void CustomEmitter::applyAllSettings()
|
||||
{
|
||||
if (!effect)
|
||||
return;
|
||||
|
||||
if (!emitter)
|
||||
{
|
||||
emitter.reset(new Emitter(getVisualEngine(), true, effect->getFullName() + ".Texture"));
|
||||
dirtyFlags = kDirty_All;
|
||||
}
|
||||
|
||||
static const Vector2 zz(0, 0);
|
||||
static const Vector3 zzz(0, 0, 0);
|
||||
static const Vector4 zzzz(0, 0, 0, 0);
|
||||
static const Vector2 cc(1, 1);
|
||||
|
||||
if (dirtyFlags & kDirty_Fast)
|
||||
{
|
||||
Vector3 boxsize(1, 1, 1);
|
||||
|
||||
if (part)
|
||||
{
|
||||
boxsize = 0.5f * part->getRenderSize();
|
||||
}
|
||||
|
||||
enabled = effect->getEnabled();
|
||||
|
||||
emitter->setBlendRatio(1.0f - effect->getLightEmission());
|
||||
emitter->setEmissionRate(effect->getRate() * effect->getEnabled());
|
||||
emitter->setSpeed(v2(effect->getSpeed()));
|
||||
emitter->setSpread(cc * effect->getSpread() / 180 * M_PI);
|
||||
emitter->setRotation(v2(effect->getRotation()) / 180 * M_PI);
|
||||
emitter->setSpin(v2(effect->getRotSpeed()) / 180 * M_PI);
|
||||
emitter->setZOffset(effect->getZOffset());
|
||||
emitter->setLife(v2(effect->getLifetime()));
|
||||
emitter->setGlobalForce(effect->getAccel());
|
||||
|
||||
emitter->setEmitterShape(0, Box(-boxsize, boxsize));
|
||||
|
||||
emitter->setDampening(effect->getDampening());
|
||||
emitter->setLockedToLocalSpace(effect->getLockedToLocalSpace());
|
||||
|
||||
emitter->setVelocityInheritance(effect->getVelocityInheritance());
|
||||
|
||||
localBox = emitter->computeBBox();
|
||||
updateWorldBounds(localBox.toWorldSpace(transform));
|
||||
|
||||
// calculate the spherical direction
|
||||
Vector2 sphericalDirection;
|
||||
switch (effect->getEmissionDirection())
|
||||
{
|
||||
case Aya::NORM_Y:
|
||||
sphericalDirection = Vector2(M_PI / 2, M_PI / 2);
|
||||
break;
|
||||
case Aya::NORM_Y_NEG:
|
||||
sphericalDirection = Vector2(-M_PI / 2, M_PI / 2);
|
||||
break;
|
||||
case Aya::NORM_X:
|
||||
sphericalDirection = Vector2(0, M_PI / 2);
|
||||
break;
|
||||
case Aya::NORM_X_NEG:
|
||||
sphericalDirection = Vector2(0, -M_PI / 2);
|
||||
break;
|
||||
case Aya::NORM_Z_NEG:
|
||||
sphericalDirection = Vector2(0, M_PI);
|
||||
break;
|
||||
case Aya::NORM_Z:
|
||||
sphericalDirection = Vector2(0, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emitter->setSphericalDirection(sphericalDirection);
|
||||
|
||||
// emit!
|
||||
if (requestedEmitCount > 0)
|
||||
{
|
||||
updateCoordinateFrame(true);
|
||||
emitter->emit(requestedEmitCount);
|
||||
requestedEmitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyFlags & kDirty_Curves)
|
||||
{
|
||||
emitter->setColorCurve(&effect->getColor());
|
||||
emitter->setAlphaCurve(&effect->getTransparency());
|
||||
emitter->setSizeCurve(&effect->getSize());
|
||||
localBox = emitter->computeBBox();
|
||||
updateWorldBounds(localBox.toWorldSpace(transform));
|
||||
}
|
||||
|
||||
if (dirtyFlags & kDirty_Slow)
|
||||
{
|
||||
Emitter::Appearance a = {};
|
||||
a.alphaStripTexture = "";
|
||||
a.colorStripTexture = "";
|
||||
a.mainTexture = effect->getTexture().c_str();
|
||||
a.colorStripBaseline = -1;
|
||||
a.shader = Emitter::Shader_Custom;
|
||||
a.blendCode = Emitter::Blend_PremultipliedAlpha;
|
||||
emitter->setAppearance(a, effect->getFullName() + ".Texture");
|
||||
}
|
||||
}
|
||||
|
||||
void CustomEmitter::onPropertyChangedEx(const Aya::Reflection::PropertyDescriptor* p)
|
||||
{
|
||||
if (p == &CustomParticleEmitter::prop_texture)
|
||||
{
|
||||
dirtyFlags |= kDirty_Slow;
|
||||
invalidateEntity();
|
||||
return;
|
||||
}
|
||||
|
||||
if (p == &CustomParticleEmitter::prop_color)
|
||||
{
|
||||
dirtyFlags |= kDirty_Curves;
|
||||
invalidateEntity();
|
||||
return;
|
||||
}
|
||||
|
||||
if (p == &CustomParticleEmitter::prop_transp)
|
||||
{
|
||||
dirtyFlags |= kDirty_Curves;
|
||||
invalidateEntity();
|
||||
return;
|
||||
}
|
||||
|
||||
if (p == &CustomParticleEmitter::prop_size)
|
||||
{
|
||||
dirtyFlags |= kDirty_Curves;
|
||||
invalidateEntity();
|
||||
return;
|
||||
}
|
||||
|
||||
dirtyFlags |= kDirty_Fast;
|
||||
invalidateEntity();
|
||||
}
|
||||
|
||||
void CustomEmitter::onParentSize(pd p)
|
||||
{
|
||||
if (p == &PartInstance::prop_Size)
|
||||
{
|
||||
dirtyFlags |= kDirty_Fast;
|
||||
invalidateEntity();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Aya
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user