MYST3: Add preliminary cursor support

This commit is contained in:
Bastien Bouclet 2012-01-04 10:54:45 +01:00
parent d677c4b3d2
commit 77cc7f1afc
7 changed files with 288 additions and 30 deletions

162
engines/myst3/cursor.cpp Normal file
View File

@ -0,0 +1,162 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#include "engines/myst3/archive.h"
#include "engines/myst3/cursor.h"
#include "graphics/surface.h"
#include "graphics/imagedec.h"
namespace Myst3 {
struct CursorData {
uint32 nodeID;
uint16 hotspotX;
uint16 hotspotY;
Graphics::Surface *surface;
double transparency;
};
static CursorData availableCursors[13] = {
{ 1000, 8, 8, 0, 0.25 },
{ 1001, 8, 8, 0, 0.5 },
{ 1002, 8, 8, 0, 0.5 },
{ 1003, 1, 5, 0, 0.5 },
{ 1004, 14, 5, 0, 0.5 },
{ 1005, 16, 14, 0, 0.5 },
{ 1006, 16, 14, 0, 0.5 },
{ 1007, 8, 8, 0, 0.55 },
{ 1000, 8, 8, 0, 0.25 },
{ 1001, 8, 8, 0, 0.5 },
{ 1011, 16, 16, 0, 0.5 },
{ 1000, 6, 1, 0, 0.5 },
{ 0, 0, 0, 0, 0 }
};
Cursor::Cursor(Archive *archive) :
_position(320, 210) {
// Load available cursors
loadAvailableCursors(archive);
// Generate texture
generateTexture();
// Set default cursor
changeCursor(8);
}
void Cursor::loadAvailableCursors(Archive *archive) {
// Load available cursors
for (uint i = 0; availableCursors[i].nodeID; i++) {
const DirectorySubEntry *cursorDesc = archive->getDescription(availableCursors[i].nodeID, 0, DirectorySubEntry::kCursor);
if (!cursorDesc)
error("Cursor %d does not exist", availableCursors[i].nodeID);
Common::MemoryReadStream *bmpStream = archive->getData(cursorDesc);
availableCursors[i].surface = Graphics::ImageDecoder::loadFile(*bmpStream, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
// Apply the colorkey for transparency
for (uint u = 0; u < availableCursors[i].surface->w; u++) {
for (uint v = 0; v < availableCursors[i].surface->h; v++) {
uint32 *pixel = (uint32*)(availableCursors[i].surface->getBasePtr(u, v));
if (*pixel == 0xFF00FF00)
*pixel = 0x0000FF00;
}
}
delete bmpStream;
}
}
void Cursor::generateTexture() {
glGenTextures(1, &_textureId);
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _textureSize, _textureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
Cursor::~Cursor() {
// Free available cursors
for (uint i = 0; availableCursors[i].nodeID; i++) {
if (availableCursors[i].surface) {
availableCursors[i].surface->free();
delete availableCursors[i].surface;
availableCursors[i].surface = 0;
}
}
// Delete texture
glDeleteTextures(1, &_textureId);
}
void Cursor::changeCursor(uint32 index) {
assert(index >= 0 && index <= 12);
if (_currentCursorID != index) {
_currentCursorID = index;
uploadTexture();
}
}
void Cursor::uploadTexture() {
Graphics::Surface *bitmap = availableCursors[_currentCursorID].surface;
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->w, bitmap->h, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->pixels);
}
void Cursor::draw()
{
CursorData & cursor = availableCursors[_currentCursorID];
// Size of the cursor
const float w = cursor.surface->w;
const float h = cursor.surface->h;
// Used fragment of texture
const float u = w / (float)(_textureSize);
const float v = h / (float)(_textureSize);
const float left = _position.x - cursor.hotspotX;
const float top = _position.y - cursor.hotspotY;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_FALSE);
glBindTexture(GL_TEXTURE_2D, _textureId);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, v); glVertex3f( left + 0, top + h, 1.0f);
glTexCoord2f(u, v); glVertex3f( left + w, top + h, 1.0f);
glTexCoord2f(0, 0); glVertex3f( left + 0, top + 0, 1.0f);
glTexCoord2f(u, 0); glVertex3f( left + w, top + 0, 1.0f);
glEnd();
glDepthMask(GL_TRUE);
}
} /* namespace Myst3 */

58
engines/myst3/cursor.h Normal file
View File

@ -0,0 +1,58 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#ifndef CURSOR_H_
#define CURSOR_H_
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "common/rect.h"
namespace Myst3 {
class Cursor {
public:
Cursor(Archive *archive);
virtual ~Cursor();
void changeCursor(uint32 index);
void draw();
private:
uint32 _currentCursorID;
GLuint _textureId;
static const uint _textureSize = 32;
/** Position of the cursor */
Common::Point _position;
void loadAvailableCursors(Archive *archive);
void generateTexture();
void uploadTexture();
};
} /* namespace Myst3 */
#endif /* CURSOR_H_ */

View File

