// Example low level rendering Unity plugin #include "PlatformBase.h" #include "RenderAPI.h" #include "IUnityLog.h" #include "IUnityRenderingExtensions.h" #include #include #include // -------------------------------------------------------------------------- // SetTimeFromUnity, an example function we export which is called by one of the scripts. static float g_Time; extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API SetTimeFromUnity (float t) { g_Time = t; } // -------------------------------------------------------------------------- // SetTextureFromUnity, an example function we export which is called by one of the scripts. static void* g_TextureHandle = NULL; static int g_TextureWidth = 0; static int g_TextureHeight = 0; static void* g_VertexBufferHandle = NULL; static int g_VertexBufferVertexCount; struct MeshVertex { float pos[3]; float normal[3]; float color[4]; float uv[2]; }; static std::vector g_VertexSource; // -------------------------------------------------------------------------- // UnitySetInterfaces static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType); static IUnityInterfaces* s_UnityInterfaces = NULL; static IUnityGraphics* s_Graphics = NULL; static IUnityLog* s_unityLogPtr = nullptr; void UnityLog(const char* msg) { UNITY_LOG(s_unityLogPtr, msg); } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces) { s_UnityInterfaces = unityInterfaces; s_Graphics = s_UnityInterfaces->Get(); s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent); s_unityLogPtr = s_UnityInterfaces->Get(); UnityLog("UnityPluginLoad"); #if SUPPORT_VULKAN if (s_Graphics->GetRenderer() == kUnityGfxRendererNull) { extern void RenderAPI_Vulkan_OnPluginLoad(IUnityInterfaces*); RenderAPI_Vulkan_OnPluginLoad(unityInterfaces); } #endif // SUPPORT_VULKAN // Run OnGraphicsDeviceEvent(initialize) manually on plugin load OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize); } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload() { s_Graphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent); } #if UNITY_WEBGL typedef void (UNITY_INTERFACE_API * PluginLoadFunc)(IUnityInterfaces* unityInterfaces); typedef void (UNITY_INTERFACE_API * PluginUnloadFunc)(); extern "C" void UnityRegisterRenderingPlugin(PluginLoadFunc loadPlugin, PluginUnloadFunc unloadPlugin); extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API RegisterPlugin() { UnityRegisterRenderingPlugin(UnityPluginLoad, UnityPluginUnload); } #endif // -------------------------------------------------------------------------- // GraphicsDeviceEvent static RenderAPI* s_CurrentAPI = NULL; static UnityGfxRenderer s_DeviceType = kUnityGfxRendererNull; static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType) { // Create graphics API implementation upon initialization if (eventType == kUnityGfxDeviceEventInitialize) { assert(s_CurrentAPI == NULL); s_DeviceType = s_Graphics->GetRenderer(); s_CurrentAPI = CreateRenderAPI(s_DeviceType); } // Let the implementation process the device related events if (s_CurrentAPI) { s_CurrentAPI->ProcessDeviceEvent(eventType, s_UnityInterfaces); } // Cleanup graphics API implementation upon shutdown if (eventType == kUnityGfxDeviceEventShutdown) { delete s_CurrentAPI; s_CurrentAPI = NULL; s_DeviceType = kUnityGfxRendererNull; } } // -------------------------------------------------------------------------- // OnRenderEvent // This will be called for GL.IssuePluginEvent script calls; eventID will // be the integer passed to IssuePluginEvent. In this example, we just ignore // that value. static void ModifyTexturePixels() { void* textureHandle = g_TextureHandle; int width = g_TextureWidth; int height = g_TextureHeight; if (!textureHandle) return; int textureRowPitch; void* textureDataPtr = s_CurrentAPI->BeginModifyTexture(textureHandle, width, height, &textureRowPitch); if (!textureDataPtr) return; const float t = g_Time * 4.0f; unsigned char* dst = (unsigned char*)textureDataPtr; for (int y = 0; y < height; ++y) { unsigned char* ptr = dst; for (int x = 0; x < width; ++x) { // Simple "plasma effect": several combined sine waves int vv = int( (127.0f + (127.0f * sinf(x / 7.0f + t))) + (127.0f + (127.0f * sinf(y / 5.0f - t))) + (127.0f + (127.0f * sinf((x + y) / 6.0f - t))) + (127.0f + (127.0f * sinf(sqrtf(float(x*x + y*y)) / 4.0f - t))) ) / 4; // Write the texture pixel ptr[0] = vv; ptr[1] = vv; ptr[2] = vv; ptr[3] = vv; // To next pixel (our pixels are 4 bpp) ptr += 4; } // To next image row dst += textureRowPitch; } s_CurrentAPI->EndModifyTexture(textureHandle, width, height, textureRowPitch, textureDataPtr); } static void ModifyVertexBuffer() { void* bufferHandle = g_VertexBufferHandle; int vertexCount = g_VertexBufferVertexCount; if (!bufferHandle) return; size_t bufferSize; void* bufferDataPtr = s_CurrentAPI->BeginModifyVertexBuffer(bufferHandle, &bufferSize); if (!bufferDataPtr) return; int vertexStride = int(bufferSize / vertexCount); // Unity should return us a buffer that is the size of `vertexCount * sizeof(MeshVertex)` // If that's not the case then we should quit to avoid unexpected results. // This can happen if https://docs.unity3d.com/ScriptReference/Mesh.GetNativeVertexBufferPtr.html returns // a pointer to a buffer with an unexpected layout. if (static_cast(vertexStride) != sizeof(MeshVertex)) return; const float t = g_Time * 3.0f; char* bufferPtr = (char*)bufferDataPtr; // modify vertex Y position with several scrolling sine waves, // copy the rest of the source data unmodified for (int i = 0; i < vertexCount; ++i) { const MeshVertex& src = g_VertexSource[i]; MeshVertex& dst = *(MeshVertex*)bufferPtr; dst.pos[0] = src.pos[0]; dst.pos[1] = src.pos[1] + sinf(src.pos[0] * 1.1f + t) * 0.4f + sinf(src.pos[2] * 0.9f - t) * 0.3f; dst.pos[2] = src.pos[2]; dst.normal[0] = src.normal[0]; dst.normal[1] = src.normal[1]; dst.normal[2] = src.normal[2]; dst.uv[0] = src.uv[0]; dst.uv[1] = src.uv[1]; bufferPtr += vertexStride; } s_CurrentAPI->EndModifyVertexBuffer(bufferHandle); } static void drawToPluginTexture() { s_CurrentAPI->drawToPluginTexture(); } static void drawToRenderTexture() { s_CurrentAPI->drawToRenderTexture(); } enum NativeRenderingEvent { EnableVRS = 1, DisableVRS, }; static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) { // Unknown / unsupported graphics device type? Do nothing if (s_CurrentAPI == NULL) return; switch ((NativeRenderingEvent)eventID) { case NativeRenderingEvent::EnableVRS: { s_CurrentAPI->enableVRS(*(int*)data); break; } case NativeRenderingEvent::DisableVRS: { s_CurrentAPI->disableVRS(); break; } default: break; } } extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetRenderEventAndDataFunc() { return OnRenderEventAndData; } // -------------------------------------------------------------------------- // DX12 plugin specific // -------------------------------------------------------------------------- extern "C" UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetRenderTexture() { return s_CurrentAPI->getRenderTexture(); } extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetRenderTexture(UnityRenderBuffer rb) { s_CurrentAPI->setRenderTextureResource(rb); } extern "C" UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsSwapChainAvailable() { return s_CurrentAPI->isSwapChainAvailable(); } extern "C" UNITY_INTERFACE_EXPORT unsigned int UNITY_INTERFACE_API GetPresentFlags() { return s_CurrentAPI->getPresentFlags(); } extern "C" UNITY_INTERFACE_EXPORT unsigned int UNITY_INTERFACE_API GetSyncInterval() { return s_CurrentAPI->getSyncInterval(); } extern "C" UNITY_INTERFACE_EXPORT unsigned int UNITY_INTERFACE_API GetBackBufferWidth() { return s_CurrentAPI->getBackbufferHeight(); } extern "C" UNITY_INTERFACE_EXPORT unsigned int UNITY_INTERFACE_API GetBackBufferHeight() { return s_CurrentAPI->getBackbufferWidth(); }