From b0e8a1f03255f95ec3cb7e13c6dd39de7ee5588f Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 16 Jan 2018 00:47:53 -0600 Subject: [PATCH 01/16] Adds a new function for defining static console fields on NetObjects - addNetworkedField() This lets you attach a 32 bit netMask to the field, so that when it is changed, it automatically flags the associated bitmasks on the netobject as dirty. This is to shortcut having to flag certain masks being marked as dirty through protected fields and just simplify/streamline the code. --- Engine/source/console/consoleObject.cpp | 11 ++- Engine/source/console/consoleObject.h | 4 +- Engine/source/console/simObject.cpp | 14 ++++ Engine/source/sim/netObject.cpp | 89 +++++++++++++++++++++++++ Engine/source/sim/netObject.h | 51 ++++++++++++++ 5 files changed, 167 insertions(+), 2 deletions(-) diff --git a/Engine/source/console/consoleObject.cpp b/Engine/source/console/consoleObject.cpp index d75405376..e1744c5e1 100644 --- a/Engine/source/console/consoleObject.cpp +++ b/Engine/source/console/consoleObject.cpp @@ -37,6 +37,7 @@ #include "console/engineTypes.h" #include "console/engineAPI.h" +#include "sim/netObject.h" IMPLEMENT_SCOPE( ConsoleAPI, Console,, "Functionality related to the legacy TorqueScript console system." ); @@ -372,6 +373,7 @@ void ConsoleObject::addGroup(const char* in_pGroupname, const char* in_pGroupDoc f.setDataFn = &defaultProtectedSetFn; f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; + f.networkMask = 0; // Add to field list. sg_tempFieldList.push_back(f); @@ -396,6 +398,7 @@ void ConsoleObject::endGroup(const char* in_pGroupname) f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; f.elementCount = 0; + f.networkMask = 0; // Add to field list. sg_tempFieldList.push_back(f); @@ -418,6 +421,7 @@ void ConsoleObject::addArray( const char *arrayName, S32 count ) f.setDataFn = &defaultProtectedSetFn; f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; + f.networkMask = 0; // Add to field list. sg_tempFieldList.push_back(f); @@ -439,6 +443,7 @@ void ConsoleObject::endArray( const char *arrayName ) f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; f.elementCount = 0; + f.networkMask = 0; // Add to field list. sg_tempFieldList.push_back(f); @@ -515,6 +520,7 @@ void ConsoleObject::addField(const char* in_pFieldname, f.setDataFn = &defaultProtectedSetFn; f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = in_writeDataFn; + f.networkMask = 0; ConsoleBaseType* conType = ConsoleBaseType::getType(in_fieldType); AssertFatal(conType, "ConsoleObject::addField - invalid console type"); @@ -609,6 +615,7 @@ void ConsoleObject::addProtectedField(const char* in_pFieldname, f.setDataFn = in_setDataFn; f.getDataFn = in_getDataFn; f.writeDataFn = in_writeDataFn; + f.networkMask = 0; ConsoleBaseType* conType = ConsoleBaseType::getType(in_fieldType); AssertFatal(conType, "ConsoleObject::addProtectedField - invalid console type"); @@ -635,6 +642,7 @@ void ConsoleObject::addFieldV(const char* in_pFieldname, f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; f.validator = v; + f.networkMask = 0; v->fieldIndex = sg_tempFieldList.size(); sg_tempFieldList.push_back(f); @@ -652,11 +660,12 @@ void ConsoleObject::addDeprecatedField(const char *fieldName) f.setDataFn = &defaultProtectedSetFn; f.getDataFn = &defaultProtectedGetFn; f.writeDataFn = &defaultProtectedWriteFn; + f.networkMask = 0; sg_tempFieldList.push_back(f); } - +//------------------------------------------------------------------ bool ConsoleObject::removeField(const char* in_pFieldname) { for (U32 i = 0; i < sg_tempFieldList.size(); i++) { diff --git a/Engine/source/console/consoleObject.h b/Engine/source/console/consoleObject.h index 2ff2bc5a3..30682d7f4 100644 --- a/Engine/source/console/consoleObject.h +++ b/Engine/source/console/consoleObject.h @@ -495,7 +495,8 @@ public: table( NULL ), validator( NULL ), setDataFn( NULL ), - getDataFn( NULL ) + getDataFn( NULL ), + networkMask(0) { doNotSubstitute = keepClearSubsOnly = false; } @@ -518,6 +519,7 @@ public: bool doNotSubstitute; bool keepClearSubsOnly; WriteDataNotify writeDataFn; ///< Function to determine whether data should be written or not. + U32 networkMask; }; typedef Vector FieldList; diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 0583606b0..ef688228f 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -41,6 +41,8 @@ #include "core/fileObject.h" #include "persistence/taml/tamlCustom.h" +#include "sim/netObject.h" + IMPLEMENT_CONOBJECT( SimObject ); // See full description in the new CHM manual @@ -912,6 +914,12 @@ void SimObject::assignFieldsFrom(SimObject *parent) if((*f->setDataFn)( this, NULL, bufferSecure ) ) Con::setData(f->type, (void *) (((const char *)this) + f->offset), j, 1, &fieldVal, f->table); + + if (f->networkMask != 0) + { + NetObject* netObj = static_cast(this); + netObj->setMaskBits(f->networkMask); + } } } } @@ -988,6 +996,12 @@ void SimObject::setDataField(StringTableEntry slotName, const char *array, const if(fld->validator) fld->validator->validateType(this, (void *) (((const char *)this) + fld->offset)); + if (fld->networkMask != 0) + { + NetObject* netObj = static_cast(this); + netObj->setMaskBits(fld->networkMask); + } + onStaticModified( slotName, value ); return; diff --git a/Engine/source/sim/netObject.cpp b/Engine/source/sim/netObject.cpp index 76564fb16..e66a16940 100644 --- a/Engine/source/sim/netObject.cpp +++ b/Engine/source/sim/netObject.cpp @@ -493,3 +493,92 @@ void NetObject::removeScopeRef() } } +//Networked fields +//------------------------------------------------------------------ +void NetObject::addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const char* in_pFieldDocs, + U32 flags, + U32 networkMask) +{ + addNetworkedField( + in_pFieldname, + in_fieldType, + in_fieldOffset, + 1, + in_pFieldDocs, + flags, + networkMask); +} + +void NetObject::addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::WriteDataNotify in_writeDataFn, + const char* in_pFieldDocs, + U32 flags, + U32 networkMask) +{ + addNetworkedField( + in_pFieldname, + in_fieldType, + in_fieldOffset, + in_writeDataFn, + 1, + in_pFieldDocs, + flags, + networkMask); +} + +void NetObject::addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const U32 in_elementCount, + const char* in_pFieldDocs, + U32 flags, + U32 networkMask) +{ + addNetworkedField(in_pFieldname, + in_fieldType, + in_fieldOffset, + &defaultProtectedWriteFn, + in_elementCount, + in_pFieldDocs, + flags, + networkMask); +} + +void NetObject::addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::WriteDataNotify in_writeDataFn, + const U32 in_elementCount, + const char* in_pFieldDocs, + U32 flags, + U32 networkMask) +{ + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(in_pFieldname); + + if (in_pFieldDocs) + f.pFieldDocs = in_pFieldDocs; + + f.type = in_fieldType; + f.offset = in_fieldOffset; + f.elementCount = in_elementCount; + f.validator = NULL; + f.flag = flags; + + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + f.writeDataFn = in_writeDataFn; + + f.networkMask = networkMask; + + ConsoleBaseType* conType = ConsoleBaseType::getType(in_fieldType); + AssertFatal(conType, "ConsoleObject::addField - invalid console type"); + f.table = conType->getEnumTable(); + + sg_tempFieldList.push_back(f); +} \ No newline at end of file diff --git a/Engine/source/sim/netObject.h b/Engine/source/sim/netObject.h index a486936be..7ea1283c4 100644 --- a/Engine/source/sim/netObject.h +++ b/Engine/source/sim/netObject.h @@ -422,6 +422,57 @@ public: void removeScopeRef(); void setScopeRegistered(bool flag) { scope_registered = flag; } bool getScopeRegistered() const { return scope_registered; } + +protected: + /// Add a networked field + /// + /// A networked field is a regular field but with a bitmask flag associated to it. + /// When the field is set, it automatically triggers a call to setMaskBits with the mask associated to the field + /// in order to streamline simple networking code + /// Register a complex field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory. + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const U32 in_elementCount = 1, + const char* in_pFieldDocs = NULL, + U32 flags = 0, + U32 networkMask = 0); + + static void addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::WriteDataNotify in_writeDataFn, + const U32 in_elementCount = 1, + const char* in_pFieldDocs = NULL, + U32 flags = 0, + U32 networkMask = 0); + + /// Register a simple field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const char* in_pFieldDocs, + U32 flags = 0, + U32 networkMask = 0); + + static void addNetworkedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::WriteDataNotify in_writeDataFn, + const char* in_pFieldDocs, + U32 flags = 0, + U32 networkMask = 0); }; //----------------------------------------------------------------------------- From cf98ef835028556110d04286cf05e2f67561b4e1 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 16 Jan 2018 13:40:09 -0600 Subject: [PATCH 02/16] adds an mWrap and mWrapF method for cycling values to within a given range. examples of usage would be say, keeping a rotation within 360 degrees, or hitting a tile boundary and resetting the offset --- Engine/source/math/mConsoleFunctions.cpp | 23 +++++++++++++++++++++++ Engine/source/math/mMathFn.h | 13 +++++++++++++ 2 files changed, 36 insertions(+) diff --git a/Engine/source/math/mConsoleFunctions.cpp b/Engine/source/math/mConsoleFunctions.cpp index 651889539..c9a1f4622 100644 --- a/Engine/source/math/mConsoleFunctions.cpp +++ b/Engine/source/math/mConsoleFunctions.cpp @@ -288,6 +288,29 @@ DefineConsoleFunction( mSaturate, F32, ( F32 v ),, return mClampF( v, 0.0f, 1.0f ); } +DefineConsoleFunction(mWrapF, F32, (F32 v, F32 min, F32 max), , + "Wrap the specified value between two bounds.\n" + "@param v Input value." + "@param min Minimum Bound." + "@param max Maximum Bound." + "@returns The specified value wrapped to the specified bounds." + "@ingroup Math") +{ + return mWrapF(v, min, max); +} + +DefineConsoleFunction(mWrap, S32, (S32 v, S32 min, S32 max), , + "Wrap the specified value between two bounds.\n" + "@param v Input value." + "@param min Minimum Bound." + "@param max Maximum Bound." + "@returns The specified value wrapped to the specified bounds." + "@ingroup Math") +{ + return mWrap(v, min, max); +} + + DefineConsoleFunction( getMax, F32, ( F32 v1, F32 v2 ),, "Calculate the greater of two specified numbers.\n" "@param v1 Input value." diff --git a/Engine/source/math/mMathFn.h b/Engine/source/math/mMathFn.h index 93596f63f..a5d2b6ebb 100644 --- a/Engine/source/math/mMathFn.h +++ b/Engine/source/math/mMathFn.h @@ -237,6 +237,19 @@ inline F32 mClampF(F32 val, F32 low, F32 high) return (F32) getMax(getMin(val, high), low); } +inline S32 mWrap(S32 val, S32 low, S32 high) +{ + int len = high - low; + return low + (val >= 0 ? val % len : -val % len ? len - (-val % len) : 0); + +} + +inline F32 mWrapF(F32 val, F32 low, F32 high) +{ + F32 t = fmod(val - low, high - low); + return t < 0 ? t + high : t + low; +} + /// Template function for doing a linear interpolation between any two /// types which implement operators for scalar multiply and addition. template From 1a89d95919697e3333982cfe46137dceb84fb93a Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 16 Jan 2018 14:14:57 -0600 Subject: [PATCH 03/16] exposes getters for typevalidators. example usage FRangeValidator gravCoefFValidator(-10.f, 10.f); addFieldV( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData), &gravCoefFValidator, "Strength of gravity on the particles." ); <- clamps gravity within a -10 to 10 range when evaluating scriptt-set changes mClamp(gravityCoefficient,gravCoefFValidator.getMin(),gravCoefFValidator.getMax()) for any calculations done on the source side would do the same at the point that is called, with a singularl lookup spot for the range. --- Engine/source/console/typeValidators.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Engine/source/console/typeValidators.h b/Engine/source/console/typeValidators.h index 414721f5d..9ca949fa7 100644 --- a/Engine/source/console/typeValidators.h +++ b/Engine/source/console/typeValidators.h @@ -53,6 +53,8 @@ public: maxV = maxValue; } void validateType(SimObject *object, void *typePtr); + F32 getMin() { return minV; }; + F32 getMax() { return maxV; }; }; /// Signed integer min/max range validator @@ -66,6 +68,8 @@ public: maxV = maxValue; } void validateType(SimObject *object, void *typePtr); + F32 getMin() { return minV; }; + F32 getMax() { return maxV; }; }; /// Scaled integer field validator @@ -93,6 +97,7 @@ class Point3NormalizeValidator : public TypeValidator public: Point3NormalizeValidator(F32 normalizeLength = 1.0f) : length(normalizeLength) { } void validateType(SimObject *object, void *typePtr); + F32 getLength() { return length; }; }; namespace CommonValidators From 19248e72470c50fa922064508d212eeb713672d0 Mon Sep 17 00:00:00 2001 From: Johxz Date: Wed, 31 Jan 2018 20:46:17 -0600 Subject: [PATCH 04/16] delete old dml --- Templates/BaseGame/game/tools/base/images/sky_skybox.dml | 7 ------- .../FPSGameplay/art/environment/precipitation/rain.dml | 1 - .../Modules/FPSGameplay/art/skies/Blank_sky/sky_skybox.dml | 7 ------- .../spectatorGameplay/art/skies/Blank_sky/sky_skybox.dml | 7 ------- 4 files changed, 22 deletions(-) delete mode 100644 Templates/BaseGame/game/tools/base/images/sky_skybox.dml delete mode 100644 Templates/Modules/FPSGameplay/art/environment/precipitation/rain.dml delete mode 100644 Templates/Modules/FPSGameplay/art/skies/Blank_sky/sky_skybox.dml delete mode 100644 Templates/Modules/spectatorGameplay/art/skies/Blank_sky/sky_skybox.dml diff --git a/Templates/BaseGame/game/tools/base/images/sky_skybox.dml b/Templates/BaseGame/game/tools/base/images/sky_skybox.dml deleted file mode 100644 index 5ca0a1cb6..000000000 --- a/Templates/BaseGame/game/tools/base/images/sky_skybox.dml +++ /dev/null @@ -1,7 +0,0 @@ -skybox_1 -skybox_2 -skybox_3 -skybox_4 -skybox_5 -skybox_6 -skybox_6 \ No newline at end of file diff --git a/Templates/Modules/FPSGameplay/art/environment/precipitation/rain.dml b/Templates/Modules/FPSGameplay/art/environment/precipitation/rain.dml deleted file mode 100644 index 3893393dd..000000000 --- a/Templates/Modules/FPSGameplay/art/environment/precipitation/rain.dml +++ /dev/null @@ -1 +0,0 @@ -rain diff --git a/Templates/Modules/FPSGameplay/art/skies/Blank_sky/sky_skybox.dml b/Templates/Modules/FPSGameplay/art/skies/Blank_sky/sky_skybox.dml deleted file mode 100644 index 5ca0a1cb6..000000000 --- a/Templates/Modules/FPSGameplay/art/skies/Blank_sky/sky_skybox.dml +++ /dev/null @@ -1,7 +0,0 @@ -skybox_1 -skybox_2 -skybox_3 -skybox_4 -skybox_5 -skybox_6 -skybox_6 \ No newline at end of file diff --git a/Templates/Modules/spectatorGameplay/art/skies/Blank_sky/sky_skybox.dml b/Templates/Modules/spectatorGameplay/art/skies/Blank_sky/sky_skybox.dml deleted file mode 100644 index 5ca0a1cb6..000000000 --- a/Templates/Modules/spectatorGameplay/art/skies/Blank_sky/sky_skybox.dml +++ /dev/null @@ -1,7 +0,0 @@ -skybox_1 -skybox_2 -skybox_3 -skybox_4 -skybox_5 -skybox_6 -skybox_6 \ No newline at end of file From 5ad9ae30bbed484100df58160a5d0cb20aa22948 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 4 Feb 2018 14:31:28 -0600 Subject: [PATCH 05/16] Bugfixes and improvements for the animation component and related asset behavior. Adds in ability to establish a shape animation asset as being cyclic or blended. Adds functionality for blended animations to integrated into shapeAssets. --- .../source/T3D/assets/ShapeAnimationAsset.cpp | 46 ++++++++++++++++++- .../source/T3D/assets/ShapeAnimationAsset.h | 28 ++++++++++- Engine/source/T3D/assets/ShapeAsset.cpp | 40 ++++++++++++++-- .../animation/animationComponent.cpp | 8 ++-- Engine/source/ts/collada/colladaImport.cpp | 13 +++++- .../scripts/shapeEditorActions.ed.cs | 10 ++-- 6 files changed, 130 insertions(+), 15 deletions(-) diff --git a/Engine/source/T3D/assets/ShapeAnimationAsset.cpp b/Engine/source/T3D/assets/ShapeAnimationAsset.cpp index ee10bbe40..4c2b0283e 100644 --- a/Engine/source/T3D/assets/ShapeAnimationAsset.cpp +++ b/Engine/source/T3D/assets/ShapeAnimationAsset.cpp @@ -92,8 +92,13 @@ ConsoleSetType(TypeShapeAnimationAssetPtr) //----------------------------------------------------------------------------- -ShapeAnimationAsset::ShapeAnimationAsset() +ShapeAnimationAsset::ShapeAnimationAsset() : + mIsEmbedded(false), mIsCyclical(true), mIsBlend(false), mBlendFrame(0), mStartFrame(0), mEndFrame(-1), mPadRotation(true), mPadTransforms(false) { + mFileName = StringTable->EmptyString(); + mAnimationName = StringTable->EmptyString(); + + mBlendAnimAssetName = StringTable->EmptyString(); } //----------------------------------------------------------------------------- @@ -116,6 +121,14 @@ void ShapeAnimationAsset::initPersistFields() addField("animationFile", TypeFilename, Offset(mFileName, ShapeAnimationAsset), "Path to the file name containing the animation"); addField("animationName", TypeString, Offset(mAnimationName, ShapeAnimationAsset), "Name of the animation"); + addField("isEmbedded", TypeBool, Offset(mIsEmbedded, ShapeAnimationAsset), "If true, this animation asset just referrs to an embedded animation of a regular shape mesh. If false, it is a self-contained animation file"); + + addField("isCyclic", TypeBool, Offset(mIsCyclical, ShapeAnimationAsset), "Is this animation looping?"); + + addField("isBlend", TypeBool, Offset(mIsBlend, ShapeAnimationAsset), "Is this animation blended with another?"); + addField("blendRefAnimation", TypeString, Offset(mBlendAnimAssetName, ShapeAnimationAsset), "AssetID of the animation to reference for our blending"); + addField("blendFrame", TypeS32, Offset(mBlendFrame, ShapeAnimationAsset), "Which frame of the reference animation do we use for our blending"); + addField("startFrame", TypeS32, Offset(mStartFrame, ShapeAnimationAsset), "What frame does this animation clip start on"); addField("endFrame", TypeS32, Offset(mEndFrame, ShapeAnimationAsset), "What fram does this animation clip end on"); addField("padRotation", TypeBool, Offset(mPadRotation, ShapeAnimationAsset), "Are the rotation values padded"); @@ -128,4 +141,35 @@ void ShapeAnimationAsset::copyTo(SimObject* object) { // Call to parent. Parent::copyTo(object); +} + +void ShapeAnimationAsset::initializeAsset(void) +{ + if (!mIsEmbedded) + { + //If we're not embedded, we need to load in our initial shape and do some prepwork + + char filenameBuf[1024]; + Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), mFileName); + + mSourceShape = ResourceManager::get().load(filenameBuf); + + if (!mSourceShape->addSequence("ambient", "", mAnimationName, mStartFrame, mEndFrame, mPadRotation, mPadTransforms)) + { + Con::errorf("ShapeAnimationAsset::initializeAsset - Unable to do initial setup of the animation clip named %s for asset %s", mAnimationName, getAssetName()); + return; + } + + S32 sequenceId = mSourceShape->findSequence(mAnimationName); + + if(mIsCyclical) + mSourceShape->sequences[sequenceId].flags |= TSShape::Cyclic; + else + mSourceShape->sequences[sequenceId].flags &= (~(TSShape::Cyclic)); + } +} + +void ShapeAnimationAsset::onAssetRefresh(void) +{ + } \ No newline at end of file diff --git a/Engine/source/T3D/assets/ShapeAnimationAsset.h b/Engine/source/T3D/assets/ShapeAnimationAsset.h index 673607d53..0b7bf1790 100644 --- a/Engine/source/T3D/assets/ShapeAnimationAsset.h +++ b/Engine/source/T3D/assets/ShapeAnimationAsset.h @@ -37,6 +37,12 @@ #ifndef _ASSET_FIELD_TYPES_H_ #include "assets/assetFieldTypes.h" #endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif //----------------------------------------------------------------------------- class ShapeAnimationAsset : public AssetBase @@ -46,6 +52,15 @@ class ShapeAnimationAsset : public AssetBase protected: StringTableEntry mFileName; + bool mIsEmbedded; + bool mIsCyclical; + + bool mIsBlend; + + StringTableEntry mBlendAnimAssetName; + + S32 mBlendFrame; + // StringTableEntry mAnimationName; S32 mStartFrame; @@ -53,6 +68,8 @@ protected: bool mPadRotation; bool mPadTransforms; + Resource mSourceShape; + public: ShapeAnimationAsset(); virtual ~ShapeAnimationAsset(); @@ -65,18 +82,25 @@ public: DECLARE_CONOBJECT(ShapeAnimationAsset); protected: - virtual void initializeAsset(void) {} - virtual void onAssetRefresh(void) {} + virtual void initializeAsset(void); + virtual void onAssetRefresh(void); public: StringTableEntry getAnimationFilename() { return mFileName; } StringTableEntry getAnimationName() { return mAnimationName; } + StringTableEntry getBlendAnimationName() { return mBlendAnimAssetName; } S32 getStartFrame() { return mStartFrame; } S32 getEndFrame() { return mEndFrame; } bool getPadRotation() { return mPadRotation; } bool getPadTransforms() { return mPadTransforms; } + + bool isEmbedded() { return mIsEmbedded; } + bool isCyclic() { return mIsCyclical; } + bool isBlend() { return mIsBlend; } + + S32 getBlendFrame() { return mBlendFrame; } }; DefineConsoleType(TypeShapeAnimationAssetPtr, ShapeAnimationAsset) diff --git a/Engine/source/T3D/assets/ShapeAsset.cpp b/Engine/source/T3D/assets/ShapeAsset.cpp index 099c3c117..2bce369ea 100644 --- a/Engine/source/T3D/assets/ShapeAsset.cpp +++ b/Engine/source/T3D/assets/ShapeAsset.cpp @@ -182,17 +182,49 @@ bool ShapeAsset::loadShape() return false; //if it failed to load, bail out } + bool hasBlends = false; + //Now that we've successfully loaded our shape and have any materials and animations loaded //we need to set up the animations we're using on our shape - for (U32 i = 0; i < mAnimationAssets.size(); i++) + for (S32 i = mAnimationAssets.size()-1; i >= 0; --i) { - String srcName; + String srcName = mAnimationAssets[i]->getAnimationName(); String srcPath(mAnimationAssets[i]->getAnimationFilename()); - SplitSequencePathAndName(srcPath, srcName); + //SplitSequencePathAndName(srcPath, srcName); - if (!mShape->addSequence(srcPath, srcName, mAnimationAssets[i]->getAnimationName(), + if (!mShape->addSequence(srcPath, srcName, srcName, mAnimationAssets[i]->getStartFrame(), mAnimationAssets[i]->getEndFrame(), mAnimationAssets[i]->getPadRotation(), mAnimationAssets[i]->getPadTransforms())) return false; + + if (mAnimationAssets[i]->isBlend()) + hasBlends = true; + } + + //if any of our animations are blends, set those up now + if (hasBlends) + { + for (U32 i=0; i < mAnimationAssets.size(); ++i) + { + if (mAnimationAssets[i]->isBlend() && mAnimationAssets[i]->getBlendAnimationName() != StringTable->EmptyString()) + { + //gotta do a bit of logic here. + //First, we need to make sure the anim asset we depend on for our blend is loaded + AssetPtr blendAnimAsset = mAnimationAssets[i]->getBlendAnimationName(); + + if (blendAnimAsset.isNull()) + { + Con::errorf("ShapeAsset::initializeAsset - Unable to acquire reference animation asset %s for asset %s to blend!", mAnimationAssets[i]->getBlendAnimationName(), mAnimationAssets[i]->getAssetName()); + return false; + } + + String refAnimName = blendAnimAsset->getAnimationName(); + if (!mShape->setSequenceBlend(mAnimationAssets[i]->getAnimationName(), true, blendAnimAsset->getAnimationName(), mAnimationAssets[i]->getBlendFrame())) + { + Con::errorf("ShapeAnimationAsset::initializeAsset - Unable to set animation clip %s for asset %s to blend!", mAnimationAssets[i]->getAnimationName(), mAnimationAssets[i]->getAssetName()); + return false; + } + } + } } return true; diff --git a/Engine/source/T3D/components/animation/animationComponent.cpp b/Engine/source/T3D/components/animation/animationComponent.cpp index c8fd645d0..cd7c2534c 100644 --- a/Engine/source/T3D/components/animation/animationComponent.cpp +++ b/Engine/source/T3D/components/animation/animationComponent.cpp @@ -222,7 +222,7 @@ U32 AnimationComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stre { U32 retMask = Parent::packUpdate(con, mask, stream); - /*for (int i = 0; i < MaxScriptThreads; i++) + for (int i = 0; i < MaxScriptThreads; i++) { Thread& st = mAnimationThreads[i]; if (stream->writeFlag((st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)))) @@ -234,7 +234,7 @@ U32 AnimationComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stre stream->writeFlag(st.atEnd); stream->writeFlag(st.transition); } - }*/ + } return retMask; } @@ -243,7 +243,7 @@ void AnimationComponent::unpackUpdate(NetConnection *con, BitStream *stream) { Parent::unpackUpdate(con, stream); - /*for (S32 i = 0; i < MaxScriptThreads; i++) + for (S32 i = 0; i < MaxScriptThreads; i++) { if (stream->readFlag()) { @@ -260,7 +260,7 @@ void AnimationComponent::unpackUpdate(NetConnection *con, BitStream *stream) else updateThread(st); } - }*/ + } } void AnimationComponent::processTick() diff --git a/Engine/source/ts/collada/colladaImport.cpp b/Engine/source/ts/collada/colladaImport.cpp index f3aea309e..1cc2909b0 100644 --- a/Engine/source/ts/collada/colladaImport.cpp +++ b/Engine/source/ts/collada/colladaImport.cpp @@ -198,7 +198,7 @@ DefineConsoleFunction( enumColladaForImport, bool, (const char * shapePath, cons for (S32 j = 0; j < libraryMats->getMaterial_array().getCount(); j++) { domMaterial* mat = libraryMats->getMaterial_array()[j]; - tree->insertItem(matsID, _GetNameOrId(mat), _GetNameOrId(mat), "", 0, 0); + tree->insertItem(matsID, _GetNameOrId(mat), "", "", 0, 0); } } @@ -256,5 +256,16 @@ DefineConsoleFunction( enumColladaForImport, bool, (const char * shapePath, cons else tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS"); + char shapesStr[16]; + dSprintf(shapesStr, 16, "%i", stats.numMeshes); + char materialsStr[16]; + dSprintf(materialsStr, 16, "%i", stats.numMaterials); + char animationsStr[16]; + dSprintf(animationsStr, 16, "%i", stats.numClips); + + tree->setItemValue(nodesID, StringTable->insert(shapesStr)); + tree->setItemValue(matsID, StringTable->insert(materialsStr)); + tree->setItemValue(animsID, StringTable->insert(animationsStr)); + return true; } diff --git a/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs index 3847226f6..15bd4cc9b 100644 --- a/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs +++ b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs @@ -346,10 +346,11 @@ function ActionAddSequence::doit( %this ) %assetDef = AssetDatabase.acquireAsset(%this.seqName); %moduleName = getWord(getToken(%this.seqName, ":", 0),0); - %idx = ShapeEdSequenceList.rowCount(); + //TODO, properly ignore and ambient entries for our idx values, but only if they're there! + %idx = ShapeEdSequenceList.rowCount() - 2; - %matSet = "ShapeEditorPlugin.selectedAssetDef.animationSequence"@%idx@"=\"@Asset="@%moduleName@":"@%this.seqName.assetName@"\";"; - eval(%matSet); + %animSet = "ShapeEditorPlugin.selectedAssetDef.animationSequence"@%idx@"=\"@Asset="@%moduleName@":"@%assetDef.assetName@"\";"; + eval(%animSet); %assetPath = AssetDatabase.getAssetFilePath(ShapeEditorPlugin.selectedAssetId); @@ -357,6 +358,9 @@ function ActionAddSequence::doit( %this ) AssetDatabase.refreshAsset(ShapeEditorPlugin.selectedAssetId); + //force a refresh + ShapeEdPropWindow.update_onShapeSelectionChanged(); + return true; } return false; From c337649019bf54042be9735ed8883d52e50e8ae1 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 4 Feb 2018 16:21:07 -0600 Subject: [PATCH 06/16] Some code cleanup, tweak and optimizations for assets, entities and components. --- Engine/source/T3D/assets/ImageAsset.cpp | 7 +------ Engine/source/T3D/assets/ImageAsset.h | 5 ----- Engine/source/T3D/assets/LevelAsset.cpp | 4 +++- Engine/source/T3D/assets/ScriptAsset.cpp | 2 +- .../animation/animationComponent.cpp | 3 +++ .../collision/collisionComponent.cpp | 8 ++++--- Engine/source/T3D/components/component.cpp | 11 ++++++++-- .../T3D/components/game/stateMachine.cpp | 4 ++++ .../source/T3D/components/game/stateMachine.h | 2 ++ .../physics/playerControllerComponent.cpp | 7 +++++-- .../T3D/components/render/meshComponent.cpp | 2 +- Engine/source/T3D/entity.cpp | 21 ++++++++++--------- Engine/source/T3D/entity.h | 14 ++++++------- .../T3D/systems/render/meshRenderSystem.cpp | 5 ++++- .../gui/editor/inspector/entityGroup.cpp | 3 ++- 15 files changed, 58 insertions(+), 40 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index 3e29b0bdf..ff4c3ac70 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -90,14 +90,9 @@ ConsoleSetType(TypeImageAssetPtr) //----------------------------------------------------------------------------- -ImageAsset::ImageAsset() +ImageAsset::ImageAsset() : AssetBase(), mImage(nullptr), mUseMips(true), mIsHDRImage(false), mIsValidImage(false) { mImageFileName = StringTable->EmptyString(); - - mImage = NULL; - mUseMips = true; - mIsHDRImage = false; - mIsValidImage = false; } //----------------------------------------------------------------------------- diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index d289ac195..f5b52db07 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -47,11 +47,6 @@ class ImageAsset : public AssetBase { typedef AssetBase Parent; - AssetManager* mpOwningAssetManager; - bool mAssetInitialized; - AssetDefinition* mpAssetDefinition; - U32 mAcquireReferenceCount; - StringTableEntry mImageFileName; GFXTexHandle mImage; diff --git a/Engine/source/T3D/assets/LevelAsset.cpp b/Engine/source/T3D/assets/LevelAsset.cpp index 849cdc8d7..34084924a 100644 --- a/Engine/source/T3D/assets/LevelAsset.cpp +++ b/Engine/source/T3D/assets/LevelAsset.cpp @@ -90,10 +90,12 @@ ConsoleSetType(TypeLevelAssetPtr) //----------------------------------------------------------------------------- -LevelAsset::LevelAsset() +LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false) { mLevelFile = StringTable->EmptyString(); mPreviewImage = StringTable->EmptyString(); + + mMainLevelAsset = StringTable->EmptyString(); } //----------------------------------------------------------------------------- diff --git a/Engine/source/T3D/assets/ScriptAsset.cpp b/Engine/source/T3D/assets/ScriptAsset.cpp index dbc5e7a2e..10c3be777 100644 --- a/Engine/source/T3D/assets/ScriptAsset.cpp +++ b/Engine/source/T3D/assets/ScriptAsset.cpp @@ -89,7 +89,7 @@ ConsoleSetType(TypeScriptAssetPtr) //----------------------------------------------------------------------------- -ScriptAsset::ScriptAsset() +ScriptAsset::ScriptAsset() : AssetBase(), mIsServerSide(true) { mScriptFilePath = StringTable->EmptyString(); } diff --git a/Engine/source/T3D/components/animation/animationComponent.cpp b/Engine/source/T3D/components/animation/animationComponent.cpp index c8fd645d0..1b4604ce5 100644 --- a/Engine/source/T3D/components/animation/animationComponent.cpp +++ b/Engine/source/T3D/components/animation/animationComponent.cpp @@ -613,6 +613,9 @@ void AnimationComponent::advanceThreads(F32 dt) if (!mOwnerRenderInst) return; + if (mOwnerShapeInstance == nullptr || !getShape()) + return; + for (U32 i = 0; i < MaxScriptThreads; i++) { Thread& st = mAnimationThreads[i]; diff --git a/Engine/source/T3D/components/collision/collisionComponent.cpp b/Engine/source/T3D/components/collision/collisionComponent.cpp index ccdf818f1..3736fbe04 100644 --- a/Engine/source/T3D/components/collision/collisionComponent.cpp +++ b/Engine/source/T3D/components/collision/collisionComponent.cpp @@ -142,10 +142,12 @@ CollisionComponent::CollisionComponent() : Component() StaticShapeObjectType | VehicleObjectType | VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType); - mPhysicsRep = NULL; - mPhysicsWorld = NULL; + mPhysicsRep = nullptr; + mPhysicsWorld = nullptr; - mTimeoutList = NULL; + mTimeoutList = nullptr; + + mAnimated = false; } CollisionComponent::~CollisionComponent() diff --git a/Engine/source/T3D/components/component.cpp b/Engine/source/T3D/components/component.cpp index ff207d2c1..181c892e5 100644 --- a/Engine/source/T3D/components/component.cpp +++ b/Engine/source/T3D/components/component.cpp @@ -67,6 +67,13 @@ Component::Component() mOriginatingAssetId = StringTable->EmptyString(); mIsServerObject = true; + + componentIdx = 0; + + mHidden = false; + mEnabled = true; + + mDirtyMaskBits = 0; } Component::~Component() @@ -535,7 +542,7 @@ const char * Component::getDescriptionText(const char *desc) if (desc == NULL) return NULL; - char *newDesc; + char *newDesc = ""; // [tom, 1/12/2007] If it isn't a file, just do it the easy way if (!Platform::isFile(desc)) @@ -568,7 +575,7 @@ const char * Component::getDescriptionText(const char *desc) } str.close(); - delete stream; + //delete stream; return newDesc; } diff --git a/Engine/source/T3D/components/game/stateMachine.cpp b/Engine/source/T3D/components/game/stateMachine.cpp index 335b69db3..fd1ef8505 100644 --- a/Engine/source/T3D/components/game/stateMachine.cpp +++ b/Engine/source/T3D/components/game/stateMachine.cpp @@ -30,6 +30,10 @@ StateMachine::StateMachine() mStartingState = ""; mCurCreateState = NULL; + + mStateMachineFile = StringTable->EmptyString(); + + mCurCreateState = nullptr; } StateMachine::~StateMachine() diff --git a/Engine/source/T3D/components/game/stateMachine.h b/Engine/source/T3D/components/game/stateMachine.h index 9ccc540e8..b440ab7fa 100644 --- a/Engine/source/T3D/components/game/stateMachine.h +++ b/Engine/source/T3D/components/game/stateMachine.h @@ -158,6 +158,8 @@ public: { if (index <= mFields.size()) return mFields[index]; + + return StateField(); //return a blank one } Signal< void(StateMachine*, S32 stateIdx) > onStateChanged; diff --git a/Engine/source/T3D/components/physics/playerControllerComponent.cpp b/Engine/source/T3D/components/physics/playerControllerComponent.cpp index 2c6cea6e9..761ac570c 100644 --- a/Engine/source/T3D/components/physics/playerControllerComponent.cpp +++ b/Engine/source/T3D/components/physics/playerControllerComponent.cpp @@ -119,8 +119,11 @@ PlayerControllerComponent::PlayerControllerComponent() : Component() mInputVelocity = Point3F(0, 0, 0); - mPhysicsRep = NULL; - mPhysicsWorld = NULL; + mPhysicsRep = nullptr; + mPhysicsWorld = nullptr; + + mOwnerCollisionInterface = nullptr; + mIntegrationCount = 0; } PlayerControllerComponent::~PlayerControllerComponent() diff --git a/Engine/source/T3D/components/render/meshComponent.cpp b/Engine/source/T3D/components/render/meshComponent.cpp index 10186c873..c87733bf8 100644 --- a/Engine/source/T3D/components/render/meshComponent.cpp +++ b/Engine/source/T3D/components/render/meshComponent.cpp @@ -59,7 +59,7 @@ ImplementEnumType(BatchingMode, ////////////////////////////////////////////////////////////////////////// // Constructor/Destructor ////////////////////////////////////////////////////////////////////////// -MeshComponent::MeshComponent() : Component() +MeshComponent::MeshComponent() : Component(), mShape(nullptr), mRenderMode(Individual) { mFriendlyName = "Mesh Component"; mComponentType = "Render"; diff --git a/Engine/source/T3D/entity.cpp b/Engine/source/T3D/entity.cpp index a7af19b33..62c0b031f 100644 --- a/Engine/source/T3D/entity.cpp +++ b/Engine/source/T3D/entity.cpp @@ -850,7 +850,7 @@ void Entity::setTransform(const MatrixF &mat) } } -void Entity::setTransform(Point3F position, RotationF rotation) +void Entity::setTransform(const Point3F& position, const RotationF& rotation) { MatrixF oldTransform = getTransform(); @@ -922,7 +922,7 @@ void Entity::setRenderTransform(const MatrixF &mat) Parent::setRenderTransform(mat); } -void Entity::setRenderTransform(Point3F position, RotationF rotation) +void Entity::setRenderTransform(const Point3F& position, const RotationF& rotation) { if (isMounted()) { @@ -977,7 +977,7 @@ MatrixF Entity::getTransform() } } -void Entity::setMountOffset(Point3F posOffset) +void Entity::setMountOffset(const Point3F& posOffset) { if (isMounted()) { @@ -987,7 +987,7 @@ void Entity::setMountOffset(Point3F posOffset) } } -void Entity::setMountRotation(EulerF rotOffset) +void Entity::setMountRotation(const EulerF& rotOffset) { if (isMounted()) { @@ -1111,11 +1111,12 @@ bool Entity::castRayRendered(const Point3F &start, const Point3F &end, RayInfo * bool Entity::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) { - Vector updaters = getComponents(); + Con::errorf("Build Poly List not yet implemented as a passthrough for Entity"); + /*Vector updaters = getComponents(); for (Vector::iterator it = updaters.begin(); it != updaters.end(); it++) { return (*it)->buildPolyList(context, polyList, box, sphere); - } + }*/ return false; } @@ -1131,7 +1132,7 @@ void Entity::buildConvex(const Box3F& box, Convex* convex) // // Mounting and heirarchy manipulation -void Entity::mountObject(SceneObject* objB, MatrixF txfm) +void Entity::mountObject(SceneObject* objB, const MatrixF& txfm) { Parent::mountObject(objB, -1, txfm); Parent::addObject(objB); @@ -1604,7 +1605,7 @@ void Entity::onCameraScopeQuery(NetConnection* connection, CameraScopeQuery* que } } // -void Entity::setObjectBox(Box3F objBox) +void Entity::setObjectBox(const Box3F& objBox) { mObjBox = objBox; resetWorldBox(); @@ -1705,8 +1706,8 @@ void Entity::setComponentDirty(Component *comp, bool forceUpdate) } } - if (!found) - return; + //if (!found) + // return; //if(mToLoadComponents.empty()) // mStartComponentUpdate = true; diff --git a/Engine/source/T3D/entity.h b/Engine/source/T3D/entity.h index db65f2866..5bf9898ea 100644 --- a/Engine/source/T3D/entity.h +++ b/Engine/source/T3D/entity.h @@ -152,14 +152,14 @@ public: virtual void setTransform(const MatrixF &mat); virtual void setRenderTransform(const MatrixF &mat); - void setTransform(Point3F position, RotationF rotation); + void setTransform(const Point3F& position, const RotationF& rotation); - void setRenderTransform(Point3F position, RotationF rotation); + void setRenderTransform(const Point3F& position, const RotationF& rotation); virtual MatrixF getTransform(); virtual Point3F getPosition() const { return mPos; } - void setRotation(RotationF rotation) { + void setRotation(const RotationF& rotation) { mRot = rotation; setMaskBits(TransformMask); }; @@ -167,8 +167,8 @@ public: static bool _setGameObject(void *object, const char *index, const char *data); - void setMountOffset(Point3F posOffset); - void setMountRotation(EulerF rotOffset); + void setMountOffset(const Point3F& posOffset); + void setMountRotation(const EulerF& rotOffset); //static bool _setEulerRotation( void *object, const char *index, const char *data ); static bool _setPosition(void *object, const char *index, const char *data); @@ -181,7 +181,7 @@ public: virtual void getRenderMountTransform(F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat); virtual void mountObject(SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity); - void mountObject(SceneObject* objB, MatrixF txfm); + void mountObject(SceneObject* objB, const MatrixF& txfm); void onMount(SceneObject *obj, S32 node); void onUnmount(SceneObject *obj, S32 node); @@ -218,7 +218,7 @@ public: return mComponents.size(); } - virtual void setObjectBox(Box3F objBox); + virtual void setObjectBox(const Box3F& objBox); void resetWorldBox() { Parent::resetWorldBox(); } void resetObjectBox() { Parent::resetObjectBox(); } diff --git a/Engine/source/T3D/systems/render/meshRenderSystem.cpp b/Engine/source/T3D/systems/render/meshRenderSystem.cpp index 214a63dfe..f777b264b 100644 --- a/Engine/source/T3D/systems/render/meshRenderSystem.cpp +++ b/Engine/source/T3D/systems/render/meshRenderSystem.cpp @@ -11,6 +11,9 @@ Vector MeshRenderSystem::mStaticBuffers(0); void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* state) { + if (sceneManager == nullptr || state == nullptr) + return; + Frustum viewFrustum = state->getCullingFrustum(); MatrixF camTransform = state->getCameraTransform(); @@ -129,7 +132,7 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat // We sort by the material then vertex buffer ri->defaultKey = matInst->getStateHint(); - ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe! + ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! // Submit our RenderInst to the RenderPassManager state->getRenderPass()->addInst(ri); diff --git a/Engine/source/gui/editor/inspector/entityGroup.cpp b/Engine/source/gui/editor/inspector/entityGroup.cpp index c833d336d..7b7559833 100644 --- a/Engine/source/gui/editor/inspector/entityGroup.cpp +++ b/Engine/source/gui/editor/inspector/entityGroup.cpp @@ -70,7 +70,8 @@ bool GuiInspectorEntityGroup::inspectGroup() { Entity* target = dynamic_cast(mParent->getInspectObject(0)); - Con::executef(this, "inspectObject", target->getIdString()); + if(target) + Con::executef(this, "inspectObject", target->getIdString()); } return true; From fafc3acd5e80fab8a01a91f29edea782a6328a69 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Mon, 5 Feb 2018 23:02:02 +0000 Subject: [PATCH 07/16] Remove nested CLASSDOC Macro --- Engine/source/console/consoleObject.h | 4 ---- Engine/source/console/engineTypes.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Engine/source/console/consoleObject.h b/Engine/source/console/consoleObject.h index 2ff2bc5a3..f187c741c 100644 --- a/Engine/source/console/consoleObject.h +++ b/Engine/source/console/consoleObject.h @@ -1263,10 +1263,6 @@ inline bool& ConsoleObject::getDynamicGroupExpand() EnginePropertyTable _propTable( sizeof( _props ) / sizeof( _props[ 0 ] ) - 1, _props ); \ } } -/// Add an auto-doc for a class. -#define ConsoleDocClass( className, docString ) \ - CLASSDOC( className, docString ) - /// @} //------------------------------------------------------------------------------ diff --git a/Engine/source/console/engineTypes.h b/Engine/source/console/engineTypes.h index 321e39686..2217ee770 100644 --- a/Engine/source/console/engineTypes.h +++ b/Engine/source/console/engineTypes.h @@ -576,7 +576,7 @@ namespace _Private { uintptr_t( ( ( const char* ) &( ( ( ThisType* ) 16 )->fieldName ) ) - 16 ) // Artificial offset to avoid compiler warnings. /// -#define CLASSDOC( className, doc ) \ +#define ConsoleDocClass( className, doc ) \ template<> const char* EngineClassTypeInfo< className, className::_ClassBase >::smDocString = doc; From f237caff5e53ff7e60bd1b10e57df63a55516e2c Mon Sep 17 00:00:00 2001 From: Areloch Date: Thu, 8 Feb 2018 16:00:11 -0600 Subject: [PATCH 08/16] Stabilizes the add/remove behavior of menubars by also tracking the modeless background control the menubars use for proper cleanup as well. Also fixes an issue where the findMenu call wasn't properly translating the incoming string to StringTableEntry. --- Engine/source/gui/core/guiCanvas.cpp | 23 +++++++++++++++-------- Engine/source/gui/core/guiCanvas.h | 1 + Engine/source/gui/editor/guiMenuBar.cpp | 5 +++-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index 5202e57bf..71a4eb760 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -133,7 +133,8 @@ GuiCanvas::GuiCanvas(): GuiControl(), mLastRenderMs(0), mPlatformWindow(NULL), mDisplayWindow(true), - mMenuBarCtrl(NULL) + mMenuBarCtrl(nullptr), + mMenuBackground(nullptr) { setBounds(0, 0, 640, 480); mAwake = true; @@ -296,8 +297,11 @@ void GuiCanvas::setMenuBar(SimObject *obj) mMenuBarCtrl = dynamic_cast(obj); //remove old menubar - if( oldMenuBar ) - Parent::removeObject( oldMenuBar ); + if (oldMenuBar) + { + Parent::removeObject(oldMenuBar); + Parent::removeObject(mMenuBackground); //also remove the modeless wrapper + } // set new menubar if (mMenuBarCtrl) @@ -312,14 +316,17 @@ void GuiCanvas::setMenuBar(SimObject *obj) return; } - GuiControl* menuBackground = new GuiControl(); - menuBackground->registerObject(); + if (mMenuBackground == nullptr) + { + mMenuBackground = new GuiControl(); + mMenuBackground->registerObject(); - menuBackground->setControlProfile(profile); + mMenuBackground->setControlProfile(profile); + } - menuBackground->addObject(mMenuBarCtrl); + mMenuBackground->addObject(mMenuBarCtrl); - Parent::addObject(menuBackground); + Parent::addObject(mMenuBackground); } // update window accelerator keys diff --git a/Engine/source/gui/core/guiCanvas.h b/Engine/source/gui/core/guiCanvas.h index b193bfbf1..f9f4a37af 100644 --- a/Engine/source/gui/core/guiCanvas.h +++ b/Engine/source/gui/core/guiCanvas.h @@ -198,6 +198,7 @@ protected: static CanvasSizeChangeSignal smCanvasSizeChangeSignal; GuiControl *mMenuBarCtrl; + GuiControl* mMenuBackground; public: DECLARE_CONOBJECT(GuiCanvas); diff --git a/Engine/source/gui/editor/guiMenuBar.cpp b/Engine/source/gui/editor/guiMenuBar.cpp index fc9623498..dc168436b 100644 --- a/Engine/source/gui/editor/guiMenuBar.cpp +++ b/Engine/source/gui/editor/guiMenuBar.cpp @@ -1519,9 +1519,10 @@ DefineConsoleMethod(GuiMenuBar, insert, void, (SimObject* pObject, S32 pos), (nu object->insert(pObject, pos); } -DefineConsoleMethod(GuiMenuBar, findMenu, S32, (StringTableEntry barTitle), (""), "(barTitle)") +DefineConsoleMethod(GuiMenuBar, findMenu, S32, (const char* barTitle), (""), "(barTitle)") { - PopupMenu* menu = object->findMenu(barTitle); + StringTableEntry barTitleStr = StringTable->insert(barTitle); + PopupMenu* menu = object->findMenu(barTitleStr); if (menu) return menu->getId(); From 937ef6c33d77201b61ee439c063e4b82208c615e Mon Sep 17 00:00:00 2001 From: Areloch Date: Thu, 8 Feb 2018 16:04:50 -0600 Subject: [PATCH 09/16] Tabs&Spaces --- Engine/source/gui/core/guiCanvas.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index 71a4eb760..efb0674ea 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -134,7 +134,7 @@ GuiCanvas::GuiCanvas(): GuiControl(), mPlatformWindow(NULL), mDisplayWindow(true), mMenuBarCtrl(nullptr), - mMenuBackground(nullptr) + mMenuBackground(nullptr) { setBounds(0, 0, 640, 480); mAwake = true; @@ -297,11 +297,11 @@ void GuiCanvas::setMenuBar(SimObject *obj) mMenuBarCtrl = dynamic_cast(obj); //remove old menubar - if (oldMenuBar) - { - Parent::removeObject(oldMenuBar); - Parent::removeObject(mMenuBackground); //also remove the modeless wrapper - } + if (oldMenuBar) + { + Parent::removeObject(oldMenuBar); + Parent::removeObject(mMenuBackground); //also remove the modeless wrapper + } // set new menubar if (mMenuBarCtrl) @@ -316,15 +316,15 @@ void GuiCanvas::setMenuBar(SimObject *obj) return; } - if (mMenuBackground == nullptr) - { - mMenuBackground = new GuiControl(); - mMenuBackground->registerObject(); + if (mMenuBackground == nullptr) + { + mMenuBackground = new GuiControl(); + mMenuBackground->registerObject(); - mMenuBackground->setControlProfile(profile); - } + mMenuBackground->setControlProfile(profile); + } - mMenuBackground->addObject(mMenuBarCtrl); + mMenuBackground->addObject(mMenuBarCtrl); Parent::addObject(mMenuBackground); } From 7fd04eabafdbc535e9cbc25c2158cc3e3c215131 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 11 Feb 2018 02:09:53 -0600 Subject: [PATCH 10/16] Update meshRenderSystem.cpp --- Engine/source/T3D/systems/render/meshRenderSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/systems/render/meshRenderSystem.cpp b/Engine/source/T3D/systems/render/meshRenderSystem.cpp index f777b264b..fe1ec6f84 100644 --- a/Engine/source/T3D/systems/render/meshRenderSystem.cpp +++ b/Engine/source/T3D/systems/render/meshRenderSystem.cpp @@ -132,7 +132,7 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat // We sort by the material then vertex buffer ri->defaultKey = matInst->getStateHint(); - ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! + ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Submit our RenderInst to the RenderPassManager state->getRenderPass()->addInst(ri); @@ -375,4 +375,4 @@ U32 MeshRenderSystem::findBufferSetByMaterial(U32 matId) } return -1; -} \ No newline at end of file +} From eb5af65daf149e4f0dfeb2b5cb56ce5b5c728922 Mon Sep 17 00:00:00 2001 From: Areloch Date: Mon, 12 Feb 2018 00:02:20 -0600 Subject: [PATCH 11/16] Removes the folder insert for the scripted object creator grid, which was causing a UI screwup making the scripted objects overlap and jumble up. Also reorgs the popup controls to match what's in the BaseGame template's setup, which should fix editor popups. --- .../worldEditor/scripts/editorPrefs.ed.cs | 3 +- .../tools/worldEditor/scripts/EditorGui.ed.cs | 324 +++++++----------- .../worldEditor/scripts/editorPrefs.ed.cs | 1 - .../worldEditor/scripts/editors/creator.ed.cs | 46 --- 4 files changed, 117 insertions(+), 257 deletions(-) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs index 1704e06ad..876ae120a 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs @@ -34,7 +34,6 @@ EditorSettings.setDefaultValue( "orthoFOV", "50" ); EditorSettings.setDefaultValue( "orthoShowGrid", "1" ); EditorSettings.setDefaultValue( "currentEditor", "WorldEditorInspectorPlugin" ); EditorSettings.setDefaultValue( "newLevelFile", "tools/levels/BlankRoom.mis" ); -EditorSettings.setDefaultValue( "newGameObjectDir", "scripts/server/gameObjects" ); if( isFile( "C:/Program Files/Torsion/Torsion.exe" ) ) EditorSettings.setDefaultValue( "torsionPath", "C:/Program Files/Torsion/Torsion.exe" ); @@ -128,7 +127,7 @@ EditorSettings.setDefaultValue( "renderInfoText", "1" ); EditorSettings.beginGroup( "Grid" ); EditorSettings.setDefaultValue( "gridColor", "255 255 255 20" ); -EditorSettings.setDefaultValue( "gridSize", "10 10 10" ); +EditorSettings.setDefaultValue( "gridSize", "1 1 1" ); EditorSettings.setDefaultValue( "snapToGrid", "0" ); //<-- Not currently used EditorSettings.setDefaultValue( "renderPlane", "0" ); EditorSettings.setDefaultValue( "renderPlaneHashes", "0" ); diff --git a/Templates/Full/game/tools/worldEditor/scripts/EditorGui.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/EditorGui.ed.cs index 535d00880..2d9e2ec53 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/EditorGui.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/EditorGui.ed.cs @@ -1302,6 +1302,7 @@ function VisibilityDropdownToggle() { EVisibility.setVisible(true); visibilityToggleBtn.setStateOn(1); + EVisibility.setExtent("200 540"); } } @@ -1569,6 +1570,27 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj ) %haveObjectEntries = false; %haveLockAndHideEntries = true; + //Set up the generic pop-up pre-emptively if we haven't already + if( !isObject( ETContextPopup ) ) + { + %popup = new PopupMenu( ETContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Rename" TAB "" TAB "EditorTree.showItemRenameCtrl( EditorTree.findItemByObjectId( %this.object ) );"; + item[ 1 ] = "Delete" TAB "" TAB "EWorldEditor.deleteMissionObject( %this.object );"; + item[ 2 ] = "Inspect" TAB "" TAB "inspectObject( %this.object );"; + item[ 3 ] = "-"; + item[ 4 ] = "Locked" TAB "" TAB "%this.object.setLocked( !%this.object.locked ); EWorldEditor.syncGui();"; + item[ 5 ] = "Hidden" TAB "" TAB "EWorldEditor.hideObject( %this.object, !%this.object.hidden ); EWorldEditor.syncGui();"; + item[ 6 ] = "-"; + item[ 7 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; + + object = -1; + }; + } + // Handle multi-selection. if( %this.getSelectedItemsCount() > 1 ) { @@ -1615,12 +1637,67 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj ) item[ 0 ] = "Add Camera Bookmark" TAB "" TAB "EditorGui.addCameraBookmarkByGui();"; }; } + + else if(%obj.isMemberOfClass("Entity")) + { + %popup = EntityObjectPopup; + if(!isObject(EntityObjectPopup)) + { + %popup = new PopupMenu( EntityObjectPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Rename" TAB "" TAB "EditorTree.showItemRenameCtrl( EditorTree.findItemByObjectId( %this.object ) );"; + item[ 1 ] = "Delete" TAB "" TAB "EWorldEditor.deleteMissionObject( %this.object );"; + item[ 2 ] = "Inspect" TAB "" TAB "inspectObject( %this.object );"; + item[ 3 ] = "-"; + item[ 4 ] = "Toggle Lock Children" TAB "" TAB "EWorldEditor.toggleLockChildren( %this.object );"; + item[ 5 ] = "Toggle Hide Children" TAB "" TAB "EWorldEditor.toggleHideChildren( %this.object );"; + item[ 6 ] = "-"; + item[ 7 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; + item[ 8 ] = "-"; + item[ 9 ] = "Add New Objects Here" TAB "" TAB "EWCreatorWindow.setNewObjectGroup( %this.object );"; + item[ 10 ] = "Add Children to Selection" TAB "" TAB "EWorldEditor.selectAllObjectsInSet( %this.object, false );"; + item[ 11 ] = "Remove Children from Selection" TAB "" TAB "EWorldEditor.selectAllObjectsInSet( %this.object, true );"; + item[ 12 ] = "-"; + item[ 13 ] = "Convert to Game Object" TAB "" TAB "EWorldEditor.createGameObject( %this.object );"; + item[ 14 ] = "Duplicate Game Object" TAB "" TAB "EWorldEditor.duplicateGameObject( %this.object );"; + item[ 15 ] = "Show in Asset Browser" TAB "" TAB "EWorldEditor.showGameObjectInAssetBrowser( %this.object );"; + + object = -1; + }; + } + + if(!isObject(AssetDatabase.acquireAsset(%obj.gameObjectAsset))) + { + EntityObjectPopup.enableItem(13, true); + EntityObjectPopup.enableItem(14, false); + EntityObjectPopup.enableItem(15, false); + } + else + { + EntityObjectPopup.enableItem(13, false); + EntityObjectPopup.enableItem(14, true); + EntityObjectPopup.enableItem(15, true); + } + + %popup.object = %obj; + + %hasChildren = %obj.getCount() > 0; + %popup.enableItem( 10, %hasChildren ); + %popup.enableItem( 11, %hasChildren ); + + %haveObjectEntries = true; + %haveLockAndHideEntries = false; + } // Open context menu if this is a SimGroup else if( !%obj.isMemberOfClass( "SceneObject" ) ) { %popup = ETSimGroupContextPopup; if( !isObject( %popup ) ) + { %popup = new PopupMenu( ETSimGroupContextPopup ) { superClass = "MenuBuilder"; @@ -1641,6 +1718,7 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj ) object = -1; }; + } %popup.object = %obj; @@ -1652,77 +1730,56 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj ) %haveLockAndHideEntries = false; } - // Open generic context menu. - else + // Specialized version for ConvexShapes. + else if( %obj.isMemberOfClass( "ConvexShape" ) ) { - %popup = ETContextPopup; + %popup = ETConvexShapeContextPopup; if( !isObject( %popup ) ) - %popup = new PopupMenu( ETContextPopup ) + { + %popup = new PopupMenu( ETConvexShapeContextPopup : ETContextPopup ) { superClass = "MenuBuilder"; isPopup = "1"; - item[ 0 ] = "Rename" TAB "" TAB "EditorTree.showItemRenameCtrl( EditorTree.findItemByObjectId( %this.object ) );"; - item[ 1 ] = "Delete" TAB "" TAB "EWorldEditor.deleteMissionObject( %this.object );"; - item[ 2 ] = "Inspect" TAB "" TAB "inspectObject( %this.object );"; - item[ 3 ] = "-"; - item[ 4 ] = "Locked" TAB "" TAB "%this.object.setLocked( !%this.object.locked ); EWorldEditor.syncGui();"; - item[ 5 ] = "Hidden" TAB "" TAB "EWorldEditor.hideObject( %this.object, !%this.object.hidden ); EWorldEditor.syncGui();"; - item[ 6 ] = "-"; - item[ 7 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; - - object = -1; + item[ 8 ] = "-"; + item[ 9 ] = "Convert to Zone" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Zone\" );"; + item[ 10 ] = "Convert to Portal" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Portal\" );"; + item[ 11 ] = "Convert to Occluder" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"OcclusionVolume\" );"; + item[ 12 ] = "Convert to Sound Space" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"SFXSpace\" );"; }; - - if(%obj.isMemberOfClass("Entity")) - { - %popup = ETEntityContextPopup; - if( !isObject( %popup ) ) - %popup = new PopupMenu( ETEntityContextPopup : ETSimGroupContextPopup ) - { - superClass = "MenuBuilder"; - isPopup = "1"; - - item[ 12 ] = "-"; - item[ 13 ] = "Convert to Game Object" TAB "" TAB "EWorldEditor.createGameObject( %this.object );"; - }; - } - - // Specialized version for ConvexShapes. - else if( %obj.isMemberOfClass( "ConvexShape" ) ) - { - %popup = ETConvexShapeContextPopup; - if( !isObject( %popup ) ) - %popup = new PopupMenu( ETConvexShapeContextPopup : ETContextPopup ) - { - superClass = "MenuBuilder"; - isPopup = "1"; - - item[ 8 ] = "-"; - item[ 9 ] = "Convert to Zone" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Zone\" );"; - item[ 10 ] = "Convert to Portal" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Portal\" );"; - item[ 11 ] = "Convert to Occluder" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"OcclusionVolume\" );"; - item[ 12 ] = "Convert to Sound Space" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"SFXSpace\" );"; - }; } - // Specialized version for polyhedral objects. - else if( %obj.isMemberOfClass( "Zone" ) || - %obj.isMemberOfClass( "Portal" ) || - %obj.isMemberOfClass( "OcclusionVolume" ) || - %obj.isMemberOfClass( "SFXSpace" ) ) + %popup.object = %obj; + %haveObjectEntries = true; + } + + // Specialized version for polyhedral objects. + else if( %obj.isMemberOfClass( "Zone" ) || + %obj.isMemberOfClass( "Portal" ) || + %obj.isMemberOfClass( "OcclusionVolume" ) || + %obj.isMemberOfClass( "SFXSpace" ) ) + { + %popup = ETPolyObjectContextPopup; + if( !isObject( %popup ) ) { - %popup = ETPolyObjectContextPopup; - if( !isObject( %popup ) ) - %popup = new PopupMenu( ETPolyObjectContextPopup : ETContextPopup ) - { - superClass = "MenuBuilder"; - isPopup = "1"; + %popup = new PopupMenu( ETPolyObjectContextPopup : ETContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; - item[ 8 ] = "-"; - item[ 9 ] = "Convert to ConvexShape" TAB "" TAB "EWorldEditor.convertSelectionToConvexShape();"; - }; + item[ 8 ] = "-"; + item[ 9 ] = "Convert to ConvexShape" TAB "" TAB "EWorldEditor.convertSelectionToConvexShape();"; + }; } + + %popup.object = %obj; + %haveObjectEntries = true; + } + + // Open generic context menu. + else + { + %popup = ETContextPopup; %popup.object = %obj; %haveObjectEntries = true; @@ -2253,155 +2310,6 @@ function EWorldEditor::deleteMissionObject( %this, %object ) EditorTree.buildVisibleTree( true ); } -function EWorldEditor::createGameObject( %this, %entity ) -{ - if(!isObject(GameObjectBuilder)) - { - new GuiControl(GameObjectBuilder, EditorGuiGroup) { - profile = "ToolsGuiDefaultProfile"; - horizSizing = "right"; - vertSizing = "bottom"; - position = "0 0"; - extent = "800 600"; - minExtent = "8 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - helpTag = "0"; - - new GuiWindowCtrl(GameObjectBuilderTargetWindow) { - profile = "ToolsGuiWindowProfile"; - horizSizing = "center"; - vertSizing = "center"; - position = "384 205"; - extent = "256 102"; - minExtent = "256 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - helpTag = "0"; - resizeWidth = "1"; - resizeHeight = "1"; - canMove = "1"; - canClose = "0"; - canMinimize = "0"; - canMaximize = "0"; - minSize = "50 50"; - text = "Create Object"; - - new GuiTextCtrl() { - profile = "GuiCenterTextProfile"; - horizSizing = "right"; - vertSizing = "bottom"; - position = "9 26"; - extent = "84 16"; - minExtent = "8 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - helpTag = "0"; - text = "Object Name:"; - }; - new GuiTextEditCtrl(GameObjectBuilderObjectName) { - class = ObjectBuilderGuiTextEditCtrl; - profile = "ToolsGuiTextEditProfile"; - horizSizing = "width"; - vertSizing = "bottom"; - position = "78 26"; - extent = "172 18"; - minExtent = "8 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - helpTag = "0"; - historySize = "0"; - }; - new GuiButtonCtrl(GameObjectBuilderOKButton) { - profile = "ToolsGuiButtonProfile"; - horizSizing = "width"; - vertSizing = "bottom"; - position = "7 250"; - extent = "156 24"; - minExtent = "8 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - command = "EWorldEditor.buildGameObject();"; - helpTag = "0"; - text = "Create New"; - Accelerator = "return"; - }; - new GuiButtonCtrl(GameObjectBuilderCancelButton) { - profile = "ToolsGuiButtonProfile"; - horizSizing = "left"; - vertSizing = "bottom"; - position = "170 250"; - extent = "80 24"; - minExtent = "8 8"; - visible = "1"; - setFirstResponder = "0"; - modal = "1"; - command = "Canvas.popDialog(GameObjectBuilder);"; - helpTag = "0"; - text = "Cancel"; - Accelerator = "escape"; - }; - }; - }; - - GameObjectBuilderTargetWindow.extent = getWord(GameObjectBuilderTargetWindow.extent, 0) SPC 88; - GameObjectBuilderOKButton.position = getWord(GameObjectBuilderOKButton.position, 0) SPC 57; - GameObjectBuilderCancelButton.position = getWord(GameObjectBuilderCancelButton.position, 0) SPC 57; - } - - GameObjectBuilderObjectName.text = ""; - GameObjectBuilder.selectedEntity = %entity; - - Canvas.pushDialog(GameObjectBuilder); -} - -function EWorldEditor::buildGameObject(%this) -{ - if(GameObjectBuilderObjectName.getText() $= "") - { - error("Attempted to make a new Game Object with no name!"); - Canvas.popDialog(GameObjectBuilder); - return; - } - - %path = EditorSettings.value( "WorldEditor/newGameObjectDir" ); - %className = GameObjectBuilderObjectName.getText(); - GameObjectBuilder.selectedEntity.class = %className; - Inspector.inspect(GameObjectBuilder.selectedEntity); - - %file = new FileObject(); - - if(%file.openForWrite(%path @ "\\" @ %className @ ".cs")) - { - %file.writeline("function " @ %className @ "::onAdd(%this)\n{\n\n}\n"); - %file.writeline("function " @ %className @ "::onRemove(%this)\n{\n\n}\n"); - - //todo, pre-write any event functions of interest - - %file.close(); - } - - //set up the paths - %tamlPath = %path @ "/" @ %className @ ".taml"; - %scriptPath = %path @ "/" @ %className @ ".cs"; - saveGameObject(%className, %tamlPath, %scriptPath); - - //reload it - execGameObjects(); - - //now, add the script file and a ref to the taml into our SGO manifest so we can readily spawn it later. - TamlWrite(GameObjectBuilder.selectedEntity, %tamlpath); - - GameObjectBuilder.selectedEntity = ""; - - Canvas.popDialog(GameObjectBuilder); -} - function EWorldEditor::selectAllObjectsInSet( %this, %set, %deselect ) { if( !isObject( %set ) ) diff --git a/Templates/Full/game/tools/worldEditor/scripts/editorPrefs.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/editorPrefs.ed.cs index 35cc85edb..876ae120a 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/editorPrefs.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/editorPrefs.ed.cs @@ -34,7 +34,6 @@ EditorSettings.setDefaultValue( "orthoFOV", "50" ); EditorSettings.setDefaultValue( "orthoShowGrid", "1" ); EditorSettings.setDefaultValue( "currentEditor", "WorldEditorInspectorPlugin" ); EditorSettings.setDefaultValue( "newLevelFile", "tools/levels/BlankRoom.mis" ); -EditorSettings.setDefaultValue( "newGameObjectDir", "scripts/server/gameObjects" ); if( isFile( "C:/Program Files/Torsion/Torsion.exe" ) ) EditorSettings.setDefaultValue( "torsionPath", "C:/Program Files/Torsion/Torsion.exe" ); diff --git a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs index 0e2813d57..006031668 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -304,36 +304,6 @@ function EWCreatorWindow::navigate( %this, %address ) %this.addShapeIcon( %obj ); } } - - //Add a separate folder for Game Objects - if(isClass("Entity")) - { - if(%address $= "") - { - %this.addFolderIcon("GameObjects"); - } - else - { - //find all GameObjectAssets - %assetQuery = new AssetQuery(); - if(!AssetDatabase.findAssetType(%assetQuery, "GameObjectAsset")) - return 0; //if we didn't find ANY, just exit - - %count = %assetQuery.getCount(); - - for(%i=0; %i < %count; %i++) - { - %assetId = %assetQuery.getAsset(%i); - - %gameObjectAsset = AssetDatabase.acquireAsset(%assetId); - - if(isFile(%gameObjectAsset.TAMLFilePath)) - { - %this.addGameObjectIcon( %gameObjectAsset.gameObjectName ); - } - } - } - } } if ( %this.tab $= "Meshes" ) @@ -768,22 +738,6 @@ function EWCreatorWindow::addPrefabIcon( %this, %fullPath ) %this.contentCtrl.addGuiControl( %ctrl ); } -function EWCreatorWindow::addGameObjectIcon( %this, %gameObjectName ) -{ - %ctrl = %this.createIcon(); - - %ctrl.altCommand = "spawnGameObject( \"" @ %gameObjectName @ "\", true );"; - %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( "Prefab" ); - %ctrl.text = %gameObjectName; - %ctrl.class = "CreatorGameObjectIconBtn"; - %ctrl.tooltip = "Spawn the " @ %gameObjectName @ " GameObject"; - - %ctrl.buttonType = "radioButton"; - %ctrl.groupNum = "-1"; - - %this.contentCtrl.addGuiControl( %ctrl ); -} - function CreatorPopupMenu::onSelect( %this, %id, %text ) { %split = strreplace( %text, "/", " " ); From d7197b0d4513e1e82c104909680d94273ad35807 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Mon, 12 Feb 2018 12:35:01 +0000 Subject: [PATCH 12/16] Fixed defects in Turret material, Fixed TP Ryder material and added a spec map --- .../art/shapes/weapons/Ryder/TP_Ryder_S.dds | Bin 0 -> 32896 bytes .../game/art/shapes/weapons/Ryder/materials.cs | 2 +- .../game/art/shapes/weapons/Turret/materials.cs | 2 +- .../art/shapes/weapons/Ryder/TP_Ryder_S.dds | Bin 0 -> 32896 bytes .../art/shapes/weapons/Ryder/materials.cs | 2 +- .../art/shapes/weapons/Turret/materials.cs | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 Templates/Full/game/art/shapes/weapons/Ryder/TP_Ryder_S.dds create mode 100644 Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/TP_Ryder_S.dds diff --git a/Templates/Full/game/art/shapes/weapons/Ryder/TP_Ryder_S.dds b/Templates/Full/game/art/shapes/weapons/Ryder/TP_Ryder_S.dds new file mode 100644 index 0000000000000000000000000000000000000000..9b7688e7ef964d790c61cec8e7107a0c3b1b659d GIT binary patch literal 32896 zcmbTf4^&&%l`nh{@Ny9$R_6`1n77hM)YM1>yLcXTO&=jJA+J_m(v0nks6oi!XKF)Z zdy>c^vO%hn*hD1p%rwN)%s5fRV8m5GX;bP{1mtHjwG%r{I*p9$SQa%-jD?Y$KeC8G zy5DbKL6c_QtnXVNtLbm;bI-jx_w0T4+5gV=>?gN^5UTO~zf(|axz#=tc{@(ojfigT#%h)MGpSySfBTRJcU4CBr ze)MnEsF;|HyDQn>jlXd^vs!=mh)Z1$%9AUL;7y5~8hWf|7xicI36d-M<%FrHW zg#$DXf=$>#7`7#(`LVNzgjyyNn!=|O6H2?n`rg($m!gGpI4oUuMKi}zli*V|r}Bx` z79g|g^?eF8SMt*Fu^KLe)!|PC;p_bwzr)TkmcO#M_iFX2GVj$N+;>aGsSOqXG&{!C zRC5nH87r=7;gXoIs~a*H-(h358X(`qS~U#FgTKH)uKZM44HuKK!kGw4 zM2E#Pv*D2DrB8jY&Xi@adi;^d4-2CLq0b`>U)wOD=zH1o>zT3vjbAesIIHVZbRFpU zbcyfDFY_Pl?})Y+*QhmvzrM4fxMt0GEI!;WY?67!*o{YW8|2cBZx3)4-?-z*k$=Qu1^c%RXk3avUi;(6hcs7|Z{Pc)hg{%)d1ZO!^7wSb??8U9 zrzT51w8AC4qMIq3iC7nM)>U)AblbWIH1}#wAFxcdYHHO#_|c9%Me3}A1D1U^4{0Wp zE4(XK9MrUG^eZc-4{Ea2^NFpgBK3sQnds`827jHOL>|rtzeRpBHwzew1?sOT`xHXo zAFoX)TNDY4B|&_@F`sH{0g927*vwUP^NXn)gnFaiU{|QPJF3=KUEM%(+-Bbaet>`V z)sTnYV5q9N<{phO+cR}_!$~ExrHsWjF2#&!Cosaz%>)(@^d2?)+ju$!hv)|sj z9qWeq&i$_?e5gH5Khw@f8M|=lw~wUh;db6b{oZZo1w!uYGx)1tD(%*5?W2KM_(sNUN z(D#sEVJe1wf_9KX6YW1=uha8afDiHp6X~b?$+*52@*sF}lTYG4-et=py=gUe)&IlO z)5=VqPyYP5X=O2<`*%-gks$HEczRVDpSwrG|9TWpK>JtmzaQNpwU_rFOykis@=NsZ zpR1Mb|93ne(-QdXIW^m6G+JB1|K{fZQ{9U7`p%iR&XE3x`Y&I4PjO>~U6J40+uK&- z*35Y3KG#&7E}J@Gcjc@b&=?oOH%^z`tM2HSnQzq;sl8`T>52bci;KGl%C0C&pDE2d zg!aVF8&kE=3o~tr3#%Y!iB!iTt%L8Ugzd!tqM|i&iT?>pJ+*u1yt93n=Z@6;bvyKb zq^YZ}27J-~NFO5pFC4hpP|c}1W^=0S(ElqdSFT(dZ)$nmUq#}|>(>{z&~utXT_M^> z)?7{`TtIPVK0KiWe*E*F6UM#ooRaXam3L6T7jFFfJoFJ~x^(fg7KNQV9UYHKav|-H zEu657en0m08t!srcM9eLYpoNGsj(lmJ%6m1=${??SsRTb@0O8~fem&=dBDV<+Q67) zXUpuV4b_|s`e<5(eGQnUmN2j?jl6_W17EA5cGI75ho+fhap*BZ+;_0-&#`s6UDKj) z!Y<(|S`<|QXiDsZ+pHwNeTr)z|M8EfVb47D&=1a#-Gk-$C;rc1ocZWKQ|3}Ixu#!- z{mKgbz^x;r$&bn3&#C854u3IfB zcP;e4b$`k^t#m0SyE+)Hci0Qg3Fw2kKO&!i{y+2S$;-t5u8WQ5iU0c=tXo^a_Zx34 zMv4C+tI%eU8CsHqB+dJlN4GpmcEj=G zM~;&m#E!?&-puXX*49S+{`tp)pA()sboDCXOJD!SOT_>Ay?bvE|1YLe7im0#s~f-D z!nxgrPQtJtxTJQEAP{zKZL$!*cQ@6U$R7D2CV=Q;{eyfB?2<|`Xu|k7M~P?bD(3aW z@&G>ozVb~w!z7Qq+2^3&4HzAa=$HXN01S1Gy9mvJu!Ard3~LF+WV&4pjY`+!`gDIW z87XvOJeXgM3+tvqPS1nA*6t?z$da%+Yt?G@IL3DndgyJS#Q#{(O30#dtP|jZIXQuO z)k=0P-UM<+_g&HRd<)*|VoDUT4%x~W4}F)uK7i-RlRQuMr6iZ232A?{0kaA~3rqcd zkbAmsc!@?k@rzA`q~EuBfX=QQ~Jxu@^tE2?Jh!>d;hT_OJa z@A^g`?%!&;v5oYTKDD?w0sUkU9ReY*_t+)*4J^1MG>B+#!Se+0hcN@MKS1jruLu5T zu!w{V_(N&@xx79={YuJ5Mvj61+xJBMB)5*7q3@*SRu4H=v2Nd`n>22hpO2-{gnp1i zGY@@3?VYcX-f{Vju-~gW$3jq&7nc|eFWo27`Y4QqBt9|u9hkRjZtvc`daNUMvFm)Q znyHyVZ^Ssk2mH}^j6$gfQ6F_8Rv59j8P-r?gSBm1Mvl9;*zK6dS%IZ>-_QGEXS%P(iCo4J2Gbm-Yu z&_`Z+>5i;|X0D+taibM_OJ8^3dRD=Nk`JWb&MN3r#KpcsLf-1LlijMH9JWbl8U+pe z>T)5=#{LFz+?#>-2n!LPqz|AFi1zs+5t7q#{Lwh7hK9yyeZ0S^t%vyO&_4AtU4P@; zmO5x#COCTo(C*Ch*BicPhyGjmhd+ES*}?mNXdpcp`OQareu5c`oQ-_c%&A!T^;8%2 z_r10s`V#XvZ!nX8V}?C9fOSds;;AK^!T!Ui>`YTZ`p06UdF6cq)(OzRgbUgF1eJLN{Hf`|UtO zB+bu2M3DH(M;vKf(5A<;5H4=P`|%5ICSmA>0YX@N+U-zLz z^ZejJyys9}>EJ2sOT5e*B0JtXl~|a8y>C5{vbNPsD3dRL{`p3r>6Q<7=xQbufo~i+ zGJt+vQ(;S6O;&-~ol4D=6{!R4mg8-RC%g^=ysf54E$~5Ltr}`guBj+ehj}xDe^4Oc zy0!xLuGMcN{mSc`I@I8Mgx3o+uE>rZy~N+j^09K#Cxewkm9WDU<*`_R>`LR#zD(lZ z7HnL&P5L9c^4He}p$EC2|8C)PtY6Nla=M6)M0Vdx_}jB@Mp_^bDeE1_i0}K}-?xu` z7iNm6sbE+y`MsKoQ)Pi)xQ%4by?c0p-&g}b*;5ib3;(XF#AEW*K(6Ous@7n=c$hG! zn^xj=(zNgez0~e_1@p#_GFJ~_n7P^sBTQ(kAXK?Dgu_gb5bZSYe2TeGm-XR3n;^vn z!b~^@yPEouU&;?7PM`*j_Jh>!Qwijk@d<|b0rs`3nXpF(oCybLy*F-of05>O$JVWU zGw8A7W0j=m0x?mbbzM|cS_(Tv9UB!^kbT;4A#sD`p=;;PDGuZN_~n;h=D@E*hYla| z=&mS_99ek>wKrHSH$1u>nsbjXUOZLSsxgM$>=^u7lQ{n(p>_7{n*(LF>dslW>>&Id zy=BHp_V8p(7$*#*X73{ZYVX~5-|bQ|rtfI!CcebtfiBvI%h#8$C;pF&m5&V2yaf1C z^1HryIlS|${HOaeGBaQms@QGcJ;4Z_PT3mb`|j3fQ;-LC>(rf)8#VXVFTyD``Y&GG zvL(&`Ab$$$pX_<^+qxG_0rJmmVLnK-tRxmaUDm3W`C}s4^-l9sIj8Yna^9P|M(unE z`#B~HT?j+(DrdrO zvKtlWF1EOS>t@h2n+^Uy;uFjTd(8zDU{Cic zM&;iN^wb>G@PhUg9oE18^wbpDyGE7h9MItRw0`%d0wV5(c?(P6!ObSKwV)2gO+q65P;F5Exbz4V+yc2G4|Mm$b)*(+w{dOcYN*Kic_XLMO= zuJ|Ix0ZAA1{H^!l`QAZ34~Q@~--Gput&6{oNKvsbf$`Aq1n_&%I}R(bPk91yx@CW# z;xB>x+lyMEPyW5(QXT1)cz^OJ=?|;#yLaA;br}%8afsSGg+p%<1};v^iqw6|^GtA% zz6r`lJ)~y>Ozxul^qdebQco)z5H`LSG(SA7rG6&2qZ{`rLOz!&tKf0P%PzrrMLD6A zt?`r)I_~t%XQ7|A#1owcZF4%Foq#08l4J1iCzPRuDE6glPIx$s)@tq~AGS{@RWQ)H znbbby5vElbPf%QQ1=u(4>!$kz;ka~PH!pD1uMi7|ko9WWD!fl9XN)o2=>OV_pD5d20wAHPfeY$+?&tBSooeq?f){}hMo&&$Z9 z{Y5|Nf17af>aH84k3W2D|91%=`rySitw7_E;Y`r#OQC_s39ZMYZ_{-#HtVA6;l~p5 zBpzn|+dB zFLc$v-vT>_nFB_$cfMEJJ4yD2;L&7b{A%8sz&?{z5EMNak6p2)aZ95e{-fL@6cXA5 z2S@jHbcMAPXQ?`#e4W~bA@7)kK3_V%AqYOA3q9dB@~hopOOSroedq96aGANrw{#FX z#Grw&5OEH|Zoe7!7_LVR5>4$b>Fdk=r0eoj5Pln0yU;mFXcN-Ux$IBh=Mc>Sx=)LK z((~{fvY)?kWF%j*5BXSrJM8gf)Kl1l{RHl~gK#qb?&UVxk4%63wypwp+hea6qg`V% zj^9S@hR`_d0N8aet#J@e-kj~@)Fo2}zni0={J)7T~p98xsf21fs?>=2cVEhe!PN%Eb z?+~zW=qg_HXtD3;D%K!QIfwo3UF<8g|Gli`r2X&HVtatDhr4Z^)V}Bw%sueuI(BZe zyaPHpn3q7^gBhm46Kdr$eJ=cyxfpojUEMdr$MsrZ#jOWNuP(Z(r=b4Ch29};H2F*kl zuIxR!ydVBYY*B>UEYRPuJ4PdzKSJ>mp;nRu?8{(0(M-Okihk$7#rlinSO043)&~!k zsaP@=$fvw!Pk3u1#j9=;8w>*)6-y*$pOO4?o-aKFI|2FgsnhU3#PyT#mk>XO{Ym|~ zV?5{#=92d!Z-(ptQUpJg{GtxnVT7he8aEGpMtNbLHx#W(x5K`XuJ2vGP6#!E_$Ff= z9iMQpTEPCOER73@M-e^x75r1XO}ZW!g+4{UfCxPdG>MX5MmUr9p9<6U5hCJaX}`;X zd@jTf{s}!_WE(5ev<~qVqAAYuf5$)Xx7@#NssF|$-1VpDrk}Gk4yQCeUQCZ0Nc_S3 zGLkp?J;Ff15BzGK$ueEdA*^sYysU7h)teXt>Ofr^igXzc3qrTY; zESt!1WLQGq>@eL&`BaLNo!Fkv*NS3NymfP*vS?(azqA1vNU_uKK!Zzxbj`zGeZN}R zgKO7{##-2$DhJu6kYAO^XOq2%oivRLOBhWft``+x#T1JCHBB?*KhA5{wQw@6$iD); zTqqa$Pqstv!ruJ*h6x3eiTv08_nS%%_Ux4|> z_(mf{M}~(Zl6**V0{JC5!E?&<)AdJ5@V`xGS7aj}>CowF9G=m`4$#~7?tSo!_xVDu zAD*|s{)GH8F;4o+AS5OG)ZCqd-H5yy6GMb?CK{G7Zl`v^FYRYhd5M?uJxRgi$G*so zA#*@^ouEwSAv>uP^;qQZ@s8mnVW3+G5gOcrm`!;AI64w?!q~Uyx+83)`$SdPMEN7p z;R_OWI%g*--XH81f>M9^Xh1@TdsNCNFxz+^(wcvXG_rBiyALnOzp8g!A{BCc6_vpBaOir%3u4}KcURuzS9hBM~ zcIH&5xyHsiBjw+$7RyfJSAYMG-D>a;%fC)q|41j_jq77$BJ8*fcC5EoYH!=o_On(s z^aIc5l78TMud7YcYjJm4k69S)x44eDxYVzX+9cRbDQY8zW$5%WNgMc5B@{$GCiHJZaAS^Fq6L^*f1|d7cS9-0`iHV?SyJh zkO|mNRm@lD0b-v*fT0g{B?2c%{-q!nq;(dt6QVyddTC6z`(VG(Iz(7oLi`^8rV{rf zphI>)*>@AjKLP{PZ&c;Mb&LxOo$M0oNA2OI`+c~d=wS(QpGvx)`J7>C-xr($_<{SK zJ_Bhtq|cDY14EdD-iv9q{{PSXU!K1;n_UWzru50Y%{?`K@fZ8g>g4ODPSlULBOj!< z35@t*n3z3%h z_;I@Zkw>Y&On3zKaOf}psf1tj-@MfSku=&i!NZ~Fr=RDRFF)^VOV9hA^t%G{N~pEH zx}Ex~d`s8mH`>#`TekGvhnFzjF1MBa_{+~1Wa-}#*z)t2ak>9552!dA&)0seCbX4q zhCa~mwUFN6gM6uEFPY$9rs)TlXxS3ovBdu*#mTda`up49&maz`C;!-)@EXaVou8lL zbC3tIroM4k>I08>fc^=+)AI0MbogD`9zukU+lB+mYR^qKJ-&iwK)*UpQ zYeSxDai6|!W<#w;6}A}354Yvs5%{>>u6Sl{ZVvHr#0{vv1?$+`KXM&;946Nr5kG_k z^6&>~zYbWhBcEln1>zpWljYvIWa;KC#5IkfJRtg+={h0BF@eE3+-I`|#WOn8zla|u z&w`f8jpE#<^mETHJ>OG@$DD>+dtFq%X9Vm<#E6-Lq9`Y`aK7Y zD@$#g3Yh5qCLVf-e}%>=GmF>1^8B;QP+@p+C++z3FN={E%mjUY2}LJ zlkj`9)Fo@j$~n{>xyMJXT*W<*$Bw2aktdn47@=RGPo^*Kn}eS_H1rJOxmZ8UDUcmm zE*I94+@_l1GRo7!uV8J6KSTW>U%N`34Dhy@4LdYiPx9G~$lIih`Vxn(mg<$brG6?6 ze&5U+kk4I3`P~zzupSCMgHak6`mab|M`gASaU!1AZiM{8eZzCJ)WHBhMgII>{Ka1= zHiExaqdr%I>oBivnu=+q*%UxthtR}|@w{Z5cb+QSp(%81pCCGA$c-2*=IVu>K=P>jug^DZOf@?4TxLHKNXImAa$Lx_1KmS|jRlu$b5jFHrqk2Kz&x_xU2| z`{Sj(MYnFwD&PYnYmf8~Xkud}vLi>`8rf7w-JXW*f({gLw2@pCtv*iix8773^IoJ@ z34Z8>X_e)LwRvY?2a1-5o0j#+eE)>vp}A-BjvR&lfxbV2h=0H5DPZ{j&oRo+{Q zdU#R(JUj{BCJLWE*H&Z34qV>r(D8m5d+ljmO(56F%8yF@w4XhfhyE$XAI1GDQTXW- zni```%YF8orh@8osXxDBM)legXE))wj`yENKj2TF=+WW%vftD7@G$%AX|93~eRtjO zpW|v`xv#R{KX+ymuDk#7X%G63%3gbVmhMCQ#v1+9n!>;ArTSmSeu4XvayO8!3qa~m zct%3$ey5B*KZNUknZWlK*I2`>`$5!^8ZCP*l$WaCTGvVOX5YBAVp?TaXiEG0Xx*({ zJyd$ItWUZ1oG7PyS>C#qQ64idLk@YH*}z4Q`!&dC3iCVXss4en5xuva;)`pGK6~z< zCJ8^`JH2OVoX%g;`@)cU|7px)3c2jwnGH1m4?m%1db5Kg+;VXgx(|Ylt2h8ObC46*EC_ zP}dL$l!Q?avf+?-O5(pip%th;3}Y|Wf!$m^xw zO$J6Pub~bhnT)I;eZU(nZ!hsXxy<)vq`XhgGdVe}sAFj2%}urHqJk4^7~(iZYKupX zx>Mw7L}7;VHR9s1K(r`&J%qmV7kda7_8SDc?${l)Ur`kmBvT&55wZ%Rm*U?b^o#|u zi)0maZT&}Wwt7`Tv3&S@ln)X=^*adtqF78A^;@)*ZyOa2PVx_5c;O{mmikSlsyK!C z)tgF>;QcGQ{?UK~`55FO4SvL(vI-)jzQ`5Tssc|qx+jaC?-qQN&r5_nF2dO7Z+x5Z z+&??qK($dX3Pg*?g$8V*D)-FA0gV|(B{>mpH>i#>;OMvQStD-npFj6f7ng&zK;5i zy~Ouag7RB-MGWygs?W>ZgSa{5Ag{DCb_V`wGPzbm@oatlHro>47nk`@^%z6XnKu(e*L&jst@`@_-#B--8sW?JAl1@VU*++Va!VUT|ViT>WK_V zhm879ih_y|@5iXVo)1P*7X|xaSOtH&NF5Zz-GpY*#}S%DE$Wppeyf1tXNgd`|M2cv%7gy=p6{*$t@QuZwBsPo7j^Er^4b9GHlZZrzfc_&pY$NU z+NWqrgk{Ch>pX9f;=W)%^gudJL2_5#Yk)n#SZ40=Nvg}jet~sqS2S+zGA{SiL7rj% z<&pnlguZJBBLAm?J&oTPEhM{|_iIm+Jk>Yq_2h?`O`3Ys}{EUrLMubJk5LLZ3(1 z()qFf`JKPop0n;vWhlA(jnl;Mk9cc>bTq!-%hf zkmw>mDNt0De`eFcvRH1eXU7%Pdq>v}mQuV%u(NFx z$KX(TN_7fy?XDs0b_mq)@VgK(@k{Y-)K{d>kvaV%lKrwMrX>0KvfN90W2C&4&fjIP zUq6|*SqHl_V$a*Wu>yHz)L8*VKkBcj-B+BZU7#H`yeaIszG-8HIdysJfreum@oGH# z+3lJNQ=DgqQP(3bSodm>2e;X_?Y+Mtry>~QZF#8kniR<|f?e-kv#JOCz*e1;<$!Oi z?^w9rQv-hPoBss$I0ctJ-gt`#*B@GaN0R*Om&DXO(fclb`qnGDBK030I&}x?;0jJv z{kQ$Ez`waejJPSEYQ2*9<5}1nmwtEuEo&ND)upTNci4nl1nC~FgJHbK{- zFdFNi2h`k*<>Do(e@`Z5PCDml5mg*nqakD0&K96xG2~OeqrCK+GOCv<&CAPsb^8Mi ze7HD#czfyQtN-#pioM(SH1J6o=iTm7G~(|+=lil8&^{!OWFkL*$4hsK(Z?s0Q8739 z+x^GNFg^Tl_a7`1gN!fMRdZXBNF{z}W^R9k{GIyVAuYTVX5ABp|D$HvBa|mpBM&(y zQXM)AGC7T3Fgvt#u0ntV(u{Z+GczV(oY|y)1}0EcVMV{yOwavJd4Avl*oCr;yrMk~ zw{E^PRk-eOL*8cpueBa8z3&IZo3aAey@FZ z!`NjN_ulqnWjpt7+qMyW!12mYyxa3OC&Tvn!wqXT>!S|mobFiJuYUQInR(;FLv2%H# zgZmcuQva?CA8-8Hk6Sf=xPPF059-5CRlPL%wI3hU9O>NGxVr&$vx~p4KD@nEBg#8mM;$rG+Z>3W0y(Wg%BIZEW4)-kqVB@NWqvwfALc#N zDfx|9Z$QlSmyqlR%2zG-|Br}uA8u=_!TL{%UMtnlj#qrQ`09qAD3XJNxzaiE1;KWT z&XXs?u;My)_f#M&${(aPa;QUV1Tatx3sTk@l zcVPdAUJ}TT;!`TveK=Q=a7xIJI-JwW7SubtojAwD8528qQofY0ZEmK0Ys1eBKh(h= zVqP&x_2>-sR&9viAg#go=&}oB3rW<=6{%&`NuJ^hC%TNF`xGmNVy0KXMtIgFMx_ z$`&x+72i^?SqSp&Xm>|LptB2Xc+OeK3F?3l$GTU&CQ9+Gdo^y+UT99C*dd?oy zlTp2=c0muszP{gTruvr-OP6$R;ak7F&_Q)zGMvvP{n{-Uz(2;lzUW^deNZS`O(X~1 zCAU}7{wW9-wn}_W#^Ms1*{oEj$$LiAX!l5H&!?ulm z;OltR;di(i*r75{vKa9e$|Dlmv9yq(q8`Z=c?dDo>n~y*>Z-9pL+=3ymE` z!1vGbd>_W0EB7M)-OM4q1f)p05R~fTAfOTo;Q(Pb=9lD!Il~5eznuw-p5j=$YH8o7 z=Gxn5Pm(^!TyeaK>|?i>iFtwCiVXV=okJ`H4U7`q%q7glF~Yn&r%3yW+;uKSzq4%{ z@>LkeB(I8Q7q}Eo7wYs1aALrI$xF|B<5#KA=p0L~KVTwcE@Oz$CKwEaGHozP`6{O` zpeIB+#Y*`qd3Z8OXp5SWx9?MEeVuW#192=D^@NC*=87h=6P?2$&{F-Np4I{65cb?p z6nOt2{eD;Dg(_PAUB=0^$iuMYpq&wa_+dJ4SIuqec<@2gxpP9M9C2hdSGj(yiq8Gb zZ>_i3p>G`_K1kRt@+Lx@mzZ!5h z6&3i`GnkwPe_7!$_=R)Ef>p9Vy64ZmaRvN%3;{LL_iIPCY{UBJwm~jyk@pJ3O7l*l zUF7v{()SL3oYC*eLOi4Xz927v=4?S8?J8D`b{A;Wdv)V{9H)=vt)PDS2%wT5whe(p zy1vy}*o|3Zp~CLM2jFkxJV$=nqt&bq2jG^^W6?PP%BK^mmQc2Y2*{w$6X#*DFHqeV zzYwYF(ZTMD4aq&Qqu}SkE{A^N8@EyZQ!oc7`+#ggv=Z$KgrJY@ir2@6r1c~?Qe!0l zi=*)ntt&x@V_n!4ZeiR{=Q*{q(Ey#h`i0GiIvKmd=Mb$DUiM4IR$x(HkBiIS{qo zhD^*~Tom%9`gl=1Kz1Fz+aZ%s#W4S%6YgQUKj^Y|6Mu|}?qd8p>OTY<@jrj$>f5A$ zQRRWUtZEiz4xHCT8Ff#%m+D}w`@}`kPtW9f#z`*T?(tH-j~Q(Y{&F?62;rYewCWhnuNaLa*NAf@Q;T2SkI3&CDL;yXOe*dY0%KpjzD#@DDL)$f@P5Rb z7OlLI_*%bp>!xKn_$ux|{eUK?vNXKu9*wtTDB4!jryMEG>DgG5!EA29atPytevYLM7hIUf8(Z`N+Bf4zM@C=9dcpDXpV&7L#@5=) z2vL`^ge?+A*>d|b)Mf$y1Ab5lHVvlEVs1NIHzCKZ}=I&+@;kOQw zhxNy+)*jR}EAF$3(D#^MW8FNRtIr>s^H6>T;x4-%_CJu_xr7v-BUFJcqMr(!58gj}iE zI1teuR^mlHC&rC!19lhF8czlw4c@K^^~V^Mtb*?+~#E$l@DNE_qObWRfhKq zngXP!Ru>gz6JKm6PD~NskH@Ya-w1n{^0x1Pq^{=mDYrYNY7WHw^4z!!Wojauy_v?` zxLu-`F?=&kKNTr_82&K>wo$zJ`;q32e9FyMN2(QiYunxQdl@+y8Kn2l*IUjL|9!vc zw!6??* zZ3uD7vpUXajoy#E^1rgT`}CCmWX>kk zmBJs@@;vdM;k*acPrdQ_VvO?Z=g(i*N^zhy-UTc9E&95~y|nuO@cb;~Rn3aBb7ZHm zUM($c7Vw`tB50qoW4&dsEw3VeoH%{r1nN+5-gzPQ=twiZ6H*>{_}t&L6{`gOo$+S7 zR_+T#BTxL4^vP|}C;pjmMYLbSNaSB7oQvGkL+!78bj42i^heW#fny)qEzF(bZ;7Zk z+^uW3((mQqkADCB`S&`A|MqUzNs0$lRhCvxEBlnP%-qZ@#2ZbqWPr};hK1C^S=}*B zMn(?mT0FW>|Gw^r*EczITGXc#_Kb$CgbueDB7Qs&76Wn|gG@ z-mPK8yVZL;9)9pv_^BCB<*0F9re>?whwH16NAt!g)Wmx~*`l>3}+s&NC(iW};4qgiw`k=q^u>t?<-!^04|D11e zG^^IG^~PEpEoCYgVuh9(RQd>fjip6nBeWA8L3^P_WgF*Z534mcRg}NHD+76HoY!`^ zl&bD1@3P}N70B~0@jrupzoTQmp7hc2+gBcUf&Z12m8H|zj~t%aDCvKE>7#C1S;HZa zGvq-YD<^00Ez0xD&i`Wxts~SkKkw1C)y%l8yaVe;ykz19-L9I~9`}rR!1t3q^K}BQ z55Ib@?hMW)*<{@5aPE~V#)3kE_W->|VXVp0+g97-sex&#K z8LaC&J>H=+cpko2v>(5xll#}+M(vju$3UavyCwqvi>q*~2~BPU8k+R_jWt&{2zQ94 zjWx&04m5S`=PGt+R;__EeJ#`!MS0i~{trnwu1H;1}{hdcO~T ztlx}-!Jz#}oDz;=KP6n?McBo#E70%D=#ywQCz|OTI6cRU=Tvip9>GETPsjQB^Thw7 z$B#<irj>gy-b8)M4$aWoTs!e^;PzhL0lmHcYRo{m z=6fYE570H73K4B{kH^kz(p2DBuC)#Mb@z!d>KRUz-8S2HKh-z9;=38Ty5U$^@heAq zuWmS4=AKV1{s#34#zz;Py;^;bMt*ow{HS_b^}=1>TCrO9o9eZiO!@!Zv|9J}NjCAD z>P&Uxfdh-Hz~8#2z1-vUwn6iG;=AA$){tF@{kdm{#D5NPhzaH2&(9!VfjGf@2!0sO zGwqo~K5klB;*a6`GA_k~J6poUSFk_P0u1m;%zrifjb1<5pFLGp*zfSeIH5_he>h)& z)}O#df>Jy48>DtN{AcLG`j^^m3HZmTS7#-?Bu4_9GLpR%9T%VK13x=2grEf=XA4F* z`4>SWzvd+P+JwNHwEq1bkF@@)DyxY9v%4R``6|Q@n}%I9?h>u5nA%_4Ki@@sHo%ghoPih66_tzv* zH%8~>J54T}_ZTP(i46IP(`BN_j5t?YCWd5)bDS<)5W68a$21YqhB(bJO+u6zbXeah zQ3d&H)kMW^toK%p7;+-MutS4v9KK%#v@-qobx)R=crE{Z-Hx)RQRctavZE|1YQ?u& zKH8X!F+QjLqiU;F%l~N8J!Sg3E&6*j)2glKue(=)zX|Js@G96rrPi2?{O_g_kCy5) zD%X1a^u4FW16#I`U*@)q6q5hq9dkIye;*c8cCw%B(att0J{AAY`g@=ckk^-!U&0&1 z^J)Ks&IzfwEp_^v#E(ING3*n}qqW&+zYq!wCEV5Rq;~FyZkBEr+%oFlWC?F0t+IY? zN=tH;N{L#^<8=mu-=uTad{ktIuhY1M%fB2s4gJM6HZ|7Yjpq$~K>Sy8RVyoR zCqAPtGDWxm2UDUGLi{A;^upFAEBP_G&v>Q0*Zh3LEZIGUE-U7N@FiqJYSoVDNFSZ& z_nLUKbgtiQ`X;sGykjw;YDP>@f9zk|Xq_;37;!Yzg}K6?)BHNe{U*wDMg3+s<iA z9{i40Fs-?|F;g8kJhpd7S*BVLV}Tv0%hLLfc>6c3(%j6vX?(J5l}7K38|b=`B|-;r zJs^);kD-1h$o#1Ls#Tj+Ap_-Mn(8;>+$QQpwuWD&d~^){5~1Jwtb@kYa<}f}H0E=4 zf29fE?Lr(!jAde<*!klw#6chj(Ne^dklzw#x+tz>5*^roo2hx2JK2H7%)VVoL zGc)z$MTa@v-W{TNXH0a(>35RR8-Gwd3cqA@H1o5kQCAa0V2kQ+@PPl`i+s_Zu`{`T+DK4wlWHjw_YtF9w1kNS8{J4ELYg!(PgITbe?K8njg z5dvW+0BIkjbKCHDIJRhn9z;CG7(;vxl8L-H`SAksfD-KqiS#?RTgGCP@7nLl@U#Mx z7NX6z`N;vAj^R(uWMsI0C{<&3cOvG(;UeKEGWKurX z*p;x6yo5%dEhhQm#d)EY@aCG2YO!7ozrO2^TdQf%Sr8dx3k@AFAY9hnM6~{_Jd*H2!UkQvURroYEZPcjApi z1a`ARrE&?BhxQ+@93lCxscF%j(P91Z{38;-4PJ)tC2}#*UvVq)DQMq9{5~*0{|Uv( zyrq?uwC;F*vdNBhm$n!F->{jpBkV$c?d;% ze(Ae@^Crkd%! zCbxbc(#nG7qbmrl=6M^TIdpV|9q*sx6|j#ZPHqk*$$mn>B9v8txF&sf5#xp)!#Q*0 zcYnQhj^v>VA4Ys&Gt~*?=YRc~wi<(&nD@5hyR-|;b>opUn|ejr@b8~S{?oE;FU2W7 zG0x7q@Z2@=VIR?p`uPOSBTg+Arm?ttiMH7>4i;CjG+n4lqtH$Eog+|%xX0bxnV}vz z?c?L(;tf0O0?r`}6Ur>@0%4Ih|HCD|C71a}`_fo{(Q2x<@pL8Jw4Yh8*=-bW(;r!Z zbKdyg%7yuP;$Q#J8Q7`NH%PcH-%n_i&b`78nVE5tekxsEy1oc{aR2_WK=%#%#fyX& zE^W2mi{H!3&d#QFJC6^Q7J+srzOfqnf#OJKp@ZVvvfR95RJT}?>lr$Q`bfcEe0&Ca zq3e@`mFiTgws{UvUCNKGsaeVk?0fWLqaEi?PAAWY35AIF6Plm5QD$&zgI)1&PnPG? z_W<0(!>>&JB<8E$p4~sL8@1V96ouf>~TfXMRpkCcu~I$^||;Si8f5Ar6u}{LnBtN%8G` zK42z!auhyQMEq@P?0}xKE3SDrNZ&&)${sj+5PCAP7vH~xoXibro;e74NQ%)C(!a-_ zf9?p`TP17#{R4=zm3)+n5#Bi*=jnSvVP7gvcEwa86-R>$|x?-#g03d5Ke~zo^^N_(QVWEQx7cZ^lU$yIex1h5oZE;^qL(uhAEOiT`;ew~(DNH6IqPsPLT|{E<9}bIGdT;me%8V1Mk1IbCDp zmb$#nXLS`~0DAbW?wciJ;%oRm>E3NECuu(38d_aM@nK6_WJa>zhbk*++^TL4>l+9l zv^lyRgbtOXSfb@_Y25BYxkTGJhmG{qKYsFw^d5BnkG{u~otKfpRb1V$ZSNCYamCDr zs*>U8M%~p7Rg=lbiczn6MU0IRe~NA?x`p;laa7dOK4zIYc{iOe&Ips}Fg9MfO37R}4Td)Iu~^LUx!KD@~DH+_rX`k-iTw)z+ma z{v8{XIzlB=i*Mtm)sSE9v{j*`5-{Rae#ZEf!HWKK4iS$i&wkC)8yIl+($_E)_;Z+4{*=+y-d$Je0X)0#GjOPJ=G;<$YdPF zKc33#M;v5k!}N^*gYN^4@%={fpMAnmlI9=VSrM%#eZY7Z^s?G=`SKL;slR`$9Da@R zRv1J?Z+e)%7eslN!AOQav(t9|D^w4k6Gi$uaf1)1Eo;phO?ngR0C_l4ywO>zl zT4H3y7)FP@f_@9-Rj^)BM>(Mo7B7DeJ5$;1c441rQ81?w_H#2g)fC`~-nX7b-5zs-oJ=0XbLmsqUO(-TThY60sX^?AO} zL%-XeT|oTrskv5m{S9+nJ5XCS-bwK@?PxUiUi-$HjrZMuKg}DK3)V{uXL;n)F#XOO zFTQYr#`DRiA4lIr`&!Wo`yT7Z8i$^5=2Y62$q45O`AzJ^&&Oj&e)iM66 z0A%j&!pq3ZaPtd)d}l&Ajd<&@gXFTQDbYsrhxo1Vg*;3A5B~)Ez!)-yNG^Jh^kP2f zJit@nKhAZb zzWd%<{+*L}{(Z9^*lB9+^33I_35@%lclxD$Kyc7^LRwH?kwyUlDWV-XkVz=G9BJB} z{?3S??M8mguDBib6m-5N`h$T_Iru+A$1gtEhVv<&%pq|X>M%N*o*&+b_^#*L(6{LO z?M2x|MaRJZ#Ewj$gu;1wzjWPU){Ac{UGTHq&JQ60)=qt(57#fe^v{^DK80C4JOlYvSZyp! zzw?t7DbpcMpR#f+5EwxGNWZ1Af%c`UkkyR*B-ZKtEb;MIzw!@~9B7?|4nmnA6cXP} zAyK63_KM3xM5By8O8J#%XNx0}T*Qqbx*ksX!iVs@C;Rhzix95~#U~eOJZ&e19vVl` zwD0|8zDVDN6;8Vz$2>O~jh9?Ngl|`pyyWDR4$^%#93+PvaosAJirznUrg?u)#hb+c z-qV^NsTb?c)sg?&GdDST3jV8hd?9oOb&wl3ru9FTzob8_{2?IbBejS)4Dxc7BXN=g z(Ykn%_zdeL&2NaO#CJgm+wq=)FpPG>>GXA1k3e?B+7({^Rp`@sWBp~W0_W*o8}eM; zfbWfIOGTW2DmbYN3v|xvtynU3mH6M^kMHQ#YEr0+LYzgtbLZ|b$$2bbK-}t0WiYkf zj<|-xY(72(dkuDSs-#u(CgMweHiLbd7p?XI&6_~cb|{Ug8%IB@I7M|XqUc7vrcV(| zcA>5p_Jf|cN%2lW*dqB?g{}aN+mo9MJ8)Y0w~y7<_9;&))@ZeBD9(U=``>6@g6H;a zAwFsy{uH6$MtLOOKOS9(N_2Egl7sMg>MGGGPsB?0`{9K(()X{g+i$xK-+yHlQQrp# zv7am^KE6iwpl7Xs_={a};Jps$fo1y!-vtK$n_)kE(|cDB&GUCQtgfVfd8K(d#Q&6U zbeQyrQ>!hW01Y!z(4dZ&*8d@VuhH#Z+;<4|^zJk9G1^~?XCwG-XszbVTN^ii@jH_L z1pcG$=*B0oU#hudze=4aJ>O+n_`5W}waff>&^Uaa!OJhj$d7V_tyaRfhs7ZMJ1lQs*0oW-0?WUY>^}F6t}Z%1c=GhZ ze#EU5nW8yF@hq(vFp)jBCLZV{zguQP9WLGnAL7J!d1}>m(b!4nU7el5IH4QgXQuBv z*^uDGeo_D$^;oFG#JLstcS>8T0eT?gPv;zG`5*2gdB`g&>ZkFnSX<>MyTqW^HGdv~m#`0Mec<#(Z|@L)wVWLL66yx@b#;Ukk0Uw3@!BP%`aaa*;rk-g zUl1JbfwE@BEtQo=590UW5Wjy6=t?9*kVB138~*SZ@{}iaZEceOS6Vbg{<`z@JnUG+ z=`LT^(R*_KP$ykS{e_A6f)8*727vV4p3`NB5Mdmrz#o<*{|Xt27{vwA4ueYSCrbR5 z{=E?L>rv1}{8zYv#P2OwFodX2#W`6VZ#uGaCFRqS;>|_kf8*B84JR@0ccu2jpKazE z4_F>)!FvvV@HXw|+-qLZM<_1%Lxj}FN&u$PnA%A6$GFU zd>_TR1FG*^8}%gVoCeu{6xVtqwQn*T=cyKa9-P;?g72RsCh0uz83@3a*Z(sAS65X* z57a0u3z0?Acizg%7{-D38z*_>dKRM4Z|U!fEu$Un%|cKcE>THi9kuipW52*(Yyo_FqSYbSf!E3Vg*UlSIcJl!`NJ%RHA5F0JCruDQ8 z{6ZF5U`Z+eBy>ys(+Y(F8Yi~lAjxZZR)u!>Q=>9G2jlkJ5wED>BEu@vH2UH5)W90( zFNSvP$HO-C$G<0`ErwqYeyp5C{tWeSpT%QEkdyZNe&D4%mT2utkzLcg>z#k2__eFh zx2^{K5a;~TdLH#!rTB~}UX|8)C(e}+pZ^exi?sejoiRPpR=?Fu$QwhbQ>^AX=VFLU zLk|p%5ZE7RvL`8+zCR}$E$0%7w>JXbhGEV&O?f1KO+6iqR`|0T3_uhT) z-QPLqo_p@O=L);S9(9+g;{b8)_~Pze7x?q^hY!Vku%-2dpA%=dMs|_P`r$GiFv_Cr zb?ZsNj$$0ZaSCD_h@3r({a`M`(wdPEIgfMu^LEUm5dcm-BzW2YKeY zXHQ!t?+^?gmQS7PMZIwQx3N4r)K)H6@)98(5%SSsZ_U<>N;X&Ulj{ZKT@eQPa! z&mSa?et&Qh@@|;9_2ONDe=Lzeneu;<_yzp^QhVTuT69tJi5~mpi7Xq?{5l* zC_nj^?qJ;6qPub9#;a2(|NX1EIpV9;mhtxL<3B8LM>B$+fx*anGX?^F z-$aMHm-+^Rj_<^JU*3lKBjQohv3Ldl>t%d<1ioz*{8GN0U*NRJ4-)x-e9!~@!6(Yl zd!pQaqL(;-otMfVD(9yPmGd02p7*y__-Rx*CE_Y1BpzmcdwW}(4EzEA;G$lfLVp$; z#xO2n9QfJu`(*s@rrgd+p5xMjfq}R#Sd%xX=dnI0YN91Jv}xzPMmW)dxbu|@*m8d7pK`f*(iQ1^p8XjKd=#L)TQyWw#ql6qYK0vSyHLqZi}Avrf*s&7JiD#= zW&Y32iR}-G)1utEU3&8r^yq_y&;s%A?Y#*50`_a5X*2KB4ZEsIWwWd!b(>_3pk77P zo3(nu4_-`kS+9rVoc#2`?6GT${rV`a;NQadMZheTaSSHQIPdCVIhgX#3O*Q;gxt3E zNbM{Sy-Piz(e(EAww9ku`PdGc0iS@j@7l$BCFvJ(4!#-T{U?S-PVxHa38nIUdv68* zBa7e1{#;VK{&Y_b#(_Q85Wfoh=)fC&^at$wW%D)Sw*O%^PyDS`dyII#GHn%npxP2; zK0y3B>!UazjZ}?Ug*}*Ts_AKMT(+tgI<*OK5@+8c}g466Im;7?Ok>4-NH{`C_OSdSQuX$Gby9Pe9*` z`@j}Lzl`8Pwil}C6+ak;e7Jm}SR^&u)hxeD``uLuF%Hy^i*W$)$sP2+y|r#t1OL;} z858q?PG>yUjdDWD#EG*V`xr=zjA=;C)NQUa(7Fj{}^4)~p|epTlK{ z?f?8H`{&Sw{#(R9S=#v$@p>QrAK+vd^bPuWy=0GLT;O?+c02hX0>?X_Z&*{m<`eM8 zxnQB-F~|mZZ;ZtMSVS#y9=r|nf8svlof#%od_JMK-Db0U3eW9rJNQq&=l=c4EBwz7 z!(aS^eDUDky{o9#ZOBJTq)I9T+NG02kr2OA<8`w=gI9J5{jYZoXnDOcl#fuJZvJBa z)kWMF>9S22UpK7WKs#RYGH)I7z`Jq2$wtH*V1W$Lc|7l=ANhfF<$6)p;#|kdIVj8< z&-)&wXs^_dxVUck0S??HKc~<_6vg ztJO;CzYJ0@elmSq<* zG>Up8q!)a(cLDsf=-&XvdNut-2uokbI65p>C*U8b`}BgNs| zdXavf`TGZ+lXr1FbzO!y-!}S`Zqz$5KoGCFl2h305%pIQ=9g=~@%wkF2bOObtfd{e z@AW;+}RCj^?J{F$w$~eV|Ky*%VDBg!5{NKtcR6S1-5VWy4|Fh*tm$(!Gn7t|IjBG zIB=iAiStuvr*0mPXDG)O`}NjG-_L$A>2$&_&{b;(&>nXD@ti-{jYd;7+x_dvcs<*N z`Y#9yg>@P6PH_LPBaVmEym@%JV%r=BE@|Wx0s+&YcG4etCaKD-JJ8SC!oX-J0f{hi3tJK** zrw|7TcLMx@*q0?q!!o#AO+-;f`e8NF&-w7E8|zQNzqxrE^k$b1Zt@H3pbv6Th_sKk zL;sM*l>tFJm4RxOZ6R4lD*5TB0S$Ovg8F3WV_a8^sPEQ|`-fq7!7m*@x%sj0!5>hV zKTmz_Nbjb-2b@Z0NShj)8rg4_OlCsR`2GZ`WQlRvU)V0A-e@)k zbeq;bF*5$x9>z~56?Z4&Cl#~)qzUoz`ax}R7xpzz9o32WN=4~^AK2L?2qpmo)* zYH4QUjvA~>fcDg^xOEQkJ5RIR$$VF}0lkG3ep<}$u~R~;%kn;m1M;Qx)M%9y@)GT+ zxvS2?kF=xC8#38Q{XWgdw>Mr|-8*w(QvB!J{u0OiYhNu`8NcOpMpiM8nH3vvE@6Fw z`jGQ+84eIq2UbdgBoQyx&iFAx#(qjKFA>jv4f|1P7ZXNf&E^?ep;LRNO z5ch#~M(`UfY(Zyu9)`^`fYE1+0KjZbHeSC*G0{@uH5a`dto^=^M8%&Gu!}8F6 z;v(T-0#Zv!!a4}>>FS!|{qL+C$x>diZDcd`ovz&Fs8a*q6i6Cd)TzTfQ^ z)F><9S3_+|?W8|XkMkaA-{QOymF1+_9$yQ;XGlL+#n7~jz}Zhnf-hgBo+g&$QNP=1Yb4wEpPSPmgUa)pZPAv@0- dLA_l8UvOPS36-xauxI$Zg8#<8(6N;({{u)&=nnt@ literal 0 HcmV?d00001 diff --git a/Templates/Full/game/art/shapes/weapons/Ryder/materials.cs b/Templates/Full/game/art/shapes/weapons/Ryder/materials.cs index 3661bcae1..47dc020ea 100644 --- a/Templates/Full/game/art/shapes/weapons/Ryder/materials.cs +++ b/Templates/Full/game/art/shapes/weapons/Ryder/materials.cs @@ -39,7 +39,7 @@ singleton Material(TP_Ryder_Base) mapTo = "TP_Ryder_Base"; diffuseMap[0] = "./TP_Ryder_D.dds"; normalMap[0] = "./TP_Ryder_N.dds"; - specularMap[0] = "./TP_Ryder_D.dds"; + specularMap[0] = "./TP_Ryder_S.dds"; specular[0] = "1.0 1.0 1.0 1"; specularPower[0] = "10"; translucentBlendOp = "None"; diff --git a/Templates/Full/game/art/shapes/weapons/Turret/materials.cs b/Templates/Full/game/art/shapes/weapons/Turret/materials.cs index 4ed4768d9..95b361714 100644 --- a/Templates/Full/game/art/shapes/weapons/Turret/materials.cs +++ b/Templates/Full/game/art/shapes/weapons/Turret/materials.cs @@ -29,7 +29,7 @@ singleton Material(Turret_Base) translucentBlendOp = "None"; normalMap[0] = "art/shapes/weapons/Turret/Turret_N.dds"; pixelSpecular[0] = "1"; - specularMap[0] = "art/shapes/weapons/Turret/Turret_D.dds"; + specularMap[0] = "art/shapes/weapons/Turret/Turret_S.dds"; useAnisotropic[0] = "1"; castDynamicShadows = true; materialTag0 = "Weapon"; diff --git a/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/TP_Ryder_S.dds b/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/TP_Ryder_S.dds new file mode 100644 index 0000000000000000000000000000000000000000..9b7688e7ef964d790c61cec8e7107a0c3b1b659d GIT binary patch literal 32896 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 literal 0 HcmV?d00001 diff --git a/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/materials.cs b/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/materials.cs index 3661bcae1..47dc020ea 100644 --- a/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/materials.cs +++ b/Templates/Modules/FPSGameplay/art/shapes/weapons/Ryder/materials.cs @@ -39,7 +39,7 @@ singleton Material(TP_Ryder_Base) mapTo = "TP_Ryder_Base"; diffuseMap[0] = "./TP_Ryder_D.dds"; normalMap[0] = "./TP_Ryder_N.dds"; - specularMap[0] = "./TP_Ryder_D.dds"; + specularMap[0] = "./TP_Ryder_S.dds"; specular[0] = "1.0 1.0 1.0 1"; specularPower[0] = "10"; translucentBlendOp = "None"; diff --git a/Templates/Modules/FPSGameplay/art/shapes/weapons/Turret/materials.cs b/Templates/Modules/FPSGameplay/art/shapes/weapons/Turret/materials.cs index 3e632d323..f6e8908a9 100644 --- a/Templates/Modules/FPSGameplay/art/shapes/weapons/Turret/materials.cs +++ b/Templates/Modules/FPSGameplay/art/shapes/weapons/Turret/materials.cs @@ -29,7 +29,7 @@ singleton Material(Turret_Base) translucentBlendOp = "None"; normalMap[0] = "data/FPSGameplay/art/shapes/weapons/Turret/Turret_N.dds"; pixelSpecular[0] = "1"; - specularMap[0] = "data/FPSGameplay/art/shapes/weapons/Turret/Turret_D.dds"; + specularMap[0] = "data/FPSGameplay/art/shapes/weapons/Turret/Turret_S.dds"; useAnisotropic[0] = "1"; castDynamicShadows = true; materialTag0 = "Weapon"; From 60f604fe9b792db900bd95a2f8e4ad7c4a2e87a7 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Mon, 12 Feb 2018 16:20:18 +0000 Subject: [PATCH 13/16] Profile change for specular files --- Engine/source/materials/processedMaterial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/materials/processedMaterial.cpp b/Engine/source/materials/processedMaterial.cpp index 1622d50e6..b38e0a400 100644 --- a/Engine/source/materials/processedMaterial.cpp +++ b/Engine/source/materials/processedMaterial.cpp @@ -456,7 +456,7 @@ void ProcessedMaterial::_setStageData() // SpecularMap if( mMaterial->mSpecularMapFilename[i].isNotEmpty() ) { - mStages[i].setTex( MFT_SpecularMap, _createTexture( mMaterial->mSpecularMapFilename[i], &GFXStaticTextureProfile) ); + mStages[i].setTex( MFT_SpecularMap, _createTexture( mMaterial->mSpecularMapFilename[i], &GFXStaticTextureSRGBProfile) ); if(!mStages[i].getTex( MFT_SpecularMap )) mMaterial->logError("Failed to load specular map %s for stage %i", _getTexturePath(mMaterial->mSpecularMapFilename[i]).c_str(), i); } From 5fd55196708486094523d57641246a195008d5a8 Mon Sep 17 00:00:00 2001 From: Areloch Date: Mon, 12 Feb 2018 21:36:19 -0600 Subject: [PATCH 14/16] Implements hold and context keybind functionality, enabling the ability to have actionmap binds for holding down a button(complete with hold time return if needed) and context binds for being able to have different events for tapping and holding on the same key. --- Engine/source/sim/actionMap.cpp | 230 +++++++++++++++++++++++++++++++- Engine/source/sim/actionMap.h | 35 ++++- 2 files changed, 259 insertions(+), 6 deletions(-) diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index f96e02585..841f3e39b 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -269,7 +269,13 @@ void ActionMap::dumpActionMap(const char* fileName, const bool append) const if (getKeyString(rNode.action, objectbuffer) == false) continue; - const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind"; + const char* command; + if (rNode.flags & Node::BindCmd) + command = "bindCmd"; + else if (rNode.flags & Node::Held) + command = "held"; + else + command = "bind"; dSprintf(lineBuffer, 1023, "%s.%s(%s, \"%s%s\"", getName(), @@ -324,7 +330,16 @@ void ActionMap::dumpActionMap(const char* fileName, const bool append) const } else dStrcat(lineBuffer, ", \"\""); - } else { + } + else if (rNode.flags & Node::Held) + { + dStrcat(lineBuffer, ", "); + dStrcat(lineBuffer, rNode.consoleFunction); + + dStrcat(lineBuffer, ", "); + dStrcat(lineBuffer, rNode.contextEvent->mConsoleFunctionHeld); + } + else { dStrcat(lineBuffer, ", "); dStrcat(lineBuffer, rNode.consoleFunction); } @@ -353,7 +368,13 @@ void ActionMap::dumpActionMap(const char* fileName, const bool append) const if (getKeyString(rNode.action, keybuffer) == false) continue; - const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind"; + const char* command; + if (rNode.flags & Node::BindCmd) + command = "bindCmd"; + else if (rNode.flags & Node::Held) + command = "held"; + else + command = "bind"; char finalBuffer[1024]; dSprintf(finalBuffer, 1023, "%s.%s(%s, \"%s%s\"", @@ -407,7 +428,16 @@ void ActionMap::dumpActionMap(const char* fileName, const bool append) const } else dStrcat(finalBuffer, ", \"\""); - } else { + } + else if (rNode.flags & Node::Held) + { + dStrcat(finalBuffer, ", "); + dStrcat(finalBuffer, rNode.consoleFunction); + + dStrcat(finalBuffer, ", "); + dStrcat(finalBuffer, rNode.contextEvent->mConsoleFunctionHeld); + } + else { dStrcat(finalBuffer, ", "); dStrcat(finalBuffer, rNode.consoleFunction); } @@ -794,6 +824,17 @@ const char* ActionMap::getCommand( const char* device, const char* action ) ( mapNode->breakConsoleCommand ? mapNode->breakConsoleCommand : "" ) ); return( returnString ); } + if (mapNode->flags & Node::Held) + { + S32 bufferLen = dStrlen(mapNode->consoleFunction) + dStrlen(mapNode->contextEvent->mConsoleFunctionHeld) + 2; + char* returnString = Con::getReturnBuffer(bufferLen); + + dSprintf(returnString, bufferLen, "%st%s", + (mapNode->consoleFunction ? mapNode->consoleFunction : ""), + (mapNode->contextEvent->mConsoleFunctionHeld ? mapNode->contextEvent->mConsoleFunctionHeld : "")); + + return(returnString); + } else return( mapNode->consoleFunction ); } @@ -1310,6 +1351,76 @@ bool ActionMap::processBind(const U32 argc, const char** argv, SimObject* object return true; } +//------------------------------------------------------------------------------ +bool ActionMap::processHoldBind(const char *device, const char *action, const char *holdFunc, const char *tapFunc, const U32 holdTime, const bool holdOnly, const bool retHoldTime) +{ + U32 deviceType; + U32 deviceInst; + + if (!getDeviceTypeAndInstance(device, deviceType, deviceInst)) + { + Con::printf("processBindCmd: unknown device: %s", device); + return false; + } + + // Ok, we now have the deviceType and instance. Create an event descriptor + // for the bind... + // + EventDescriptor eventDescriptor; + if (createEventDescriptor(action, &eventDescriptor) == false) { + Con::printf("Could not create a description for binding: %s", action); + return false; + } + + // SI_POV == SI_MOVE, and the POV works fine with bindCmd, so we have to add these manually. + if ((eventDescriptor.eventCode == SI_XAXIS) || + (eventDescriptor.eventCode == SI_YAXIS) || + (eventDescriptor.eventCode == SI_ZAXIS) || + (eventDescriptor.eventCode == SI_RXAXIS) || + (eventDescriptor.eventCode == SI_RYAXIS) || + (eventDescriptor.eventCode == SI_RZAXIS) || + (eventDescriptor.eventCode == SI_SLIDER) || + (eventDescriptor.eventCode == SI_XPOV) || + (eventDescriptor.eventCode == SI_YPOV) || + (eventDescriptor.eventCode == SI_XPOV2) || + (eventDescriptor.eventCode == SI_YPOV2)) + { + Con::warnf("ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead."); + return false; + } + + // Event has now been described, and device determined. we need now to extract + // any modifiers that the action map will apply to incoming events before + // calling the bound function... + // + // DMMTODO + F32 deadZoneBegin = 0.0f; + F32 deadZoneEnd = 0.0f; + F32 scaleFactor = 1.0f; + + // Ensure that the console function is properly specified? + // + // DMMTODO + + // Create the full bind entry, and place it in the map + // + // DMMTODO + Node* pBindNode = getNode(deviceType, deviceInst, + eventDescriptor.flags, + eventDescriptor.eventCode); + + pBindNode->flags = Node::Held; + pBindNode->deadZoneBegin = deadZoneBegin; + pBindNode->deadZoneEnd = deadZoneEnd; + pBindNode->scaleFactor = scaleFactor; + pBindNode->consoleFunction = StringTable->insert(dStrdup(tapFunc)); + + pBindNode->contextEvent = new ContextAction(StringTable->insert(dStrdup(holdFunc)), holdTime, pBindNode, holdOnly); + pBindNode->contextEvent->mReturnHoldTime = retHoldTime; + + return true; +} + //------------------------------------------------------------------------------ bool ActionMap::processAction(const InputEventInfo* pEvent) { @@ -1328,7 +1439,9 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) // Enter the break into the table if this is a make event... // Do this now rather than after command is processed because // command might add a binding which can move the vector of nodes. - enterBreakEvent(pEvent, pNode); + // Filter to prevent Hold buttons from being eaten + if (!(pNode->flags & Node::Held)) + enterBreakEvent(pEvent, pNode); // Whadda ya know, we have this bound. Set up, and call the console // function associated with it... @@ -1369,6 +1482,15 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) if(pNode->makeConsoleCommand) Con::evaluate(pNode->makeConsoleCommand); } + else if (pNode->flags & Node::Held) + { + //check if we're already holding, if not, start our timer + if (!pNode->contextEvent->mActive) { + pNode->contextEvent->mActive = true; + pNode->contextEvent->mStartTime = Sim::getCurrentTime(); + pNode->contextEvent->mEventValue = value; + } + } else if ( pNode->consoleFunction[0] ) { argv[0] = pNode->consoleFunction; @@ -1529,6 +1651,20 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) } else if (pEvent->action == SI_BREAK) { + const Node* button = findNode(pEvent->deviceType, pEvent->deviceInst, + pEvent->modifier, pEvent->objInst); + + if (button != NULL) + { + if (button->flags == Node::Held) + { + if (!button->contextEvent->mBreakEvent) + button->contextEvent->mBreakEvent = true; + + return true; + } + } + return checkBreakTable(pEvent); } else if (pEvent->action == SI_VALUE) @@ -1808,6 +1944,78 @@ void ActionMap::fireBreakEvent( U32 i, F32 fValue ) smBreakTable.erase(i); } +//------------------------------------------------------------------------------ +//Context actions +ContextAction::ContextAction(StringTableEntry func, F32 minHoldTime, ActionMap::Node* button, bool holdOnly) + : mStartTime(0), mEventValue(1.0f), mBreakEvent(false), mDidHold(false), mActive(false), mReturnHoldTime(false) +{ + mButton = button; + mMinHoldTime = minHoldTime; + mConsoleFunctionHeld = func; + + mHoldOnly = holdOnly; +} + +void ContextAction::processTick() +{ + if (mActive) + { + F32 currTime = Sim::getCurrentTime(); + static const char *argv[2]; + + //see if this key even is still active + if (!mBreakEvent) + { + //are we only checking if it's holding? + if (mHoldOnly) + { + //yes, we are, and since it's held, we fire off our function + if (mReturnHoldTime) + { + argv[0] = mConsoleFunctionHeld; + argv[1] = Con::getFloatArg(mEventValue); + argv[2] = Con::getFloatArg((currTime - mStartTime)); + Con::execute(3, argv); + } + else + { + argv[0] = mConsoleFunctionHeld; + argv[1] = Con::getFloatArg(mEventValue); + Con::execute(2, argv); + } + } + //if we don't care if we're just holding, check our time + //have we passed our min limit? + else if ((currTime - mStartTime) >= mMinHoldTime) + { + //holy crap, we have, fire off our hold function + mDidHold = true; + argv[0] = mConsoleFunctionHeld; + argv[1] = Con::getFloatArg(mEventValue); + Con::execute(2, argv); + } + //otherwise we haven't yet, so keep our active status + return; + } + //hmm, apparently not, so see if we tapped the key instead + else + { + if (!mHoldOnly && !mDidHold) + { + //yes, we tapped and we care, so fire off the tap function. + argv[0] = mButton->consoleFunction; + argv[1] = Con::getFloatArg(mEventValue); + Con::execute(2, argv); + } + //otherwise we don't care and we're done, so reset everything + mActive = false; + mStartTime = 0; + mBreakEvent = false; + mDidHold = false; + } + } +} + //------------------------------------------------------------------------------ // Console interop version. @@ -1959,6 +2167,18 @@ DefineEngineMethod( ActionMap, bindCmd, bool, ( const char* device, const char* return object->processBindCmd( device, action, makeCmd, breakCmd ); } +DefineEngineMethod(ActionMap, bindContext, void, (const char* device, const char* action, const char* holdFunction, const char* tapFunction, U32 holdTime), + ("", "", "", "", 0), "actionMap.bindCmd( device, action, holdFunction, tapFunction, holdTime)") +{ + object->processHoldBind(device, action, holdFunction, tapFunction, holdTime, false); +} + +DefineEngineMethod(ActionMap, bindHold, void, (const char* device, const char* action, const char* holdFunction, bool returnHoldTime), + ("", "", "", false), "actionMap.bindCmd( device, action, holdFunction, returnHoldTime)") +{ + object->processHoldBind(device, action, holdFunction, "", 0, true, returnHoldTime); +} + DefineEngineMethod( ActionMap, unbind, bool, ( const char* device, const char* action ),, "@brief Removes the binding on an input device and action.\n" "@param device The device to unbind from. Can be a keyboard, mouse, joystick or a gamepad.\n" diff --git a/Engine/source/sim/actionMap.h b/Engine/source/sim/actionMap.h index 7b3d55261..c2c4cbd72 100644 --- a/Engine/source/sim/actionMap.h +++ b/Engine/source/sim/actionMap.h @@ -32,7 +32,11 @@ #ifndef _SIMBASE_H_ #include "console/simBase.h" #endif +#ifndef _ITICKABLE_H_ +#include "core/iTickable.h" +#endif +class ContextAction; struct InputEventInfo; struct EventDescriptor @@ -48,6 +52,7 @@ struct EventDescriptor class ActionMap : public SimObject { typedef SimObject Parent; + friend class ContextAction; protected: bool onAdd(); @@ -62,7 +67,9 @@ class ActionMap : public SimObject HasDeadZone = BIT(2), ///< Dead zone is present. Inverted = BIT(3), ///< Input is inverted. NonLinear = BIT(4), ///< Input should be re-fit to a non-linear scale - BindCmd = BIT(5) ///< Bind a console command to this. + BindCmd = BIT(5), ///< Bind a console command to this. + Held = BIT(6), + DoubleTap = BIT(7) }; U32 flags; ///< @see Node::Flags @@ -75,6 +82,7 @@ class ActionMap : public SimObject char *makeConsoleCommand; ///< Console command to execute when we make this command. char *breakConsoleCommand; ///< Console command to execute when we break this command. + ContextAction* contextEvent; ///< Event that kicks off via context-keybind actions such as holding or double-tapping }; /// Used to represent a devices. @@ -143,6 +151,7 @@ class ActionMap : public SimObject bool processBind(const U32 argc, const char** argv, SimObject* object = NULL); bool processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd); bool processUnbind(const char *device, const char *action, SimObject* object = NULL); + bool processHoldBind(const char *device, const char *action, const char *holdFunc, const char *tapFunc, const U32 holdTime, const bool holdOnly, const bool returnHoldTime = false); /// @name Console Interface Functions /// @{ @@ -185,4 +194,28 @@ class ActionMap : public SimObject DECLARE_CONOBJECT(ActionMap); }; +class ContextAction : public ITickable +{ + ActionMap::Node* mButton; ///< our button we're holding + F32 mMinHoldTime; ///< minimum time to qualify as 'held'. If we hold less than this, + ///< it's a 'press', otherwise it's a 'held' +public: + F32 mStartTime; ///< Our timestamp when we first pressed. + F32 mEventValue; ///< Event value from our key event. + StringTableEntry mConsoleFunctionHeld; ///< Console function to call with new values if we held over + ///< a certain time. + + bool mHoldOnly; ///< does this only care if we're holding? + ///< true means that it only fires a function while holding + ///< false time-contexts it + bool mBreakEvent; ///< Button is no longer being pressed! + bool mDidHold; ///< did we, at some point in the process, hold the button? + bool mActive; ///< do we be tickin? + bool mReturnHoldTime; ///< Do we return back our time held? + + ContextAction(StringTableEntry func, F32 minHoldTime, ActionMap::Node* button, bool holdOnly); + virtual void processTick(); + virtual void interpolateTick(F32 delta) {} + virtual void advanceTime(F32 timeDelta) {} +}; #endif // _ACTIONMAP_H_ From f9ab317f655849224aa27c9e611b043c4c32b4c8 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 Feb 2018 02:51:28 -0600 Subject: [PATCH 15/16] companion to #2203: corrects a couple backend profile assignment mismatches found with @rextimmys debug spew --- Engine/source/gfx/sim/cubemapData.cpp | 2 +- Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Engine/source/gfx/sim/cubemapData.cpp b/Engine/source/gfx/sim/cubemapData.cpp index 35e921b5b..647615263 100644 --- a/Engine/source/gfx/sim/cubemapData.cpp +++ b/Engine/source/gfx/sim/cubemapData.cpp @@ -111,7 +111,7 @@ void CubemapData::createMap() { if (!mCubeFaceFile[i].isEmpty()) { - if (!mCubeFace[i].set(mCubeFaceFile[i], &GFXStaticTextureSRGBProfile, avar("%s() - mCubeFace[%d] (line %d)", __FUNCTION__, i, __LINE__))) + if (!mCubeFace[i].set(mCubeFaceFile[i], &GFXStaticTextureProfile, avar("%s() - mCubeFace[%d] (line %d)", __FUNCTION__, i, __LINE__))) { Con::errorf("CubemapData::createMap - Failed to load texture '%s'", mCubeFaceFile[i].c_str()); initSuccess = false; diff --git a/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp index b357fc3b5..2e9b6d0aa 100644 --- a/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp +++ b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp @@ -307,22 +307,22 @@ void GuiBitmapButtonCtrl::setBitmap( const String& name ) if( mUseModifiers ) baseName += modifiers[ i ]; - mTextures[ i ].mTextureNormal = GFXTexHandle( baseName, &GFXTexturePersistentSRGBProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); + mTextures[ i ].mTextureNormal = GFXTexHandle( baseName, &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); if( mUseStates ) { if( !mTextures[ i ].mTextureNormal ) - mTextures[ i ].mTextureNormal = GFXTexHandle( baseName + s_n, &GFXTexturePersistentSRGBProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); + mTextures[ i ].mTextureNormal = GFXTexHandle( baseName + s_n, &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); - mTextures[ i ].mTextureHilight = GFXTexHandle( baseName + s_h, &GFXTexturePersistentSRGBProfile, avar("%s() - mTextureHighlight (line %d)", __FUNCTION__, __LINE__)); + mTextures[ i ].mTextureHilight = GFXTexHandle( baseName + s_h, &GFXDefaultGUIProfile, avar("%s() - mTextureHighlight (line %d)", __FUNCTION__, __LINE__)); if( !mTextures[ i ].mTextureHilight ) mTextures[ i ].mTextureHilight = mTextures[ i ].mTextureNormal; - mTextures[ i ].mTextureDepressed = GFXTexHandle( baseName + s_d, &GFXTexturePersistentSRGBProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + mTextures[ i ].mTextureDepressed = GFXTexHandle( baseName + s_d, &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); if( !mTextures[ i ].mTextureDepressed ) mTextures[ i ].mTextureDepressed = mTextures[ i ].mTextureHilight; - mTextures[ i ].mTextureInactive = GFXTexHandle( baseName + s_i, &GFXTexturePersistentSRGBProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__)); + mTextures[ i ].mTextureInactive = GFXTexHandle( baseName + s_i, &GFXDefaultGUIProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__)); if( !mTextures[ i ].mTextureInactive ) mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal; } From 7f043c49e79a01e217143456b51fcf9cf46312e1 Mon Sep 17 00:00:00 2001 From: Areloch Date: Wed, 14 Feb 2018 01:03:25 -0600 Subject: [PATCH 16/16] Updates the basegame template as well. --- .../worldEditor/scripts/editors/creator.ed.cs | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs index 0e2813d57..006031668 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -304,36 +304,6 @@ function EWCreatorWindow::navigate( %this, %address ) %this.addShapeIcon( %obj ); } } - - //Add a separate folder for Game Objects - if(isClass("Entity")) - { - if(%address $= "") - { - %this.addFolderIcon("GameObjects"); - } - else - { - //find all GameObjectAssets - %assetQuery = new AssetQuery(); - if(!AssetDatabase.findAssetType(%assetQuery, "GameObjectAsset")) - return 0; //if we didn't find ANY, just exit - - %count = %assetQuery.getCount(); - - for(%i=0; %i < %count; %i++) - { - %assetId = %assetQuery.getAsset(%i); - - %gameObjectAsset = AssetDatabase.acquireAsset(%assetId); - - if(isFile(%gameObjectAsset.TAMLFilePath)) - { - %this.addGameObjectIcon( %gameObjectAsset.gameObjectName ); - } - } - } - } } if ( %this.tab $= "Meshes" ) @@ -768,22 +738,6 @@ function EWCreatorWindow::addPrefabIcon( %this, %fullPath ) %this.contentCtrl.addGuiControl( %ctrl ); } -function EWCreatorWindow::addGameObjectIcon( %this, %gameObjectName ) -{ - %ctrl = %this.createIcon(); - - %ctrl.altCommand = "spawnGameObject( \"" @ %gameObjectName @ "\", true );"; - %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( "Prefab" ); - %ctrl.text = %gameObjectName; - %ctrl.class = "CreatorGameObjectIconBtn"; - %ctrl.tooltip = "Spawn the " @ %gameObjectName @ " GameObject"; - - %ctrl.buttonType = "radioButton"; - %ctrl.groupNum = "-1"; - - %this.contentCtrl.addGuiControl( %ctrl ); -} - function CreatorPopupMenu::onSelect( %this, %id, %text ) { %split = strreplace( %text, "/", " " );