scummvm/sword2/driver/render.cpp

1131 lines
25 KiB
C++
Raw Normal View History

2003-07-28 01:47:41 +00:00
/* Copyright (C) 1994-2003 Revolution Software Ltd
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*/
//=============================================================================
//
// Filename : render.c
// Created : 26th September 1996
// By : P.R.Porter
//
// Summary : This module holds the functions which deal with rendering
// the background and parallax layers, and controlling the
// speed of the scroll (number of frames)
//
// Functions
// ---------
//
// ---------------------------------------------------------------------------
//
// int32 RenderParallax(_parallax *p, int16 layer)
//
// Draws a parallax layer at the current position determined by the scroll.
// A parallax can be either foreground, background or the main screen.
//
// ---------------------------------------------------------------------------
//
// int32 CopyScreenBuffer(void)
//
// Copies the screen buffer to screen memory. This function should be called
// when the drawing should be done to the back buffer. It only does this
// when we are using a software screen buffer.
//
// ---------------------------------------------------------------------------
//
// int32 SetScrollTarget(int16 sx, int16 sy)
//
// Sets the scroll target position for the end of the game cycle. The drivers
// will then automatically scroll as many times as it can to reach this
// position in the allotted time.
//
// --------------------------------------------------------------------------
//
// int32 StartRenderCycle(void)
//
// This function should be called when the game engine is ready to start
// the render cycle.
//
// --------------------------------------------------------------------------
//
// int32 EndRenderCycle(BOOL *end)
//
// This function should be called at the end of the render cycle. If the
// render cycle is to be terminated, the function sets *end to 1. Otherwise,
// the render cycle should continue.
//
// --------------------------------------------------------------------------
//
// int32 SetLocationMetrics(uint16 w, uint16 h)
//
// This function tells the drivers the size of the background screen for the
// current location.
//
// --------------------------------------------------------------------------
//
// int32 PlotPoint(uint16 x, uint16 y, uint8 colour)
//
// Plots the point x,y in relation to the top left corner of the background.
//
// --------------------------------------------------------------------------
//
// int32 DrawLine(int16 x1, int16 y1, int16 x2, int16 y2, uint8 colour)
//
// Draws a line from the point x1,y1 to x2,y2 of the specified colour.
//
// --------------------------------------------------------------------------
//
// int32 InitialiseBackgroundLayer(_parallax *p)
//
// This function should be called five times with either the parallax layer
// or a NULL pointer in order of background parallax to foreground parallax.
//
// --------------------------------------------------------------------------
//
// int32 CloseBackgroundLayer(void)
//
// Should be called once after leaving a room to free up video memory.
//
// --------------------------------------------------------------------------
//
// int32 PlotDots(int16 x, int16 y, int16 count)
//
// Plots 'count' dots at the position x,y.
//
//=============================================================================
//#include "ddraw.h"
2003-07-28 03:12:49 +00:00
#include "stdafx.h"
2003-07-28 01:47:41 +00:00
#include "driver96.h"
#include "d_draw.h"
#include "rdwin.h"
#include "_mouse.h"
#include "render.h"
#include "menu.h"
#include "../sword2.h"
2003-07-28 01:47:41 +00:00
#define MILLISECSPERCYCLE 83
#if PROFILING == 1
int32 profileCopyScreenBuffer = 0;
int32 profileRenderLayers = 0;
int32 profileSpriteRender = 0;
int32 profileDecompression = 0;
#endif
// Scroll variables. scrollx and scrolly hold the current scroll position,
// and scrollxTarget and scrollyTarget are the target position for the end
// of the game cycle.
//int16 scrollx;
//int16 scrolly;
extern int16 scrollx;
extern int16 scrolly;
int16 parallaxScrollx;
int16 parallaxScrolly;
int16 locationWide;
int16 locationDeep;
static int16 scrollxTarget;
static int16 scrollyTarget;
static int16 scrollxOld;
static int16 scrollyOld;
static uint16 layer = 0;
2003-07-28 01:47:41 +00:00
#define RENDERAVERAGETOTAL 4
int32 renderCountIndex = 0;
int32 renderTimeLog[RENDERAVERAGETOTAL] = {60, 60, 60, 60};
int32 initialTime;
int32 startTime;
int32 totalTime;
int32 renderAverageTime = 60;
int32 framesPerGameCycle;
int32 renderTooSlow;
#define BLOCKWIDTH 64
#define BLOCKHEIGHT 64
#define BLOCKWBITS 6
#define BLOCKHBITS 6
#define MAXLAYERS 5
uint8 xblocks[MAXLAYERS];
uint8 yblocks[MAXLAYERS];
uint8 restoreLayer[MAXLAYERS];
// blockSurfaces stores an array of sub-blocks for each of the parallax layers.
typedef struct {
byte data[BLOCKWIDTH * BLOCKHEIGHT];
bool transparent;
} BlockSurface;
BlockSurface **blockSurfaces[MAXLAYERS] = { 0, 0, 0, 0, 0 };
void UploadRect(ScummVM::Rect *r) {
g_system->copy_rect(lpBackBuffer + r->top * screenWide + r->left,
screenWide, r->left, r->top, r->right - r->left, r->bottom - r->top);
}
void BlitBlockSurface(BlockSurface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect) {
if (r->top > clip_rect->bottom || r->left > clip_rect->right || r->bottom <= clip_rect->top || r->right <= clip_rect->left)
return;
byte *src = s->data;
if (r->top < clip_rect->top) {
src -= BLOCKWIDTH * (r->top - clip_rect->top);
r->top = clip_rect->top;
}
if (r->left < clip_rect->left) {
src -= (r->left - clip_rect->left);
r->left = clip_rect->left;
}
if (r->bottom > clip_rect->bottom)
r->bottom = clip_rect->bottom;
if (r->right > clip_rect->right)
r->right = clip_rect->right;
byte *dst = lpBackBuffer + r->top * screenWide + r->left;
int i, j;
if (s->transparent) {
for (i = 0; i < r->bottom - r->top; i++) {
for (j = 0; j < r->right - r->left; j++) {
if (src[j])
dst[j] = src[j];
}
src += BLOCKWIDTH;
dst += screenWide;
}
} else {
for (i = 0; i < r->bottom - r->top; i++) {
memcpy(dst, src, r->right - r->left);
src += BLOCKWIDTH;
dst += screenWide;
}
}
// UploadRect(r);
SetNeedRedraw();
}
2003-07-28 01:47:41 +00:00
#define SCALE_MAXWIDTH 512
#define SCALE_MAXHEIGHT 512
static uint16 xScale[SCALE_MAXWIDTH];
static uint16 yScale[SCALE_MAXHEIGHT];
// I've made the scaling two separate functions because there were cases from
// DrawSprite() where it wasn't obvious if the sprite should grow or shrink,
// which caused crashes.
//
// Keeping them separate might be a good idea anyway, for readability.
//
// The code is based on the original DrawSprite() code, so apart from not
// knowing if I got it right, I don't know how good the original really is.
//
// The backbuf parameter points to the buffer where the image will eventually
// be drawn. This is only used at the highest graphics detail setting (and not
// always even then) and is used to help anti-alias the image.
void SquashImage(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
int32 ince, incne, d;
int16 x, y;
// Work out the x-scale
ince = 2 * dstWidth;
incne = 2 * (dstWidth - srcWidth);
d = 2 * dstWidth - srcWidth;
x = y = 0;
xScale[y] = x;
while (x < srcWidth) {
if (d <= 0) {
d += ince;
x++;
} else {
d += incne;
x++;
y++;
}
xScale[y] = x;
}
// Work out the y-scale
ince = 2 * dstHeight;
incne = 2 * (dstHeight - srcHeight);
d = 2 * dstHeight - srcHeight;
x = y = 0;
yScale[y] = x;
while (x < srcHeight) {
if (d <= 0) {
d += ince;
x++;
} else {
d += incne;
x++;
y++;
}
yScale[y] = x;
}
// Copy the image (with or without anti-aliasing)
if (backbuf) {
for (y = 0; y < dstHeight; y++) {
for (x = 0; x < dstWidth; x++) {
uint8 p;
uint8 p1 = 0;
int count = 0;
int spriteCount = 0;
int red = 0;
int green = 0;
int blue = 0;
int i, j;
for (j = yScale[y]; j < yScale[y + 1]; j++) {
for (i = xScale[x]; i < xScale[x + 1]; i++) {
p = src[j * srcPitch + i];
if (p) {
red += palCopy[p][0];
green += palCopy[p][1];
blue += palCopy[p][2];
p1 = p;
spriteCount++;
} else {
red += palCopy[backbuf[x]][0];
green += palCopy[backbuf[x]][1];
blue += palCopy[backbuf[x]][2];
}
count++;
}
}
if (spriteCount == 0)
dst[x] = 0;
else if (spriteCount == 1)
dst[x] = p1;
else
dst[x] = QuickMatch((uint8) (red / count), (uint8) (green / count), (uint8) (blue / count));
}
dst += dstPitch;
backbuf += screenWide;
}
} else {
for (y = 0; y < dstHeight; y++) {
for (x = 0; x < dstWidth; x++) {
dst[x] = src[yScale[y] * srcPitch + xScale[x]];
}
dst += dstPitch;
}
}
}
void StretchImage(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
byte *origDst = dst;
int32 ince, incne, d;
int16 x, y, i, j, k;
// Work out the x-scale
ince = 2 * srcWidth;
incne = 2 * (srcWidth - dstWidth);
d = 2 * srcWidth - dstWidth;
x = y = 0;
xScale[y] = x;
while (x < dstWidth) {
if (d <= 0) {
d += ince;
x++;
} else {
d += incne;
x++;
y++;
xScale[y] = x;
}
}
// Work out the y-scale
ince = 2 * srcHeight;
incne = 2 * (srcHeight - dstHeight);
d = 2 * srcHeight - dstHeight;
x = y = 0;
yScale[y] = x;
while (x < dstHeight) {
if (d <= 0) {
d += ince;
x++;
} else {
d += incne;
x++;
y++;
yScale[y] = x;
}
}
// Copy the image
for (y = 0; y < srcHeight; y++) {
for (j = yScale[y]; j < yScale[y + 1]; j++) {
k = 0;
for (x = 0; x < srcWidth; x++) {
for (i = xScale[x]; i < xScale[x + 1]; i++) {
dst[k++] = src[y * srcPitch + x];
}
}
dst += dstPitch;
}
}
// Anti-aliasing
if (backbuf) {
byte *newDst = (byte *) malloc(dstWidth * dstHeight);
if (!newDst)
return;
memcpy(newDst, origDst, dstWidth);
for (y = 1; y < dstHeight - 1; y++) {
src = origDst + y * dstPitch;
dst = newDst + y * dstWidth;
*dst++ = *src++;
for (x = 1; x < dstWidth - 1; x++) {
byte pt[5];
byte *p = backbuf + y * 640 + x;
int count = 0;
if (*src) {
count++;
pt[0] = *src;
} else
pt[0] = *p;
pt[1] = *(src - dstPitch);
if (pt[1] == 0)
pt[1] = *(p - 640);
else
count++;
pt[2] = *(src - 1);
if (pt[2] == 0)
pt[2] = *(p - 1);
else
count++;
pt[3] = *(src + 1);
if (pt[3] == 0)
pt[3] = *(p + 1);
else
count++;
pt[4] = *(src + dstPitch);
if (pt[4] == 0)
pt[4] = *(p + 640);
else
count++;
if (count) {
int red = palCopy[pt[0]][0] << 2;
int green = palCopy[pt[0]][1] << 2;
int blue = palCopy[pt[0]][2] << 2;
for (i = 1; i < 5; i++) {
red += palCopy[pt[i]][0];
green += palCopy[pt[i]][1];
blue += palCopy[pt[i]][2];
}
*dst++ = QuickMatch((uint8) (red >> 3), (uint8) (green >> 3), (uint8) (blue >> 3));
} else
*dst++ = 0;
src++;
}
*dst++ = *src++;
}
memcpy(dst, src, dstWidth);
src = newDst;
dst = origDst;
for (i = 0; i < dstHeight; i++) {
memcpy(dst, src, dstWidth);
dst += dstPitch;
src += dstWidth;
}
free(newDst);
}
}
2003-07-28 01:47:41 +00:00
int32 RestoreBackgroundLayer(_parallax *p, int16 l)
{
int16 oldLayer = layer;
int16 i;
debug(2, "RestoreBackgroundLayer %d", l);
layer = l;
if (blockSurfaces[l]) {
for (i = 0; i < xblocks[l] * yblocks[l]; i++)
if (blockSurfaces[l][i])
free(blockSurfaces[l][i]);
free(blockSurfaces[l]);
blockSurfaces[l] = NULL;
}
InitialiseBackgroundLayer(p);
layer = oldLayer;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 PlotPoint(uint16 x, uint16 y, uint8 colour)
{
warning("stub PlotPoint( %d, %d, %d )", x, y, colour);
/*
int16 newx, newy;
newx = x - scrollx;
newy = y - scrolly;
if ((newx < 0) || (newx > RENDERWIDE) || (newy < 0) || (newy > RENDERDEEP))
{
return(RD_OK);
}
if (renderCaps & RDBLTFX_ALLHARDWARE)
{
DDSURFACEDESC ddsd;
HRESULT hr;
ddsd.dwSize = sizeof(DDSURFACEDESC);
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
if (hr != DD_OK)
{
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
}
if (hr == DD_OK)
{
*((uint8 *) ddsd.lpSurface + (newy + 40) * ddsd.lPitch + newx) = colour;
IDirectDrawSurface2_Unlock(lpBackBuffer, ddsd.lpSurface);
}
}
else
myScreenBuffer[newy * RENDERWIDE + newx] = colour;
*/
return(RD_OK);
}
// Uses Bressnham's incremental algorithm!
int32 DrawLine(int16 x0, int16 y0, int16 x1, int16 y1, uint8 colour)
{
warning("stub DrawLine( %d, %d, %d, %d, %d )", x0, y0, x1, y1, colour);
/*
int dx, dy;
int dxmod, dymod;
int ince, incne;
int d;
int x, y;
int addTo;
DDSURFACEDESC ddsd;
HRESULT hr;
x1 -= scrollx;
y1 -= scrolly;
x0 -= scrollx;
y0 -= scrolly;
// Lock the surface if we're rendering to the back buffer.
if (renderCaps & RDBLTFX_ALLHARDWARE)
{
ddsd.dwSize = sizeof(DDSURFACEDESC);
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
if (hr != DD_OK)
{
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
}
if (hr != DD_OK)
return(RD_OK);
(uint8 *) ddsd.lpSurface += (40 * ddsd.lPitch);
}
//Make sure we're going from left to right
if (x1 < x0)
{
x = x1;
x1 = x0;
x0 = x;
y = y1;
y1 = y0;
y0 = y;
}
dx = x1 - x0;
dy = y1 - y0;
if (dx < 0)
dxmod = -dx;
else
dxmod = dx;
if (dy < 0)
dymod = -dy;
else
dymod = dy;
if (dxmod >= dymod)
{
if (dy > 0)
{
d = 2 * dy - dx;
ince = 2 * dy;
incne = 2 * (dy - dx);
x = x0;
y = y0;
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
else
myScreenBuffer[y * RENDERWIDE + x] = colour;
while (x < x1)
{
if (d <= 0)
{
d += ince;
x += 1;
}
else
{
d += incne;
x += 1;
y += 1;
}
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
else
myScreenBuffer[y * RENDERWIDE + x] = colour;
}
}
else
{
addTo = y0;
y0 = 0;
y1 -= addTo;
y1 = -y1;
dy = y1 - y0;
d = 2 * dy - dx;
ince = 2 * dy;
incne = 2 * (dy - dx);
x = x0;
y = y0;
if ((x >= 0) && (x < RENDERWIDE) && (addTo - y >= 0) && (addTo - y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + (addTo - y) * ddsd.lPitch + x) = colour;
else
myScreenBuffer[(addTo - y) * RENDERWIDE + x] = colour;
while (x < x1)
{
if (d <= 0)
{
d += ince;
x += 1;
}
else
{
d += incne;
x += 1;
y += 1;
}
if ((x >= 0) && (x < RENDERWIDE) && (addTo - y >= 0) && (addTo - y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + (addTo - y) * ddsd.lPitch + x) = colour;
else
myScreenBuffer[(addTo - y) * RENDERWIDE + x] = colour;
}
}
}
else
{
//OK, y is now going to be the single increment.
// Ensure the line is going top to bottom
if (y1 < y0)
{
x = x1;
x1 = x0;
x0 = x;
y = y1;
y1 = y0;
y0 = y;
}
dx = x1 - x0;
dy = y1 - y0;
if (dx > 0)
{
d = 2 * dx - dy;
ince = 2 * dx;
incne = 2 * (dx - dy);
x = x0;
y = y0;
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
else
myScreenBuffer[y * RENDERWIDE + x] = colour;
while (y < y1)
{
if (d <= 0)
{
d += ince;
y += 1;
}
else
{
d += incne;
x += 1;
y += 1;
}
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
else
myScreenBuffer[y * RENDERWIDE + x] = colour;
}
}
else
{
addTo = x0;
x0 = 0;
x1 -= addTo;
x1 = -x1;
dx = x1 - x0;
d = 2 * dx - dy;
ince = 2 * dx;
incne = 2 * (dx - dy);
x = x0;
y = y0;
if ((addTo - x >= 0) && (addTo - x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + addTo - x) = colour;
else
myScreenBuffer[y * RENDERWIDE + addTo - x] = colour;
while (y < y1)
{
if (d <= 0)
{
d += ince;
y += 1;
}
else
{
d += incne;
x += 1;
y += 1;
}
if ((addTo - x >= 0) && (addTo - x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
if (renderCaps & RDBLTFX_ALLHARDWARE)
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + addTo - x) = colour;
else
myScreenBuffer[y * RENDERWIDE + addTo - x] = colour;
}
}
}
if (renderCaps & RDBLTFX_ALLHARDWARE)
{
(uint8 *) ddsd.lpSurface -= (40 * ddsd.lPitch);
IDirectDrawSurface2_Unlock(lpBackBuffer, ddsd.lpSurface);
}
*/
return(RD_OK);
}
int32 SetLocationMetrics(uint16 w, uint16 h)
{
locationWide = w;
locationDeep = h;
return(RD_OK);
}
int32 RenderParallax(_parallax *p, int16 l) {
int16 x, y;
int16 i, j;
ScummVM::Rect r;
if (locationWide == screenWide)
x = 0;
else
x = ((int32) ((p->w - screenWide) * scrollx) / (int32) (locationWide - screenWide));
if (locationDeep == (screenDeep - MENUDEEP * 2))
y = 0;
else
y = ((int32) ((p->h - (screenDeep - MENUDEEP * 2)) * scrolly) / (int32) (locationDeep - (screenDeep - MENUDEEP * 2)));
ScummVM::Rect clip_rect;
// Leave enough space for the top and bottom menues
clip_rect.left = 0;
clip_rect.right = screenWide;
clip_rect.top = MENUDEEP;
clip_rect.bottom = screenDeep - MENUDEEP;
for (j = 0; j < yblocks[l]; j++) {
for (i = 0; i < xblocks[l]; i++) {
if (blockSurfaces[l][i + j * xblocks[l]]) {
r.left = i * BLOCKWIDTH - x;
r.right = r.left + BLOCKWIDTH;
r.top = j * BLOCKHEIGHT - y + 40;
r.bottom = r.top + BLOCKHEIGHT;
BlitBlockSurface(blockSurfaces[l][i + j * xblocks[l]], &r, &clip_rect);
}
}
}
parallaxScrollx = scrollx - x;
parallaxScrolly = scrolly - y;
2003-07-28 01:47:41 +00:00
return RD_OK;
2003-07-28 01:47:41 +00:00
}
/*
#define LOGSIZE 10
int32 previousTimeLog[LOGSIZE];
int32 renderCycleStartLog[LOGSIZE];
int32 lastRenderTimeLog[LOGSIZE];
int32 renderCycleEndLog[LOGSIZE];
int32 timeLeftLog[LOGSIZE];
int32 scrollxOldLog[LOGSIZE];
int32 scrollxLog[LOGSIZE];
int32 scrollxTargetLog[LOGSIZE];
void LogMe(int32 in)
{
static int32 i;
if (in == 0)
i = 0;
previousTimeLog[i] = previousTime;
aveRenderCycleLog[i] = aveRenderCycle;
renderCycleStartLog[i] = renderCycleStart;
lastRenderTimeLog[i] = lastRenderTime;
renderCycleEndLog[i] = renderCycleEnd;
timeLeftLog[i] = timeLeft;
scrollxOldLog[i] = scrollxOld;
scrollxLog[i] = scrollx;
scrollxTargetLog[i] = scrollxTarget;
if (++i == LOGSIZE)
i = 0;
}
*/
// Uncomment this when benchmarking the drawing routines.
#define LIMIT_FRAME_RATE
2003-07-28 01:47:41 +00:00
int32 InitialiseRenderCycle(void) {
initialTime = SVM_timeGetTime();
2003-07-28 01:47:41 +00:00
totalTime = initialTime + MILLISECSPERCYCLE;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 StartRenderCycle(void) {
2003-07-28 01:47:41 +00:00
scrollxOld = scrollx;
scrollyOld = scrolly;
startTime = SVM_timeGetTime();
2003-07-28 01:47:41 +00:00
if (startTime + renderAverageTime >= totalTime) {
2003-07-28 01:47:41 +00:00
scrollx = scrollxTarget;
scrolly = scrollyTarget;
renderTooSlow = 1;
} else {
2003-07-28 11:21:25 +00:00
scrollx = (int16) (scrollxOld + ((scrollxTarget - scrollxOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
scrolly = (int16) (scrollyOld + ((scrollyTarget - scrollyOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
2003-07-28 01:47:41 +00:00
renderTooSlow = 0;
}
framesPerGameCycle = 0;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
// FIXME: Move this to some better place?
void sleepUntil(int32 time) {
while ((int32) SVM_timeGetTime() < time) {
g_sword2->parseEvents();
// Make sure menu animations and fades don't suffer
ProcessMenu();
ServiceWindows();
g_system->delay_msecs(10);
}
}
2003-07-28 01:47:41 +00:00
int32 EndRenderCycle(bool *end) {
2003-07-28 01:47:41 +00:00
int32 time;
time = SVM_timeGetTime();
2003-07-28 01:47:41 +00:00
renderTimeLog[renderCountIndex] = time - startTime;
startTime = time;
2003-07-28 01:47:41 +00:00
renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
framesPerGameCycle += 1;
if (++renderCountIndex == RENDERAVERAGETOTAL)
renderCountIndex = 0;
if (renderTooSlow) {
*end = true;
2003-07-28 01:47:41 +00:00
InitialiseRenderCycle();
} else if (startTime + renderAverageTime >= totalTime) {
*end = true;
2003-07-28 01:47:41 +00:00
totalTime += MILLISECSPERCYCLE;
initialTime = time;
#ifdef LIMIT_FRAME_RATE
} else if (scrollxTarget == scrollx && scrollyTarget == scrolly) {
// If we have already reached the scroll target sleep for the
// rest of the render cycle.
*end = true;
sleepUntil(totalTime);
initialTime = SVM_timeGetTime();
totalTime += MILLISECSPERCYCLE;
#endif
} else {
*end = false;
// This is an attempt to ensure that we always reach the scroll
// target. Otherwise the game frequently tries to pump out new
// interpolation frames without ever getting anywhere.
if (ABS(scrollx - scrollxTarget) <= 1 && ABS(scrolly - scrollyTarget) <= 1) {
scrollx = scrollxTarget;
scrolly = scrollyTarget;
} else {
scrollx = (int16) (scrollxOld + ((scrollxTarget - scrollxOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
scrolly = (int16) (scrollyOld + ((scrollyTarget - scrollyOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
}
2003-07-28 01:47:41 +00:00
}
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 SetScrollTarget(int16 sx, int16 sy) {
2003-07-28 01:47:41 +00:00
scrollxTarget = sx;
scrollyTarget = sy;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 CopyScreenBuffer(void) {
// FIXME: This function no longer seems to be needed. We copy the
// back buffer to the screen in ServiceWindows() instead.
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 InitialiseBackgroundLayer(_parallax *p) {
uint8 *memchunk;
uint8 zeros;
uint16 count;
uint16 i, j, k;
uint16 x;
uint8 *data;
uint8 *dst;
_parallaxLine *line;
debug(2, "InitialiseBackgroundLayer");
// This function is called to re-initialise the layers if they have
// been lost. We know this if the layers have already been assigned.
// TODO: Can layers still be lost, or is that a DirectDraw-ism?
if (layer == MAXLAYERS)
CloseBackgroundLayer();
if (!p) {
layer++;
return RD_OK;
}
xblocks[layer] = (p->w + BLOCKWIDTH - 1) >> BLOCKWBITS;
yblocks[layer] = (p->h + BLOCKHEIGHT - 1) >> BLOCKHBITS;
blockSurfaces[layer] = (BlockSurface **) calloc(xblocks[layer] * yblocks[layer], sizeof(BlockSurface *));
if (!blockSurfaces[layer])
return RDERR_OUTOFMEMORY;
// Decode the parallax layer into a large chunk of memory
memchunk = (uint8 *) malloc(xblocks[layer] * BLOCKWIDTH * yblocks[layer] * BLOCKHEIGHT);
if (!memchunk)
return RDERR_OUTOFMEMORY;
// We clear not the entire memory chunk, but enough of it to store
// the entire parallax layer.
memset(memchunk, 0, p->w * p->h);
for (i = 0; i < p->h; i++) {
if (p->offset[i] == 0)
continue;
2003-09-07 03:18:27 +00:00
line = (_parallaxLine *) ((uint8 *) p + FROM_LE_32(p->offset[i]));
data = (uint8 *) line + sizeof(_parallaxLine);
2003-09-07 03:18:27 +00:00
x = FROM_LE_16(line->offset);
dst = memchunk + i * p->w + x;
zeros = 0;
if (line->packets == 0) {
memcpy(dst, data, p->w);
continue;
}
2003-09-07 03:18:27 +00:00
for (j = 0; j < FROM_LE_16(line->packets); j++) {
if (zeros) {
dst += *data;
x += *data;
data++;
zeros = 0;
} else if (*data == 0) {
data++;
zeros = 1;
} else {
count = *data++;
memcpy(dst, data, count);
data += count;
dst += count;
x += count;
zeros = 1;
}
}
}
// Now create the surfaces!
for (i = 0; i < xblocks[layer] * yblocks[layer]; i++) {
bool block_has_data = false;
bool block_is_transparent = false;
data = memchunk + (p->w * BLOCKHEIGHT * (i / xblocks[layer])) + BLOCKWIDTH * (i % xblocks[layer]);
for (j = 0; j < BLOCKHEIGHT; j++) {
for (k = 0; k < BLOCKWIDTH; k++) {
if (data[j * p->w + k])
block_has_data = true;
else
block_is_transparent = true;
}
}
// Only assign a surface to the block if it contains data.
if (block_has_data) {
blockSurfaces[layer][i] = (BlockSurface *) malloc(sizeof(BlockSurface));
// Copy the data into the surfaces.
dst = blockSurfaces[layer][i]->data;
for (j = 0; j < BLOCKHEIGHT; j++) {
memcpy(dst, data, BLOCKWIDTH);
data += p->w;
dst += BLOCKWIDTH;
}
blockSurfaces[layer][i]->transparent = block_is_transparent;
} else
blockSurfaces[layer][i] = NULL;
}
free(memchunk);
layer++;
return RD_OK;
2003-07-28 01:47:41 +00:00
}
int32 CloseBackgroundLayer(void) {
debug(2, "CloseBackgroundLayer");
int16 i, j;
for (j = 0; j < MAXLAYERS; j++) {
if (blockSurfaces[j]) {
for (i = 0; i < xblocks[j] * yblocks[j]; i++)
if (blockSurfaces[j][i])
free(blockSurfaces[j][i]);
free(blockSurfaces[j]);
blockSurfaces[j] = NULL;
}
}
layer = 0;
return RD_OK;
2003-07-28 01:47:41 +00:00
}