rendered paste body/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "DynamicColor.h"
/* system implementation headers */
#include <math.h>
#include <string.h>
#include <vector>
/* common implementation headers */
#include "GameTime.h"
#include "Pack.h"
// this applies to all function periods
const float DynamicColor::minPeriod = 0.01f;
//
// Dynamic Color Manager
//
DynamicColorManager DYNCOLORMGR;
DynamicColorManager::DynamicColorManager()
{
return;
}
DynamicColorManager::~DynamicColorManager()
{
clear();
return;
}
void DynamicColorManager::clear()
{
std::vector<DynamicColor*>::iterator it;
for (it = colors.begin(); it != colors.end(); it++) {
delete *it;
}
colors.clear();
return;
}
void DynamicColorManager::update()
{
const double gameTime = GameTime::getStepTime();
std::vector<DynamicColor*>::iterator it;
for (it = colors.begin(); it != colors.end(); it++) {
DynamicColor* color = *it;
color->update(gameTime);
}
return;
}
int DynamicColorManager::addColor(DynamicColor* color)
{
colors.push_back (color);
return ((int)colors.size() - 1);
}
int DynamicColorManager::findColor(const std::string& dyncol) const
{
if (dyncol.size() <= 0) {
return -1;
}
else if ((dyncol[0] >= '0') && (dyncol[0] <= '9')) {
int index = atoi (dyncol.c_str());
if ((index < 0) || (index >= (int)colors.size())) {
return -1;
} else {
return index;
}
}
else {
for (int i = 0; i < (int)colors.size(); i++) {
if (colors[i]->getName() == dyncol) {
return i;
}
}
return -1;
}
}
const DynamicColor* DynamicColorManager::getColor(int id) const
{
if ((id >= 0) && (id < (int)colors.size())) {
return colors[id];
} else {
return NULL;
}
}
void * DynamicColorManager::pack(void *buf) const
{
std::vector<DynamicColor*>::const_iterator it;
buf = nboPackUInt(buf, (int)colors.size());
for (it = colors.begin(); it != colors.end(); it++) {
const DynamicColor* color = *it;
buf = color->pack(buf);
}
return buf;
}
void * DynamicColorManager::unpack(void *buf)
{
unsigned int i;
uint32_t count;
buf = nboUnpackUInt (buf, count);
for (i = 0; i < count; i++) {
DynamicColor* color = new DynamicColor;
buf = color->unpack(buf);
addColor(color);
}
return buf;
}
int DynamicColorManager::packSize() const
{
int fullSize = sizeof (uint32_t);
std::vector<DynamicColor*>::const_iterator it;
for (it = colors.begin(); it != colors.end(); it++) {
DynamicColor* color = *it;
fullSize = fullSize + color->packSize();
}
return fullSize;
}
void DynamicColorManager::print(std::ostream& out, const std::string& indent) const
{
std::vector<DynamicColor*>::const_iterator it;
for (it = colors.begin(); it != colors.end(); it++) {
DynamicColor* color = *it;
color->print(out, indent);
}
return;
}
//
// Dynamic Color
//
DynamicColor::DynamicColor()
{
// setup the channels
for (int c = 0; c < 4; c++) {
// the parameters are setup so that all channels
// are at 1.0f, and that there are no variations
color[c] = 1.0f;
channels[c].minValue = 0.0f;
channels[c].maxValue = 1.0f;
channels[c].sequence.count = 0;
channels[c].sequence.list = NULL;
}
possibleAlpha = false;
name = "";
return;
}
DynamicColor::~DynamicColor()
{
// sanitize the sequence value
for (int c = 0; c < 4; c++) {
sequenceParams& seq = channels[c].sequence;
delete[] seq.list;
}
return;
}
void DynamicColor::finalize()
{
// sanitize the sequence value, and check for alpha middle values
bool noAlphaSeqMid = true;
for (int c = 0; c < 4; c++) {
sequenceParams& seq = channels[c].sequence;
for (unsigned int i = 0; i < seq.count; i++) {
if (seq.list[i] < colorMin) {
seq.list[i] = colorMin;
} else if (seq.list[i] > colorMax) {
seq.list[i] = colorMax;
} else if ((c == 3) && (seq.list[i] == colorMid)) {
noAlphaSeqMid = false;
}
}
}
// check if there might be translucency
possibleAlpha = true;
const ChannelParams& p = channels[3]; // the alpha channel
if ((p.minValue == 1.0f) && (p.maxValue == 1.0f)) {
// opaque regardless of functions
possibleAlpha = false;
}
else {
// check the a special sequence case
const sequenceParams& seq = p.sequence;
if ((seq.count > 0) &&
(noAlphaSeqMid && (p.minValue == 0.0f) && (p.maxValue == 1.0f))) {
// transparency, not translucency
possibleAlpha = false;
}
else {
// check for the common case
if ((p.sinusoids.size() == 0) &&
(p.clampUps.size() == 0) &&
(p.clampDowns.size() == 0) &&
(p.sequence.count == 0)) {
// not using any functions
if (p.maxValue == 1.0f) {
possibleAlpha = false;
}
}
}
}
return;
}
bool DynamicColor::setName(const std::string& dyncol)
{
if (dyncol.size() <= 0) {
name = "";
return false;
}
else if ((dyncol[0] >= '0') && (dyncol[0] <= '9')) {
name = "";
return false;
}
else {
name = dyncol;
}
return true;
}
const std::string& DynamicColor::getName() const
{
return name;
}
void DynamicColor::setLimits(int channel, float min, float max)
{
if (min < 0.0f) {
min = 0.0f;
}
else if (min > 1.0f) {
min = 1.0f;
}
if (max < 0.0f) {
max = 0.0f;
}
else if (max > 1.0f) {
max = 1.0f;
}
channels[channel].minValue = min;
channels[channel].maxValue = max;
return;
}
void DynamicColor::setSequence(int channel,float period, float offset,
std::vector<char>& list)
{
sequenceParams& seq = channels[channel].sequence;
delete[] seq.list;
seq.list = NULL;
seq.count = 0;
if (period >= minPeriod) {
seq.period = period;
seq.offset = offset;
seq.count = (unsigned int)list.size();
seq.list = new char[seq.count];
for (unsigned int i = 0; i < seq.count; i++) {
seq.list[i] = list[i];
}
}
return;
}
void DynamicColor::addSinusoid(int channel, const float sinusoid[3])
{
sinusoidParams params;
// check period and weight
if ((sinusoid[0] >= minPeriod) && (sinusoid[2] > 0.0f)) {
params.period = sinusoid[0];
params.offset = sinusoid[1];
params.weight = sinusoid[2];
channels[channel].sinusoids.push_back(params);
}
return;
}
void DynamicColor::addClampUp(int channel, const float clampUp[3])
{
clampParams params;
// check period and width
if ((clampUp[0] >= minPeriod) && (clampUp[2] > 0.0f)) {
params.period = clampUp[0];
params.offset = clampUp[1];
params.width = clampUp[2];
channels[channel].clampUps.push_back(params);
}
return;
}
void DynamicColor::addClampDown(int channel, const float clampDown[3])
{
clampParams params;
// check period and width
if ((clampDown[0] >= minPeriod) && (clampDown[2] > 0.0f)) {
params.period = clampDown[0];
params.offset = clampDown[1];
params.width = clampDown[2];
channels[channel].clampDowns.push_back(params);
}
return;
}
void DynamicColor::update (double t)
{
for (int c = 0; c < 4; c++) {
const ChannelParams& channel = channels[c];
unsigned int i;
bool clampUp = false;
bool clampDown = false;
// sequence rules over the clamps
const sequenceParams& seq = channel.sequence;
if (seq.count > 0) {
const double seqPeriod = (double)seq.period;
const double fullPeriod = (double)seq.count * seqPeriod;
double indexTime = (t - (double)seq.offset);
if (indexTime < 0.0) {
indexTime -= fullPeriod * floor(indexTime / fullPeriod);
}
indexTime = fmod (indexTime, fullPeriod);
const unsigned int index = (unsigned int)(indexTime / seqPeriod);
if (seq.list[index] == colorMin) {
clampDown = true;
}
else if (seq.list[index] == colorMax) {
clampUp = true;
}
}
else {
// check for active clampUp
for (i = 0; i < channel.clampUps.size(); i++) {
const clampParams& clamp = channel.clampUps[i];
double upTime = (t - (double)clamp.offset);
const double clampPeriod = (double)clamp.period;
if (upTime < 0.0) {
upTime -= (double)clamp.period * floor(upTime / clampPeriod);
}
upTime = fmod (upTime, clampPeriod);
if (upTime < (double)clamp.width) {
clampUp = true;
break;
}
}
// check for active clampDown
for (i = 0; i < channel.clampDowns.size(); i++) {
const clampParams& clamp = channel.clampDowns[i];
double downTime = (t - (double)clamp.offset);
const double clampPeriod = (double)clamp.period;
if (downTime < 0.0) {
downTime -= clampPeriod * floor(downTime / clampPeriod);
}
downTime = fmod (downTime, clampPeriod);
if (downTime < (double)clamp.width) {
clampDown = true;
break;
}
}
}
// the amount of 'max' in the resultant channel's value
float factor = 1.0f;
// check the clamps
if (clampUp && clampDown) {
factor = 0.5f;
}
else if (clampUp) {
factor = 1.0f;
}
else if (clampDown) {
factor = 0.0f;
}
// no clamps, try sinusoids
else if (channel.sinusoids.size() > 0) {
float value = 0.0f;
for (i = 0; i < channel.sinusoids.size(); i++) {
const sinusoidParams& s = channel.sinusoids[i];
const double phase = ((t - (double)s.offset) / (double)s.period);
const double clampedPhase = fmod(phase, 1.0);
value += s.weight * (float)cos(clampedPhase * (M_PI * 2.0));
}
// center the factor
factor = 0.5f + (0.5f * value);
if (factor < 0.0f) {
factor = 0.0f;
}
else if (factor > 1.0f) {
factor = 1.0f;
}
}
color[c] = (channel.minValue * (1.0f - factor)) +
(channel.maxValue * factor);
}
return;
}
void * DynamicColor::pack(void *buf) const
{
buf = nboPackStdString(buf, name);
for (int c = 0; c < 4; c++) {
const ChannelParams& p = channels[c];
unsigned int i;
buf = nboPackFloat (buf, p.minValue);
buf = nboPackFloat (buf, p.maxValue);
// sinusoids
buf = nboPackUInt (buf, (unsigned int)p.sinusoids.size());
for (i = 0; i < p.sinusoids.size(); i++) {
buf = nboPackFloat (buf, p.sinusoids[i].period);
buf = nboPackFloat (buf, p.sinusoids[i].offset);
buf = nboPackFloat (buf, p.sinusoids[i].weight);
}
// clampUps
buf = nboPackUInt (buf, (unsigned int)p.clampUps.size());
for (i = 0; i < p.clampUps.size(); i++) {
buf = nboPackFloat (buf, p.clampUps[i].period);
buf = nboPackFloat (buf, p.clampUps[i].offset);
buf = nboPackFloat (buf, p.clampUps[i].width);
}
// clampDowns
buf = nboPackUInt (buf, (unsigned int)p.clampDowns.size());
for (i = 0; i < p.clampDowns.size(); i++) {
buf = nboPackFloat (buf, p.clampDowns[i].period);
buf = nboPackFloat (buf, p.clampDowns[i].offset);
buf = nboPackFloat (buf, p.clampDowns[i].width);
}
// sequence
const sequenceParams& seq = p.sequence;
buf = nboPackUInt (buf, seq.count);
if (seq.count > 0) {
buf = nboPackFloat (buf, seq.period);
buf = nboPackFloat (buf, seq.offset);
for (i = 0; i < seq.count; i++) {
buf = nboPackUByte (buf, (uint8_t)seq.list[i]);
}
}
}
return buf;
}
void * DynamicColor::unpack(void *buf)
{
buf = nboUnpackStdString(buf, name);
for (int c = 0; c < 4; c++) {
ChannelParams& p = channels[c];
unsigned int i;
uint32_t size;
buf = nboUnpackFloat (buf, p.minValue);
buf = nboUnpackFloat (buf, p.maxValue);
// sinusoids
buf = nboUnpackUInt (buf, size);
p.sinusoids.resize(size);
for (i = 0; i < size; i++) {
buf = nboUnpackFloat (buf, p.sinusoids[i].period);
buf = nboUnpackFloat (buf, p.sinusoids[i].offset);
buf = nboUnpackFloat (buf, p.sinusoids[i].weight);
}
// clampUps
buf = nboUnpackUInt (buf, size);
p.clampUps.resize(size);
for (i = 0; i < size; i++) {
buf = nboUnpackFloat (buf, p.clampUps[i].period);
buf = nboUnpackFloat (buf, p.clampUps[i].offset);
buf = nboUnpackFloat (buf, p.clampUps[i].width);
}
// clampDowns
buf = nboUnpackUInt (buf, size);
p.clampDowns.resize(size);
for (i = 0; i < size; i++) {
buf = nboUnpackFloat (buf, p.clampDowns[i].period);
buf = nboUnpackFloat (buf, p.clampDowns[i].offset);
buf = nboUnpackFloat (buf, p.clampDowns[i].width);
}
// sequence
buf = nboUnpackUInt (buf, size);
sequenceParams& seq = p.sequence;
seq.count = size;
if (size > 0) {
buf = nboUnpackFloat (buf, seq.period);
buf = nboUnpackFloat (buf, seq.offset);
seq.list = new char[size];
for (i = 0; i < size; i++) {
uint8_t value;
buf = nboUnpackUByte (buf, value);
seq.list[i] = value;
}
} else {
seq.list = NULL;
}
}
finalize();
return buf;
}
int DynamicColor::packSize() const
{
int fullSize = nboStdStringPackSize(name);
for (int c = 0; c < 4; c++) {
fullSize += sizeof(float) * 2; // the limits
fullSize += sizeof(uint32_t);
fullSize += (int)(channels[c].sinusoids.size() * (sizeof(sinusoidParams)));
fullSize += sizeof(uint32_t);
fullSize += (int)(channels[c].clampUps.size() * (sizeof(clampParams)));
fullSize += sizeof(uint32_t);
fullSize += (int)(channels[c].clampDowns.size() * (sizeof(clampParams)));
fullSize += sizeof(uint32_t);
const sequenceParams& seq = channels[c].sequence;
if (seq.count > 0) {
fullSize += sizeof(float) * 2; // period and offset
fullSize += seq.count * sizeof(char);
}
}
return fullSize;
}
void DynamicColor::print(std::ostream& out, const std::string& indent) const
{
const char *colorStrings[4] = { "red", "green", "blue", "alpha" };
out << indent << "dynamicColor" << std::endl;
if (name.size() > 0) {
out << indent << " name " << name << std::endl;
}
for (int c = 0; c < 4; c++) {
const char *colorStr = colorStrings[c];
const ChannelParams& p = channels[c];
if ((p.minValue != 0.0f) || (p.maxValue != 1.0f)) {
out << indent << " " << colorStr << " limits "
<< p.minValue << " " << p.maxValue << std::endl;
}
unsigned int i;
if (p.sequence.count > 0) {
out << indent << " " << colorStr << " sequence " << p.sequence.period << " "
<< p.sequence.offset;
for (i = 0; i < p.sequence.count; i++) {
out << " " << (int)p.sequence.list[i];
}
out << std::endl;
}
for (i = 0; i < p.sinusoids.size(); i++) {
const sinusoidParams& f = p.sinusoids[i];
out << indent << " " << colorStr << " sinusoid "
<< f.period << " " << f.offset << " " << f.weight << std::endl;
}
for (i = 0; i < p.clampUps.size(); i++) {
const clampParams& f = p.clampUps[i];
out << indent << " " << colorStr << " clampup "
<< f.period << " " << f.offset << " " << f.width << std::endl;
}
for (i = 0; i < p.clampDowns.size(); i++) {
const clampParams& f = p.clampDowns[i];
out << indent << " " << colorStr << " clampdown "
<< f.period << " " << f.offset << " " << f.width << std::endl;
}
}
out << indent << "end" << std::endl << std::endl;
return;
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8