Torque3D/Engine/source/afx/ce/afxZodiac.cpp
marauder2k7 73ad92b757 review notes from Az
Should render fallback for namedTarget if namedTarget fails
Add safety around namedtarget getTexture to stop assert
Missing assets should revert to fallback image and print a warning to console
Remove REFACTOR tag from all macros.
2025-03-30 11:22:42 +01:00

415 lines
16 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 "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "core/frameAllocator.h"
#include "terrain/terrRender.h"
#include "gfx/primBuilder.h"
#include "afx/ce/afxZodiac.h"
GFX_ImplementTextureProfile(AFX_GFXZodiacTextureProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::Static | GFXTextureProfile::NoMipmap | GFXTextureProfile::PreserveSize,
GFXTextureProfile::NONE);
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
void afxZodiacData::convertGradientRangeFromDegrees(Point2F& gradrange, const Point2F& gradrange_deg)
{
F32 x = mCos(mDegToRad(gradrange_deg.x));
F32 y = mCos(mDegToRad(gradrange_deg.y));
if (y > x)
gradrange.set(x, y);
else
gradrange.set(y, x);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// afxZodiacData
IMPLEMENT_CO_DATABLOCK_V1(afxZodiacData);
ConsoleDocClass( afxZodiacData,
"@brief A datablock that specifies a decal-like Zodiac effect.\n\n"
"Zodiacs are special-purpose decal textures, often circular, that are always projected vertically onto the ground. Parameters "
"control dynamic rotation and scale as well as texture, color, and blending style."
"\n\n"
"Zodiacs render on objects of type TerrainBlock, InteriorInstance, GroundPlane, MeshRoad, and TSStatic. They are very "
"effective as spellcasting lighting rings, explosion shockwaves, scorched earth decals, and selection indicators."
"\n\n"
"@ingroup afxEffects\n"
"@ingroup AFX\n"
"@ingroup Datablocks\n"
);
StringTableEntry afxZodiacData::GradientRangeSlot;
bool afxZodiacData::sPreferDestinationGradients = false;
afxZodiacData::afxZodiacData()
{
radius_xy = 1;
vert_range.set(0.0f, 0.0f);
start_ang = 0;
ang_per_sec = 0;
color.set(1,1,1,1);
grow_in_time = 0.0f;
shrink_out_time = 0.0f;
growth_rate = 0.0f;
blend_flags = BLEND_NORMAL;
terrain_ok = true;
interiors_ok = true;
reflected_ok = false;
non_reflected_ok = true;
respect_ori_cons = false;
scale_vert_range = true;
interior_h_only = false;
interior_v_ignore = false;
interior_back_ignore = false;
interior_opaque_ignore = false;
interior_transp_ignore = true;
altitude_max = 0.0f;
altitude_falloff = 0.0f;
altitude_shrinks = false;
altitude_fades = false;
distance_max = 75.0f;
distance_falloff = 30.0f;
use_grade_range = false;
prefer_dest_grade = sPreferDestinationGradients;
grade_range_user.set(0.0f, 45.0f);
afxZodiacData::convertGradientRangeFromDegrees(grade_range, grade_range_user);
inv_grade_range = false;
zflags = 0;
}
afxZodiacData::afxZodiacData(const afxZodiacData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
CLONE_ASSET_REFACTOR(Texture);
radius_xy = other.radius_xy;
vert_range = other.vert_range;
start_ang = other.start_ang;
ang_per_sec = other.ang_per_sec;
grow_in_time = other.grow_in_time;
shrink_out_time = other.shrink_out_time;
growth_rate = other.growth_rate;
color = other.color;
altitude_max = other.altitude_max;
altitude_falloff = other.altitude_falloff;
altitude_shrinks = other.altitude_shrinks;
altitude_fades = other.altitude_fades;
distance_max = other.distance_max;
distance_falloff = other.distance_falloff;
use_grade_range = other.use_grade_range;
prefer_dest_grade = other.prefer_dest_grade;
grade_range = other.grade_range;
inv_grade_range = other.inv_grade_range;
zflags = other.zflags;
expand_zflags();
}
ImplementEnumType( afxZodiac_BlendType, "Possible zodiac blend types.\n" "@ingroup afxZodiac\n\n" )
{ afxZodiacData::BLEND_NORMAL, "normal", "..." },
{ afxZodiacData::BLEND_ADDITIVE, "additive", "..." },
{ afxZodiacData::BLEND_SUBTRACTIVE, "subtractive", "..." },
EndImplementEnumType;
void afxZodiacData::initPersistFields()
{
docsURL;
INITPERSISTFIELD_IMAGEASSET(Texture, afxZodiacData, "An image to use as the zodiac's texture.");
addField("radius", TypeF32, Offset(radius_xy, afxZodiacData),
"The zodiac's radius in scene units.");
addField("verticalRange", TypePoint2F, Offset(vert_range, afxZodiacData),
"For interior zodiacs only, verticalRange specifies distances above and below the "
"zodiac's position. If both values are 0.0, the radius is used.");
addField("scaleVerticalRange", TypeBool, Offset(scale_vert_range, afxZodiacData),
"Specifies if the zodiac's verticalRange should scale according to changes in the "
"radius. When a zodiacs is used as an expanding shockwave, this value should be set "
"to false, otherwise the zodiac can expand to cover an entire interior.");
addFieldV("startAngle", TypeRangedF32, Offset(start_ang, afxZodiacData), &CommonValidators::DegreeRange,
"The starting angle in degrees of the zodiac's rotation.");
addFieldV("rotationRate", TypeRangedF32, Offset(ang_per_sec, afxZodiacData), &CommonValidators::DegreeRange,
"The rate of rotation in degrees-per-second. Zodiacs with a positive rotationRate "
"rotate clockwise, while those with negative values turn counter-clockwise.");
addFieldV("growInTime", TypeRangedF32, Offset(grow_in_time, afxZodiacData), &CommonValidators::PositiveFloat,
"A duration of time in seconds over which the zodiac grows from a zero size to its "
"full size as specified by the radius.");
addFieldV("shrinkOutTime", TypeRangedF32, Offset(shrink_out_time, afxZodiacData), &CommonValidators::PositiveFloat,
"A duration of time in seconds over which the zodiac shrinks from full size to "
"invisible.");
addFieldV("growthRate", TypeRangedF32, Offset(growth_rate, afxZodiacData), &CommonValidators::F32Range,
"A rate in meters-per-second at which the zodiac grows in size. A negative value will "
"shrink the zodiac.");
addField("color", TypeColorF, Offset(color, afxZodiacData),
"A color value for the zodiac.");
addField("blend", TYPEID<BlendType>(), Offset(blend_flags, afxZodiacData),
"A blending style for the zodiac. Possible values: normal, additive, or subtractive.");
addField("showOnTerrain", TypeBool, Offset(terrain_ok, afxZodiacData),
"Specifies if the zodiac should be rendered on terrain or terrain-like surfaces.");
addField("showOnInteriors", TypeBool, Offset(interiors_ok, afxZodiacData),
"Specifies if the zodiac should be rendered on interior or interior-like surfaces.");
addField("showInReflections", TypeBool, Offset(reflected_ok, afxZodiacData),
"Specifies if the zodiac should be rendered on the reflection rendering pass of the "
"object it will be projected onto.");
addField("showInNonReflections", TypeBool, Offset(non_reflected_ok, afxZodiacData),
"Specifies if the zodiac should be rendered on the non-reflection rendering pass of "
"the object it will be projected onto.");
addField("trackOrientConstraint", TypeBool, Offset(respect_ori_cons, afxZodiacData),
"Specifies if the zodiac's rotation should be defined by its constrained "
"transformation.");
addField("interiorHorizontalOnly", TypeBool, Offset(interior_h_only, afxZodiacData),
"Specifies if interior zodiacs should be rendered exclusively on perfectly horizontal "
"interior surfaces.");
addField("interiorIgnoreVertical", TypeBool, Offset(interior_v_ignore, afxZodiacData),
"Specifies if interior zodiacs should not be rendered on perfectly vertical interior "
"surfaces.");
addField("interiorIgnoreBackfaces", TypeBool, Offset(interior_back_ignore, afxZodiacData),
"Specifies if interior zodiacs should not be rendered on interior surface which are "
"backfacing to the zodiac's center.");
addField("interiorIgnoreOpaque", TypeBool, Offset(interior_opaque_ignore, afxZodiacData),
"");
addField("interiorIgnoreTransparent", TypeBool, Offset(interior_transp_ignore, afxZodiacData),
"");
addFieldV("altitudeMax", TypeRangedF32, Offset(altitude_max, afxZodiacData), &CommonValidators::F32Range,
"The altitude at which zodiac becomes invisible as the result of fading out or "
"becoming too small.");
addFieldV("altitudeFalloff", TypeRangedF32, Offset(altitude_falloff, afxZodiacData), &CommonValidators::F32Range,
"The altitude at which zodiac begins to fade and/or shrink.");
addField("altitudeShrinks", TypeBool, Offset(altitude_shrinks, afxZodiacData),
"When true, zodiac becomes smaller as altitude increases.");
addField("altitudeFades", TypeBool, Offset(altitude_fades, afxZodiacData),
"When true, zodiac fades out as altitude increases.");
addFieldV("distanceMax", TypeRangedF32, Offset(distance_max, afxZodiacData), &CommonValidators::PositiveFloat,
"The distance from camera at which the zodiac becomes invisible as the result of "
"fading out.");
addFieldV("distanceFalloff", TypeRangedF32, Offset(distance_falloff, afxZodiacData), &CommonValidators::PositiveFloat,
"The distance from camera at which the zodiac begins to fade out.");
addField("useGradientRange", TypeBool, Offset(use_grade_range, afxZodiacData),
"When true, gradientRange will be used to determine on which polygons the zodiac will "
"render.");
addField("preferDestinationGradients", TypeBool, Offset(prefer_dest_grade, afxZodiacData),
"When true, a gradientRange specified on an InteriorInstance or TSStatic will be used "
"instead of the zodiac's gradientRange.");
addField("gradientRange", TypePoint2F, Offset(grade_range_user, afxZodiacData),
"Zodiac will render on polygons with gradients within the range specified by "
"gradientRange. 0 for floor polys, 90 for wall polys, 180 for ceiling polys.");
addField("invertGradientRange", TypeBool, Offset(inv_grade_range, afxZodiacData),
"When true, the zodiac will render on polygons with gradients outside of the range "
"specified by gradientRange.");
Parent::initPersistFields();
GradientRangeSlot = StringTable->lookup("gradientRange");
Con::addVariable("pref::afxZodiac::preferDestinationGradients", TypeBool, &sPreferDestinationGradients);
}
bool afxZodiacData::onAdd()
{
if (Parent::onAdd() == false)
return false;
if (altitude_falloff > altitude_max)
altitude_falloff = altitude_max;
if (distance_falloff > distance_max)
distance_falloff = distance_max;
return true;
}
void afxZodiacData::packData(BitStream* stream)
{
Parent::packData(stream);
merge_zflags();
PACKDATA_ASSET_REFACTOR(Texture);
stream->write(radius_xy);
stream->write(vert_range.x);
stream->write(vert_range.y);
stream->write(grade_range.x);
stream->write(grade_range.y);
stream->write(start_ang);
stream->write(ang_per_sec);
stream->write(grow_in_time);
stream->write(shrink_out_time);
stream->write(growth_rate);
stream->write(color);
stream->write(zflags);
stream->write(altitude_max);
stream->write(altitude_falloff);
stream->writeFlag(altitude_shrinks);
stream->writeFlag(altitude_fades);
stream->write(distance_max);
stream->write(distance_falloff);
}
void afxZodiacData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
UNPACKDATA_ASSET_REFACTOR(Texture);
stream->read(&radius_xy);
stream->read(&vert_range.x);
stream->read(&vert_range.y);
stream->read(&grade_range.x);
stream->read(&grade_range.y);
stream->read(&start_ang);
stream->read(&ang_per_sec);
stream->read(&grow_in_time);
stream->read(&shrink_out_time);
stream->read(&growth_rate);
stream->read(&color);
stream->read(&zflags);
stream->read(&altitude_max);
stream->read(&altitude_falloff);
altitude_shrinks = stream->readFlag();
altitude_fades = stream->readFlag();
stream->read(&distance_max);
stream->read(&distance_falloff);
expand_zflags();
}
bool afxZodiacData::preload(bool server, String &errorStr)
{
if (!Parent::preload(server, errorStr))
return false;
if (vert_range.x == 0.0f && vert_range.y == 0.0f)
vert_range.x = vert_range.y = radius_xy;
if (mTextureAsset.notNull())
{
getTexture();
}
return true;
}
void afxZodiacData::onStaticModified(const char* slot, const char* newValue)
{
Parent::onStaticModified(slot, newValue);
if (slot == GradientRangeSlot)
{
convertGradientRangeFromDegrees(grade_range, grade_range_user);
return;
}
merge_zflags();
}
void afxZodiacData::onPerformSubstitutions()
{
if (mTextureAsset.notNull())
{
getTexture();
}
}
F32 afxZodiacData::calcRotationAngle(F32 elapsed, F32 rate_factor)
{
F32 angle = start_ang + elapsed*ang_per_sec*rate_factor;
angle = mFmod(angle, 360.0f);
return angle;
}
void afxZodiacData::expand_zflags()
{
blend_flags = (zflags & BLEND_MASK);
terrain_ok = ((zflags & SHOW_ON_TERRAIN) != 0);
interiors_ok = ((zflags & SHOW_ON_INTERIORS) != 0);
reflected_ok = ((zflags & SHOW_IN_REFLECTIONS) != 0);
non_reflected_ok = ((zflags & SHOW_IN_NON_REFLECTIONS) != 0);
respect_ori_cons = ((zflags & RESPECT_ORIENTATION) != 0);
scale_vert_range = ((zflags & SCALE_VERT_RANGE) != 0);
interior_h_only = ((zflags & INTERIOR_HORIZ_ONLY) != 0);
interior_v_ignore = ((zflags & INTERIOR_VERT_IGNORE) != 0);
interior_back_ignore = ((zflags & INTERIOR_BACK_IGNORE) != 0);
interior_opaque_ignore = ((zflags & INTERIOR_OPAQUE_IGNORE) != 0);
interior_transp_ignore = ((zflags & INTERIOR_TRANSP_IGNORE) != 0);
use_grade_range = ((zflags & USE_GRADE_RANGE) != 0);
prefer_dest_grade = ((zflags & PREFER_DEST_GRADE) != 0);
inv_grade_range = ((zflags & INVERT_GRADE_RANGE) != 0);
}
void afxZodiacData::merge_zflags()
{
zflags = (blend_flags & BLEND_MASK);
if (terrain_ok)
zflags |= SHOW_ON_TERRAIN;
if (interiors_ok)
zflags |= SHOW_ON_INTERIORS;
if (reflected_ok)
zflags |= SHOW_IN_REFLECTIONS;
if (non_reflected_ok)
zflags |= SHOW_IN_NON_REFLECTIONS;
if (respect_ori_cons)
zflags |= RESPECT_ORIENTATION;
if (scale_vert_range)
zflags |= SCALE_VERT_RANGE;
if (interior_h_only)
zflags |= INTERIOR_HORIZ_ONLY;
if (interior_v_ignore)
zflags |= INTERIOR_VERT_IGNORE;
if (interior_back_ignore)
zflags |= INTERIOR_BACK_IGNORE;
if (interior_opaque_ignore)
zflags |= INTERIOR_OPAQUE_IGNORE;
if (interior_transp_ignore)
zflags |= INTERIOR_TRANSP_IGNORE;
if (use_grade_range)
zflags |= USE_GRADE_RANGE;
if (prefer_dest_grade)
zflags |= PREFER_DEST_GRADE;
if (inv_grade_range)
zflags |= INVERT_GRADE_RANGE;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//