mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-02-03 12:31:15 +00:00
Implement lighting
This commit is contained in:
parent
6660082fef
commit
87f4c83ff2
@ -9,30 +9,35 @@
|
|||||||
|
|
||||||
bool g_rendering = false;
|
bool g_rendering = false;
|
||||||
|
|
||||||
#define DISPLAY_TRANSFER_FLAGS \
|
|
||||||
(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
|
|
||||||
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
|
|
||||||
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
|
|
||||||
|
|
||||||
static DVLB_s* vshader_dvlb;
|
static DVLB_s* vshader_dvlb;
|
||||||
static shaderProgram_s program;
|
static shaderProgram_s program;
|
||||||
static int uLoc_projection;
|
static int uLoc_projection;
|
||||||
static int uLoc_modelView;
|
static int uLoc_modelView;
|
||||||
static int uLoc_meshColor;
|
static int uLoc_meshColor;
|
||||||
|
static int uLoc_lightVec;
|
||||||
|
static int uLoc_lightClr;
|
||||||
|
static int uLoc_shininess;
|
||||||
|
|
||||||
Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height)
|
Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height)
|
||||||
{
|
{
|
||||||
m_width = 400;
|
m_width = 320;
|
||||||
m_height = 240;
|
m_height = 240;
|
||||||
m_virtualWidth = width;
|
m_virtualWidth = width;
|
||||||
m_virtualHeight = height;
|
m_virtualHeight = height;
|
||||||
|
|
||||||
gfxInitDefault();
|
gfxInitDefault();
|
||||||
consoleInit(GFX_BOTTOM, nullptr);
|
consoleInit(GFX_TOP, nullptr);
|
||||||
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
|
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
|
||||||
|
|
||||||
m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
|
m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
|
||||||
C3D_RenderTargetSetOutput(m_renderTarget, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
|
C3D_RenderTargetSetOutput(
|
||||||
|
m_renderTarget,
|
||||||
|
GFX_BOTTOM,
|
||||||
|
GFX_LEFT,
|
||||||
|
GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) |
|
||||||
|
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) |
|
||||||
|
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)
|
||||||
|
);
|
||||||
|
|
||||||
vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size);
|
vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size);
|
||||||
shaderProgramInit(&program);
|
shaderProgramInit(&program);
|
||||||
@ -44,6 +49,9 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height)
|
|||||||
uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
|
uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
|
||||||
uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView");
|
uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView");
|
||||||
uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor");
|
uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor");
|
||||||
|
uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec");
|
||||||
|
uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr");
|
||||||
|
uLoc_shininess = shaderInstanceGetUniformLocation(program.vertexShader, "shininess");
|
||||||
|
|
||||||
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
|
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
|
||||||
AttrInfo_Init(attrInfo);
|
AttrInfo_Init(attrInfo);
|
||||||
@ -60,9 +68,9 @@ Citro3DRenderer::~Citro3DRenderer()
|
|||||||
gfxExit();
|
gfxExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count)
|
void Citro3DRenderer::PushLights(const SceneLight* lights, size_t count)
|
||||||
{
|
{
|
||||||
MINIWIN_NOT_IMPLEMENTED();
|
m_lights.assign(lights, lights + count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back)
|
void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back)
|
||||||
@ -362,10 +370,59 @@ void Citro3DRenderer::StartFrame()
|
|||||||
C3D_FrameDrawOn(m_renderTarget);
|
C3D_FrameDrawOn(m_renderTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out)
|
||||||
|
{
|
||||||
|
float f_h = in[0][0];
|
||||||
|
float f_v = in[1][1];
|
||||||
|
|
||||||
|
float aspect = f_v / f_h;
|
||||||
|
float fovY = 2.0f * atanf(1.0f / f_v);
|
||||||
|
|
||||||
|
float nearZ = -in[3][2] / in[2][2];
|
||||||
|
float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f);
|
||||||
|
|
||||||
|
Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true);
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT Citro3DRenderer::BeginFrame()
|
HRESULT Citro3DRenderer::BeginFrame()
|
||||||
{
|
{
|
||||||
StartFrame();
|
StartFrame();
|
||||||
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL);
|
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL);
|
||||||
|
|
||||||
|
C3D_Mtx projection;
|
||||||
|
ConvertPerspective(m_projection, &projection);
|
||||||
|
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
|
||||||
|
|
||||||
|
for (const auto& light : m_lights) {
|
||||||
|
FColor lightColor = light.color;
|
||||||
|
if (light.positional == 0.0f && light.directional == 0.0f) {
|
||||||
|
// Ambient light
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, lightColor.r, lightColor.g, lightColor.b, 1.0f);
|
||||||
|
}
|
||||||
|
else if (light.directional == 1.0f) {
|
||||||
|
C3D_FVUnifSet(
|
||||||
|
GPU_VERTEX_SHADER,
|
||||||
|
uLoc_lightVec + 1,
|
||||||
|
-light.direction.x,
|
||||||
|
-light.direction.y,
|
||||||
|
-light.direction.z,
|
||||||
|
0.0f
|
||||||
|
);
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, lightColor.r, lightColor.g, lightColor.b, 0.0f);
|
||||||
|
}
|
||||||
|
else if (light.positional == 1.0f) {
|
||||||
|
C3D_FVUnifSet(
|
||||||
|
GPU_VERTEX_SHADER,
|
||||||
|
uLoc_lightVec + 0,
|
||||||
|
light.position.x,
|
||||||
|
light.position.y,
|
||||||
|
light.position.z,
|
||||||
|
0.0f
|
||||||
|
);
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, lightColor.r, lightColor.g, lightColor.b, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,20 +441,6 @@ void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out)
|
|
||||||
{
|
|
||||||
float f_h = in[0][0];
|
|
||||||
float f_v = in[1][1];
|
|
||||||
|
|
||||||
float aspect = f_v / f_h;
|
|
||||||
float fovY = 2.0f * atanf(1.0f / f_v);
|
|
||||||
|
|
||||||
float nearZ = -in[3][2] / in[2][2];
|
|
||||||
float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f);
|
|
||||||
|
|
||||||
Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Citro3DRenderer::SubmitDraw(
|
void Citro3DRenderer::SubmitDraw(
|
||||||
DWORD meshId,
|
DWORD meshId,
|
||||||
const D3DRMMATRIX4D& modelViewMatrix,
|
const D3DRMMATRIX4D& modelViewMatrix,
|
||||||
@ -407,12 +450,8 @@ void Citro3DRenderer::SubmitDraw(
|
|||||||
const Appearance& appearance
|
const Appearance& appearance
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
C3D_Mtx projection, modelView;
|
C3D_Mtx modelView;
|
||||||
|
|
||||||
ConvertPerspective(m_projection, &projection);
|
|
||||||
ConvertMatrix(modelViewMatrix, &modelView);
|
ConvertMatrix(modelViewMatrix, &modelView);
|
||||||
|
|
||||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
|
|
||||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
||||||
|
|
||||||
auto& mesh = m_meshs[meshId];
|
auto& mesh = m_meshs[meshId];
|
||||||
@ -430,6 +469,8 @@ void Citro3DRenderer::SubmitDraw(
|
|||||||
appearance.color.a / 255.0f
|
appearance.color.a / 255.0f
|
||||||
);
|
);
|
||||||
|
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, appearance.shininess / 255.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
if (appearance.textureId != NO_TEXTURE_ID) {
|
if (appearance.textureId != NO_TEXTURE_ID) {
|
||||||
C3D_TexBind(0, &m_textures[appearance.textureId].c3dTex);
|
C3D_TexBind(0, &m_textures[appearance.textureId].c3dTex);
|
||||||
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
||||||
@ -470,6 +511,8 @@ void Citro3DRenderer::Clear(float r, float g, float b)
|
|||||||
void Citro3DRenderer::Flip()
|
void Citro3DRenderer::Flip()
|
||||||
{
|
{
|
||||||
C3D_FrameEnd(0);
|
C3D_FrameEnd(0);
|
||||||
|
gfxFlushBuffers();
|
||||||
|
gspWaitForVBlank();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect)
|
void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect)
|
||||||
@ -489,6 +532,16 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con
|
|||||||
Mtx_Identity(&modelView);
|
Mtx_Identity(&modelView);
|
||||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
||||||
|
|
||||||
|
// Set light directions
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 0, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 1, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
// Set light colors
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, 1.0f, 1.0f, 1.0f, 1.0f); // Ambient
|
||||||
|
|
||||||
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f);
|
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
C3DTextureCacheEntry& texture = m_textures[textureId];
|
C3DTextureCacheEntry& texture = m_textures[textureId];
|
||||||
|
|||||||
@ -1,31 +1,29 @@
|
|||||||
; From https://github.com/devkitPro/citro3d/blob/master/test/3ds/source/vshader.v.pica
|
|
||||||
; zlib license
|
|
||||||
; ----------
|
|
||||||
|
|
||||||
; Example PICA200 vertex shader
|
|
||||||
|
|
||||||
; Uniforms
|
; Uniforms
|
||||||
.fvec projection[4], modelView[4], normView[2], meshColor
|
.fvec projection[4], modelView[4], meshColor
|
||||||
|
.fvec lightVec[2], lightClr[3], shininess
|
||||||
|
|
||||||
; Constants
|
; Constants
|
||||||
.constf myconst(0.0, 1.0, -1.0, -0.5)
|
.constf myconst(0.0, 1.0, -1.0, -0.5)
|
||||||
.alias zeros myconst.xxxx ; Vector full of zeros
|
|
||||||
.alias ones myconst.yyyy ; Vector full of ones
|
|
||||||
|
|
||||||
; Outputs
|
; Outputs
|
||||||
.out outpos position
|
.out outpos position
|
||||||
.out outtc0 texcoord0
|
.out outtc0 texcoord0
|
||||||
.out outclr color
|
.out outclr color
|
||||||
|
|
||||||
; Inputs (defined as aliases for convenience)
|
; Inputs
|
||||||
.alias inpos v0
|
.alias inpos v0
|
||||||
.alias innrm v1
|
.alias innrm v1
|
||||||
.alias intex v2
|
.alias intex v2
|
||||||
|
|
||||||
.proc main
|
.proc main
|
||||||
|
; Prepare constants in usable temp regs
|
||||||
|
mov r15.x, myconst.x ; 0.0
|
||||||
|
mov r15.y, myconst.y ; 1.0
|
||||||
|
mov r15.z, myconst.z ; -1.0
|
||||||
|
|
||||||
; Force the w component of inpos to be 1.0
|
; Force the w component of inpos to be 1.0
|
||||||
mov r0.xyz, inpos
|
mov r0.xyz, inpos
|
||||||
mov r0.w, ones
|
mov r0.w, r15.y
|
||||||
|
|
||||||
; r1 = modelView * inpos
|
; r1 = modelView * inpos
|
||||||
dp4 r1.x, modelView[0], r0
|
dp4 r1.x, modelView[0], r0
|
||||||
@ -42,9 +40,91 @@
|
|||||||
; outtex = intex
|
; outtex = intex
|
||||||
mov outtc0, intex
|
mov outtc0, intex
|
||||||
mov outtc0.zw, myconst.xy
|
mov outtc0.zw, myconst.xy
|
||||||
; outclr = ones
|
|
||||||
mov outclr, meshColor
|
|
||||||
|
|
||||||
; We're finished
|
; Transform normal
|
||||||
|
mov r2.xyz, innrm
|
||||||
|
mov r2.w, r15.x
|
||||||
|
dp4 r3.x, modelView[0], r2
|
||||||
|
dp4 r3.y, modelView[1], r2
|
||||||
|
dp4 r3.z, modelView[2], r2
|
||||||
|
mov r3.w, r15.x
|
||||||
|
dp3 r4.x, r3, r3
|
||||||
|
rsq r4.x, r4.x
|
||||||
|
mul r3, r4.xxxx, r3 ; r3 = normalized normal
|
||||||
|
|
||||||
|
; Normalize lightVec[0]
|
||||||
|
mov r5, lightVec[0]
|
||||||
|
dp3 r6.x, r5, r5
|
||||||
|
rsq r6.x, r6.x
|
||||||
|
mul r5, r6.xxxx, r5
|
||||||
|
|
||||||
|
; dot(normal, lightVec[0])
|
||||||
|
dp3 r6.x, r3, r5
|
||||||
|
max r6.x, r6.x, r15.xxxx
|
||||||
|
|
||||||
|
; Normalize lightVec[1]
|
||||||
|
mov r7, lightVec[1]
|
||||||
|
dp3 r8.x, r7, r7
|
||||||
|
rsq r8.x, r8.x
|
||||||
|
mul r7, r8.xxxx, r7
|
||||||
|
|
||||||
|
; dot(normal, lightVec[1])
|
||||||
|
dp3 r6.y, r3, r7
|
||||||
|
max r6.y, r6.y, r15.xxxx
|
||||||
|
|
||||||
|
; Load lightClr
|
||||||
|
mov r8, lightClr[2] ; ambient
|
||||||
|
mov r9, lightClr[0] ; point
|
||||||
|
mov r10, lightClr[1] ; directional
|
||||||
|
|
||||||
|
; diffuse = ambient + (lightClr[0] * dot0) + (lightClr[1] * dot1)
|
||||||
|
mul r11, r9, r6.xxxx
|
||||||
|
add r8, r8, r11
|
||||||
|
mul r11, r10, r6.yyyy
|
||||||
|
add r8, r8, r11 ; r8 = diffuse
|
||||||
|
|
||||||
|
; Check if shininess > 0
|
||||||
|
mov r12, shininess
|
||||||
|
slt r13.x, r15.x, r12.x
|
||||||
|
|
||||||
|
; viewVec = normalize(-position.xyz)
|
||||||
|
mov r14.xyz, r1.xyz
|
||||||
|
mul r14.xyz, r14.xyz, r15.zzz
|
||||||
|
dp3 r4.x, r14, r14
|
||||||
|
rsq r4.x, r4.x
|
||||||
|
mul r14, r4.xxxx, r14
|
||||||
|
|
||||||
|
; H = normalize(view + lightVec[1])
|
||||||
|
add r11, r14, r7
|
||||||
|
dp3 r4.x, r11, r11
|
||||||
|
rsq r4.x, r4.x
|
||||||
|
mul r11, r4.xxxx, r11
|
||||||
|
|
||||||
|
; dot(normal, H)
|
||||||
|
dp3 r4.x, r3, r11
|
||||||
|
max r4.x, r4.x, r15.x
|
||||||
|
|
||||||
|
; Approximate pow(dotNH, 10) by repeated multiplication
|
||||||
|
mul r5.x, r4.x, r4.x ; dotNH^2
|
||||||
|
mul r5.x, r5.x, r5.x ; dotNH^4
|
||||||
|
mul r5.x, r5.x, r5.x ; dotNH^8
|
||||||
|
mul r4.x, r5.x, r4.x ; dotNH^9
|
||||||
|
mul r4.x, r4.x, r4.x ; dotNH^10
|
||||||
|
|
||||||
|
; Multiply by shininess > 0 flag
|
||||||
|
mul r4.x, r4.x, r13.x
|
||||||
|
|
||||||
|
; specular = lightClr[1] * spec
|
||||||
|
mul r5, r10, r4.xxxx
|
||||||
|
|
||||||
|
; final = diffuse * meshColor + specular * lightClr[1]
|
||||||
|
mov r9, meshColor
|
||||||
|
mul r6, r8, r9 ; diffuse * meshColor
|
||||||
|
add r7.xyz, r6.xyz, r5.xyz ; add specular (already multiplied by lightClr)
|
||||||
|
min r7.xyz, r7.xyz, r15.yyyy
|
||||||
|
|
||||||
|
mov outclr.xyz, r7.xyz
|
||||||
|
mov outclr.w, meshColor.w
|
||||||
|
|
||||||
end
|
end
|
||||||
.end
|
.end
|
||||||
|
|||||||
@ -155,6 +155,10 @@ void Direct3DRMDevice2Impl::Resize()
|
|||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
SDL_GetWindowSizeInPixels(DDWindow, &width, &height);
|
SDL_GetWindowSizeInPixels(DDWindow, &width, &height);
|
||||||
|
#ifdef USE_CITRO3D
|
||||||
|
width = 320; // We are on the lower screen
|
||||||
|
height = 240;
|
||||||
|
#endif
|
||||||
m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height);
|
m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height);
|
||||||
m_renderer->Resize(width, height, m_viewportTransform);
|
m_renderer->Resize(width, height, m_viewportTransform);
|
||||||
for (int i = 0; i < m_viewports->GetSize(); i++) {
|
for (int i = 0; i < m_viewports->GetSize(); i++) {
|
||||||
|
|||||||
@ -64,6 +64,7 @@ class Citro3DRenderer : public Direct3DRMRenderer {
|
|||||||
std::vector<C3DTextureCacheEntry> m_textures;
|
std::vector<C3DTextureCacheEntry> m_textures;
|
||||||
std::vector<C3DMeshCacheEntry> m_meshs;
|
std::vector<C3DMeshCacheEntry> m_meshs;
|
||||||
ViewportTransform m_viewportTransform;
|
ViewportTransform m_viewportTransform;
|
||||||
|
std::vector<SceneLight> m_lights;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
|
inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user