update license in source code itself
[libpicofe.git] / win32 / direct.cpp
1 /*\r
2  * (C) GraÅžvydas "notaz" Ignotas, 2009\r
3  *\r
4  * This work is licensed under the terms of any of these licenses\r
5  * (at your option):\r
6  *  - GNU GPL, version 2 or later.\r
7  *  - GNU LGPL, version 2.1 or later.\r
8  *  - MAME license.\r
9  * See the COPYING file in the top-level directory.\r
10  */\r
11 \r
12 #include <ddraw.h>\r
13 #include "../lprintf.h"\r
14 #include "direct.h"\r
15 #include "main.h"\r
16 \r
17 #define EmuWidth 320\r
18 #define EmuHeight 240\r
19 \r
20 #define RELEASE(x) if (x) x->Release();  x=NULL;\r
21 #define LOGFAIL() lprintf("fail: %s %s:%i\n", __FUNCTION__, __FILE__, __LINE__)\r
22 \r
23 static LPDIRECTDRAW7        m_pDD = NULL;\r
24 static LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer = NULL;\r
25 static LPDIRECTDRAWSURFACE7 m_pddsBackBuffer = NULL;\r
26 \r
27 // quick and dirty stuff..\r
28 void DirectExit(void)\r
29 {\r
30   RELEASE(m_pddsBackBuffer);\r
31   RELEASE(m_pddsFrontBuffer);\r
32   RELEASE(m_pDD);\r
33 }\r
34 \r
35 int DirectInit(void)\r
36 {\r
37   HRESULT ret;\r
38   LPDIRECTDRAWCLIPPER pcClipper = NULL;\r
39   DDSURFACEDESC2 ddsd;\r
40 \r
41   ret = DirectDrawCreateEx(NULL, (VOID**)&m_pDD, IID_IDirectDraw7, NULL);\r
42   if (ret) { LOGFAIL(); return 1; }\r
43 \r
44   // Set cooperative level\r
45   ret = m_pDD->SetCooperativeLevel( FrameWnd, DDSCL_NORMAL );\r
46   if (ret) { LOGFAIL(); goto fail; }\r
47 \r
48   // Create the primary surface\r
49   ZeroMemory( &ddsd, sizeof( ddsd ) );\r
50   ddsd.dwSize         = sizeof( ddsd );\r
51   ddsd.dwFlags        = DDSD_CAPS;\r
52   ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;\r
53 \r
54   ret = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL );\r
55   if (ret) { LOGFAIL(); goto fail; }\r
56 \r
57   // Create the backbuffer surface\r
58   ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;\r
59   ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;\r
60   ddsd.dwWidth        = EmuWidth;\r
61   ddsd.dwHeight       = EmuHeight;\r
62 \r
63   ret = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL );\r
64   if (ret) { LOGFAIL(); goto fail; }\r
65 \r
66   // clipper\r
67   ret = m_pDD->CreateClipper( 0, &pcClipper, NULL );\r
68   if (ret) { LOGFAIL(); goto fail; }\r
69 \r
70   ret = pcClipper->SetHWnd( 0, FrameWnd );\r
71   if (ret) { LOGFAIL(); goto fail; }\r
72 \r
73   ret = m_pddsFrontBuffer->SetClipper( pcClipper );\r
74   if (ret) { LOGFAIL(); goto fail; }\r
75 \r
76   RELEASE(pcClipper);\r
77   return 0;\r
78 \r
79 fail:\r
80   RELEASE(pcClipper);\r
81   DirectExit();\r
82   return 1;\r
83 }\r
84 \r
85 int DirectScreen(const void *emu_screen)\r
86 {\r
87   const unsigned short *ps = (const unsigned short *)emu_screen;\r
88   DDSURFACEDESC2 sd;\r
89   int ret, x, y;\r
90 \r
91   memset(&sd, 0, sizeof(sd));\r
92   sd.dwSize = sizeof(sd);\r
93   ret = m_pddsBackBuffer->Lock(NULL, &sd, DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL);\r
94   if (ret) { LOGFAIL(); return 1; }\r
95 \r
96   //lprintf("w: %i h: %i pi: %i pf: %i\n", sd.dwWidth, sd.dwHeight, sd.lPitch, sd.ddpfPixelFormat.dwRGBBitCount);\r
97 \r
98   if (sd.ddpfPixelFormat.dwRGBBitCount == 32)\r
99   {\r
100     int *dst = (int *)sd.lpSurface;\r
101     for (y = 0; y < EmuHeight; y++)\r
102     {\r
103       for (x = 0; x < EmuWidth; x++)\r
104       {\r
105         int s = *ps++;\r
106         dst[x] = ((s&0xf800)<<8) | ((s&0x07e0)<<5) | ((s&0x001f)<<3);\r
107       }\r
108       dst = (int *)((char *)dst + sd.lPitch);\r
109     }\r
110   }\r
111   else if (sd.ddpfPixelFormat.dwRGBBitCount == 24) /* wine uses this for me */\r
112   {\r
113     void *dst = sd.lpSurface;\r
114     for (y = 0; y < EmuHeight; y++)\r
115     {\r
116       unsigned char *dst1 = (unsigned char *) dst;\r
117       for (x = 0; x < EmuWidth; x++, dst1 += 3)\r
118       {\r
119         int s = *ps++;\r
120         dst1[2] = (s&0xf800)>>8; dst1[1] = (s&0x07e0)>>3; dst1[0] = s<<3; // BGR\r
121       }\r
122       dst = (void *)((char *)dst + sd.lPitch);\r
123     }\r
124   }\r
125   else if (sd.ddpfPixelFormat.dwRGBBitCount == 16)\r
126   {\r
127     unsigned short *dst = (unsigned short *)sd.lpSurface;\r
128     for (y = 0; y < EmuHeight; y++)\r
129     {\r
130       memcpy(dst, ps, EmuWidth*2);\r
131       ps += EmuWidth;\r
132       dst = (unsigned short *)((char *)dst + sd.lPitch);\r
133     }\r
134   }\r
135   else\r
136   {\r
137     LOGFAIL();\r
138   }\r
139 \r
140   ret = m_pddsBackBuffer->Unlock(NULL);\r
141   if (ret) { LOGFAIL(); return 1; }\r
142   return 0;\r
143 }\r
144 \r
145 int DirectClear(unsigned int colour)\r
146 {\r
147   int ret = 0;\r
148   DDBLTFX ddbltfx;\r
149   ZeroMemory( &ddbltfx, sizeof(ddbltfx) );\r
150   ddbltfx.dwSize      = sizeof(ddbltfx);\r
151   ddbltfx.dwFillColor = colour;\r
152 \r
153   if (m_pddsBackBuffer != NULL)\r
154     ret = m_pddsBackBuffer->Blt( NULL, NULL, NULL, DDBLT_COLORFILL, &ddbltfx );\r
155   if (ret) { LOGFAIL(); return 1; }\r
156   return 0;\r
157 }\r
158 \r
159 int DirectPresent(void)\r
160 {\r
161   int ret = 0;\r
162   if (FrameRectMy.right - FrameRectMy.left > 0 && FrameRectMy.bottom - FrameRectMy.top > 0)\r
163     ret = m_pddsFrontBuffer->Blt(&FrameRectMy, m_pddsBackBuffer, &EmuScreenRect, DDBLT_WAIT, NULL);\r
164   if (ret) { LOGFAIL(); return 1; }\r
165   return 0;\r
166 }\r
167 \r
168 \r
169 /* D3D */\r
170 #ifdef USE_D3D\r
171 static IDirect3D8 *Direct3D=NULL;\r
172 IDirect3DDevice8 *Device=NULL;\r
173 IDirect3DSurface8 *DirectBack=NULL; // Back Buffer\r
174 \r
175 static IDirect3DVertexBuffer8 *VertexBuffer=NULL;\r
176 \r
177 struct CustomVertex\r
178 {\r
179   float x,y,z; // Vertex cordinates\r
180   unsigned int colour;\r
181   float u,v; // Texture coordinates\r
182 };\r
183 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)\r
184 \r
185 static CustomVertex VertexList[4];\r
186 \r
187 int DirectInit()\r
188 {\r
189   D3DPRESENT_PARAMETERS d3dpp;\r
190   D3DDISPLAYMODE mode;\r
191   int i,u,ret=0;\r
192 \r
193   memset(&d3dpp,0,sizeof(d3dpp));\r
194   memset(&mode,0,sizeof(mode));\r
195 \r
196   Direct3D=Direct3DCreate8(D3D_SDK_VERSION); if (Direct3D==NULL) return 1;\r
197 \r
198   // Set up the structure used to create the D3D device:\r
199   d3dpp.BackBufferWidth =MainWidth;\r
200   d3dpp.BackBufferHeight=MainHeight;\r
201   d3dpp.BackBufferCount =1;\r
202   d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;\r
203   d3dpp.MultiSampleType =D3DMULTISAMPLE_NONE;\r
204 \r
205 #ifdef _XBOX\r
206   d3dpp.BackBufferFormat=D3DFMT_X8R8G8B8;\r
207   d3dpp.FullScreen_RefreshRateInHz=60;\r
208 #else\r
209   Direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&mode);\r
210   d3dpp.BackBufferFormat=mode.Format;\r
211   d3dpp.Windowed=1;\r
212   d3dpp.hDeviceWindow=FrameWnd;\r
213 #endif\r
214 \r
215   // Try to create a device with hardware vertex processing:\r
216   for (i=0;i<3;i++)\r
217   {\r
218     int behave=D3DCREATE_HARDWARE_VERTEXPROCESSING;\r
219 \r
220     // Try software vertex processing:\r
221     if (i==1) behave=D3DCREATE_MIXED_VERTEXPROCESSING;\r
222     if (i==2) behave=D3DCREATE_SOFTWARE_VERTEXPROCESSING;\r
223 \r
224     Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,FrameWnd,\r
225         behave|D3DCREATE_MULTITHREADED,&d3dpp,&Device);\r
226     if (Device) break;\r
227   }\r
228 \r
229   if (Device==NULL)\r
230   {\r
231 #if 0\r
232     // try ref\r
233     Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_REF,FrameWnd,\r
234         D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED,&d3dpp,&Device);\r
235     if (Device==NULL) goto fail0;\r
236     HMODULE test = LoadLibrary("d3d8d.dll");\r
237     if (test != NULL) FreeLibrary(test);\r
238     else {\r
239       error("Sorry, but this program requires Direct3D with hardware acceleration.\n\n"\r
240             "You can try using Direct3D software emulation, but you have to install "\r
241             "DirectX SDK for it to work\n(it seems to be missing now).");\r
242       goto fail1;\r
243     }\r
244 #else\r
245     goto fail1;\r
246 #endif\r
247   }\r
248 \r
249   Device->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&DirectBack);\r
250   if (DirectBack==NULL) goto fail1;\r
251 \r
252   Device->CreateVertexBuffer(sizeof(VertexList),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&VertexBuffer);\r
253   if (VertexBuffer==NULL) goto fail2;\r
254 \r
255   ret=TexScreenInit(); if (ret) goto fail3;\r
256 \r
257   //FontInit();\r
258 \r
259   Device->SetRenderState(D3DRS_LIGHTING,0); // Turn off lighting\r
260 \r
261   // Set up texture modes:\r
262   Device->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_CLAMP);\r
263   Device->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);\r
264 \r
265   return 0;\r
266 \r
267 fail3:\r
268   RELEASE(VertexBuffer)\r
269 fail2:\r
270   RELEASE(DirectBack)\r
271 fail1:\r
272   RELEASE(Device)\r
273 fail0:\r
274   RELEASE(Direct3D)\r
275 \r
276   return 1;\r
277 }\r
278 \r
279 void DirectExit()\r
280 {\r
281   TexScreenExit();\r
282 \r
283   // d3d\r
284   RELEASE(VertexBuffer)\r
285   RELEASE(DirectBack)\r
286   RELEASE(Device)\r
287   RELEASE(Direct3D)\r
288 }\r
289 \r
290 int DirectClear(unsigned int colour)\r
291 {\r
292   if (Device != NULL) {\r
293     Device->Clear(0,NULL,D3DCLEAR_TARGET,colour,1.0f,0);\r
294     return 0;\r
295   }\r
296 \r
297   return 1;\r
298 }\r
299 \r
300 int DirectPresent()\r
301 {\r
302   if (Device != NULL) {\r
303     Device->Present(NULL,NULL,NULL,NULL);\r
304     return 0;\r
305   }\r
306 \r
307   return 1;\r
308 }\r
309 \r
310 #define PI 3.14159265f\r
311 \r
312 static int MakeVertexList()\r
313 {\r
314   struct CustomVertex *vert=NULL,*pv=NULL;\r
315   float dist=0.0f;\r
316   float scalex=0.0f,scaley=0.0f;\r
317   unsigned int colour=0xffffff;\r
318   float right=0.0f,bottom=0.0f;\r
319 \r
320   if (LoopMode!=8) colour=0x102040;\r
321 \r
322   dist=10.0f; scalex=dist*1.3333f; scaley=dist;\r
323 \r
324   scalex*=640.0f/(float)MainWidth;\r
325   scaley*=448.0f/(float)MainHeight;\r
326 \r
327   vert=VertexList;\r
328 \r
329   // Put the vertices for the corners of the screen:\r
330   pv=vert;\r
331   pv->z=dist;\r
332   pv->x=-scalex; pv->y=scaley;\r
333   pv->colour=colour; pv++;\r
334 \r
335   *pv=vert[0]; pv->x= scalex; pv->y= scaley; pv++;\r
336   *pv=vert[0]; pv->x=-scalex; pv->y=-scaley; pv++;\r
337   *pv=vert[0]; pv->x= scalex; pv->y=-scaley; pv++;\r
338 \r
339   // Find where the screen images ends on the texture\r
340   right =(float)EmuWidth /(float)TexWidth;\r
341   bottom=(float)EmuHeight/(float)TexHeight;\r
342 \r
343   // Write texture coordinates:\r
344   pv=vert;\r
345   pv->u=0.0f;  pv->v=0.00f;  pv++;\r
346   pv->u=right; pv->v=0.00f;  pv++;\r
347   pv->u=0.0f;  pv->v=bottom; pv++;\r
348   pv->u=right; pv->v=bottom; pv++;\r
349 \r
350   return 0;\r
351 }\r
352 \r
353 static int SetupMatrices()\r
354 {\r
355   D3DXVECTOR3 eye ( 0.0f, 0.0f, 0.0f );\r
356   D3DXVECTOR3 look( 0.0f, 0.0f, 0.0f );\r
357   D3DXVECTOR3 up  ( 0.0f, 1.0f, 0.0f );\r
358   D3DXMATRIX mat;\r
359   float nudgex=0.0f,nudgey=0.0f;\r
360 \r
361   memset(&mat,0,sizeof(mat));\r
362 \r
363   mat.m[0][0]=mat.m[1][1]=mat.m[2][2]=mat.m[3][3]=1.0f;\r
364   Device->SetTransform(D3DTS_WORLD,&mat);\r
365 \r
366   look.x=(float)Inp.axis[2]/2457.6f;\r
367   look.y=(float)Inp.axis[3]/2457.6f;\r
368   look.z=10.0f;\r
369 \r
370   // Nudge pixels to the centre of each screen pixel:\r
371   nudgex=13.3333f/(float)(MainWidth <<1);\r
372   nudgey=10.0000f/(float)(MainHeight<<1);\r
373   eye.x +=nudgex; eye.y +=nudgey;\r
374   look.x+=nudgex; look.y+=nudgey;\r
375 \r
376   D3DXMatrixLookAtLH(&mat,&eye,&look,&up);\r
377   Device->SetTransform(D3DTS_VIEW,&mat);\r
378 \r
379   D3DXMatrixPerspectiveFovLH(&mat, 0.5f*PI, 1.3333f, 0.2f, 1000.0f);\r
380   Device->SetTransform(D3DTS_PROJECTION,&mat);\r
381   return 0;\r
382 }\r
383 \r
384 int DirectScreen()\r
385 {\r
386   unsigned char *lock=NULL;\r
387   int ret;\r
388 \r
389   if (Device == NULL)\r
390     return DirectScreenDDraw();\r
391 \r
392   // Copy the screen to the screen texture:\r
393 #ifdef _XBOX\r
394   TexScreenSwizzle();\r
395 #else\r
396   ret=TexScreenLinear();\r
397   if (ret) lprintf("TexScreenLinear failed\n");\r
398 #endif\r
399 \r
400   SetupMatrices();\r
401 \r
402   MakeVertexList();\r
403 \r
404   // Copy vertices in:\r
405   VertexBuffer->Lock(0,sizeof(VertexList),&lock,0);\r
406   if (lock==NULL) { lprintf("VertexBuffer->Lock failed\n"); return 1; }\r
407   memcpy(lock,VertexList,sizeof(VertexList));\r
408   VertexBuffer->Unlock();\r
409 \r
410   Device->BeginScene();\r
411   Device->SetTexture(0,TexScreen);\r
412   Device->SetStreamSource(0,VertexBuffer,sizeof(CustomVertex));\r
413   Device->SetVertexShader(D3DFVF_CUSTOMVERTEX);\r
414   Device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);\r
415   Device->EndScene();\r
416 \r
417   return 0;\r
418 }\r
419 #endif\r
420 \r