mirror of
https://github.com/Pecusx/libretro-atari800.git
synced 2026-05-21 21:33:56 +02:00
405 lines
13 KiB
C++
405 lines
13 KiB
C++
#include "config.h"
|
|
#include "screen.h"
|
|
#include "render_direct3d.h"
|
|
#include <windows.h>
|
|
|
|
#include <d3d9.h>
|
|
#include <d3dx9.h>
|
|
|
|
extern "C" HWND hWndMain;
|
|
extern "C" {
|
|
#include "colours.h"
|
|
#include "log.h"
|
|
}
|
|
|
|
typedef D3DXMATRIX* (WINAPI* D3DXMatrixLookAtLHFunc)(D3DXMATRIX*, CONST D3DXVECTOR3*, CONST D3DXVECTOR3*, CONST D3DXVECTOR3*);
|
|
typedef D3DXMATRIX* (WINAPI* D3DXMatrixPerspectiveFovLHFunc)(D3DXMATRIX*, FLOAT, FLOAT, FLOAT, FLOAT);
|
|
typedef D3DXMATRIX* (WINAPI* D3DXMatrixRotationYFunc)(D3DXMATRIX*, FLOAT);
|
|
|
|
// D3D declarations
|
|
static LPDIRECT3D9 d3d;
|
|
static LPDIRECT3DDEVICE9 d3d_device;
|
|
static LPDIRECT3DVERTEXBUFFER9 vertex_buffer = NULL;
|
|
static LPDIRECT3DTEXTURE9 texture_buffer = NULL;
|
|
static D3DPRESENT_PARAMETERS d3dpp;
|
|
|
|
struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR; FLOAT U, V;};
|
|
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
|
|
|
|
static float const pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862f;
|
|
|
|
static float texturehorizclip = 0.0f;
|
|
static float texturevertclip = 0.0f;
|
|
|
|
static HMODULE hD3DX = NULL;
|
|
static D3DXMatrixLookAtLHFunc D3DXMatrixLookAtLHPtr = NULL;
|
|
static D3DXMatrixPerspectiveFovLHFunc D3DXMatrixPerspectiveFovLHPtr = NULL;
|
|
static D3DXMatrixRotationYFunc D3DXMatrixRotationYPtr = NULL;
|
|
|
|
extern "C" void startupdirect3d(int screenwidth, int screenheight, BOOL windowed, FRAMEPARAMS *fp)
|
|
{
|
|
// look for the newest, oldest, then generic versions
|
|
if ((hD3DX = LoadLibrary("d3dx9_42.dll")) != NULL || (hD3DX = LoadLibrary("d3dx9_24.dll")) != NULL || (hD3DX = LoadLibrary("d3dx9.dll")) != NULL)
|
|
{
|
|
D3DXMatrixLookAtLHPtr = (D3DXMatrixLookAtLHFunc) GetProcAddress(hD3DX, "D3DXMatrixLookAtLH");
|
|
D3DXMatrixPerspectiveFovLHPtr = (D3DXMatrixPerspectiveFovLHFunc) GetProcAddress(hD3DX, "D3DXMatrixPerspectiveFovLH");
|
|
D3DXMatrixRotationYPtr = (D3DXMatrixRotationYFunc) GetProcAddress(hD3DX, "D3DXMatrixRotationY");
|
|
// unlikely failure to load function pointers
|
|
if (D3DXMatrixLookAtLHPtr == NULL || D3DXMatrixPerspectiveFovLHPtr == NULL || D3DXMatrixRotationYPtr == NULL)
|
|
{
|
|
FreeLibrary(hD3DX);
|
|
hD3DX = NULL;
|
|
Log_print("Extended Direct3D functions disabled - load failure");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log_print("Extended Direct3D functions disabled - updated DirectX runtime needed");
|
|
}
|
|
|
|
d3d = Direct3DCreate9(D3D_SDK_VERSION);
|
|
|
|
initpresentparams(screenwidth, screenheight, windowed);
|
|
|
|
// create the D3D device
|
|
d3d->CreateDevice(D3DADAPTER_DEFAULT,
|
|
D3DDEVTYPE_HAL,
|
|
hWndMain,
|
|
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
|
|
&d3dpp,
|
|
&d3d_device);
|
|
|
|
initdevice(fp);
|
|
|
|
return;
|
|
}
|
|
|
|
// Function used when we need to change present params
|
|
void resetdevice(int screenwidth, int screenheight, BOOL windowed, FRAMEPARAMS *fp)
|
|
{
|
|
texture_buffer->Release(); // close and release the texture
|
|
initpresentparams(screenwidth, screenheight, windowed);
|
|
d3d_device->Reset(&d3dpp);
|
|
initdevice(fp);
|
|
return;
|
|
}
|
|
|
|
void initpresentparams(int screenwidth, int screenheight, BOOL windowed)
|
|
{
|
|
ZeroMemory(&d3dpp, sizeof(d3dpp));
|
|
d3dpp.Windowed = windowed;
|
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
d3dpp.hDeviceWindow = hWndMain;
|
|
d3dpp.BackBufferWidth = screenwidth;
|
|
d3dpp.BackBufferHeight = screenheight;
|
|
d3dpp.EnableAutoDepthStencil = TRUE;
|
|
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
|
|
|
|
// Only need this in fullscreen mode.
|
|
if (!windowed) {
|
|
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void initdevice(FRAMEPARAMS *fp)
|
|
{
|
|
UINT texturesize;
|
|
D3DTEXTUREFILTERTYPE filtertype;
|
|
|
|
// compute a horizontal & vertical offset
|
|
// with compensation for possible cropping & stretching
|
|
int clip_width = fp->view.right - fp->view.left;
|
|
int clip_height = fp->view.bottom - fp->view.top;
|
|
|
|
// set the type of interpolation filtering selected by the user
|
|
// only bilinear or none are available.
|
|
if (fp->filter == BILINEAR)
|
|
filtertype = D3DTEXF_LINEAR;
|
|
else
|
|
filtertype = D3DTEXF_POINT;
|
|
|
|
d3d_device->SetRenderState(D3DRS_LIGHTING, FALSE); // turn off the 3D lighting
|
|
d3d_device->SetRenderState(D3DRS_ZENABLE, TRUE); // turn on the z-buffer
|
|
d3d_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, filtertype);
|
|
d3d_device->SetFVF(CUSTOMFVF);
|
|
|
|
// In Direct3D we paint the Atari screen onto a texture that overlays a vertex buffer polygon.
|
|
// Although most newer graphics cards support rectangular textures of variable size,
|
|
// for compatibility and performance reasons, it is better that the texture be square with
|
|
// sides that are a power of two. Since the size of the bitmap changes depending
|
|
// on the scanline mode, we set that here. We also set the clipping parameters here
|
|
// that we'll use in the vertex buffer init to only map the drawn portion of the
|
|
// texture to the vertex buffer.
|
|
|
|
switch (fp->scanlinemode)
|
|
{
|
|
case NONE: // we are just mapping the standard 320x240 Atari screen bitmap.
|
|
texturesize = 512;
|
|
texturehorizclip = clip_width / (float)texturesize;
|
|
texturevertclip = clip_height / (float)texturesize;
|
|
break;
|
|
case LOW: // LOW res scanlines produces a 320x480 pixel bitmap
|
|
texturesize = 512;
|
|
texturehorizclip = clip_width / (float)texturesize;
|
|
texturevertclip = (clip_height * 2 + 1) / (float)texturesize;
|
|
break;
|
|
case MEDIUM: // MEDIUM res scanlines produces a 320x720 pixel bitmap
|
|
texturesize = 1024;
|
|
texturehorizclip = clip_width / (float)texturesize;
|
|
texturevertclip = (clip_height * 3 + 2) / (float)texturesize;
|
|
break;
|
|
case HIGH: // HIGH res canlines produces a 320x960 pixel bitmap
|
|
texturesize = 1024;
|
|
texturehorizclip = clip_width / (float)texturesize;
|
|
texturevertclip = (clip_height * 4 + 3) / (float)texturesize;
|
|
break;
|
|
|
|
}
|
|
|
|
// now lets go ahead and create a blank texture of the appropriate dimensions
|
|
d3d_device->CreateTexture(texturesize, texturesize, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture_buffer, NULL );
|
|
return;
|
|
}
|
|
|
|
// this function creates the 2D vertex buffer polygon and attaches the texture to it
|
|
void init_vertices(float x, float y, float z)
|
|
{
|
|
// Create the vertices.
|
|
// What we're actually doing here is specifying two right triangles
|
|
// attached to one another to form a wireframe rectangle. We also specify
|
|
// that our texture (the screen) should be mapped to extend to the edges
|
|
// of this rectangle. This is basically how we create a 2D surface with
|
|
// a 3D toolset.
|
|
|
|
struct CUSTOMVERTEX vertex_def[] =
|
|
{
|
|
{-x, y, z, 0xffffffff, 0, 0,},
|
|
{x, y, z, 0xffffffff, texturehorizclip, 0,},
|
|
{-x, -y, -z, 0xffffffff, 0, texturevertclip,},
|
|
{x, -y, -z, 0xffffffff, texturehorizclip, texturevertclip,},
|
|
};
|
|
|
|
d3d_device->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX),
|
|
0,
|
|
CUSTOMFVF,
|
|
D3DPOOL_MANAGED,
|
|
&vertex_buffer,
|
|
NULL);
|
|
|
|
VOID* pVoid; // a void pointer
|
|
|
|
// lock vertex_buffer and load the vertices into it
|
|
vertex_buffer->Lock(0, 0, (void**)&pVoid, 0);
|
|
memcpy(pVoid, vertex_def, sizeof(vertex_def));
|
|
vertex_buffer->Unlock();
|
|
|
|
return;
|
|
}
|
|
|
|
// this is the function used to render each frame
|
|
void refreshv_direct3d(UBYTE *scr_ptr, FRAMEPARAMS *fp)
|
|
{
|
|
// clear the contents of the vertex buffer polygon and make it black
|
|
d3d_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
|
|
d3d_device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
|
|
|
|
// If the caller has set fp->d3dRefresh to true it means that the vertex
|
|
// buffer has changed (usually because the window or texture size has changed).
|
|
// In these cases we will re-init the vertices prior to building the frame.
|
|
if (fp->d3dRefresh)
|
|
{
|
|
if (hD3DX != NULL)
|
|
{
|
|
init_vertices(fp->d3dWidth, 0, fp->d3dHeight);
|
|
}
|
|
else
|
|
{
|
|
init_vertices(fp->d3dWidth, fp->d3dHeight, 0);
|
|
}
|
|
fp->d3dRefresh = FALSE;
|
|
}
|
|
|
|
// We can now unlock the backbuffer and start to draw our frame
|
|
d3d_device->BeginScene();
|
|
|
|
// refresh the texture buffer for the frame
|
|
// this function call actually draws the contents of the Atari screen buffer
|
|
// to the Direct3D backbuffer.
|
|
refresh_frame(scr_ptr, fp);
|
|
|
|
// set the texture
|
|
d3d_device->SetTexture(0, texture_buffer);
|
|
|
|
// select the vertex buffer to display
|
|
d3d_device->SetStreamSource(0, vertex_buffer, 0, sizeof(CUSTOMVERTEX));
|
|
|
|
//***********************************************************************
|
|
// This next section supports the 3D screensaver and tilt functions by
|
|
// using some simple matrix tranformations on the vertex buffer polygon.
|
|
// Note: If you want to remove this 3D screensaver effect and operate
|
|
// in a more purely 2D view, change the init_vertices statement above to
|
|
// init_vertices(fp->d3dWidth, fp->d3dHeight, 0);
|
|
// and remove all statements from here to the end of this
|
|
// 3D Matrix Transformations section.
|
|
//***********************************************************************
|
|
if (hD3DX != NULL)
|
|
{
|
|
|
|
D3DXMATRIX matView;
|
|
|
|
static float index = pi; // rotation amount
|
|
static float cy = 2.41f; // amount of zoom
|
|
static float cz = 0.00001f; // amount of tilt
|
|
static float by = 0.0;
|
|
|
|
if (fp->screensaver)
|
|
{
|
|
fp->tiltlevel = TILTLEVEL0; // make sure tiltlevel is disabled
|
|
|
|
// this transformation creates and animates the 3D screensaver
|
|
|
|
if (cy > 1.5f)
|
|
cy -= 0.002f; // zoom
|
|
if (cz < 2.2)
|
|
cz += 0.005f; // tilt
|
|
|
|
index+=0.005f; // rotation index
|
|
}
|
|
else if (fp->tiltlevel != TILTLEVEL0)
|
|
{
|
|
index = pi;
|
|
|
|
switch (fp->tiltlevel)
|
|
{
|
|
case TILTLEVEL1:
|
|
if (cz < 1.0)
|
|
cz += 0.012f; // tilt
|
|
if (by > -0.2f)
|
|
by -= 0.003f; // slide up
|
|
break;
|
|
case TILTLEVEL2:
|
|
if (cz < 1.5)
|
|
cz += 0.016f; // tilt
|
|
if (cy > 2.3f)
|
|
cy -= 0.004f; // zoom
|
|
break;
|
|
case TILTLEVEL3:
|
|
if (cz < 1.8)
|
|
cz += 0.012f; // tilt
|
|
if (cy > 2.1f)
|
|
cy -= 0.008f; // zoom
|
|
if (by > -0.25f)
|
|
by -= 0.002f; // slide up
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this transformation creates a standard 2D view
|
|
|
|
index = pi;
|
|
|
|
cy = 2.41f;
|
|
cz = 0.00001f;
|
|
by = 0.0;
|
|
}
|
|
|
|
// Create the transform matView
|
|
D3DXMatrixLookAtLHPtr(&matView,
|
|
&D3DXVECTOR3 (0.0f, cy, cz), // tilt & zoom the camera
|
|
&D3DXVECTOR3 (0.0f, by, 0.0f), // raise & lower the camera
|
|
&D3DXVECTOR3 (0.0f, 1.0, 0.0f)); // flip the camera
|
|
|
|
d3d_device->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView
|
|
|
|
// set the projection transform
|
|
D3DXMATRIX matProjection; // the projection transform matrix
|
|
D3DXMatrixPerspectiveFovLHPtr(&matProjection,
|
|
D3DXToRadian(45), // the horizontal field of view
|
|
1.0f, // aspect ratio
|
|
1.0f, // the near view-plane
|
|
100.0f); // the far view-plane
|
|
d3d_device->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection
|
|
|
|
// set the world transform
|
|
D3DXMATRIX matRotateY; // a matrix to store the rotation for each triangle
|
|
D3DXMatrixRotationYPtr(&matRotateY, index); // the rotation matrix
|
|
d3d_device->SetTransform(D3DTS_WORLD, &(matRotateY)); // set the world transform
|
|
}
|
|
|
|
//***********************************************************************
|
|
// End 3D transformations
|
|
//***********************************************************************
|
|
|
|
// draw the fully rendered and transformed surface to the backbuffer
|
|
d3d_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
|
|
|
// We now lock the backbuffer
|
|
d3d_device->EndScene();
|
|
|
|
// Now tell Direct3D to flip the image to the screen.
|
|
d3d_device->Present(NULL, NULL, NULL, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
// shutdown the Direct3D engine
|
|
extern "C" void shutdowndirect3d(void)
|
|
{
|
|
vertex_buffer->Release(); // close and release the vertex buffer
|
|
texture_buffer->Release(); // close and release the texture
|
|
d3d_device->Release(); // close and release the 3D device
|
|
d3d->Release(); // close and release Direct3D
|
|
|
|
if (hD3DX != NULL)
|
|
{
|
|
FreeLibrary(hD3DX);
|
|
hD3DX = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Copy the raw screen buffer data to the texture buffer
|
|
void refresh_frame(UBYTE *scr_ptr, FRAMEPARAMS *fp)
|
|
{
|
|
int pixel = 0;
|
|
int i, x, y;
|
|
int texturewidth;
|
|
|
|
// calculate texture width
|
|
int viewwidth = fp->view.right - fp->view.left;
|
|
|
|
D3DLOCKED_RECT d3dlr;
|
|
texture_buffer->LockRect(0, &d3dlr, 0, 0);
|
|
|
|
DWORD* pixels = (DWORD*)d3dlr.pBits;
|
|
texturewidth = d3dlr.Pitch / 4;
|
|
|
|
// copy screen buffer to the texture
|
|
scr_ptr += fp->view.top * Screen_WIDTH + fp->view.left;
|
|
for (y = fp->view.top; y < fp->view.bottom; y++) {
|
|
for (x = fp->view.left; x < fp->view.right; x++) {
|
|
if (y < 0 || y >= Screen_HEIGHT || x < 0 || x >= Screen_WIDTH)
|
|
pixels[pixel] = Colours_table[0];
|
|
else
|
|
pixels[pixel] = Colours_table[*scr_ptr];
|
|
|
|
for (i = 0; i < fp->scanlinemode; i++) {
|
|
pixels[pixel + i * texturewidth] = pixels[pixel];
|
|
}
|
|
|
|
scr_ptr++;
|
|
pixel++;
|
|
}
|
|
scr_ptr += Screen_WIDTH - viewwidth;
|
|
pixel += texturewidth - viewwidth;
|
|
pixel += texturewidth * fp->scanlinemode;
|
|
}
|
|
|
|
texture_buffer->UnlockRect(0);
|
|
}
|
|
|