EMI: Implement projection shadows.

This commit is contained in:
Joni Vähämäki 2014-07-20 20:58:22 +03:00
parent b80dea5a90
commit 1081471119
7 changed files with 255 additions and 61 deletions

View File

@ -1756,7 +1756,12 @@ bool Actor::shouldDrawShadow(int shadowId) {
Math::Vector3d bboxPos, bboxSize;
getBBoxInfo(bboxPos, bboxSize);
Math::Vector3d centerOffset = bboxPos + bboxSize * 0.5f;
Math::Vector3d centerOffset;
if (g_grim->getGameType() == GType_GRIM) {
centerOffset = bboxPos + bboxSize * 0.5f;
} else {
centerOffset.set(0.0f, 0.01f, 0.0f);
}
p = getPos() + centerOffset;
bool actorSide = n.x() * p.x() + n.y() * p.y() + n.z() * p.z() + d < 0.f;
@ -1798,24 +1803,34 @@ void Actor::setShadowPoint(const Math::Vector3d &p) {
_shadowArray[_activeShadowSlot].pos = p;
}
void Actor::setShadowColor(const Color &color) {
assert(_activeShadowSlot != -1);
_shadowArray[_activeShadowSlot].color = color;
}
void Actor::clearShadowPlanes() {
for (int i = 0; i < MAX_SHADOWS; i++) {
Shadow *shadow = &_shadowArray[i];
while (!shadow->planeList.empty()) {
delete shadow->planeList.back().sector;
shadow->planeList.pop_back();
}
delete[] shadow->shadowMask;
shadow->shadowMaskSize = 0;
shadow->shadowMask = nullptr;
shadow->active = false;
shadow->dontNegate = false;
// TODO: Clean up the userData properly
shadow->userData = nullptr;
clearShadowPlane(i);
}
}
void Actor::clearShadowPlane(int i) {
Shadow *shadow = &_shadowArray[i];
while (!shadow->planeList.empty()) {
delete shadow->planeList.back().sector;
shadow->planeList.pop_back();
}
delete[] shadow->shadowMask;
shadow->shadowMaskSize = 0;
shadow->shadowMask = nullptr;
shadow->active = false;
shadow->dontNegate = false;
// TODO: Clean up the userData properly
shadow->userData = nullptr;
}
void Actor::putInSet(const Common::String &set) {
// The set should change immediately, otherwise a very rapid set change
// for an actor will be recognized incorrectly and the actor will be lost.
@ -2259,6 +2274,62 @@ int Actor::getEffectiveSortOrder() const {
return _haveSectorSortOrder ? _sectorSortOrder : getSortOrder();
}
void Actor::activateShadow(bool active, const char *shadowName) {
Set *set = g_grim->getCurrSet();
if (!shadowName) {
for (int i = 0; i < set->getShadowCount(); ++i) {
activateShadow(active, set->getShadow(i));
}
} else {
SetShadow *shadow = set->getShadowByName(shadowName);
if (shadow)
activateShadow(active, shadow);
}
}
void Actor::activateShadow(bool active, SetShadow *setShadow) {
if (active)
_shadowActive = true;
int shadowId = -1;
for (int i = 0; i < MAX_SHADOWS; i++) {
if (setShadow->_name.equals(_shadowArray[i].name)) {
shadowId = i;
break;
}
}
if (shadowId == -1) {
for (int i = 0; i < MAX_SHADOWS; i++) {
if (!_shadowArray[i].active) {
shadowId = i;
break;
}
}
}
if (shadowId == -1) {
warning("Actor %s trying to activate shadow %s, but all shadow slots are in use", getName().c_str(), setShadow->_name.c_str());
return;
}
clearShadowPlane(shadowId);
setActivateShadow(shadowId, active);
if (active) {
setActiveShadow(shadowId);
setShadowPoint(setShadow->_shadowPoint);
setShadowPlane(setShadow->_name.c_str());
setShadowColor(setShadow->_color);
setShadowValid(-1); // Don't negate the normal.
Common::List<Common::String>::iterator it;
for (it = setShadow->_sectorNames.begin(); it != setShadow->_sectorNames.end(); ++it) {
addShadowPlane((*it).c_str(), g_grim->getCurrSet(), shadowId);
}
}
}
void Actor::attachToActor(Actor *other, const char *joint) {
assert(other != nullptr);
if (other->getId() == _attachedActor)

View File

@ -39,6 +39,7 @@ class LipSync;
class Font;
class Set;
class Material;
class SetShadow;
struct Joint;
struct Plane {
@ -60,6 +61,7 @@ struct Shadow {
int shadowMaskSize;
bool active;
bool dontNegate;
Color color;
void *userData;
};
@ -474,9 +476,11 @@ public:
void setActiveShadow(int shadowId);
void setShadowPoint(const Math::Vector3d &pos);
void setShadowColor(const Color &color);
void setShadowPlane(const char *name);
void addShadowPlane(const char *name);
void clearShadowPlanes();
void clearShadowPlane(int i);
void setShadowValid(int);
void setActivateShadow(int, bool);
@ -539,7 +543,8 @@ public:
void setSortOrder(const int order) { _sortOrder = order; }
int getEffectiveSortOrder() const;
void activateShadow(bool active) { _shadowActive = active; }
void activateShadow(bool active, const char *shadowName);
void activateShadow(bool active, SetShadow *shadow);
void restoreCleanBuffer();
void drawToCleanBuffer();

View File

@ -416,7 +416,7 @@ void Lua_V2::GetActorSortOrder() {
void Lua_V2::ActorActivateShadow() {
lua_Object actorObj = lua_getparam(1);
lua_Object qualityObj = lua_getparam(2);
lua_Object activeObj = lua_getparam(2);
lua_Object planeObj = lua_getparam(3);
if (!lua_isuserdata(actorObj) || lua_tag(actorObj) != MKTAG('A','C','T','R'))
@ -425,12 +425,11 @@ void Lua_V2::ActorActivateShadow() {
Actor *actor = getactor(actorObj);
if (!actor)
return;
int quality = (int)lua_getnumber(qualityObj);
const char *plane = "NULL";
bool active = (int)lua_getnumber(activeObj) == 1;
const char *plane = nullptr;
if (lua_isstring(planeObj))
plane = lua_getstring(planeObj);
warning("Lua_V2::ActorActivateShadow, actor: %s, aquality: %d, plane: %s", actor->getName().c_str(), quality, plane);
actor->activateShadow(quality);
actor->activateShadow(active, plane);
}
void Lua_V2::ActorStopMoving() {

View File

@ -158,7 +158,9 @@ byte *GfxOpenGL::setupScreen(int screenW, int screenH, bool fullscreen) {
GLfloat specularReflectance[] = { 0.3f, 0.3f, 0.3f, 1.0f };
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularReflectance);
glPolygonOffset(-6.0, -6.0);
if (g_grim->getGameType() == GType_GRIM) {
glPolygonOffset(-6.0, -6.0);
}
initExtensions();
glGetIntegerv(GL_MAX_LIGHTS, &_maxLights);
@ -497,10 +499,19 @@ void GfxOpenGL::getBoundingBoxPos(const EMIModel *model, int *x1, int *y1, int *
void GfxOpenGL::startActorDraw(const Actor *actor) {
_currentActor = actor;
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4 && !actor->isInOverworld()) {
// Apply the view transform.
Math::Matrix4 worldRot = _currentQuat.toMatrix();
glMultMatrixf(worldRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
if (_currentShadowArray) {
// TODO find out why shadowMask at device in woods is null
if (!_currentShadowArray->shadowMask) {
@ -508,11 +519,16 @@ void GfxOpenGL::startActorDraw(const Actor *actor) {
_currentShadowArray->shadowMaskSize = _screenWidth * _screenHeight;
}
Sector *shadowSector = _currentShadowArray->planeList.front().sector;
glDepthMask(GL_FALSE);
glEnable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
// glColor3f(0.0f, 1.0f, 0.0f); // debug draw color
glColor3ub(_shadowColorR, _shadowColorG, _shadowColorB);
if (g_grim->getGameType() == GType_GRIM) {
glColor3ub(_shadowColorR, _shadowColorG, _shadowColorB);
} else {
glColor3ub(_currentShadowArray->color.getRed(), _currentShadowArray->color.getGreen(), _currentShadowArray->color.getBlue());
}
glShadowProjection(_currentShadowArray->pos, shadowSector->getVertices()[0], shadowSector->getNormal(), _currentShadowArray->dontNegate);
}
@ -543,10 +559,6 @@ void GfxOpenGL::startActorDraw(const Actor *actor) {
glTranslatef(pos.x(), pos.y(), pos.z());
glMultMatrixf(quat.toMatrix().getData());
} else {
Math::Matrix4 worldRot = _currentQuat.toMatrix();
glMultMatrixf(worldRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
Math::Matrix4 m = actor->getFinalMatrix();
m.transpose();
glMultMatrixf(m.getData());
@ -562,7 +574,7 @@ void GfxOpenGL::startActorDraw(const Actor *actor) {
glMultMatrixf(quat.toMatrix().getData());
}
if (actor->getSortOrder() >= 100) {
if (!_currentShadowArray && actor->getSortOrder() >= 100) {
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
}
@ -612,6 +624,16 @@ void GfxOpenGL::drawShadowPlanes() {
}
*/
glPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4) {
// Apply the view transform.
Math::Matrix4 worldRot = _currentQuat.toMatrix();
glMultMatrixf(worldRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glClearStencil(~0);
@ -634,6 +656,8 @@ void GfxOpenGL::drawShadowPlanes() {
glStencilFunc(GL_EQUAL, 1, (GLuint)~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPopMatrix();
}
void GfxOpenGL::setShadowMode() {
@ -671,7 +695,7 @@ void GfxOpenGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_LIGHTING);
if (face->_hasTexture)
if (!_currentShadowArray && face->_hasTexture)
glEnable(GL_TEXTURE_2D);
else
glDisable(GL_TEXTURE_2D);
@ -680,16 +704,18 @@ void GfxOpenGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
float dim = 1.0f - _dimLevel;
for (uint j = 0; j < face->_faceLength * 3; j++) {
int index = indices[j];
if (face->_hasTexture) {
glTexCoord2f(model->_texVerts[index].getX(), model->_texVerts[index].getY());
if (!_currentShadowArray) {
if (face->_hasTexture) {
glTexCoord2f(model->_texVerts[index].getX(), model->_texVerts[index].getY());
}
Math::Vector3d lighting = model->_lighting[index];
byte r = (byte)(model->_colorMap[index].r * lighting.x() * dim);
byte g = (byte)(model->_colorMap[index].g * lighting.y() * dim);
byte b = (byte)(model->_colorMap[index].b * lighting.z() * dim);
byte a = (int)(model->_colorMap[index].a * _alpha);
glColor4ub(r, g, b, a);
}
Math::Vector3d lighting = model->_lighting[index];
byte r = (byte)(model->_colorMap[index].r * lighting.x() * dim);
byte g = (byte)(model->_colorMap[index].g * lighting.y() * dim);
byte b = (byte)(model->_colorMap[index].b * lighting.z() * dim);
byte a = (int)(model->_colorMap[index].a * _alpha);
glColor4ub(r, g, b, a);
Math::Vector3d normal = model->_normals[index];
Math::Vector3d vertex = model->_drawVertices[index];
@ -704,8 +730,9 @@ void GfxOpenGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
glEnable(GL_ALPHA_TEST);
glEnable(GL_LIGHTING);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glColor3f(1.0f, 1.0f, 1.0f);
if (!_currentShadowArray)
glDepthMask(GL_TRUE);
}
void GfxOpenGL::drawModelFace(const Mesh *mesh, const MeshFace *face) {

View File

@ -517,7 +517,16 @@ void GfxTinyGL::startActorDraw(const Actor *actor) {
tglPushMatrix();
tglMatrixMode(TGL_MODELVIEW);
tglPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4 && !actor->isInOverworld()) {
// Apply the view transform.
Math::Matrix4 worldRot = _currentQuat.toMatrix();
tglMultMatrixf(worldRot.getData());
tglTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
if (_currentShadowArray) {
tglDepthMask(TGL_FALSE);
// TODO find out why shadowMask at device in woods is null
if (!_currentShadowArray->shadowMask) {
_currentShadowArray->shadowMask = new byte[_gameWidth * _gameHeight];
@ -525,7 +534,11 @@ void GfxTinyGL::startActorDraw(const Actor *actor) {
}
assert(_currentShadowArray->shadowMask);
//tglSetShadowColor(255, 255, 255);
tglSetShadowColor(_shadowColorR, _shadowColorG, _shadowColorB);
if (g_grim->getGameType() == GType_GRIM) {
tglSetShadowColor(_shadowColorR, _shadowColorG, _shadowColorB);
} else {
tglSetShadowColor(_currentShadowArray->color.getRed(), _currentShadowArray->color.getGreen(), _currentShadowArray->color.getBlue());
}
tglSetShadowMaskBuf(_currentShadowArray->shadowMask);
SectorListType::iterator i = _currentShadowArray->planeList.begin();
Sector *shadowSector = i->sector;
@ -559,10 +572,6 @@ void GfxTinyGL::startActorDraw(const Actor *actor) {
tglTranslatef(pos.x(), pos.y(), pos.z());
tglMultMatrixf(quat.toMatrix().getData());
} else {
Math::Matrix4 worldRot = _currentQuat.toMatrix();
tglMultMatrixf(worldRot.getData());
tglTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
Math::Matrix4 m = actor->getFinalMatrix();
m.transpose();
tglMultMatrixf(m.getData());
@ -578,7 +587,7 @@ void GfxTinyGL::startActorDraw(const Actor *actor) {
tglMultMatrixf(quat.toMatrix().getData());
}
if (actor->getSortOrder() >= 100) {
if (!_currentShadowArray && actor->getSortOrder() >= 100) {
tglColorMask(TGL_FALSE, TGL_FALSE, TGL_FALSE, TGL_FALSE);
tglDepthMask(TGL_TRUE);
}
@ -611,6 +620,16 @@ void GfxTinyGL::finishActorDraw() {
void GfxTinyGL::drawShadowPlanes() {
tglEnable(TGL_SHADOW_MASK_MODE);
tglDepthMask(TGL_FALSE);
tglPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4) {
// Apply the view transform.
Math::Matrix4 worldRot = _currentQuat.toMatrix();
tglMultMatrixf(worldRot.getData());
tglTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
if (!_currentShadowArray->shadowMask) {
_currentShadowArray->shadowMask = new byte[_gameWidth * _gameHeight];
_currentShadowArray->shadowMaskSize = _gameWidth * _gameHeight;
@ -629,6 +648,8 @@ void GfxTinyGL::drawShadowPlanes() {
}
tglSetShadowMaskBuf(nullptr);
tglDisable(TGL_SHADOW_MASK_MODE);
tglPopMatrix();
}
void GfxTinyGL::setShadowMode() {
@ -639,6 +660,7 @@ void GfxTinyGL::setShadowMode() {
void GfxTinyGL::clearShadowMode() {
GfxBase::clearShadowMode();
tglDisable(TGL_SHADOW_MODE);
tglDepthMask(TGL_TRUE);
}
void GfxTinyGL::set3DMode() {
@ -651,7 +673,7 @@ void GfxTinyGL::setShadow(Shadow *shadow) {
_currentShadowArray = shadow;
if (shadow)
tglDisable(TGL_LIGHTING);
else
else if (g_grim->getGameType() == GType_GRIM)
tglEnable(TGL_LIGHTING);
}
@ -673,7 +695,7 @@ void GfxTinyGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
tglEnable(TGL_DEPTH_TEST);
tglDisable(TGL_ALPHA_TEST);
//tglDisable(TGL_LIGHTING); // not apply here in TinyGL
if (face->_hasTexture)
if (!_currentShadowArray && face->_hasTexture)
tglEnable(TGL_TEXTURE_2D);
else
tglDisable(TGL_TEXTURE_2D);
@ -682,16 +704,18 @@ void GfxTinyGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
float dim = 1.0f - _dimLevel;
for (uint j = 0; j < face->_faceLength * 3; j++) {
int index = indices[j];
if (face->_hasTexture) {
tglTexCoord2f(model->_texVerts[index].getX(), model->_texVerts[index].getY());
}
if (!_currentShadowArray) {
if (face->_hasTexture) {
tglTexCoord2f(model->_texVerts[index].getX(), model->_texVerts[index].getY());
}
Math::Vector3d lighting = model->_lighting[index];
byte r = (byte)(model->_colorMap[index].r * lighting.x() * dim);
byte g = (byte)(model->_colorMap[index].g * lighting.y() * dim);
byte b = (byte)(model->_colorMap[index].b * lighting.z() * dim);
byte a = (int)(model->_colorMap[index].a * _alpha);
tglColor4ub(r, g, b, a);
Math::Vector3d lighting = model->_lighting[index];
byte r = (byte)(model->_colorMap[index].r * lighting.x() * dim);
byte g = (byte)(model->_colorMap[index].g * lighting.y() * dim);
byte b = (byte)(model->_colorMap[index].b * lighting.z() * dim);
byte a = (int)(model->_colorMap[index].a * _alpha);
tglColor4ub(r, g, b, a);
}
Math::Vector3d normal = model->_normals[index];
Math::Vector3d vertex = model->_drawVertices[index];
@ -706,8 +730,9 @@ void GfxTinyGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face)
tglEnable(TGL_ALPHA_TEST);
//tglEnable(TGL_LIGHTING); // not apply here in TinyGL
tglDisable(TGL_BLEND);
tglDepthMask(TGL_TRUE);
tglColor3f(1.0f, 1.0f, 1.0f);
if (!_currentShadowArray)
tglDepthMask(TGL_TRUE);
}
void GfxTinyGL::drawModelFace(const Mesh *mesh, const MeshFace *face) {

View File

@ -55,8 +55,8 @@ Set::Set(const Common::String &sceneName, Common::SeekableReadStream *data) :
Set::Set() :
_cmaps(nullptr), _locked(false), _enableLights(false), _numSetups(0),
_numLights(0), _numSectors(0), _numObjectStates(0), _minVolume(0),
_maxVolume(0), _numCmaps(0), _currSetup(nullptr), _setups(nullptr),
_lights(nullptr), _sectors(nullptr) {
_maxVolume(0), _numCmaps(0), _numShadows(0), _currSetup(nullptr),
_setups(nullptr), _lights(nullptr), _sectors(nullptr), _shadows(nullptr) {
}
@ -79,6 +79,7 @@ Set::~Set() {
_states.pop_front();
delete s;
}
delete[] _shadows;
}
}
@ -176,6 +177,7 @@ void Set::loadBinary(Common::SeekableReadStream *data) {
_numLights = 0;
_lights = nullptr;
_sectors = nullptr;
_shadows = nullptr;
_minVolume = 0;
_maxVolume = 0;
@ -198,6 +200,13 @@ void Set::loadBinary(Common::SeekableReadStream *data) {
_sectors[i]->loadBinary(data);
}
_numShadows = data->readUint32LE();
_shadows = new SetShadow[_numShadows];
for (int i = 0; i < _numShadows; ++i) {
_shadows[i].loadBinary(data);
}
// Enable lights by default
_enableLights = true;
}
@ -553,6 +562,34 @@ bool Light::restoreState(SaveGame *savedState) {
return true;
}
void SetShadow::loadBinary(Common::SeekableReadStream *data) {
uint32 nameLen = data->readUint32LE();
char *name = new char[nameLen];
data->read(name, nameLen);
_name = Common::String(name);
data->skip(5); // Unknown
data->read(&_shadowPoint.x(), 4);
data->read(&_shadowPoint.y(), 4);
data->read(&_shadowPoint.z(), 4);
int numSectors = data->readSint32LE();
for (int i = 0; i < numSectors; ++i) {
uint32 sectorNameLen = data->readUint32LE();
char *sectorName = new char[sectorNameLen];
data->read(sectorName, sectorNameLen);
_sectorNames.push_back(sectorName);
delete[] sectorName;
}
data->skip(4); // Unknown
_color._vals[0] = (byte)data->readSint32LE();
_color._vals[1] = (byte)data->readSint32LE();
_color._vals[2] = (byte)data->readSint32LE();
delete[] name;
}
void Set::Setup::setupCamera() const {
// Ignore nclip_ and fclip_ for now. This fixes:
// (a) Nothing was being displayed in the Land of the Living
@ -931,4 +968,17 @@ void Set::moveObjectStateToBack(const ObjectState::Ptr &s) {
_states.push_back(s);
}
SetShadow *Set::getShadow(int i) {
return &_shadows[i];
}
SetShadow *Set::getShadowByName(const Common::String &name) {
for (int i = 0; i < _numShadows; ++i) {
SetShadow *shadow = &_shadows[i];
if (shadow->_name.equalsIgnoreCase(name))
return shadow;
}
return nullptr;
}
} // end of namespace Grim

View File

@ -39,6 +39,7 @@ namespace Grim {
class SaveGame;
class CMap;
class Light;
class SetShadow;
class Set : public PoolObject<Set> {
public:
@ -134,17 +135,22 @@ public:
const Common::List<Light *> &getLights() { return _lightsList; }
const Math::Frustum &getFrustum() { return _frustum; }
int getShadowCount() const { return _numShadows; }
SetShadow *getShadow(int i);
SetShadow *getShadowByName(const Common::String &name);
private:
bool _locked;
Common::String _name;
int _numCmaps;
ObjectPtr<CMap> *_cmaps;
int _numSetups, _numLights, _numSectors, _numObjectStates;
int _numSetups, _numLights, _numSectors, _numObjectStates, _numShadows;
bool _enableLights;
Sector **_sectors;
Light *_lights;
Common::List<Light *> _lightsList;
Setup *_setups;
SetShadow *_shadows;
Setup *_currSetup;
typedef Common::List<ObjectState::Ptr> StateList;
@ -179,6 +185,17 @@ public:
int _id;
};
class SetShadow { // Set shadow data (EMI)
public:
void loadBinary(Common::SeekableReadStream *data);
Common::String _name;
Math::Vector3d _shadowPoint;
int _numSectors;
Common::List<Common::String> _sectorNames;
Color _color;
};
} // end of namespace Grim
#endif