Torque3D/Engine/source/afx/xm/afxXM_WaveBase.cpp
2025-03-09 11:53:23 -05:00

613 lines
18 KiB
C++

//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "afx/arcaneFX.h"
#include "math/mathIO.h"
#include "afx/afxEffectWrapper.h"
#include "afx/afxChoreographer.h"
#include "afx/xm/afxXM_WaveBase.h"
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
IMPLEMENT_CO_DATABLOCK_V1(afxXM_WaveBaseData);
ConsoleDocClass( afxXM_WaveBaseData,
"@brief An xmod datablock.\n\n"
"@ingroup afxXMods\n"
"@ingroup AFX\n"
"@ingroup Datablocks\n"
);
afxXM_WaveBaseData::afxXM_WaveBaseData()
{
waveform_type = WAVEFORM_SINE;
parameter = PARAM_NONE;
op = OP_ADD;
speed = 1.0f;
speed_vari = 0.0;
accel = 0.0f;
phase_shift = 0.0f;
duty_cycle = 1.0f;
duty_shift = 0.0f;
off_duty_t = 0.0f;
waves_per_pulse.set(1,1);
waves_per_rest.set(0,0);
rest_dur = 0.0f;
rest_dur_vari = 0.0f;
axis.zero();
local_axis = true;
}
afxXM_WaveBaseData::afxXM_WaveBaseData(const afxXM_WaveBaseData& other, bool temp_clone) : afxXM_WeightedBaseData(other, temp_clone)
{
waveform_type = other.waveform_type;
parameter = other.parameter;
op = other.op;
speed = other.speed;
speed_vari = other.speed;
accel = other.accel;
phase_shift = other.phase_shift;
duty_cycle = other.duty_cycle;
duty_shift = other.duty_shift;
off_duty_t = other.off_duty_t;
waves_per_pulse = other.waves_per_pulse;
waves_per_rest = other.waves_per_rest;
rest_dur = other.rest_dur;
rest_dur_vari = other.rest_dur_vari;
axis = other.axis;
local_axis = true;
}
ImplementEnumType( afxXM_WaveFormType, "Possible waveform types.\n" "@ingroup afxXM_WaveBase\n\n" )
{ afxXM_WaveBaseData::WAVEFORM_NONE, "none", "..." },
{ afxXM_WaveBaseData::WAVEFORM_SINE, "sine", "..." },
{ afxXM_WaveBaseData::WAVEFORM_SQUARE, "square", "..." },
{ afxXM_WaveBaseData::WAVEFORM_TRIANGLE, "triangle", "..." },
{ afxXM_WaveBaseData::WAVEFORM_SAWTOOTH, "sawtooth", "..." },
{ afxXM_WaveBaseData::WAVEFORM_NOISE, "noise", "..." },
{ afxXM_WaveBaseData::WAVEFORM_ONE, "one", "..." },
EndImplementEnumType;
ImplementEnumType( afxXM_WaveParamType, "Possible wave parameter types.\n" "@ingroup afxXM_WaveBase\n\n" )
{ afxXM_WaveBaseData::PARAM_NONE, "none", "..." },
{ afxXM_WaveBaseData::PARAM_POS, "pos", "..." },
{ afxXM_WaveBaseData::PARAM_POS_X, "pos.x", "..." },
{ afxXM_WaveBaseData::PARAM_POS_Y, "pos.y", "..." },
{ afxXM_WaveBaseData::PARAM_POS_Z, "pos.z", "..." },
{ afxXM_WaveBaseData::PARAM_ORI, "ori", "..." },
{ afxXM_WaveBaseData::PARAM_POS2, "pos2", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_X, "pos2.x", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_Y, "pos2.y", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_Z, "pos2.z", "..." },
{ afxXM_WaveBaseData::PARAM_SCALE, "scale", "..." },
{ afxXM_WaveBaseData::PARAM_SCALE_X, "scale.x", "..." },
{ afxXM_WaveBaseData::PARAM_SCALE_Y, "scale.y", "..." },
{ afxXM_WaveBaseData::PARAM_SCALE_Z, "scale.z", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR, "color", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_R, "color.red", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_G, "color.green", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_B, "color.blue", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_A, "color.alpha", "..." },
{ afxXM_WaveBaseData::PARAM_VIS, "vis", "..." },
{ afxXM_WaveBaseData::PARAM_POS, "position", "..." },
{ afxXM_WaveBaseData::PARAM_POS_X, "position.x", "..." },
{ afxXM_WaveBaseData::PARAM_POS_Y, "position.y", "..." },
{ afxXM_WaveBaseData::PARAM_POS_Z, "position.z", "..." },
{ afxXM_WaveBaseData::PARAM_ORI, "orientation", "..." },
{ afxXM_WaveBaseData::PARAM_POS2, "position2", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_X, "position2.x", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_Y, "position2.y", "..." },
{ afxXM_WaveBaseData::PARAM_POS2_Z, "position2.z", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_R, "color.r", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_G, "color.g", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_B, "color.b", "..." },
{ afxXM_WaveBaseData::PARAM_COLOR_A, "color.a", "..." },
{ afxXM_WaveBaseData::PARAM_VIS, "visibility", "..." },
EndImplementEnumType;
ImplementEnumType( afxXM_WaveOpType, "Possible wave operation types.\n" "@ingroup afxXM_WaveBase\n\n" )
{ afxXM_WaveBaseData::OP_ADD, "add", "..." },
{ afxXM_WaveBaseData::OP_MULTIPLY, "multiply", "..." },
{ afxXM_WaveBaseData::OP_REPLACE, "replace", "..." },
{ afxXM_WaveBaseData::OP_MULTIPLY, "mult", "..." },
EndImplementEnumType;
void afxXM_WaveBaseData::initPersistFields()
{
docsURL;
addField("waveform", TYPEID< afxXM_WaveBaseData::WaveFormType >(), Offset(waveform_type, afxXM_WaveBaseData),
"...");
addField("parameter", TYPEID< afxXM_WaveBaseData::WaveParamType >(), Offset(parameter, afxXM_WaveBaseData),
"...");
addField("op", TYPEID< afxXM_WaveBaseData::WaveOpType >(), Offset(op, afxXM_WaveBaseData),
"...");
addFieldV("speed", TypeRangedF32, Offset(speed, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"waves per second");
addFieldV("speedVariance", TypeRangedF32, Offset(speed_vari, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("acceleration", TypeRangedF32, Offset(accel, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("phaseShift", TypeRangedF32, Offset(phase_shift, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("dutyCycle", TypeRangedF32, Offset(duty_cycle, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("dutyShift", TypeRangedF32, Offset(duty_shift, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("offDutyT", TypeRangedF32, Offset(off_duty_t, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addField("wavesPerPulse", TypeByteRange2, Offset(waves_per_pulse, afxXM_WaveBaseData),
"...");
addField("wavesPerRest", TypeByteRange2, Offset(waves_per_rest, afxXM_WaveBaseData),
"...");
addFieldV("restDuration", TypeRangedF32, Offset(rest_dur, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addFieldV("restDurationVariance", TypeRangedF32, Offset(rest_dur_vari, afxXM_WaveBaseData), &CommonValidators::PositiveFloat,
"...");
addField("axis", TypePoint3F, Offset(axis, afxXM_WaveBaseData),
"...");
addField("axisIsLocal", TypeBool, Offset(local_axis, afxXM_WaveBaseData),
"...");
Parent::initPersistFields();
}
void afxXM_WaveBaseData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->writeInt(waveform_type, WAVEFORM_BITS);
stream->writeInt(parameter, PARAM_BITS);
stream->writeInt(op, OP_BITS);
stream->write(speed);
stream->write(speed_vari);
stream->write(accel);
stream->write(phase_shift);
stream->write(duty_cycle);
stream->write(duty_shift);
stream->write(off_duty_t);
stream->write(waves_per_pulse.low);
stream->write(waves_per_pulse.high);
stream->write(waves_per_rest.low);
stream->write(waves_per_rest.high);
stream->write(rest_dur);
stream->write(rest_dur_vari);
mathWrite(*stream, axis);
stream->writeFlag(local_axis);
}
void afxXM_WaveBaseData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
waveform_type = stream->readInt(WAVEFORM_BITS);
parameter = stream->readInt(PARAM_BITS);
op = stream->readInt(OP_BITS);
stream->read(&speed);
stream->read(&speed_vari);
stream->read(&accel);
stream->read(&phase_shift);
stream->read(&duty_cycle);
stream->read(&duty_shift);
stream->read(&off_duty_t);
stream->read(&waves_per_pulse.low);
stream->read(&waves_per_pulse.high);
stream->read(&waves_per_rest.low);
stream->read(&waves_per_rest.high);
stream->read(&rest_dur);
stream->read(&rest_dur_vari);
mathRead(*stream, &axis);
local_axis = stream->readFlag();
}
void afxXM_WaveBaseData::initParamInfo(U32 parameter, U32& parambit, S32& component)
{
switch (parameter)
{
case PARAM_POS:
parambit = POSITION;
component = -1;
break;
case PARAM_POS_X:
parambit = POSITION;
component = 0;
break;
case PARAM_POS_Y:
parambit = POSITION;
component = 1;
break;
case PARAM_POS_Z:
parambit = POSITION;
component = 2;
break;
case PARAM_ORI:
parambit = ORIENTATION;
component = -1;
break;
case PARAM_POS2:
parambit = POSITION2;
component = -1;
break;
case PARAM_POS2_X:
parambit = POSITION2;
component = 0;
break;
case PARAM_POS2_Y:
parambit = POSITION2;
component = 1;
break;
case PARAM_POS2_Z:
parambit = POSITION2;
component = 2;
break;
case PARAM_SCALE:
parambit = SCALE;
component = -1;
break;
case PARAM_SCALE_X:
parambit = SCALE;
component = 0;
break;
case PARAM_SCALE_Y:
parambit = SCALE;
component = 1;
break;
case PARAM_SCALE_Z:
parambit = SCALE;
component = 2;
break;
case PARAM_COLOR:
parambit = COLOR;
component = -1;
break;
case PARAM_COLOR_R:
parambit = COLOR;
component = 0;
break;
case PARAM_COLOR_G:
parambit = COLOR;
component = 1;
break;
case PARAM_COLOR_B:
parambit = COLOR;
component = 2;
break;
case PARAM_COLOR_A:
parambit = COLOR;
component = 3;
break;
case PARAM_VIS:
parambit = VISIBILITY;
component = -1;
break;
default:
parambit = 0;
component = -1;
break;
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
IMPLEMENT_CO_DATABLOCK_V1(afxXM_WaveRiderBaseData);
ConsoleDocClass( afxXM_WaveRiderBaseData,
"@brief An xmod datablock.\n\n"
"@ingroup afxXMods\n"
"@ingroup AFX\n"
"@ingroup Datablocks\n"
);
afxXM_WaveRiderBaseData::afxXM_WaveRiderBaseData()
{
waveform_type = afxXM_WaveBaseData::WAVEFORM_NONE;
parameter = afxXM_WaveBaseData::PARAM_NONE;
op = afxXM_WaveBaseData::OP_ADD;
off_duty_t = 0.0f;
axis.zero();
local_axis = true;
}
afxXM_WaveRiderBaseData::afxXM_WaveRiderBaseData(const afxXM_WaveRiderBaseData& other, bool temp_clone) : afxXM_WeightedBaseData(other, temp_clone)
{
waveform_type = other.waveform_type;
parameter = other.parameter;
op = other.op;
off_duty_t = other.off_duty_t;
axis = other.axis;
local_axis = true;
}
void afxXM_WaveRiderBaseData::initPersistFields()
{
docsURL;
addField("waveform", TYPEID< afxXM_WaveBaseData::WaveFormType >(), Offset(waveform_type, afxXM_WaveRiderBaseData),
"...");
addField("parameter", TYPEID< afxXM_WaveBaseData::WaveParamType >(), Offset(parameter, afxXM_WaveRiderBaseData),
"...");
addField("op", TYPEID< afxXM_WaveBaseData::WaveOpType >(), Offset(op, afxXM_WaveRiderBaseData),
"...");
addField("offDutyT", TypeF32, Offset(off_duty_t, afxXM_WaveRiderBaseData),
"...");
addField("axis", TypePoint3F, Offset(axis, afxXM_WaveRiderBaseData),
"...");
addField("axisIsLocal", TypeBool, Offset(local_axis, afxXM_WaveRiderBaseData),
"...");
Parent::initPersistFields();
}
void afxXM_WaveRiderBaseData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->writeInt(waveform_type, afxXM_WaveBaseData::WAVEFORM_BITS);
stream->writeInt(parameter, afxXM_WaveBaseData::PARAM_BITS);
stream->writeInt(op, afxXM_WaveBaseData::OP_BITS);
stream->write(off_duty_t);
mathWrite(*stream, axis);
stream->writeFlag(local_axis);
}
void afxXM_WaveRiderBaseData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
waveform_type = stream->readInt(afxXM_WaveBaseData::WAVEFORM_BITS);
parameter = stream->readInt(afxXM_WaveBaseData::PARAM_BITS);
op = stream->readInt(afxXM_WaveBaseData::OP_BITS);
stream->read(&off_duty_t);
mathRead(*stream, &axis);
local_axis = stream->readFlag();
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
// WAVEFORMS
F32 afxXM_WaveformSine::evaluate(F32 t)
{
t = (0.75f + t)*Float_2Pi;
return 0.5f*(1.0f + mSin(t));
}
F32 afxXM_WaveformSquare::evaluate(F32 t)
{
return (t < 0.25f || t >= 0.75) ? 0.0f : 1.0f;
}
F32 afxXM_WaveformTriangle::evaluate(F32 t)
{
return (t < 0.5f) ? 2.0f*t : 2.0f*(1.0f - t);
}
//~~~~~~~~~~~~~~~~~~~~//
afxXM_Waveform* afxXM_WaveBaseData::getWaveform(U32 waveform_type)
{
static afxXM_WaveformSine sine;
static afxXM_WaveformSquare square;
static afxXM_WaveformTriangle triangle;
static afxXM_WaveformSawtooth sawtooth;
static afxXM_WaveformNoise noise;
static afxXM_WaveformOne one;
switch (waveform_type)
{
case WAVEFORM_SINE:
return &sine;
case WAVEFORM_SQUARE:
return &square;
case WAVEFORM_TRIANGLE:
return &triangle;
case WAVEFORM_SAWTOOTH:
return &sawtooth;
case WAVEFORM_NOISE:
return &noise;
case WAVEFORM_ONE:
return &one;
default:
// error condition
return &sine;
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
bool afxXM_WaveBase::last_was_pulsed = false;
bool afxXM_WaveBase::last_was_off_duty = true;
F32 afxXM_WaveBase::last_t = 0.0f;
F32 afxXM_WaveBase::last_wave_t = 0.0f;
afxXM_WaveBase::afxXM_WaveBase(afxXM_WaveBaseData* db, afxEffectWrapper* fxw, afxXM_WaveInterp* interp)
: afxXM_WeightedBase(db, fxw)
{
this->db = db;
interpolator = interp;
waveform = afxXM_WaveBaseData::getWaveform(db->waveform_type);
speed_is_randomized = !mIsZero(db->speed_vari);
speed = calc_initial_speed();
fixed_weight = db->hasFixedWeight();
is_resting = false;
cur_pulse_time = db->delay;
next_pulse_time = cur_pulse_time + ((F32)calc_new_wavesPerPulse())/speed;
interpolator->pulse();
last_was_pulsed = false;
last_was_off_duty = true;
last_t = 0.0f;
last_wave_t = 0.0f;
}
afxXM_WaveBase::~afxXM_WaveBase()
{
delete interpolator;
}
void afxXM_WaveBase::updateParams(F32 dt, F32 elapsed, afxXM_Params& params)
{
elapsed -= db->delay;
if (elapsed > next_pulse_time)
{
is_resting = !is_resting;
cur_pulse_time = next_pulse_time;
if (is_resting)
{
F32 rest_dt = ((F32)(calc_new_wavesPerRest())/speed) + calc_new_restDur();
if (rest_dt < 0.01)
is_resting = false;
else
next_pulse_time = cur_pulse_time + rest_dt;
}
if (!is_resting)
{
speed = calc_new_speed();
next_pulse_time = cur_pulse_time + ((F32)calc_new_wavesPerPulse())/speed;
interpolator->pulse();
last_was_pulsed = true;
}
}
if (is_resting)
{
last_was_off_duty = true;
interpolator->interpolate(db->off_duty_t, params);
return;
}
F32 n_waves = db->phase_shift + (elapsed - cur_pulse_time)*speed;
F32 wave_t = (n_waves - mFloor(n_waves))/db->duty_cycle;
// we are beyond the duty portion of the wave, use off_duty_t
if (wave_t > 1.0f)
{
last_was_off_duty = true;
interpolator->interpolate(db->off_duty_t, params);
return;
}
if (db->duty_shift > 0.0f)
{
wave_t += db->duty_shift;
if (wave_t > 1.0)
wave_t -= 1.0f;
}
last_was_off_duty = false;
last_wave_t = wave_t;
last_t = waveform->evaluate(wave_t);
if (fixed_weight)
{
interpolator->interpolate(last_t, params);
}
else
{
F32 wt_factor = calc_weight_factor(elapsed);
F32 final_t = mLerp(db->off_duty_t, last_t, wt_factor);
interpolator->interpolate(final_t, params);
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
afxXM_WaveRiderBase::afxXM_WaveRiderBase(afxXM_WaveRiderBaseData* db, afxEffectWrapper* fxw, afxXM_WaveInterp* interp)
: afxXM_WeightedBase(db, fxw)
{
this->db = db;
interpolator = interp;
waveform = afxXM_WaveBaseData::getWaveform(db->waveform_type);
fixed_weight = db->hasFixedWeight();
interpolator->pulse();
}
afxXM_WaveRiderBase::~afxXM_WaveRiderBase()
{
delete interpolator;
}
void afxXM_WaveRiderBase::updateParams(F32 dt, F32 elapsed, afxXM_Params& params)
{
if (afxXM_WaveBase::last_was_pulsed)
interpolator->pulse();
if (afxXM_WaveBase::last_was_off_duty)
{
interpolator->interpolate(db->off_duty_t, params);
return;
}
F32 t;
if (db->waveform_type != afxXM_WaveBaseData::WAVEFORM_NONE)
t = waveform->evaluate(afxXM_WaveBase::last_wave_t);
else
t = afxXM_WaveBase::last_t;
if (fixed_weight)
interpolator->interpolate(t, params);
else
{
F32 wt_factor = calc_weight_factor(elapsed);
F32 final_t = mLerp(db->off_duty_t, t, wt_factor);
interpolator->interpolate(final_t, params);
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//