Files
BlitzNext/gxruntime/gxgraphics.cpp
T
Michael Fabian Dirks 581c640149 Uh, Updates?
2016-10-03 17:11:15 +02:00

611 lines
16 KiB
C++

#include "std.h"
#include "gxgraphics.h"
#include "gxruntime.h"
extern gxRuntime *gx_runtime;
gxGraphics::gxGraphics(gxRuntime *rt, IDirectDraw7 *dd, IDirectDrawSurface7 *fs, IDirectDrawSurface7 *bs, bool d3d) :
runtime(rt), dirDraw(dd), dir3d(0), dir3dDev(0), def_font(0), gfx_lost(false), dummy_mesh(0) {
dirDraw->QueryInterface(IID_IDirectDraw, (void**)&ds_dirDraw);
front_canvas = new gxCanvas(this, fs, 0);
back_canvas = new gxCanvas(this, bs, 0);
front_canvas->cls();
back_canvas->cls();
def_font = loadFont("courier", 12, 0);
front_canvas->setFont(def_font);
back_canvas->setFont(def_font);
memset(&primFmt, 0, sizeof(primFmt));
primFmt.dwSize = sizeof(primFmt);
fs->GetPixelFormat(&primFmt);
//are we fullscreen?
_gamma = 0;
if (fs != bs) {
if (fs->QueryInterface(IID_IDirectDrawGammaControl, (void**)&_gamma) >= 0) {
if (_gamma->GetGammaRamp(0, &_gammaRamp) < 0) _gamma = 0;
}
}
if (!_gamma) {
for (int k = 0; k < 256; ++k) _gammaRamp.red[k] = _gammaRamp.blue[k] = _gammaRamp.green[k] = k;
}
}
gxGraphics::~gxGraphics() {
if (_gamma) _gamma->Release();
while (scene_set.size()) freeScene(*scene_set.begin());
while (movie_set.size()) closeMovie(*movie_set.begin());
while (font_set.size()) freeFont(*font_set.begin());
while (canvas_set.size()) freeCanvas(*canvas_set.begin());
set<string>::iterator it;
for (it = font_res.begin(); it != font_res.end(); ++it) RemoveFontResource((*it).c_str());
font_res.clear();
delete back_canvas;
delete front_canvas;
ds_dirDraw->Release();
dirDraw->RestoreDisplayMode();
dirDraw->Release();
}
void gxGraphics::setGamma(int r, int g, int b, float dr, float dg, float db) {
_gammaRamp.red[r & 255] = dr*257.0f;
_gammaRamp.green[g & 255] = dg*257.0f;
_gammaRamp.blue[b & 255] = db*257.0f;
}
void gxGraphics::updateGamma(bool calibrate) {
if (!_gamma) return;
_gamma->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, &_gammaRamp);
}
void gxGraphics::getGamma(int r, int g, int b, float *dr, float *dg, float *db) {
*dr = _gammaRamp.red[r & 255] / 257.0f;
*dg = _gammaRamp.green[g & 255] / 257.0f;
*db = _gammaRamp.blue[b & 255] / 257.0f;
}
void gxGraphics::backup() {
}
bool gxGraphics::restore() {
while (dirDraw->TestCooperativeLevel() != DD_OK) {
if (dirDraw->TestCooperativeLevel() == DDERR_WRONGMODE) return false;
Sleep(100);
}
if (back_canvas->getSurface()->IsLost() == DD_OK) return true;
dirDraw->RestoreAllSurfaces();
//restore all canvases
set<gxCanvas*>::iterator it;
for (it = canvas_set.begin(); it != canvas_set.end(); ++it) {
(*it)->restore();
}
//restore all meshes (b3d surfaces)
set<gxMesh*>::iterator mesh_it;
for (mesh_it = mesh_set.begin(); mesh_it != mesh_set.end(); ++mesh_it) {
(*mesh_it)->restore();
}
if (dir3d) dir3d->EvictManagedTextures();
return true;
}
gxCanvas *gxGraphics::getFrontCanvas()const {
return front_canvas;
}
gxCanvas *gxGraphics::getBackCanvas()const {
return back_canvas;
}
gxFont *gxGraphics::getDefaultFont()const {
return def_font;
}
void gxGraphics::vwait() {
dirDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
}
void gxGraphics::flip(bool v) {
runtime->flip(v);
}
void gxGraphics::copy(gxCanvas *dest, int dx, int dy, int dw, int dh, gxCanvas *src, int sx, int sy, int sw, int sh) {
RECT r = { dx,dy,dx + dw,dy + dh };
ddUtil::copy(dest->getSurface(), dx, dy, dw, dh, src->getSurface(), sx, sy, sw, sh);
dest->damage(r);
}
int gxGraphics::getScanLine()const {
DWORD t = 0;
dirDraw->GetScanLine(&t);
return t;
}
int gxGraphics::getTotalVidmem()const {
DDCAPS caps = { sizeof(caps) };
dirDraw->GetCaps(&caps, 0);
return caps.dwVidMemTotal;
}
int gxGraphics::getAvailVidmem()const {
DDCAPS caps = { sizeof(caps) };
dirDraw->GetCaps(&caps, 0);
return caps.dwVidMemFree;
}
gxMovie *gxGraphics::openMovie(const string &file, int flags) {
/*IAMMultiMediaStream *iam_stream;
if (CoCreateInstance(
CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
IID_IAMMultiMediaStream, (void **)&iam_stream) == S_OK) {
if (iam_stream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL) == S_OK) {
if (iam_stream->AddMediaStream(ds_dirDraw, &MSPID_PrimaryVideo, 0, NULL) == S_OK) {
iam_stream->AddMediaStream(NULL, &MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NULL);
WCHAR *path = new WCHAR[file.size() + 1];
MultiByteToWideChar(CP_ACP, 0, file.c_str(), -1, path, sizeof(WCHAR)*(file.size() + 1));
int n = iam_stream->OpenFile(path, 0);
delete path;
if (n == S_OK) {
gxMovie *movie = new gxMovie(this, iam_stream);
movie_set.insert(movie);
return movie;
}
}
}
iam_stream->Release();
}*/
return 0;
}
gxMovie *gxGraphics::verifyMovie(gxMovie *m) {
return movie_set.count(m) ? m : 0;
}
void gxGraphics::closeMovie(gxMovie *m) {
if (movie_set.erase(m)) delete m;
}
gxCanvas *gxGraphics::createCanvas(int w, int h, int flags) {
ddSurf *s = ddUtil::createSurface(w, h, flags, this);
if (!s) return 0;
gxCanvas *c = new gxCanvas(this, s, flags);
canvas_set.insert(c);
c->cls();
return c;
}
gxCanvas *gxGraphics::loadCanvas(const string &f, int flags) {
ddSurf *s = ddUtil::loadSurface(f, flags, this);
if (!s) return 0;
gxCanvas *c = new gxCanvas(this, s, flags);
canvas_set.insert(c);
return c;
}
gxCanvas *gxGraphics::verifyCanvas(gxCanvas *c) {
return canvas_set.count(c) || c == front_canvas || c == back_canvas ? c : 0;
}
void gxGraphics::freeCanvas(gxCanvas *c) {
if (canvas_set.erase(c)) delete c;
}
int gxGraphics::getWidth()const {
return front_canvas->getWidth();
}
int gxGraphics::getHeight()const {
return front_canvas->getHeight();
}
int gxGraphics::getDepth()const {
return front_canvas->getDepth();
}
gxFont *gxGraphics::loadFont(const string &f, int height, int flags) {
int bold = flags & gxFont::FONT_BOLD ? FW_BOLD : FW_REGULAR;
int italic = flags & gxFont::FONT_ITALIC ? 1 : 0;
int underline = flags & gxFont::FONT_UNDERLINE ? 1 : 0;
int strikeout = 0;
string t;
int n = f.find('.');
if (n != string::npos) {
t = fullfilename(f);
if (!font_res.count(t) && AddFontResource(t.c_str())) font_res.insert(t);
t = filenamefile(f.substr(0, n));
} else {
t = f;
}
//save and turn off font smoothing....
BOOL smoothing = FALSE;
SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &smoothing, 0);
SystemParametersInfo(SPI_SETFONTSMOOTHING, FALSE, 0, 0);
HFONT hfont = CreateFont(
height, 0, 0, 0,
bold, italic, underline, strikeout,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, t.c_str());
if (!hfont) {
//restore font smoothing
SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0);
return 0;
}
HDC hdc = CreateCompatibleDC(0);
HFONT pfont = (HFONT)SelectObject(hdc, hfont);
TEXTMETRIC tm = { 0 };
if (!GetTextMetrics(hdc, &tm)) {
SelectObject(hdc, pfont);
DeleteDC(hdc);
DeleteObject(hfont);
SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0);
return 0;
}
height = tm.tmHeight;
int first = tm.tmFirstChar, last = tm.tmLastChar;
int sz = last - first + 1;
int *offs = new int[sz];
int *widths = new int[sz];
int *as = new int[sz];
//calc size of canvas to hold font.
int x = 0, y = 0, max_x = 0;
for (int k = 0; k < sz; ++k) {
char t = k + first;
SIZE sz;
GetTextExtentPoint32(hdc, &t, 1, &sz);
int w = sz.cx;
as[k] = 0;
ABC abc;
if (GetCharABCWidths(hdc, t, t, &abc)) {
if (abc.abcA < 0) {
as[k] = ceil(-abc.abcA);
w += as[k];
}
if (abc.abcC < 0) w += (int)ceil(-abc.abcC);
}
if (x && x + w > getWidth()) { x = 0; y += height; }
offs[k] = (x << 16) | y;
widths[k] = w;
x += w; if (x > max_x) max_x = x;
}
SelectObject(hdc, pfont);
DeleteDC(hdc);
int cw = max_x, ch = y + height;
if (gxCanvas *c = createCanvas(cw, ch, 0)) {
ddSurf *surf = c->getSurface();
HDC surf_hdc;
if (surf->GetDC(&surf_hdc) >= 0) {
HFONT pfont = (HFONT)SelectObject(surf_hdc, hfont);
SetBkColor(surf_hdc, 0x000000);
SetTextColor(surf_hdc, 0xffffff);
for (int k = 0; k < sz; ++k) {
int x = (offs[k] >> 16) & 0xffff, y = offs[k] & 0xffff;
char t = k + first;
RECT rect = { x,y,x + widths[k],y + height };
ExtTextOut(surf_hdc, x + as[k], y, ETO_CLIPPED, &rect, &t, 1, 0);
}
SelectObject(surf_hdc, pfont);
surf->ReleaseDC(surf_hdc);
DeleteObject(hfont);
delete[] as;
c->backup();
gxFont *font = new gxFont(this, c, tm.tmMaxCharWidth, height, first, last + 1, tm.tmDefaultChar, offs, widths);
font_set.insert(font);
//restore font smoothing
SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0);
return font;
} else {
}
freeCanvas(c);
} else {
}
DeleteObject(hfont);
delete[] as;
delete[] widths;
delete[] offs;
//restore font smoothing
SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0);
return 0;
}
gxFont *gxGraphics::verifyFont(gxFont *f) {
return font_set.count(f) ? f : 0;
}
void gxGraphics::freeFont(gxFont *f) {
if (font_set.erase(f)) delete f;
}
//////////////
// 3D STUFF //
//////////////
static int maxDevType;
static HRESULT CALLBACK enumDevice(char *desc, char *name, D3DDEVICEDESC7 *devDesc, void *context) {
gxGraphics *g = (gxGraphics*)context;
int t = 0;
GUID guid = devDesc->deviceGUID;
if (guid == IID_IDirect3DRGBDevice) t = 1;
else if (guid == IID_IDirect3DHALDevice) t = 2;
else if (guid == IID_IDirect3DTnLHalDevice) t = 3;
if (t > maxDevType) {
g->dir3dDevDesc = *devDesc;
maxDevType = t;
}
return D3DENUMRET_OK;
}
static HRESULT CALLBACK enumZbuffFormat(LPDDPIXELFORMAT format, void *context) {
gxGraphics *g = (gxGraphics*)context;
if (format->dwZBufferBitDepth == g->primFmt.dwRGBBitCount) {
g->zbuffFmt = *format;
return D3DENUMRET_CANCEL;
}
if (format->dwZBufferBitDepth > g->zbuffFmt.dwZBufferBitDepth) {
if (format->dwZBufferBitDepth < g->primFmt.dwRGBBitCount) {
g->zbuffFmt = *format;
}
}
return D3DENUMRET_OK;
}
struct TexFmt {
DDPIXELFORMAT fmt;
int bits, a_bits, rgb_bits;
};
static int cntBits(int mask) {
int n = 0;
for (int k = 0; k < 32; ++k) {
if (mask & (1 << k)) ++n;
}
return n;
}
static vector<TexFmt> tex_fmts;
static HRESULT CALLBACK enumTextureFormat(DDPIXELFORMAT *fmt, void *p) {
TexFmt t;
t.fmt = *fmt;
t.bits = fmt->dwRGBBitCount;
t.a_bits = (fmt->dwFlags & DDPF_ALPHAPIXELS) ? cntBits(fmt->dwRGBAlphaBitMask) : 0;
t.rgb_bits = (fmt->dwFlags & DDPF_RGB) ? cntBits(fmt->dwRBitMask | fmt->dwGBitMask | fmt->dwBBitMask) : 0;
tex_fmts.push_back(t);
return D3DENUMRET_OK;
}
static string itobin(int n) {
string t;
for (int k = 0; k < 32; n <<= 1, ++k) {
t += (n & 0x80000000) ? '1' : '0';
}
return t;
}
static void debugPF(const DDPIXELFORMAT &pf) {
string t;
t = "Bits:" + itoa(pf.dwRGBBitCount);
gx_runtime->debugLog(t.c_str());
t = "R Mask:" + itobin(pf.dwRBitMask);
gx_runtime->debugLog(t.c_str());
t = "G Mask:" + itobin(pf.dwGBitMask);
gx_runtime->debugLog(t.c_str());
t = "B Mask:" + itobin(pf.dwBBitMask);
gx_runtime->debugLog(t.c_str());
t = "A Mask:" + itobin(pf.dwRGBAlphaBitMask);
gx_runtime->debugLog(t.c_str());
}
static void pickTexFmts(gxGraphics *g, int hi) {
//texRGBFmt.
{
int pick = -1, max = 0, bits;
for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) {
for (int k = 0; k < tex_fmts.size(); ++k) {
const TexFmt &t = tex_fmts[k];
if (t.bits > d || !t.rgb_bits || t.rgb_bits < max) continue;
if (t.rgb_bits == max && t.bits >= bits) continue;
pick = k; max = t.rgb_bits; bits = t.bits;
}
if (!hi && pick >= 0) break;
}
if (pick < 0) g->texRGBFmt[hi] = g->primFmt;
else g->texRGBFmt[hi] = tex_fmts[pick].fmt;
}
//texAlphaFmt
{
int pick = -1, max = 0, bits;
for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) {
for (int k = 0; k < tex_fmts.size(); ++k) {
const TexFmt &t = tex_fmts[k];
if (t.bits > d || !t.a_bits || t.a_bits < max) continue;
if (t.a_bits == max && t.bits >= bits) continue;
pick = k; max = t.a_bits; bits = t.bits;
}
if (!hi && pick >= 0) break;
}
if (pick < 0) g->texAlphaFmt[hi] = g->primFmt;
else g->texAlphaFmt[hi] = tex_fmts[pick].fmt;
}
//texRGBAlphaFmt
{
int pick = -1, a8rgb8 = -1, max = 0, bits;
for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) {
for (int k = 0; k < tex_fmts.size(); ++k) {
const TexFmt &t = tex_fmts[k];
if (t.a_bits == 8 && t.bits == 16) { a8rgb8 = k; continue; }
if (t.bits > d || !t.a_bits || !t.rgb_bits || t.a_bits < max) continue;
if (t.a_bits == max && t.bits >= bits) continue;
pick = k; max = t.a_bits; bits = t.bits;
}
if (!hi && pick >= 0) break;
}
if (pick < 0) pick = a8rgb8;
if (pick < 0) g->texRGBAlphaFmt[hi] = g->primFmt;
else g->texRGBAlphaFmt[hi] = tex_fmts[pick].fmt;
}
//texRGBMaskFmt...
{
int pick = -1, max = 0, bits;
for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) {
for (int k = 0; k < tex_fmts.size(); ++k) {
const TexFmt &t = tex_fmts[k];
if (!t.a_bits || !t.rgb_bits || t.rgb_bits < max) continue;
if (t.rgb_bits == max && t.bits >= bits) continue;
pick = k; max = t.rgb_bits; bits = t.bits;
}
if (!hi && pick >= 0) break;
}
if (pick < 0) g->texRGBMaskFmt[hi] = g->primFmt;
else g->texRGBMaskFmt[hi] = tex_fmts[pick].fmt;
}
}
gxScene *gxGraphics::createScene(int flags) {
if (scene_set.size()) return 0;
//get d3d
if (dirDraw->QueryInterface(IID_IDirect3D7, (void**)&dir3d) >= 0) {
//enum devices
maxDevType = 0;
if (dir3d->EnumDevices(enumDevice, this) >= 0 && maxDevType > 1) {
//enum zbuffer formats
zbuffFmt.dwZBufferBitDepth = 0;
if (dir3d->EnumZBufferFormats(dir3dDevDesc.deviceGUID, enumZbuffFormat, this) >= 0) {
//create zbuff for back buffer
if (back_canvas->attachZBuffer()) {
//create 3d device
if (dir3d->CreateDevice(dir3dDevDesc.deviceGUID, back_canvas->getSurface(), &dir3dDev) >= 0) {
//enum texture formats
tex_fmts.clear();
if (dir3dDev->EnumTextureFormats(enumTextureFormat, this) >= 0) {
pickTexFmts(this, 0);
pickTexFmts(this, 1);
tex_fmts.clear();
#ifdef BETA
gx_runtime->debugLog("Texture RGB format:");
debugPF(texRGBFmt);
gx_runtime->debugLog("Texture Alpha format:");
debugPF(texAlphaFmt);
gx_runtime->debugLog("Texture RGB Alpha format:");
debugPF(texRGBAlphaFmt);
gx_runtime->debugLog("Texture RGB Mask format:");
debugPF(texRGBMaskFmt);
gx_runtime->debugLog("Texture Primary format:");
debugPF(primFmt);
string ts = "ZBuffer Bit Depth:" + itoa(zbuffFmt.dwZBufferBitDepth);
gx_runtime->debugLog(ts.c_str());
#endif
gxScene *scene = new gxScene(this, back_canvas);
scene_set.insert(scene);
dummy_mesh = createMesh(8, 12, 0);
return scene;
}
dir3dDev->Release();
dir3dDev = 0;
}
back_canvas->releaseZBuffer();
}
}
}
dir3d->Release();
dir3d = 0;
}
return 0;
}
gxScene *gxGraphics::verifyScene(gxScene *s) {
return scene_set.count(s) ? s : 0;
}
void gxGraphics::freeScene(gxScene *scene) {
if (!scene_set.erase(scene)) return;
dummy_mesh = 0;
while (mesh_set.size()) freeMesh(*mesh_set.begin());
back_canvas->releaseZBuffer();
if (dir3dDev) { dir3dDev->Release(); dir3dDev = 0; }
if (dir3d) { dir3d->Release(); dir3d = 0; }
delete scene;
}
gxMesh *gxGraphics::createMesh(int max_verts, int max_tris, int flags) {
static const int VTXFMT =
D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 |
D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE2(1);
int vbflags = 0;
//XP or less?
/*if (runtime->osinfo.dwMajorVersion < 6) {
vbflags |= D3DVBCAPS_WRITEONLY;
}*/
D3DVERTEXBUFFERDESC desc = { sizeof(desc),vbflags,VTXFMT,max_verts };
IDirect3DVertexBuffer7 *buff;
if (dir3d->CreateVertexBuffer(&desc, &buff, 0) < 0) return 0;
WORD *indices = new WORD[max_tris * 3];
gxMesh *mesh = new gxMesh(this, buff, indices, max_verts, max_tris);
mesh_set.insert(mesh);
return mesh;
}
gxMesh *gxGraphics::verifyMesh(gxMesh *m) {
return mesh_set.count(m) ? m : 0;
}
void gxGraphics::freeMesh(gxMesh *mesh) {
if (mesh_set.erase(mesh)) delete mesh;
}