306 lines
8.1 KiB
C++
Raw Normal View History

2024-11-01 16:55:46 +08:00
// Example low level rendering Unity plugin
#include "PlatformBase.h"
#include "RenderAPI.h"
#include "IUnityLog.h"
#include "IUnityRenderingExtensions.h"
#include <assert.h>
#include <math.h>
#include <vector>
// --------------------------------------------------------------------------
// 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<MeshVertex> 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<IUnityGraphics>();
s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
s_unityLogPtr = s_UnityInterfaces->Get<IUnityLog>();
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<unsigned int>(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();
}