scummvm/driver_tinygl.cpp

400 lines
13 KiB
C++
Raw Normal View History

// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003-2005 The ScummVM-Residual Team (www.scummvm.org)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "debug.h"
#include "colormap.h"
#include "material.h"
#include "driver_tinygl.h"
#include "tinygl/gl.h"
#include "tinygl/zgl.h"
// enable define below to turn on lights without texture mapping,
// TinyGL doesn't support texture mapping with lights currently
//#define TURN_ON_LIGTHS_WITHOUT_TEXTURES
// func below is from Mesa glu sources
static void lookAt(TGLfloat eyex, TGLfloat eyey, TGLfloat eyez, TGLfloat centerx,
TGLfloat centery, TGLfloat centerz, TGLfloat upx, TGLfloat upy, TGLfloat upz) {
TGLfloat m[16];
TGLfloat x[3], y[3], z[3];
TGLfloat mag;
z[0] = eyex - centerx;
z[1] = eyey - centery;
z[2] = eyez - centerz;
mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
if (mag) {
z[0] /= mag;
z[1] /= mag;
z[2] /= mag;
}
y[0] = upx;
y[1] = upy;
y[2] = upz;
x[0] = y[1] * z[2] - y[2] * z[1];
x[1] = -y[0] * z[2] + y[2] * z[0];
x[2] = y[0] * z[1] - y[1] * z[0];
y[0] = z[1] * x[2] - z[2] * x[1];
y[1] = -z[0] * x[2] + z[2] * x[0];
y[2] = z[0] * x[1] - z[1] * x[0];
mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
if (mag) {
x[0] /= mag;
x[1] /= mag;
x[2] /= mag;
}
mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
if (mag) {
y[0] /= mag;
y[1] /= mag;
y[2] /= mag;
}
#define M(row,col) m[col * 4 + row]
M(0, 0) = x[0];
M(0, 1) = x[1];
M(0, 2) = x[2];
M(0, 3) = 0.0;
M(1, 0) = y[0];
M(1, 1) = y[1];
M(1, 2) = y[2];
M(1, 3) = 0.0;
M(2, 0) = z[0];
M(2, 1) = z[1];
M(2, 2) = z[2];
M(2, 3) = 0.0;
M(3, 0) = 0.0;
M(3, 1) = 0.0;
M(3, 2) = 0.0;
M(3, 3) = 1.0;
#undef M
tglMultMatrixf(m);
tglTranslatef(-eyex, -eyey, -eyez);
}
DriverTinyGL::DriverTinyGL(int screenW, int screenH, int screenBPP) {
_screen = SDL_SetVideoMode(screenW, screenH, screenBPP, SDL_HWSURFACE);
if (_screen == NULL)
error("Could not initialize video");
SDL_WM_SetCaption("Residual: Modified TinyGL - Software Renderer", "Residual");
byte *frameBuffer = (byte *)_screen->pixels;
_zb = ZB_open(screenW, screenH, ZB_MODE_5R6G5B, 0, NULL, NULL, frameBuffer);
tglInit(_zb);
_zbufferSurface = SDL_CreateRGBSurfaceFrom(_zb->zbuf, screenW, screenH, 16, screenW * 2, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
_fullScreenBitmapData = NULL;
_fullScreenZBitmapData = NULL;
_smushSurface = NULL;
}
DriverTinyGL::~DriverTinyGL() {
SDL_FreeSurface(_zbufferSurface);
tglClose();
ZB_close(_zb);
}
void DriverTinyGL::setupCamera(float fov, float nclip, float fclip, float roll) {
tglMatrixMode(TGL_PROJECTION);
tglLoadIdentity();
float right = nclip * std::tan(fov / 2 * (M_PI / 180));
tglFrustum(-right, right, -right * 0.75, right * 0.75, nclip, fclip);
tglMatrixMode(TGL_MODELVIEW);
tglLoadIdentity();
tglRotatef(roll, 0, 0, -1);
}
void DriverTinyGL::positionCamera(Vector3d pos, Vector3d interest) {
Vector3d up_vec(0, 0, 1);
if (pos.x() == interest.x() && pos.y() == interest.y())
up_vec = Vector3d(0, 1, 0);
lookAt(pos.x(), pos.y(), pos.z(), interest.x(), interest.y(), interest.z(), up_vec.x(), up_vec.y(), up_vec.z());
}
void DriverTinyGL::clearScreen() {
tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT);
}
void DriverTinyGL::flipBuffer() {
SDL_Flip(_screen);
}
void DriverTinyGL::startActorDraw(Vector3d pos, float yaw, float pitch, float roll) {
#ifndef TURN_ON_LIGTHS_WITHOUT_TEXTURES
tglEnable(TGL_TEXTURE_2D);
#endif
tglMatrixMode(TGL_MODELVIEW);
tglPushMatrix();
tglTranslatef(pos.x(), pos.y(), pos.z());
tglRotatef(yaw, 0, 0, 1);
tglRotatef(pitch, 1, 0, 0);
tglRotatef(roll, 0, 1, 0);
}
void DriverTinyGL::finishActorDraw() {
tglMatrixMode(TGL_MODELVIEW);
tglPopMatrix();
tglDisable(TGL_TEXTURE_2D);
}
void DriverTinyGL::set3DMode() {
tglMatrixMode(TGL_MODELVIEW);
tglEnable(TGL_DEPTH_TEST);
}
void DriverTinyGL::drawModelFace(const Model::Face *face, float *vertices, float *vertNormals, float *textureVerts) {
tglNormal3fv((float *)face->_normal._coords);
tglBegin(TGL_POLYGON);
for (int i = 0; i < face->_numVertices; i++) {
tglNormal3fv(vertNormals + 3 * face->_vertices[i]);
if (face->_texVertices != NULL)
tglTexCoord2fv(textureVerts + 2 * face->_texVertices[i]);
tglVertex3fv(vertices + 3 * face->_vertices[i]);
}
tglEnd();
}
void DriverTinyGL::drawHierachyNode(const Model::HierNode *node) {
if (node->_hierVisible) {
tglPushMatrix();
tglTranslatef(node->_animPos.x() / node->_totalWeight, node->_animPos.y() / node->_totalWeight, node->_animPos.z() / node->_totalWeight);
tglRotatef(node->_animYaw / node->_totalWeight, 0, 0, 1);
tglRotatef(node->_animPitch / node->_totalWeight, 1, 0, 0);
tglRotatef(node->_animRoll / node->_totalWeight, 0, 1, 0);
if (node->_mesh != NULL && node->_meshVisible) {
tglPushMatrix();
tglTranslatef(node->_pivot.x(), node->_pivot.y(), node->_pivot.z());
node->_mesh->draw();
tglMatrixMode(TGL_MODELVIEW);
tglPopMatrix();
}
if (node->_child != NULL) {
node->_child->draw();
tglMatrixMode(TGL_MODELVIEW);
}
tglPopMatrix();
}
if (node->_sibling != NULL)
node->_sibling->draw();
}
void DriverTinyGL::disableLights() {
tglDisable(TGL_LIGHTING);
}
void DriverTinyGL::setupLight(Scene::Light *light, int lightId) {
tglEnable(TGL_LIGHTING);
float ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
float diffuseLight[] = { 0.5f, 0.5f, 0.5f, 1.0f };
float specularLight[] = { 0.0f, 0.0f, 0.0f, 1.0f };
float lightPos[] = { 0.0f, 0.0f, 0.0f, 0.0f };
float lightDir[] = { 0.0f, 0.0f, 0.0f, 0.0f };
lightPos[0] = light->_pos.x();
lightPos[1] = light->_pos.y();
lightPos[2] = light->_pos.z();
ambientLight[0] = (float)light->_color.red() / 256.0f;
ambientLight[1] = (float)light->_color.blue() / 256.0f;
ambientLight[2] = (float)light->_color.green() / 256.0f;
// diffuseLight[0] = (float)light->_intensity;
// diffuseLight[1] = (float)light->_intensity;
// diffuseLight[2] = (float)light->_intensity;
if (strcmp(light->_type.c_str(), "omni") == 0) {
// tglLightfv(TGL_LIGHT0 + lightId, TGL_AMBIENT, ambientLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_DIFFUSE, diffuseLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_SPECULAR, specularLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_POSITION, lightPos);
// tglLightf(TGL_LIGHT0 + lightId, TGL_SPOT_CUTOFF, 1.8f);
// tglLightf(TGL_LIGHT0 + lightId, TGL_LINEAR_ATTENUATION, light->_intensity);
tglEnable(TGL_LIGHT0 + lightId);
} else if (strcmp(light->_type.c_str(), "direct") == 0) {
lightDir[0] = light->_dir.x();
lightDir[1] = light->_dir.y();
lightDir[2] = light->_dir.z();
lightDir[3] = 0.0f;
// tglLightfv(TGL_LIGHT0 + lightId, TGL_AMBIENT, ambientLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_DIFFUSE, diffuseLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_SPECULAR, specularLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_POSITION, lightPos);
tglLightfv(TGL_LIGHT0 + lightId, TGL_SPOT_DIRECTION, lightDir);
// tglLightf(TGL_LIGHT0 + lightId, TGL_SPOT_CUTOFF, 1.8f);
// tglLightf(TGL_LIGHT0 + lightId, TGL_SPOT_EXPONENT, 2.0f);
2005-01-19 09:24:40 +00:00
// tglLightf(TGL_LIGHT0 + lightId, TGL_LINEAR_ATTENUATION, light->_intensity);
tglEnable(TGL_LIGHT0 + lightId);
} else if (strcmp(light->_type.c_str(), "spot")) {
lightDir[0] = light->_dir.x();
lightDir[1] = light->_dir.y();
lightDir[2] = light->_dir.z();
lightDir[3] = 0.0f;
// tglLightfv(TGL_LIGHT0 + lightId, TGL_AMBIENT, ambientLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_DIFFUSE, diffuseLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_SPECULAR, specularLight);
tglLightfv(TGL_LIGHT0 + lightId, TGL_POSITION, lightPos);
tglLightfv(TGL_LIGHT0 + lightId, TGL_SPOT_DIRECTION, lightDir);
// tglLightf(TGL_LIGHT0 + lightId, TGL_SPOT_CUTOFF, 1.8f);
// tglLightf(TGL_LIGHT0 + lightId, TGL_SPOT_EXPONENT, 2.0f);
// tglLightf(TGL_LIGHT0 + lightId, TGL_LINEAR_ATTENUATION, light->_intensity);
tglEnable(TGL_LIGHT0 + lightId);
} else {
error("Scene::setupLights() Unknown type of light: %s", light->_type);
}
}
void DriverTinyGL::createBitmap(Bitmap *bitmap) {
if (bitmap->_format == 1) {
} else {
for (int pic = 0; pic < bitmap->_numImages; pic++) {
uint16 *zbufPtr = reinterpret_cast<uint16 *>(bitmap->_data[pic]);
for (int i = 0; i < (bitmap->_width * bitmap->_height); i++) {
uint16 val = READ_LE_UINT16(bitmap->_data[pic] + 2 * i);
zbufPtr[i] = ((uint32) val) * 0x10000 / 100 / (0x10000 - val);
}
}
}
}
void DriverTinyGL::drawBitmap(const Bitmap *bitmap) {
SDL_Surface *tmpSurface = NULL;
SDL_Rect srcRect, dstRect;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = bitmap->width();
srcRect.h = bitmap->height();
dstRect.x = bitmap->x();
dstRect.y = bitmap->y();
dstRect.w = bitmap->width();
dstRect.h = bitmap->height();
if (bitmap->_format == 1) {
char *tmp = bitmap->_data[bitmap->_currImage - 1];
tmpSurface = SDL_CreateRGBSurfaceFrom(tmp, bitmap->width(), bitmap->height(),
16, bitmap->width() * 2, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
SDL_SetColorKey(tmpSurface, SDL_SRCCOLORKEY, 0xf81f);
SDL_BlitSurface(tmpSurface, &srcRect, _screen, &dstRect);
SDL_FreeSurface(tmpSurface);
} else {
char *tmp = bitmap->_data[bitmap->_currImage - 1];
tmpSurface = SDL_CreateRGBSurfaceFrom(tmp, bitmap->width(), bitmap->height(),
16, bitmap->width() * 2, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
SDL_BlitSurface(tmpSurface, &srcRect, _zbufferSurface, &dstRect);
SDL_FreeSurface(tmpSurface);
}
}
2005-01-12 23:28:47 +00:00
void DriverTinyGL::destroyBitmap(Bitmap *) { }
void DriverTinyGL::drawDepthBitmap(int, int, int, int, char *) { }
void DriverTinyGL::createMaterial(Material *material, const char *data, const CMap *cmap) {
material->_textures = new TGLuint[material->_numImages];
tglGenTextures(material->_numImages, (TGLuint *)material->_textures);
char *texdata = new char[material->_width * material->_height * 4];
for (int i = 0; i < material->_numImages; i++) {
char *texdatapos = texdata;
for (int y = 0; y < material->_height; y++) {
for (int x = 0; x < material->_width; x++) {
int col = *(uint8 *)(data);
if (col == 0)
memset(texdatapos, 0, 3); // transparent
else {
memcpy(texdatapos, cmap->_colors + 3 * (*(uint8 *)(data)), 3);
}
texdatapos += 3;
data++;
}
}
TGLuint *textures = (TGLuint *)material->_textures;
tglBindTexture(TGL_TEXTURE_2D, textures[i]);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_REPEAT);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_REPEAT);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR);
tglTexImage2D(TGL_TEXTURE_2D, 0, 3, material->_width, material->_height, 0, TGL_RGB, TGL_UNSIGNED_BYTE, texdata);
data += 24;
}
delete[] texdata;
}
void DriverTinyGL::selectMaterial(const Material *material) {
#ifdef TURN_ON_LIGTHS_WITHOUT_TEXTURES
return;
#endif
TGLuint *textures = (TGLuint *)material->_textures;
tglBindTexture(TGL_TEXTURE_2D, textures[material->_currImage]);
tglPushMatrix();
tglMatrixMode(TGL_TEXTURE);
tglLoadIdentity();
tglScalef(1.0f / material->_width, 1.0f / material->_height, 1);
tglMatrixMode(TGL_MODELVIEW);
tglPopMatrix();
}
void DriverTinyGL::destroyMaterial(Material *material) {
tglDeleteTextures(material->_numImages, (TGLuint *)material->_textures);
2005-01-12 23:28:47 +00:00
delete[] (TGLuint *)material->_textures;
}
void DriverTinyGL::prepareSmushFrame(int width, int height, byte *bitmap) {
_smushWidth = width;
_smushHeight = height;
if (_smushSurface) {
SDL_FreeSurface(_smushSurface);
_smushSurface = NULL;
}
_smushSurface = SDL_CreateRGBSurfaceFrom(bitmap, _smushWidth, _smushHeight, 16, _smushWidth * 2, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
}
void DriverTinyGL::drawSmushFrame(int offsetX, int offsetY) {
SDL_Rect srcRect, dstRect;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = _smushWidth;
srcRect.h = _smushHeight;
dstRect.x = offsetX;
dstRect.y = offsetY;
dstRect.w = _smushWidth;
dstRect.h = _smushHeight;
SDL_BlitSurface(_smushSurface, &srcRect, _screen, &dstRect);
}
void DriverTinyGL::loadEmergFont() {
}
2005-01-12 23:28:47 +00:00
void DriverTinyGL::drawEmergString(int /*x*/, int /*y*/, const char * /*text*/, const Color &/*fgColor*/) {
}