Files
BlitzNext/Runtime/gfx/gxgraphics.cpp
T
2019-01-18 19:05:43 +01:00

716 lines
16 KiB
C++

#include "gxgraphics.hpp"
#include "gxruntime.hpp"
#include "gxcanvas.hpp"
#include "gxmesh.hpp"
#include "gxfont.hpp"
#include "gxscene.hpp"
#include <stdutil.hpp>
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());
std::set<std::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
std::set<gxCanvas*>::iterator it;
for (it = canvas_set.begin(); it != canvas_set.end(); ++it) {
(*it)->restore();
}
//restore all meshes (b3d surfaces)
std::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 std::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)
{
IDirectDrawSurface7* 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 std::string& f, int flags)
{
IDirectDrawSurface7* 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 std::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;
std::string t;
int n = f.find('.');
if (n != std::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)) {
IDirectDrawSurface7* 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 std::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 std::string itobin(int n)
{
std::string t;
for (int k = 0; k < 32; n <<= 1, ++k) {
t += (n & 0x80000000) ? '1' : '0';
}
return t;
}
static void debugPF(const DDPIXELFORMAT& pf)
{
std::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;
}