tcod-haskell-0.1.0.0: libtcod/samples/ripples/util_ripples.cpp
/*
* Copyright (c) 2010 Jice
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The name of Jice may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Jice ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Jice BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
// windows specific inclusion of alloca
// all other platforms have alloca in stdlib.h
#ifndef alloca
#include <malloc.h>
#endif
#include "main.hpp"
// dummy height indicating that a cell is not water
#define NO_WATER -1000.0f
// how much wave energy is lost per second
#define DAMPING_COEF 1.3f
// wave height below which the water is considered still
#define ACTIVE_THRESHOLD 0.02f
// height on the triggering ripple
#define RIPPLE_TRIGGER 3.0f
// how many times ripples are updated per second
#define RIPPLE_FPS 10
RippleManager::RippleManager(TCODMap *waterMap) {
init(waterMap);
}
void RippleManager::init(TCODMap *waterMap) {
this->width = waterMap->getWidth();
this->height = waterMap->getHeight();
bool *visited = (bool*)alloca(sizeof(bool)*width*height);
memset(visited,0,sizeof(bool)*width*height);
// first time : compute water zones
zone.cumulatedElapsed=0.0f;
zone.isActive=false;
zone.data = new float[ width*height ];
zone.oldData = new float[ width*height ];
for (int dx=0; dx < width; dx++) {
for (int dy=0; dy < height; dy++) {
float value = waterMap->isWalkable(dx,dy) ? 0.0f : NO_WATER;
// set water height to 0.0, not water cells to -1000
zone.data[dx+dy*width] = value;
}
}
memcpy(zone.oldData,zone.data,sizeof(float)*width * height);
}
void RippleManager::startRipple(int x, int y) {
// look for the water zone
int off=x+y*width;
if ( zone.data[off] != NO_WATER ) {
zone.data[off] = -RIPPLE_TRIGGER;
zone.isActive=true;
}
}
bool RippleManager::updateRipples(float elapsed) {
bool updated=false;
zone.cumulatedElapsed+=elapsed;
if ( zone.isActive ) {
// update the ripples
if ( zone.cumulatedElapsed > 1.0f/RIPPLE_FPS ) {
zone.cumulatedElapsed=0.0f;
// swap grids
float *tmp=zone.data;
zone.data=zone.oldData;
zone.oldData=tmp;
zone.isActive=false;
for (int zx2=1; zx2 < width-1; zx2++) {
for (int zy2=1; zy2 < height-1; zy2++) {
int off = zx2 + zy2 * width;
if (zone.data[off] != NO_WATER) {
float sum=0.0f;
int count=0;
// wave smoothing + spreading
if ( zone.oldData[off-1] != NO_WATER ) {
sum += zone.oldData[off-1];
count++;
}
if ( zone.oldData[off+1] != NO_WATER ) {
sum += zone.oldData[off+1];
count++;
}
if ( zone.oldData[off-width] != NO_WATER ) {
sum += zone.oldData[off-width];
count++;
}
if ( zone.oldData[off+width] != NO_WATER ) {
sum += zone.oldData[off+width];
count++;
}
sum = sum * 2 / count;
zone.data[off] = sum - zone.data[off];
// damping
zone.data[off] *= 1.0f-DAMPING_COEF/RIPPLE_FPS;
if ( ABS(zone.data[off]) > ACTIVE_THRESHOLD ) {
zone.isActive=true;
updated=true;
}
}
}
}
}
}
return updated;
}
void RippleManager::renderRipples(const TCODImage *ground, TCODImage *groundWithRipples) {
float elCoef=TCODSystem::getElapsedSeconds()*2.0f;
for ( int x = 1; x < width-1; x++ ) {
for ( int y = 1; y < height-1; y++ ) {
if ( getData(x,y) != NO_WATER ) {
float xOffset=(getData(x-1,y)-getData(x+1,y));
float yOffset=(getData(x,y-1)-getData(x,y+1));
float f[3]={float(x),float(y),elCoef};
xOffset+=noise3d.get(f, TCOD_NOISE_SIMPLEX)*0.3f;
if ( ABS(xOffset) < 250 && ABS(yOffset) < 250 ) {
TCODColor col=ground->getPixel(x+(int)(xOffset),y+(int)(yOffset));
col = col + TCODColor::white*xOffset*0.1f;
groundWithRipples->putPixel(x,y,col);
}
}
}
}
}