@ -279,7 +279,7 @@ HotSpot Database::loadHotspot(Common::ReadStream &s) {
if (hotspot.condition != -1) {
hotspot.rects = loadRects(s);
hotspot.unk2 = s.readUint16LE();
hotspot.cursor = s.readUint16LE();
}
hotspot.script = loadOpcodes(s);

View File

@ -46,6 +46,7 @@ class DirectorySubEntry {
kFaceMask = 1,
kSpotItem = 5,
kFrame = 6,
kCursor = 7,
kMovie = 8,
kStillMovie = 10,
kImagerMovie = 72

View File

@ -49,7 +49,7 @@ class HotSpot {
public:
int16 condition;
Common::Array<PolarRect> rects;
int16 unk2;
int16 cursor;
Common::Array<Opcode> script;
bool isPointInRectsCube(const Common::Point &p);

View File

@ -70,6 +70,7 @@ Myst3Engine::Myst3Engine(OSystem *syst, int gameFlags) :
Myst3Engine::~Myst3Engine() {
DebugMan.clearAllDebugChannels();
delete _cursor;
delete _scene;
delete _archive;
delete _db;
@ -80,8 +81,8 @@ Myst3Engine::~Myst3Engine() {
}
Common::Error Myst3Engine::run() {
const int w = 800;
const int h = 600;
const int w = 640;
const int h = 480;
_rnd = new Common::RandomSource("sprint");
_console = new Console(this);
@ -93,6 +94,12 @@ Common::Error Myst3Engine::run() {
_system->setupScreen(w, h, false, true);
Archive *archiveRSRC = new Archive();
archiveRSRC->open("RSRC.m3r");
_cursor = new Cursor(archiveRSRC);
archiveRSRC->close();
delete archiveRSRC;
_scene->init(w, h);
// Var init script
@ -118,6 +125,45 @@ Common::Error Myst3Engine::run() {
return Common::kNoError;
}
Common::Array<HotSpot *> Myst3Engine::listHoveredHotspots() {
Common::Array<HotSpot *> hovered;
NodePtr nodeData = _db->getNodeData(_vars->getLocationNode());
if (_viewType == kCube) {
Common::Point mouse = _scene->getMousePos();
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsCube(mouse)) {
hovered.push_back(&nodeData->hotspots[j]);
}
}
} else if (_viewType == kFrame) {
Common::Point mouse = _system->getEventManager()->getMousePos();
Common::Point scaledMouse = Common::Point(
mouse.x * Scene::_originalWidth / _system->getWidth(),
CLIP<uint>(mouse.y * Scene::_originalHeight / _system->getHeight()
- Scene::_topBorderHeight, 0, Scene::_frameHeight));
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsFrame(scaledMouse)) {
hovered.push_back(&nodeData->hotspots[j]);
}
}
}
return hovered;
}
void Myst3Engine::updateCursor() {
Common::Array<HotSpot *> hovered = listHoveredHotspots();
if (hovered.size() > 0) {
HotSpot *h = hovered.back();
_cursor->changeCursor(h->cursor);
} else {
_cursor->changeCursor(8);
}
}
void Myst3Engine::processInput(bool lookOnly) {
// Process events
Common::Event event;
@ -129,39 +175,21 @@ void Myst3Engine::processInput(bool lookOnly) {
if (_viewType == kCube) {
_scene->updateCamera(event.relMouse);
}
updateCursor();
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
// Skip the event when in look only mode
if (lookOnly) continue;
NodePtr nodeData = _db->getNodeData(_vars->getLocationNode());
if (_viewType == kCube) {
Common::Point mouse = _scene->getMousePos();
Common::Array<HotSpot *> hovered = listHoveredHotspots();
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsCube(mouse)
&& _vars->evaluate(nodeData->hotspots[j].condition)) {
_scriptEngine->run(&nodeData->hotspots[j].script);
}
}
} else if (_viewType == kFrame) {
static const uint originalWidth = 640;
static const uint originalHeight = 480;
static const uint frameHeight = 360;
Common::Point mouse = _system->getEventManager()->getMousePos();
Common::Point scaledMouse = Common::Point(
mouse.x * originalWidth / _system->getWidth(),
CLIP<uint>(mouse.y * originalHeight / _system->getHeight()
- 30, 0, frameHeight));
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsFrame(scaledMouse)
&& _vars->evaluate(nodeData->hotspots[j].condition)) {
_scriptEngine->run(&nodeData->hotspots[j].script);
}
for (uint j = 0; j < hovered.size(); j++) {
if (_vars->evaluate(hovered[j]->condition)) {
_scriptEngine->run(&hovered[j]->script);
}
}
} else if (event.type == Common::EVENT_KEYDOWN) {
switch (event.kbd.keycode) {
case Common::KEYCODE_d:
@ -203,6 +231,7 @@ void Myst3Engine::drawFrame() {
}
_scene->drawBlackBorders();
_cursor->draw();
_system->updateScreen();
_system->delayMillis(10);
@ -300,6 +329,7 @@ void Myst3Engine::runNodeBackgroundScripts() {
void Myst3Engine::loadNodeCubeFaces(uint16 nodeID) {
_viewType = kCube;
updateCursor();
_system->showMouse(false);
_node = new NodeCube(this, _archive, nodeID);
@ -308,6 +338,7 @@ void Myst3Engine::loadNodeCubeFaces(uint16 nodeID) {
void Myst3Engine::loadNodeFrame(uint16 nodeID) {
_viewType = kFrame;
updateCursor();
_system->showMouse(true);
_node = new NodeFrame(this, _archive, nodeID);

View File

@ -35,6 +35,7 @@
#include "engines/myst3/node.h"
#include "engines/myst3/scene.h"
#include "engines/myst3/script.h"
#include "engines/myst3/cursor.h"
namespace Myst3 {
@ -55,6 +56,7 @@ enum ViewType {
class Console;
class Variables;
class HotSpot;
class Myst3Engine : public Engine {
@ -94,6 +96,7 @@ private:
Archive *_archive;
Script *_scriptEngine;
Database *_db;
Cursor *_cursor;
Common::Array<ScriptedMovie *> _movies;
Common::Array<Drawable *> _drawables;
@ -103,6 +106,9 @@ private:
uint _frameCount;
bool _shouldQuit;
Common::Array<HotSpot *> listHoveredHotspots();
void updateCursor();
friend class Console;
};