//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // 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() { INIT_ASSET(Texture); 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(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() { 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."); addField("startAngle", TypeF32, Offset(start_ang, afxZodiacData), "The starting angle in degrees of the zodiac's rotation."); addField("rotationRate", TypeF32, Offset(ang_per_sec, afxZodiacData), "The rate of rotation in degrees-per-second. Zodiacs with a positive rotationRate " "rotate clockwise, while those with negative values turn counter-clockwise."); addField("growInTime", TypeF32, Offset(grow_in_time, afxZodiacData), "A duration of time in seconds over which the zodiac grows from a zero size to its " "full size as specified by the radius."); addField("shrinkOutTime", TypeF32, Offset(shrink_out_time, afxZodiacData), "A duration of time in seconds over which the zodiac shrinks from full size to " "invisible."); addField("growthRate", TypeF32, Offset(growth_rate, afxZodiacData), "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(), 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), ""); addField("altitudeMax", TypeF32, Offset(altitude_max, afxZodiacData), "The altitude at which zodiac becomes invisible as the result of fading out or " "becoming too small."); addField("altitudeFalloff", TypeF32, Offset(altitude_falloff, afxZodiacData), "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."); addField("distanceMax", TypeF32, Offset(distance_max, afxZodiacData), "The distance from camera at which the zodiac becomes invisible as the result of " "fading out."); addField("distanceFalloff", TypeF32, Offset(distance_falloff, afxZodiacData), "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(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(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; 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 (mTextureAssetId != StringTable->EmptyString()) { mTextureAsset = mTextureAssetId; if (mTextureAsset.notNull()) { if (getTexture() != StringTable->EmptyString() && mTextureName != StringTable->insert("texhandle")) { if (mTextureAsset.notNull()) { mTextureAsset->getChangedSignal().notify(this, &afxZodiacData::onImageChanged); } mTexture.set(getTexture(), mTextureProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__)); } } } } 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; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//