mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-14 16:14:38 +00:00
Merge branch 'development-gg' into glpboimprovement
Conflicts: Engine/source/gfx/gl/gfxGLTextureManager.cpp
This commit is contained in:
commit
e43a0ba6e9
211 changed files with 18371 additions and 2429 deletions
138
Engine/source/T3D/assets/ComponentAsset.cpp
Normal file
138
Engine/source/T3D/assets/ComponentAsset.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2013 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef COMPONENT_ASSET_H
|
||||||
|
#include "ComponentAsset.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_MANAGER_H_
|
||||||
|
#include "assets/assetManager.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _CONSOLETYPES_H_
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _TAML_
|
||||||
|
#include "persistence/taml/taml.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_PTR_H_
|
||||||
|
#include "assets/assetPtr.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Debug Profiling.
|
||||||
|
#include "platform/profiler.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(ComponentAsset);
|
||||||
|
|
||||||
|
ConsoleType(ComponentAssetPtr, TypeComponentAssetPtr, ComponentAsset, ASSET_ID_FIELD_PREFIX)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleGetType(TypeComponentAssetPtr)
|
||||||
|
{
|
||||||
|
// Fetch asset Id.
|
||||||
|
return (*((AssetPtr<ComponentAsset>*)dptr)).getAssetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleSetType(TypeComponentAssetPtr)
|
||||||
|
{
|
||||||
|
// Was a single argument specified?
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
// Yes, so fetch field value.
|
||||||
|
const char* pFieldValue = argv[0];
|
||||||
|
|
||||||
|
// Fetch asset pointer.
|
||||||
|
AssetPtr<ComponentAsset>* pAssetPtr = dynamic_cast<AssetPtr<ComponentAsset>*>((AssetPtrBase*)(dptr));
|
||||||
|
|
||||||
|
// Is the asset pointer the correct type?
|
||||||
|
if (pAssetPtr == NULL)
|
||||||
|
{
|
||||||
|
// No, so fail.
|
||||||
|
//Con::warnf("(TypeTextureAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set asset.
|
||||||
|
pAssetPtr->setAssetId(pFieldValue);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn.
|
||||||
|
Con::warnf("(TypeTextureAssetPtr) - Cannot set multiple args to a single asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ComponentAsset::ComponentAsset() :
|
||||||
|
mAcquireReferenceCount(0),
|
||||||
|
mpOwningAssetManager(NULL),
|
||||||
|
mAssetInitialized(false)
|
||||||
|
{
|
||||||
|
// Generate an asset definition.
|
||||||
|
mpAssetDefinition = new AssetDefinition();
|
||||||
|
|
||||||
|
mComponentName = StringTable->lookup("");
|
||||||
|
mComponentClass = StringTable->lookup("");
|
||||||
|
mFriendlyName = StringTable->lookup("");
|
||||||
|
mComponentType = StringTable->lookup("");
|
||||||
|
mDescription = StringTable->lookup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ComponentAsset::~ComponentAsset()
|
||||||
|
{
|
||||||
|
// If the asset manager does not own the asset then we own the
|
||||||
|
// asset definition so delete it.
|
||||||
|
if (!getOwned())
|
||||||
|
delete mpAssetDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ComponentAsset::initPersistFields()
|
||||||
|
{
|
||||||
|
// Call parent.
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("componentName", TypeString, Offset(mComponentName, ComponentAsset), "Unique Name of the component. Defines the namespace of the scripts for the component.");
|
||||||
|
addField("componentClass", TypeString, Offset(mComponentClass, ComponentAsset), "Class of object this component uses.");
|
||||||
|
addField("friendlyName", TypeString, Offset(mFriendlyName, ComponentAsset), "The human-readble name for the component.");
|
||||||
|
addField("componentType", TypeString, Offset(mComponentType, ComponentAsset), "The category of the component for organizing in the editor.");
|
||||||
|
addField("description", TypeString, Offset(mDescription, ComponentAsset), "Simple description of the component.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ComponentAsset::copyTo(SimObject* object)
|
||||||
|
{
|
||||||
|
// Call to parent.
|
||||||
|
Parent::copyTo(object);
|
||||||
|
}
|
||||||
83
Engine/source/T3D/assets/ComponentAsset.h
Normal file
83
Engine/source/T3D/assets/ComponentAsset.h
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#pragma once
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2013 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef COMPONENT_ASSET_H
|
||||||
|
#define COMPONENT_ASSET_H
|
||||||
|
|
||||||
|
#ifndef _ASSET_BASE_H_
|
||||||
|
#include "assets/assetBase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_DEFINITION_H_
|
||||||
|
#include "assets/assetDefinition.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _STRINGUNIT_H_
|
||||||
|
#include "string/stringUnit.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_FIELD_TYPES_H_
|
||||||
|
#include "assets/assetFieldTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
class ComponentAsset : public AssetBase
|
||||||
|
{
|
||||||
|
typedef AssetBase Parent;
|
||||||
|
|
||||||
|
AssetManager* mpOwningAssetManager;
|
||||||
|
bool mAssetInitialized;
|
||||||
|
AssetDefinition* mpAssetDefinition;
|
||||||
|
U32 mAcquireReferenceCount;
|
||||||
|
|
||||||
|
StringTableEntry mComponentName;
|
||||||
|
StringTableEntry mComponentClass;
|
||||||
|
StringTableEntry mFriendlyName;
|
||||||
|
StringTableEntry mComponentType;
|
||||||
|
StringTableEntry mDescription;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ComponentAsset();
|
||||||
|
virtual ~ComponentAsset();
|
||||||
|
|
||||||
|
/// Engine.
|
||||||
|
static void initPersistFields();
|
||||||
|
virtual void copyTo(SimObject* object);
|
||||||
|
|
||||||
|
/// Declare Console Object.
|
||||||
|
DECLARE_CONOBJECT(ComponentAsset);
|
||||||
|
|
||||||
|
StringTableEntry getComponentName() { return mComponentName; }
|
||||||
|
StringTableEntry getComponentClass() { return mComponentClass; }
|
||||||
|
StringTableEntry getFriendlyName() { return mFriendlyName; }
|
||||||
|
StringTableEntry getFriendlyType() { return mComponentType; }
|
||||||
|
StringTableEntry getDescription() { return mDescription; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void initializeAsset(void) {}
|
||||||
|
virtual void onAssetRefresh(void) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineConsoleType(TypeComponentAssetPtr, ComponentAsset)
|
||||||
|
|
||||||
|
#endif // _ASSET_BASE_H_
|
||||||
|
|
||||||
134
Engine/source/T3D/assets/GameObjectAsset.cpp
Normal file
134
Engine/source/T3D/assets/GameObjectAsset.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2013 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef GAME_OBJECT_ASSET_H
|
||||||
|
#include "GameObjectAsset.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_MANAGER_H_
|
||||||
|
#include "assets/assetManager.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _CONSOLETYPES_H_
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _TAML_
|
||||||
|
#include "persistence/taml/taml.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_PTR_H_
|
||||||
|
#include "assets/assetPtr.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Debug Profiling.
|
||||||
|
#include "platform/profiler.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(GameObjectAsset);
|
||||||
|
|
||||||
|
ConsoleType(GameObjectAssetPtr, TypeGameObjectAssetPtr, GameObjectAsset, ASSET_ID_FIELD_PREFIX)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleGetType(TypeGameObjectAssetPtr)
|
||||||
|
{
|
||||||
|
// Fetch asset Id.
|
||||||
|
return (*((AssetPtr<GameObjectAsset>*)dptr)).getAssetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleSetType(TypeGameObjectAssetPtr)
|
||||||
|
{
|
||||||
|
// Was a single argument specified?
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
// Yes, so fetch field value.
|
||||||
|
const char* pFieldValue = argv[0];
|
||||||
|
|
||||||
|
// Fetch asset pointer.
|
||||||
|
AssetPtr<GameObjectAsset>* pAssetPtr = dynamic_cast<AssetPtr<GameObjectAsset>*>((AssetPtrBase*)(dptr));
|
||||||
|
|
||||||
|
// Is the asset pointer the correct type?
|
||||||
|
if (pAssetPtr == NULL)
|
||||||
|
{
|
||||||
|
// No, so fail.
|
||||||
|
//Con::warnf("(TypeTextureAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set asset.
|
||||||
|
pAssetPtr->setAssetId(pFieldValue);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn.
|
||||||
|
Con::warnf("(TypeTextureAssetPtr) - Cannot set multiple args to a single asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
GameObjectAsset::GameObjectAsset() :
|
||||||
|
mAcquireReferenceCount(0),
|
||||||
|
mpOwningAssetManager(NULL),
|
||||||
|
mAssetInitialized(false)
|
||||||
|
{
|
||||||
|
// Generate an asset definition.
|
||||||
|
mpAssetDefinition = new AssetDefinition();
|
||||||
|
|
||||||
|
mGameObjectName = StringTable->lookup("");
|
||||||
|
mScriptFilePath = StringTable->lookup("");
|
||||||
|
mTAMLFilePath = StringTable->lookup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
GameObjectAsset::~GameObjectAsset()
|
||||||
|
{
|
||||||
|
// If the asset manager does not own the asset then we own the
|
||||||
|
// asset definition so delete it.
|
||||||
|
if (!getOwned())
|
||||||
|
delete mpAssetDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void GameObjectAsset::initPersistFields()
|
||||||
|
{
|
||||||
|
// Call parent.
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("gameObjectName", TypeString, Offset(mGameObjectName, GameObjectAsset), "Name of the game object. Defines the created object's class.");
|
||||||
|
addField("scriptFilePath", TypeString, Offset(mScriptFilePath, GameObjectAsset), "Path to the script file for the GameObject's script code.");
|
||||||
|
addField("TAMLFilePath", TypeString, Offset(mTAMLFilePath, GameObjectAsset), "Path to the taml file for the GameObject's heirarchy.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void GameObjectAsset::copyTo(SimObject* object)
|
||||||
|
{
|
||||||
|
// Call to parent.
|
||||||
|
Parent::copyTo(object);
|
||||||
|
}
|
||||||
75
Engine/source/T3D/assets/GameObjectAsset.h
Normal file
75
Engine/source/T3D/assets/GameObjectAsset.h
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2013 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef GAME_OBJECT_ASSET_H
|
||||||
|
#define GAME_OBJECT_ASSET_H
|
||||||
|
|
||||||
|
#ifndef _ASSET_BASE_H_
|
||||||
|
#include "assets/assetBase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_DEFINITION_H_
|
||||||
|
#include "assets/assetDefinition.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _STRINGUNIT_H_
|
||||||
|
#include "string/stringUnit.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_FIELD_TYPES_H_
|
||||||
|
#include "assets/assetFieldTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
class GameObjectAsset : public AssetBase
|
||||||
|
{
|
||||||
|
typedef AssetBase Parent;
|
||||||
|
|
||||||
|
AssetManager* mpOwningAssetManager;
|
||||||
|
bool mAssetInitialized;
|
||||||
|
AssetDefinition* mpAssetDefinition;
|
||||||
|
U32 mAcquireReferenceCount;
|
||||||
|
|
||||||
|
StringTableEntry mGameObjectName;
|
||||||
|
StringTableEntry mScriptFilePath;
|
||||||
|
StringTableEntry mTAMLFilePath;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameObjectAsset();
|
||||||
|
virtual ~GameObjectAsset();
|
||||||
|
|
||||||
|
/// Engine.
|
||||||
|
static void initPersistFields();
|
||||||
|
virtual void copyTo(SimObject* object);
|
||||||
|
|
||||||
|
/// Declare Console Object.
|
||||||
|
DECLARE_CONOBJECT(GameObjectAsset);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void initializeAsset(void) {}
|
||||||
|
virtual void onAssetRefresh(void) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineConsoleType(TypeGameObjectAssetPtr, GameObjectAsset)
|
||||||
|
|
||||||
|
#endif // _ASSET_BASE_H_
|
||||||
|
|
||||||
|
|
@ -100,8 +100,6 @@ mAcquireReferenceCount(0),
|
||||||
mpOwningAssetManager(NULL),
|
mpOwningAssetManager(NULL),
|
||||||
mAssetInitialized(false)
|
mAssetInitialized(false)
|
||||||
{
|
{
|
||||||
// Generate an asset definition.
|
|
||||||
mpAssetDefinition = new AssetDefinition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -155,3 +153,7 @@ void ShapeAsset::copyTo(SimObject* object)
|
||||||
// Call to parent.
|
// Call to parent.
|
||||||
Parent::copyTo(object);
|
Parent::copyTo(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeAsset::onAssetRefresh(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#ifndef _SHAPE_ASSET_H_
|
#ifndef SHAPE_ASSET_H
|
||||||
#define _SHAPE_ASSET_H_
|
#define SHAPE_ASSET_H
|
||||||
|
|
||||||
#ifndef _ASSET_BASE_H_
|
#ifndef _ASSET_BASE_H_
|
||||||
#include "assets/assetBase.h"
|
#include "assets/assetBase.h"
|
||||||
|
|
@ -76,11 +76,13 @@ public:
|
||||||
|
|
||||||
TSShape* getShape() { return mShape; }
|
TSShape* getShape() { return mShape; }
|
||||||
|
|
||||||
|
Resource<TSShape> getShapeResource() { return mShape; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onAssetRefresh(void) {}
|
virtual void onAssetRefresh(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
DefineConsoleType(TypeShapeAssetPtr, ShapeAsset)
|
DefineConsoleType(TypeShapeAssetPtr, ShapeAsset)
|
||||||
|
|
||||||
#endif // _ASSET_BASE_H_
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
717
Engine/source/T3D/components/animation/animationComponent.cpp
Normal file
717
Engine/source/T3D/components/animation/animationComponent.cpp
Normal file
|
|
@ -0,0 +1,717 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/animation/animationComponent.h"
|
||||||
|
#include "T3D/components/animation/animationComponent_ScriptBinding.h"
|
||||||
|
#include "T3D/components/render/meshComponent.h"
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
#include "gfx/sim/debugDraw.h"
|
||||||
|
|
||||||
|
extern bool gEditingMission;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Callbacks
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
IMPLEMENT_CALLBACK( AnimationComponent, onAnimationStart, void, ( Component* obj, const String& animName ), ( obj, animName ),
|
||||||
|
"@brief Called when we collide with another object.\n\n"
|
||||||
|
"@param obj The ShapeBase object\n"
|
||||||
|
"@param collObj The object we collided with\n"
|
||||||
|
"@param vec Collision impact vector\n"
|
||||||
|
"@param len Length of the impact vector\n" );
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK(AnimationComponent, onAnimationEnd, void, (Component* obj, const char* animName), (obj, animName),
|
||||||
|
"@brief Called when we collide with another object.\n\n"
|
||||||
|
"@param obj The ShapeBase object\n"
|
||||||
|
"@param collObj The object we collided with\n"
|
||||||
|
"@param vec Collision impact vector\n"
|
||||||
|
"@param len Length of the impact vector\n" );
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK(AnimationComponent, onAnimationTrigger, void, (Component* obj, const String& animName, S32 triggerID), (obj, animName, triggerID),
|
||||||
|
"@brief Called when we collide with another object.\n\n"
|
||||||
|
"@param obj The ShapeBase object\n"
|
||||||
|
"@param collObj The object we collided with\n"
|
||||||
|
"@param vec Collision impact vector\n"
|
||||||
|
"@param len Length of the impact vector\n" );
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
AnimationComponent::AnimationComponent() : Component()
|
||||||
|
{
|
||||||
|
mNetworked = true;
|
||||||
|
mNetFlags.set(Ghostable | ScopeAlways);
|
||||||
|
|
||||||
|
mFriendlyName = "Animation(Component)";
|
||||||
|
mComponentType = "Render";
|
||||||
|
|
||||||
|
mDescription = getDescriptionText("Allows a rendered mesh to be animated");
|
||||||
|
|
||||||
|
mOwnerRenderInst = NULL;
|
||||||
|
|
||||||
|
mOwnerShapeInstance = NULL;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
mAnimationThreads[i].sequence = -1;
|
||||||
|
mAnimationThreads[i].thread = 0;
|
||||||
|
mAnimationThreads[i].sound = 0;
|
||||||
|
mAnimationThreads[i].state = Thread::Stop;
|
||||||
|
mAnimationThreads[i].atEnd = false;
|
||||||
|
mAnimationThreads[i].timescale = 1.f;
|
||||||
|
mAnimationThreads[i].position = -1.f;
|
||||||
|
mAnimationThreads[i].transition = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationComponent::~AnimationComponent()
|
||||||
|
{
|
||||||
|
for(S32 i = 0;i < mFields.size();++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(AnimationComponent);
|
||||||
|
|
||||||
|
bool AnimationComponent::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//we need at least one layer
|
||||||
|
for (U32 i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[i];
|
||||||
|
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
// TG: Need to see about suppressing non-cyclic sounds
|
||||||
|
// if the sequences were activated before the object was
|
||||||
|
// ghosted.
|
||||||
|
// TG: Cyclic animations need to have a random pos if
|
||||||
|
// they were started before the object was ghosted.
|
||||||
|
|
||||||
|
// If there was something running on the old shape, the thread
|
||||||
|
// needs to be reset. Otherwise we assume that it's been
|
||||||
|
// initialized either by the constructor or from the server.
|
||||||
|
bool reset = st.thread != 0;
|
||||||
|
st.thread = 0;
|
||||||
|
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
setThreadSequence(i, st.sequence, reset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.thread)
|
||||||
|
updateThread(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
|
||||||
|
mOwnerRenderInst = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
//test if this is a shape component!
|
||||||
|
RenderComponentInterface *shapeInstanceInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||||
|
if (shapeInstanceInterface)
|
||||||
|
{
|
||||||
|
shapeInstanceInterface->onShapeInstanceChanged.notify(this, &AnimationComponent::targetShapeChanged);
|
||||||
|
targetShapeChanged(shapeInstanceInterface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
RenderComponentInterface *shapeInstanceInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||||
|
if (shapeInstanceInterface)
|
||||||
|
{
|
||||||
|
shapeInstanceInterface->onShapeInstanceChanged.notify(this, &AnimationComponent::targetShapeChanged);
|
||||||
|
targetShapeChanged(shapeInstanceInterface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId()) //?????????
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
RenderComponentInterface *shapeInstanceInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||||
|
if (shapeInstanceInterface)
|
||||||
|
{
|
||||||
|
shapeInstanceInterface->onShapeInstanceChanged.remove(this, &AnimationComponent::targetShapeChanged);
|
||||||
|
mOwnerRenderInst = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
|
||||||
|
{
|
||||||
|
mOwnerRenderInst = instanceInterface;
|
||||||
|
|
||||||
|
if (!mOwnerRenderInst || !getShape())
|
||||||
|
return;
|
||||||
|
|
||||||
|
MeshComponent* meshComp = dynamic_cast<MeshComponent*>(mOwnerRenderInst);
|
||||||
|
|
||||||
|
mOwnerShapeInstance = meshComp->getShapeInstance();
|
||||||
|
|
||||||
|
if (!mOwnerShapeInstance)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[i];
|
||||||
|
|
||||||
|
st.thread = mOwnerShapeInstance->addThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 AnimationComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
//early test if we lack an owner, ghost-wise
|
||||||
|
//no point in trying, just re-queue the mask and go
|
||||||
|
if (!mOwner || con->getGhostIndex(mOwner) == -1)
|
||||||
|
{
|
||||||
|
stream->writeFlag(false);
|
||||||
|
return retMask |= ThreadMask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream->writeFlag(true);
|
||||||
|
|
||||||
|
for (int i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[i];
|
||||||
|
if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) )
|
||||||
|
{
|
||||||
|
stream->writeInt(st.sequence,ThreadSequenceBits);
|
||||||
|
stream->writeInt(st.state,2);
|
||||||
|
stream->write(st.timescale);
|
||||||
|
stream->write(st.position);
|
||||||
|
stream->writeFlag(st.atEnd);
|
||||||
|
stream->writeFlag(st.transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[i];
|
||||||
|
U32 seq = stream->readInt(ThreadSequenceBits);
|
||||||
|
st.state = stream->readInt(2);
|
||||||
|
stream->read( &st.timescale );
|
||||||
|
stream->read( &st.position );
|
||||||
|
st.atEnd = stream->readFlag();
|
||||||
|
bool transition = stream->readFlag();
|
||||||
|
|
||||||
|
if (!st.thread || st.sequence != seq && st.state != Thread::Destroy)
|
||||||
|
setThreadSequence(i, seq, false, transition);
|
||||||
|
else
|
||||||
|
updateThread(st);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AnimationComponent::processTick()
|
||||||
|
{
|
||||||
|
Parent::processTick();
|
||||||
|
|
||||||
|
if (!isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
// Server only...
|
||||||
|
advanceThreads(TickSec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::advanceTime(F32 dt)
|
||||||
|
{
|
||||||
|
Parent::advanceTime(dt);
|
||||||
|
|
||||||
|
// On the client, the shape threads and images are
|
||||||
|
// advanced at framerate.
|
||||||
|
advanceThreads(dt);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
const char *AnimationComponent::getThreadSequenceName(U32 slot)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence == -1)
|
||||||
|
{
|
||||||
|
// Invalid Animation.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name Index
|
||||||
|
TSShape* shape = getShape();
|
||||||
|
|
||||||
|
if (shape)
|
||||||
|
{
|
||||||
|
const U32 nameIndex = shape->sequences[st.sequence].nameIndex;
|
||||||
|
|
||||||
|
// Return Name.
|
||||||
|
return shape->getName(nameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::setThreadSequence(U32 slot, S32 seq, bool reset, bool transition, F32 transTime)
|
||||||
|
{
|
||||||
|
if (!mOwnerShapeInstance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.thread && st.sequence == seq && st.state == Thread::Play && !reset)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Handle a -1 sequence, as this may be set when a thread has been destroyed.
|
||||||
|
if (seq == -1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (seq < MaxSequenceIndex)
|
||||||
|
{
|
||||||
|
setMaskBits(-1);
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.sequence = seq;
|
||||||
|
st.transition = transition;
|
||||||
|
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
st.state = Thread::Play;
|
||||||
|
st.atEnd = false;
|
||||||
|
st.timescale = 1.f;
|
||||||
|
st.position = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOwnerShapeInstance)
|
||||||
|
{
|
||||||
|
if (!st.thread)
|
||||||
|
st.thread = mOwnerShapeInstance->addThread();
|
||||||
|
|
||||||
|
if (transition)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->transitionToSequence(st.thread, seq, st.position, transTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->setSequence(st.thread, seq, 0);
|
||||||
|
stopThreadSound(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateThread(st);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 AnimationComponent::getThreadSequenceID(S32 slot)
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
return mAnimationThreads[slot].sequence;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::updateThread(Thread& st)
|
||||||
|
{
|
||||||
|
if (!mOwnerRenderInst)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (st.state)
|
||||||
|
{
|
||||||
|
case Thread::Stop:
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
|
||||||
|
mOwnerShapeInstance->setPos(st.thread, (st.timescale > 0.f) ? 0.0f : 1.0f);
|
||||||
|
} // Drop through to pause state
|
||||||
|
|
||||||
|
case Thread::Pause:
|
||||||
|
{
|
||||||
|
if (st.position != -1.f)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
|
||||||
|
mOwnerShapeInstance->setPos(st.thread, st.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 0.f);
|
||||||
|
stopThreadSound(st);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Thread::Play:
|
||||||
|
{
|
||||||
|
if (st.atEnd)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 1);
|
||||||
|
mOwnerShapeInstance->setPos(st.thread, (st.timescale > 0.f) ? 1.0f : 0.0f);
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 0);
|
||||||
|
stopThreadSound(st);
|
||||||
|
st.state = Thread::Stop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (st.position != -1.f)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
|
||||||
|
mOwnerShapeInstance->setPos(st.thread, st.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwnerShapeInstance->setTimeScale(st.thread, st.timescale);
|
||||||
|
if (!st.sound)
|
||||||
|
{
|
||||||
|
startSequenceSound(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Thread::Destroy:
|
||||||
|
{
|
||||||
|
stopThreadSound(st);
|
||||||
|
st.atEnd = true;
|
||||||
|
st.sequence = -1;
|
||||||
|
if (st.thread)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->destroyThread(st.thread);
|
||||||
|
st.thread = 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::stopThread(U32 slot)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1 && st.state != Thread::Stop)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.state = Thread::Stop;
|
||||||
|
updateThread(st);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::destroyThread(U32 slot)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1 && st.state != Thread::Destroy)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.state = Thread::Destroy;
|
||||||
|
updateThread(st);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::pauseThread(U32 slot)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1 && st.state != Thread::Pause)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.state = Thread::Pause;
|
||||||
|
updateThread(st);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::playThread(U32 slot)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1 && st.state != Thread::Play)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.state = Thread::Play;
|
||||||
|
updateThread(st);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::playThread(U32 slot, const char* name, bool transition, F32 transitionTime)
|
||||||
|
{
|
||||||
|
if (slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (!dStrEqual(name, ""))
|
||||||
|
{
|
||||||
|
if (TSShape* shape = getShape())
|
||||||
|
{
|
||||||
|
S32 seq = shape->findSequence(name);
|
||||||
|
if (seq != -1 && setThreadSequence(slot, seq, true, transition, transitionTime))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (seq == -1)
|
||||||
|
{
|
||||||
|
//We tried to play a non-existaint sequence, so stop the thread just in case
|
||||||
|
destroyThread(slot);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (playThread(slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::setThreadAnimation(U32 slot, const char* name)
|
||||||
|
{
|
||||||
|
if (slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (!dStrEqual(name, ""))
|
||||||
|
{
|
||||||
|
if (TSShape* shape = getShape())
|
||||||
|
{
|
||||||
|
S32 seq = shape->findSequence(name);
|
||||||
|
if (seq != -1 && setThreadSequence(slot, seq, false, false))
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.position == -1)
|
||||||
|
st.position = 0;
|
||||||
|
//st.state = Thread::Pause;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (seq == -1)
|
||||||
|
{
|
||||||
|
//We tried to play a non-existaint sequence, so stop the thread just in case
|
||||||
|
destroyThread(slot);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (playThread(slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::setThreadPosition(U32 slot, F32 pos)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.position = pos;
|
||||||
|
st.atEnd = false;
|
||||||
|
updateThread(st);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::setThreadDir(U32 slot, bool forward)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
if ((st.timescale >= 0.f) != forward)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.timescale *= -1.f;
|
||||||
|
st.atEnd = false;
|
||||||
|
updateThread(st);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationComponent::setThreadTimeScale(U32 slot, F32 timeScale)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[slot];
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
if (st.timescale != timeScale)
|
||||||
|
{
|
||||||
|
setMaskBits(ThreadMaskN << slot);
|
||||||
|
st.timescale = timeScale;
|
||||||
|
updateThread(st);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::stopThreadSound(Thread& thread)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::startSequenceSound(Thread& thread)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationComponent::advanceThreads(F32 dt)
|
||||||
|
{
|
||||||
|
if (!mOwnerRenderInst)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < MaxScriptThreads; i++)
|
||||||
|
{
|
||||||
|
Thread& st = mAnimationThreads[i];
|
||||||
|
if (st.thread && st.sequence != -1)
|
||||||
|
{
|
||||||
|
bool cyclic = getShape()->sequences[st.sequence].isCyclic();
|
||||||
|
|
||||||
|
if (!getShape()->sequences[st.sequence].isCyclic() &&
|
||||||
|
!st.atEnd &&
|
||||||
|
((st.timescale > 0.f) ? mOwnerShapeInstance->getPos(st.thread) >= 1.0 : mOwnerShapeInstance->getPos(st.thread) <= 0))
|
||||||
|
{
|
||||||
|
st.atEnd = true;
|
||||||
|
updateThread(st);
|
||||||
|
|
||||||
|
if (!isGhost())
|
||||||
|
{
|
||||||
|
Con::executef(this, "onAnimationEnd", st.thread->getSequenceName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the thread is still valid after the call to onEndSequence_callback().
|
||||||
|
// Someone could have called destroyThread() while in there.
|
||||||
|
if (st.thread)
|
||||||
|
{
|
||||||
|
mOwnerShapeInstance->advanceTime(dt, st.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOwnerShapeInstance && !isGhost())
|
||||||
|
{
|
||||||
|
for (U32 i = 1; i < 32; i++)
|
||||||
|
{
|
||||||
|
if (mOwnerShapeInstance->getTriggerState(i))
|
||||||
|
{
|
||||||
|
const char* animName = st.thread->getSequenceName().c_str();
|
||||||
|
onAnimationTrigger_callback(this, animName, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGhost())
|
||||||
|
mOwnerShapeInstance->animate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TSShape* AnimationComponent::getShape()
|
||||||
|
{
|
||||||
|
if (mOwner == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (mOwnerRenderInst == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return mOwnerRenderInst->getShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 AnimationComponent::getAnimationCount()
|
||||||
|
{
|
||||||
|
if (getShape())
|
||||||
|
return getShape()->sequences.size();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 AnimationComponent::getAnimationIndex(const char* name)
|
||||||
|
{
|
||||||
|
if (getShape())
|
||||||
|
return getShape()->findSequence(name);
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* AnimationComponent::getAnimationName(S32 index)
|
||||||
|
{
|
||||||
|
if (getShape())
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < getShape()->sequences.size())
|
||||||
|
return getShape()->getName(getShape()->sequences[index].nameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
138
Engine/source/T3D/components/animation/animationComponent.h
Normal file
138
Engine/source/T3D/components/animation/animationComponent.h
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ANIMATION_COMPONENT_H
|
||||||
|
#define ANIMATION_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/render/renderComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SceneRenderState;
|
||||||
|
|
||||||
|
class AnimationComponent : public Component
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
public:
|
||||||
|
enum PublicConstants {
|
||||||
|
ThreadSequenceBits = 6,
|
||||||
|
MaxSequenceIndex = (1 << ThreadSequenceBits) - 1,
|
||||||
|
MaxScriptThreads = 16, ///< Should be a power of 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MaskBits {
|
||||||
|
ThreadMaskN = Parent::NextFreeMask << 0,
|
||||||
|
ThreadMask = (ThreadMaskN << MaxScriptThreads) - ThreadMaskN,
|
||||||
|
NextFreeMask = ThreadMaskN << MaxScriptThreads
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct Thread
|
||||||
|
{
|
||||||
|
/// State of the animation thread.
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
Play, Stop, Pause, Destroy
|
||||||
|
};
|
||||||
|
TSThread* thread; ///< Pointer to 3space data.
|
||||||
|
U32 state; ///< State of the thread
|
||||||
|
///
|
||||||
|
/// @see Thread::State
|
||||||
|
S32 sequence; ///< The animation sequence which is running in this thread.
|
||||||
|
F32 timescale; ///< Timescale
|
||||||
|
U32 sound; ///< Handle to sound.
|
||||||
|
bool atEnd; ///< Are we at the end of this thread?
|
||||||
|
F32 position;
|
||||||
|
bool transition;
|
||||||
|
};
|
||||||
|
|
||||||
|
Thread mAnimationThreads[MaxScriptThreads];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RenderComponentInterface * mOwnerRenderInst;
|
||||||
|
|
||||||
|
TSShapeInstance *mOwnerShapeInstance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimationComponent();
|
||||||
|
virtual ~AnimationComponent();
|
||||||
|
DECLARE_CONOBJECT(AnimationComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
TSShape* getShape();
|
||||||
|
|
||||||
|
void targetShapeChanged(RenderComponentInterface* instanceInterface);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
virtual void advanceTime(F32 dt);
|
||||||
|
|
||||||
|
const char *getThreadSequenceName(U32 slot);
|
||||||
|
bool setThreadSequence(U32 slot, S32 seq, bool reset = true, bool transition = true, F32 transitionTime = 0.5);
|
||||||
|
void updateThread(Thread& st);
|
||||||
|
bool stopThread(U32 slot);
|
||||||
|
bool destroyThread(U32 slot);
|
||||||
|
bool pauseThread(U32 slot);
|
||||||
|
bool playThread(U32 slot);
|
||||||
|
bool playThread(U32 slot, const char* name, bool transition, F32 transitionTime);
|
||||||
|
bool setThreadAnimation(U32 slot, const char* name);
|
||||||
|
bool setThreadPosition(U32 slot, F32 pos);
|
||||||
|
bool setThreadDir(U32 slot, bool forward);
|
||||||
|
bool setThreadTimeScale(U32 slot, F32 timeScale);
|
||||||
|
void stopThreadSound(Thread& thread);
|
||||||
|
void startSequenceSound(Thread& thread);
|
||||||
|
void advanceThreads(F32 dt);
|
||||||
|
|
||||||
|
S32 getThreadSequenceID(S32 slot);
|
||||||
|
|
||||||
|
//other helper functions
|
||||||
|
S32 getAnimationCount();
|
||||||
|
S32 getAnimationIndex(const char* name);
|
||||||
|
const char* getAnimationName(S32 index);
|
||||||
|
|
||||||
|
//callbacks
|
||||||
|
DECLARE_CALLBACK(void, onAnimationStart, (Component* obj, const String& animName));
|
||||||
|
DECLARE_CALLBACK(void, onAnimationEnd, (Component* obj, const char* animName));
|
||||||
|
DECLARE_CALLBACK(void, onAnimationTrigger, (Component* obj, const String& animName, S32 triggerID));
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_ANIMATION_COMPONENT_H
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/components/animation/animationComponent.h"
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, playThread, bool, (S32 slot, const char* name, bool transition, F32 transitionTime), (-1, "", true, 0.5),
|
||||||
|
"@brief Start a new animation thread, or restart one that has been paused or "
|
||||||
|
"stopped.\n\n"
|
||||||
|
|
||||||
|
"@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = AnimationComponent::MaxScriptThreads-1
|
||||||
|
"@param name name of the animation sequence to play in this slot. If not "
|
||||||
|
"specified, the paused or stopped thread in this slot will be resumed.\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@tsexample\n"
|
||||||
|
"%obj.playThread( 0, \"ambient\" ); // Play the ambient sequence in slot 0\n"
|
||||||
|
"%obj.setThreadTimeScale( 0, 0.5 ); // Play at half-speed\n"
|
||||||
|
"%obj.pauseThread( 0 ); // Pause the sequence\n"
|
||||||
|
"%obj.playThread( 0 ); // Resume playback\n"
|
||||||
|
"%obj.playThread( 0, \"spin\" ); // Replace the sequence in slot 0\n"
|
||||||
|
"@endtsexample\n"
|
||||||
|
|
||||||
|
"@see pauseThread()\n"
|
||||||
|
"@see stopThread()\n"
|
||||||
|
"@see setThreadDir()\n"
|
||||||
|
"@see setThreadTimeScale()\n"
|
||||||
|
"@see destroyThread()\n")
|
||||||
|
{
|
||||||
|
return object->playThread(slot, name, transition, transitionTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, setThreadDir, bool, (S32 slot, bool fwd), ,
|
||||||
|
"@brief Set the playback direction of an animation thread.\n\n"
|
||||||
|
|
||||||
|
"@param slot thread slot to modify\n"
|
||||||
|
"@param fwd true to play the animation forwards, false to play backwards\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread()\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->setThreadDir(slot, fwd))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, setThreadTimeScale, bool, (S32 slot, F32 scale), ,
|
||||||
|
"@brief Set the playback time scale of an animation thread.\n\n"
|
||||||
|
|
||||||
|
"@param slot thread slot to modify\n"
|
||||||
|
"@param scale new thread time scale (1=normal speed, 0.5=half speed etc)\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->setThreadTimeScale(slot, scale))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, setThreadPosition, bool, (S32 slot, F32 pos), ,
|
||||||
|
"@brief Set the position within an animation thread.\n\n"
|
||||||
|
|
||||||
|
"@param slot thread slot to modify\n"
|
||||||
|
"@param pos position within thread\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->setThreadPosition(slot, pos))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, setThreadAnimation, bool, (S32 slot, const char* name), (""),
|
||||||
|
"@brief Force-sets the animation in a particular thread without starting it playing."
|
||||||
|
|
||||||
|
"@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = AnimationComponent::MaxScriptThreads-1
|
||||||
|
"@param name name of the animation sequence to play in this slot. If not "
|
||||||
|
"specified, the paused or stopped thread in this slot will be resumed.\n"
|
||||||
|
"@return true if successful, false if failed\n\n")
|
||||||
|
{
|
||||||
|
return object->setThreadAnimation(slot, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, getThreadAnimation, String, (S32 slot), ,
|
||||||
|
"@brief Force-sets the animation in a particular thread without starting it playing."
|
||||||
|
|
||||||
|
"@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = AnimationComponent::MaxScriptThreads-1
|
||||||
|
"@param name name of the animation sequence to play in this slot. If not "
|
||||||
|
"specified, the paused or stopped thread in this slot will be resumed.\n"
|
||||||
|
"@return true if successful, false if failed\n\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (TSShape* shape = object->getShape())
|
||||||
|
{
|
||||||
|
S32 seq = object->getThreadSequenceID(slot);
|
||||||
|
if (seq != -1)
|
||||||
|
{
|
||||||
|
String animationName = object->getAnimationName(seq);
|
||||||
|
return animationName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, stopThread, bool, (S32 slot), ,
|
||||||
|
"@brief Stop an animation thread.\n\n"
|
||||||
|
|
||||||
|
"If restarted using playThread, the animation "
|
||||||
|
"will start from the beginning again.\n"
|
||||||
|
"@param slot thread slot to stop\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->stopThread(slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, destroyThread, bool, (S32 slot), ,
|
||||||
|
"@brief Destroy an animation thread, which prevents it from playing.\n\n"
|
||||||
|
|
||||||
|
"@param slot thread slot to destroy\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->destroyThread(slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, pauseThread, bool, (S32 slot), ,
|
||||||
|
"@brief Pause an animation thread.\n\n"
|
||||||
|
|
||||||
|
"If restarted using playThread, the animation "
|
||||||
|
"will resume from the paused position.\n"
|
||||||
|
"@param slot thread slot to stop\n"
|
||||||
|
"@return true if successful, false if failed\n\n"
|
||||||
|
|
||||||
|
"@see playThread\n")
|
||||||
|
{
|
||||||
|
if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
|
||||||
|
{
|
||||||
|
if (object->pauseThread(slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, getAnimationCount, S32, (), ,
|
||||||
|
"Get the total number of sequences in the shape.\n"
|
||||||
|
"@return the number of sequences in the shape\n\n")
|
||||||
|
{
|
||||||
|
return object->getAnimationCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, getAnimationIndex, S32, (const char* name), ,
|
||||||
|
"Find the index of the sequence with the given name.\n"
|
||||||
|
"@param name name of the sequence to lookup\n"
|
||||||
|
"@return index of the sequence with matching name, or -1 if not found\n\n"
|
||||||
|
"@tsexample\n"
|
||||||
|
"// Check if a given sequence exists in the shape\n"
|
||||||
|
"if ( %this.getSequenceIndex( \"walk\" ) == -1 )\n"
|
||||||
|
" echo( \"Could not find 'walk' sequence\" );\n"
|
||||||
|
"@endtsexample\n")
|
||||||
|
{
|
||||||
|
return object->getAnimationIndex(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AnimationComponent, getAnimationName, const char*, (S32 index), ,
|
||||||
|
"Get the name of the indexed sequence.\n"
|
||||||
|
"@param index index of the sequence to query (valid range is 0 - getSequenceCount()-1)\n"
|
||||||
|
"@return the name of the sequence\n\n"
|
||||||
|
"@tsexample\n"
|
||||||
|
"// print the name of all sequences in the shape\n"
|
||||||
|
"%count = %this.getSequenceCount();\n"
|
||||||
|
"for ( %i = 0; %i < %count; %i++ )\n"
|
||||||
|
" echo( %i SPC %this.getSequenceName( %i ) );\n"
|
||||||
|
"@endtsexample\n")
|
||||||
|
{
|
||||||
|
return object->getAnimationName(index);
|
||||||
|
}
|
||||||
483
Engine/source/T3D/components/camera/cameraComponent.cpp
Normal file
483
Engine/source/T3D/components/camera/cameraComponent.cpp
Normal file
|
|
@ -0,0 +1,483 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/camera/cameraComponent.h"
|
||||||
|
#include "T3D/components/camera/cameraComponent_ScriptBinding.h"
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "T3D/gameFunctions.h"
|
||||||
|
#include "math/mathUtils.h"
|
||||||
|
#include "T3D/components/render/renderComponentInterface.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( CameraComponent, validateCameraFov, F32, (F32 fov), (fov),
|
||||||
|
"@brief Called on the server when the client has requested a FOV change.\n\n"
|
||||||
|
|
||||||
|
"When the client requests that its field of view should be changed (because "
|
||||||
|
"they want to use a sniper scope, for example) this new FOV needs to be validated "
|
||||||
|
"by the server. This method is called if it exists (it is optional) to validate "
|
||||||
|
"the requested FOV, and modify it if necessary. This could be as simple as checking "
|
||||||
|
"that the FOV falls within a correct range, to making sure that the FOV matches the "
|
||||||
|
"capabilities of the current weapon.\n\n"
|
||||||
|
|
||||||
|
"Following this method, ShapeBase ensures that the given FOV still falls within "
|
||||||
|
"the datablock's mCameraMinFov and mCameraMaxFov. If that is good enough for your "
|
||||||
|
"purposes, then you do not need to define the validateCameraFov() callback for "
|
||||||
|
"your ShapeBase.\n\n"
|
||||||
|
|
||||||
|
"@param fov The FOV that has been requested by the client.\n"
|
||||||
|
"@return The FOV as validated by the server.\n\n"
|
||||||
|
|
||||||
|
"@see ShapeBaseData\n\n");
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CameraComponent::CameraComponent() : Component()
|
||||||
|
{
|
||||||
|
mClientScreen = Point2F(1, 1);
|
||||||
|
|
||||||
|
mCameraFov = mCameraDefaultFov = 80;
|
||||||
|
mCameraMinFov = 5;
|
||||||
|
mCameraMaxFov = 175;
|
||||||
|
|
||||||
|
mTargetNodeIdx = -1;
|
||||||
|
|
||||||
|
mPosOffset = Point3F(0, 0, 0);
|
||||||
|
mRotOffset = EulerF(0, 0, 0);
|
||||||
|
|
||||||
|
mTargetNode = "";
|
||||||
|
|
||||||
|
mUseParentTransform = true;
|
||||||
|
|
||||||
|
mFriendlyName = "Camera(Component)";
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraComponent::~CameraComponent()
|
||||||
|
{
|
||||||
|
for(S32 i = 0;i < mFields.size();++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(CameraComponent);
|
||||||
|
|
||||||
|
bool CameraComponent::onAdd()
|
||||||
|
{
|
||||||
|
if(! Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addProtectedField("FOV", TypeF32, Offset(mCameraFov, CameraComponent), &_setCameraFov, defaultProtectedGetFn, "");
|
||||||
|
|
||||||
|
addField("MinFOV", TypeF32, Offset(mCameraMinFov, CameraComponent), "");
|
||||||
|
|
||||||
|
addField("MaxFOV", TypeF32, Offset(mCameraMaxFov, CameraComponent), "");
|
||||||
|
|
||||||
|
addField("ScreenAspect", TypePoint2I, Offset(mClientScreen, CameraComponent), "");
|
||||||
|
|
||||||
|
addProtectedField("targetNode", TypeString, Offset(mTargetNode, CameraComponent), &_setNode, defaultProtectedGetFn, "");
|
||||||
|
|
||||||
|
addProtectedField("positionOffset", TypePoint3F, Offset(mPosOffset, CameraComponent), &_setPosOffset, defaultProtectedGetFn, "");
|
||||||
|
|
||||||
|
addProtectedField("rotationOffset", TypeRotationF, Offset(mRotOffset, CameraComponent), &_setRotOffset, defaultProtectedGetFn, "");
|
||||||
|
|
||||||
|
addField("useParentTransform", TypeBool, Offset(mUseParentTransform, CameraComponent), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::_setNode(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
CameraComponent *mcc = static_cast<CameraComponent*>(object);
|
||||||
|
|
||||||
|
mcc->mTargetNode = StringTable->insert(data);
|
||||||
|
mcc->setMaskBits(OffsetMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::_setPosOffset(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
CameraComponent *mcc = static_cast<CameraComponent*>(object);
|
||||||
|
|
||||||
|
if (mcc)
|
||||||
|
{
|
||||||
|
Point3F pos;
|
||||||
|
Con::setData(TypePoint3F, &pos, 0, 1, &data);
|
||||||
|
|
||||||
|
mcc->mPosOffset = pos;
|
||||||
|
mcc->setMaskBits(OffsetMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::_setRotOffset(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
CameraComponent *mcc = static_cast<CameraComponent*>(object);
|
||||||
|
|
||||||
|
if (mcc)
|
||||||
|
{
|
||||||
|
RotationF rot;
|
||||||
|
Con::setData(TypeRotationF, &rot, 0, 1, &data);
|
||||||
|
|
||||||
|
mcc->mRotOffset = rot;
|
||||||
|
mcc->setMaskBits(OffsetMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::isValidCameraFov(F32 fov)
|
||||||
|
{
|
||||||
|
return((fov >= mCameraMinFov) && (fov <= mCameraMaxFov));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::_setCameraFov(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
CameraComponent *cCI = static_cast<CameraComponent*>(object);
|
||||||
|
cCI->setCameraFov(dAtof(data));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::setCameraFov(F32 fov)
|
||||||
|
{
|
||||||
|
mCameraFov = mClampF(fov, mCameraMinFov, mCameraMaxFov);
|
||||||
|
|
||||||
|
if (isClientObject())
|
||||||
|
GameSetCameraTargetFov(mCameraFov);
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
setMaskBits(FOVMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
|
||||||
|
{
|
||||||
|
// update the camera query
|
||||||
|
query->camera = this;
|
||||||
|
|
||||||
|
if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
|
||||||
|
{
|
||||||
|
// get the fov from the connection (in deg)
|
||||||
|
F32 fov;
|
||||||
|
if (con->getControlCameraFov(&fov))
|
||||||
|
{
|
||||||
|
query->fov = mDegToRad(fov/2);
|
||||||
|
query->sinFov = mSin(query->fov);
|
||||||
|
query->cosFov = mCos(query->fov);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query->fov = mDegToRad(mCameraFov/2);
|
||||||
|
query->sinFov = mSin(query->fov);
|
||||||
|
query->cosFov = mCos(query->fov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use eye rather than camera transform (good enough and faster)
|
||||||
|
MatrixF camTransform = mOwner->getTransform();
|
||||||
|
camTransform.getColumn(3, &query->pos);
|
||||||
|
camTransform.getColumn(1, &query->orientation);
|
||||||
|
|
||||||
|
// Get the visible distance.
|
||||||
|
if (mOwner->getSceneManager() != NULL)
|
||||||
|
query->visibleDistance = mOwner->getSceneManager()->getVisibleDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraComponent::getCameraTransform(F32* pos,MatrixF* mat)
|
||||||
|
{
|
||||||
|
// Returns camera to world space transform
|
||||||
|
// Handles first person / third person camera position
|
||||||
|
bool isServer = isServerObject();
|
||||||
|
|
||||||
|
if (mTargetNodeIdx == -1)
|
||||||
|
{
|
||||||
|
if (mUseParentTransform)
|
||||||
|
{
|
||||||
|
MatrixF rMat = mOwner->getRenderTransform();
|
||||||
|
|
||||||
|
rMat.mul(mRotOffset.asMatrixF());
|
||||||
|
|
||||||
|
mat->set(rMat.toEuler(), rMat.getPosition() + mPosOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mat->set(mRotOffset.asEulerF(), mPosOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||||
|
|
||||||
|
if (!renderInterface)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mUseParentTransform)
|
||||||
|
{
|
||||||
|
MatrixF rMat = mOwner->getRenderTransform();
|
||||||
|
|
||||||
|
Point3F position = rMat.getPosition();
|
||||||
|
|
||||||
|
RotationF rot = mRotOffset;
|
||||||
|
|
||||||
|
if (mTargetNodeIdx != -1)
|
||||||
|
{
|
||||||
|
Point3F nodPos;
|
||||||
|
MatrixF nodeTrans = renderInterface->getNodeTransform(mTargetNodeIdx);
|
||||||
|
nodeTrans.getColumn(3, &nodPos);
|
||||||
|
|
||||||
|
// Scale the camera position before applying the transform
|
||||||
|
const Point3F& scale = mOwner->getScale();
|
||||||
|
nodPos.convolve(scale);
|
||||||
|
|
||||||
|
mOwner->getRenderTransform().mulP(nodPos, &position);
|
||||||
|
|
||||||
|
nodeTrans.mul(rMat);
|
||||||
|
|
||||||
|
rot = nodeTrans;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += mPosOffset;
|
||||||
|
|
||||||
|
MatrixF rotMat = rot.asMatrixF();
|
||||||
|
|
||||||
|
MatrixF rotOffsetMat = mRotOffset.asMatrixF();
|
||||||
|
|
||||||
|
rotMat.mul(rotOffsetMat);
|
||||||
|
|
||||||
|
rot = RotationF(rotMat);
|
||||||
|
|
||||||
|
mat->set(rot.asEulerF(), position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MatrixF rMat = mOwner->getRenderTransform();
|
||||||
|
|
||||||
|
Point3F position = rMat.getPosition();
|
||||||
|
|
||||||
|
RotationF rot = mRotOffset;
|
||||||
|
|
||||||
|
if (mTargetNodeIdx != -1)
|
||||||
|
{
|
||||||
|
Point3F nodPos;
|
||||||
|
MatrixF nodeTrans = renderInterface->getNodeTransform(mTargetNodeIdx);
|
||||||
|
nodeTrans.getColumn(3, &nodPos);
|
||||||
|
|
||||||
|
// Scale the camera position before applying the transform
|
||||||
|
const Point3F& scale = mOwner->getScale();
|
||||||
|
nodPos.convolve(scale);
|
||||||
|
|
||||||
|
position = nodPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += mPosOffset;
|
||||||
|
|
||||||
|
mat->set(rot.asEulerF(), position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::getCameraParameters(F32 *min, F32* max, Point3F* off, MatrixF* rot)
|
||||||
|
{
|
||||||
|
*min = 0.2f;
|
||||||
|
*max = 0.f;
|
||||||
|
off->set(0, 0, 0);
|
||||||
|
rot->identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 CameraComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retmask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & FOVMask))
|
||||||
|
{
|
||||||
|
stream->write(mCameraFov);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & OffsetMask))
|
||||||
|
{
|
||||||
|
RenderComponentInterface* renderInterface = getOwner()->getComponent<RenderComponentInterface>();
|
||||||
|
|
||||||
|
if (renderInterface && renderInterface->getShape())
|
||||||
|
{
|
||||||
|
S32 nodeIndex = renderInterface->getShape()->findNode(mTargetNode);
|
||||||
|
|
||||||
|
mTargetNodeIdx = nodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->writeInt(mTargetNodeIdx, 32);
|
||||||
|
//send offsets here
|
||||||
|
|
||||||
|
stream->writeCompressedPoint(mPosOffset);
|
||||||
|
stream->writeCompressedPoint(mRotOffset.asEulerF());
|
||||||
|
|
||||||
|
stream->writeFlag(mUseParentTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
F32 fov;
|
||||||
|
stream->read(&fov);
|
||||||
|
setCameraFov(fov);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stream->readFlag())
|
||||||
|
{
|
||||||
|
mTargetNodeIdx = stream->readInt(32);
|
||||||
|
|
||||||
|
stream->readCompressedPoint(&mPosOffset);
|
||||||
|
|
||||||
|
EulerF rot;
|
||||||
|
stream->readCompressedPoint(&rot);
|
||||||
|
|
||||||
|
mRotOffset = RotationF(rot);
|
||||||
|
|
||||||
|
mUseParentTransform = stream->readFlag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::setForwardVector(VectorF newForward, VectorF upVector)
|
||||||
|
{
|
||||||
|
MatrixF mat;
|
||||||
|
F32 pos = 0;
|
||||||
|
getCameraTransform(&pos, &mat);
|
||||||
|
|
||||||
|
mPosOffset = mat.getPosition();
|
||||||
|
|
||||||
|
VectorF up(0.0f, 0.0f, 1.0f);
|
||||||
|
VectorF axisX;
|
||||||
|
VectorF axisY = newForward;
|
||||||
|
VectorF axisZ;
|
||||||
|
|
||||||
|
if (upVector != VectorF::Zero)
|
||||||
|
up = upVector;
|
||||||
|
|
||||||
|
// Validate and normalize input:
|
||||||
|
F32 lenSq;
|
||||||
|
lenSq = axisY.lenSquared();
|
||||||
|
if (lenSq < 0.000001f)
|
||||||
|
{
|
||||||
|
axisY.set(0.0f, 1.0f, 0.0f);
|
||||||
|
Con::errorf("Entity::setForwardVector() - degenerate forward vector");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
axisY /= mSqrt(lenSq);
|
||||||
|
}
|
||||||
|
|
||||||
|
lenSq = up.lenSquared();
|
||||||
|
if (lenSq < 0.000001f)
|
||||||
|
{
|
||||||
|
up.set(0.0f, 0.0f, 1.0f);
|
||||||
|
Con::errorf("SceneObject::setForwardVector() - degenerate up vector - too small");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
up /= mSqrt(lenSq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(mDot(up, axisY)) > 0.9999f)
|
||||||
|
{
|
||||||
|
Con::errorf("SceneObject::setForwardVector() - degenerate up vector - same as forward");
|
||||||
|
// i haven't really tested this, but i think it generates something which should be not parallel to the previous vector:
|
||||||
|
F32 tmp = up.x;
|
||||||
|
up.x = -up.y;
|
||||||
|
up.y = up.z;
|
||||||
|
up.z = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct the remaining axes:
|
||||||
|
mCross(axisY, up, &axisX);
|
||||||
|
mCross(axisX, axisY, &axisZ);
|
||||||
|
|
||||||
|
mat.setColumn(0, axisX);
|
||||||
|
mat.setColumn(1, axisY);
|
||||||
|
mat.setColumn(2, axisZ);
|
||||||
|
|
||||||
|
mRotOffset = RotationF(mat.toEuler());
|
||||||
|
mRotOffset.y = 0;
|
||||||
|
|
||||||
|
setMaskBits(OffsetMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::setPosition(Point3F newPos)
|
||||||
|
{
|
||||||
|
mPosOffset = newPos;
|
||||||
|
setMaskBits(OffsetMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraComponent::setRotation(RotationF newRot)
|
||||||
|
{
|
||||||
|
mRotOffset = newRot;
|
||||||
|
setMaskBits(OffsetMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Frustum CameraComponent::getFrustum()
|
||||||
|
{
|
||||||
|
Frustum visFrustum;
|
||||||
|
F32 left, right, top, bottom;
|
||||||
|
F32 aspectRatio = mClientScreen.x / mClientScreen.y;
|
||||||
|
|
||||||
|
visFrustum.set(false, mDegToRad(mCameraFov), aspectRatio, 0.1f, 1000, mOwner->getTransform());
|
||||||
|
|
||||||
|
return visFrustum;
|
||||||
|
}
|
||||||
159
Engine/source/T3D/components/camera/cameraComponent.h
Normal file
159
Engine/source/T3D/components/camera/cameraComponent.h
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CAMERA_COMPONENT_H
|
||||||
|
#define CAMERA_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SceneRenderState;
|
||||||
|
struct CameraScopeQuery;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class CameraComponent : public Component, public CameraInterface
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
F32 mCameraFov; ///< The camera vertical FOV in degrees.
|
||||||
|
|
||||||
|
Point2F mClientScreen; ///< The dimensions of the client's screen. Used to calculate the aspect ratio.
|
||||||
|
|
||||||
|
F32 mCameraDefaultFov; ///< Default vertical FOV in degrees.
|
||||||
|
F32 mCameraMinFov; ///< Min vertical FOV allowed in degrees.
|
||||||
|
F32 mCameraMaxFov; ///< Max vertical FOV allowed in degrees.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Point3F mPosOffset;
|
||||||
|
RotationF mRotOffset;
|
||||||
|
|
||||||
|
StringTableEntry mTargetNode;
|
||||||
|
S32 mTargetNodeIdx;
|
||||||
|
|
||||||
|
bool mUseParentTransform;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FOVMask = Parent::NextFreeMask,
|
||||||
|
OffsetMask = Parent::NextFreeMask << 1,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CameraComponent();
|
||||||
|
virtual ~CameraComponent();
|
||||||
|
DECLARE_CONOBJECT(CameraComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
static bool _setCameraFov(void *object, const char *index, const char *data);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
static bool _setNode(void *object, const char *index, const char *data);
|
||||||
|
static bool _setPosOffset(void *object, const char *index, const char *data);
|
||||||
|
static bool _setRotOffset(void *object, const char *index, const char *data);
|
||||||
|
|
||||||
|
void setRotOffset(RotationF rot)
|
||||||
|
{
|
||||||
|
mRotOffset = rot;
|
||||||
|
setMaskBits(OffsetMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
RotationF getRotOffset()
|
||||||
|
{
|
||||||
|
return mRotOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point3F getPosOffset()
|
||||||
|
{
|
||||||
|
return mPosOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the minimum viewing distance, maximum viewing distance, camera offsetand rotation
|
||||||
|
/// for this object, if the world were to be viewed through its eyes
|
||||||
|
/// @param min Minimum viewing distance
|
||||||
|
/// @param max Maximum viewing distance
|
||||||
|
/// @param offset Offset of the camera from the origin in local space
|
||||||
|
/// @param rot Rotation matrix
|
||||||
|
virtual void getCameraParameters(F32 *min, F32* max, Point3F* offset, MatrixF* rot);
|
||||||
|
|
||||||
|
/// Gets the camera to world space transform matrix
|
||||||
|
/// @todo Find out what pos does
|
||||||
|
/// @param pos TODO: Find out what this does
|
||||||
|
/// @param mat Camera transform (out)
|
||||||
|
virtual bool getCameraTransform(F32* pos, MatrixF* mat);
|
||||||
|
|
||||||
|
/// Returns the vertical field of view in degrees for
|
||||||
|
/// this object if used as a camera.
|
||||||
|
virtual F32 getCameraFov() { return mCameraFov; }
|
||||||
|
|
||||||
|
/// Returns the default vertical field of view in degrees
|
||||||
|
/// if this object is used as a camera.
|
||||||
|
virtual F32 getDefaultCameraFov() { return mCameraDefaultFov; }
|
||||||
|
|
||||||
|
/// Sets the vertical field of view in degrees for this
|
||||||
|
/// object if used as a camera.
|
||||||
|
/// @param yfov The vertical FOV in degrees to test.
|
||||||
|
virtual void setCameraFov(F32 fov);
|
||||||
|
|
||||||
|
/// Returns true if the vertical FOV in degrees is within
|
||||||
|
/// allowable parameters of the datablock.
|
||||||
|
/// @param yfov The vertical FOV in degrees to test.
|
||||||
|
/// @see ShapeBaseData::cameraMinFov
|
||||||
|
/// @see ShapeBaseData::cameraMaxFov
|
||||||
|
virtual bool isValidCameraFov(F32 fov);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
virtual Frustum getFrustum();
|
||||||
|
|
||||||
|
/// Control object scoping
|
||||||
|
void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);
|
||||||
|
|
||||||
|
void setForwardVector(VectorF newForward, VectorF upVector = VectorF::Zero);
|
||||||
|
void setPosition(Point3F newPos);
|
||||||
|
void setRotation(RotationF newRot);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DECLARE_CALLBACK(F32, validateCameraFov, (F32 fov));
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CAMERA_BEHAVIOR_H
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/components/camera/cameraComponent.h"
|
||||||
|
|
||||||
|
//Basically, this only exists for backwards compatibility for parts of the editors
|
||||||
|
ConsoleMethod(CameraComponent, getMode, const char*, 2, 2, "() - We get the first behavior of the requested type on our owner object.\n"
|
||||||
|
"@return (string name) The type of the behavior we're requesting")
|
||||||
|
{
|
||||||
|
return "fly";
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CameraComponent, getForwardVector, VectorF, (), ,
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
F32 pos = 0;
|
||||||
|
MatrixF cameraMat;
|
||||||
|
object->getCameraTransform(&pos, &cameraMat);
|
||||||
|
|
||||||
|
VectorF returnVec = cameraMat.getForwardVector();
|
||||||
|
returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
|
||||||
|
returnVec.normalize();
|
||||||
|
return returnVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CameraComponent, getRightVector, VectorF, (), ,
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
F32 pos = 0;
|
||||||
|
MatrixF cameraMat;
|
||||||
|
object->getCameraTransform(&pos, &cameraMat);
|
||||||
|
|
||||||
|
VectorF returnVec = cameraMat.getRightVector();
|
||||||
|
returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
|
||||||
|
returnVec.normalize();
|
||||||
|
return returnVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CameraComponent, getUpVector, VectorF, (), ,
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
F32 pos = 0;
|
||||||
|
MatrixF cameraMat;
|
||||||
|
object->getCameraTransform(&pos, &cameraMat);
|
||||||
|
|
||||||
|
VectorF returnVec = cameraMat.getUpVector();
|
||||||
|
returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
|
||||||
|
returnVec.normalize();
|
||||||
|
return returnVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CameraComponent, setForwardVector, void, (VectorF newForward), (VectorF(0, 0, 0)),
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
object->setForwardVector(newForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CameraComponent, getWorldPosition, Point3F, (), ,
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
F32 pos = 0;
|
||||||
|
MatrixF mat;
|
||||||
|
object->getCameraTransform(&pos, &mat);
|
||||||
|
|
||||||
|
return mat.getPosition();
|
||||||
|
}
|
||||||
172
Engine/source/T3D/components/camera/cameraOrbiterComponent.cpp
Normal file
172
Engine/source/T3D/components/camera/cameraOrbiterComponent.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/camera/cameraOrbiterComponent.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "math/mathUtils.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
CameraOrbiterComponent::CameraOrbiterComponent() : Component()
|
||||||
|
{
|
||||||
|
mMinOrbitDist = 0.0f;
|
||||||
|
mMaxOrbitDist = 0.0f;
|
||||||
|
mCurOrbitDist = 8.0f;
|
||||||
|
mPosition.set(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
mMaxPitchAngle = 70;
|
||||||
|
mMinPitchAngle = -10;
|
||||||
|
|
||||||
|
mRotation.set(0, 0, 0);
|
||||||
|
|
||||||
|
mCamera = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraOrbiterComponent::~CameraOrbiterComponent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(CameraOrbiterComponent);
|
||||||
|
|
||||||
|
bool CameraOrbiterComponent::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
void CameraOrbiterComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("orbitDistance", TypeF32, Offset(mCurOrbitDist, CameraOrbiterComponent), "Object world orientation.");
|
||||||
|
addField("Rotation", TypeRotationF, Offset(mRotation, CameraOrbiterComponent), "Object world orientation.");
|
||||||
|
addField("maxPitchAngle", TypeF32, Offset(mMaxPitchAngle, CameraOrbiterComponent), "Object world orientation.");
|
||||||
|
addField("minPitchAngle", TypeF32, Offset(mMinPitchAngle, CameraOrbiterComponent), "Object world orientation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||||
|
void CameraOrbiterComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
CameraComponent *cam = mOwner->getComponent<CameraComponent>();
|
||||||
|
if (cam)
|
||||||
|
{
|
||||||
|
mCamera = cam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
CameraComponent *camComponent = dynamic_cast<CameraComponent*>(comp);
|
||||||
|
if (camComponent)
|
||||||
|
{
|
||||||
|
mCamera = camComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId()) //?????????
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
CameraComponent *camComponent = dynamic_cast<CameraComponent*>(comp);
|
||||||
|
if (camComponent)
|
||||||
|
{
|
||||||
|
mCamera = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 CameraOrbiterComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraOrbiterComponent::processTick()
|
||||||
|
{
|
||||||
|
Parent::processTick();
|
||||||
|
|
||||||
|
if (!mOwner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mCamera)
|
||||||
|
{
|
||||||
|
//Clamp our pitch to whatever range we allow, first.
|
||||||
|
mRotation.x = mClampF(mRotation.x, mDegToRad(mMinPitchAngle), mDegToRad(mMaxPitchAngle));
|
||||||
|
|
||||||
|
MatrixF ownerTrans = mOwner->getRenderTransform();
|
||||||
|
Point3F ownerPos = ownerTrans.getPosition();
|
||||||
|
|
||||||
|
Point3F pos;
|
||||||
|
pos.x = mCurOrbitDist * mSin(mRotation.x + M_HALFPI_F) * mCos(-1.0f * (mRotation.z + M_HALFPI_F));
|
||||||
|
pos.y = mCurOrbitDist * mSin(mRotation.x + M_HALFPI_F) * mSin(-1.0f * (mRotation.z + M_HALFPI_F));
|
||||||
|
pos.z = mCurOrbitDist * mSin(mRotation.x);
|
||||||
|
|
||||||
|
//orient the camera towards the owner
|
||||||
|
VectorF ownerVec = ownerPos - pos;
|
||||||
|
ownerVec.normalize();
|
||||||
|
|
||||||
|
MatrixF xRot, zRot, cameraMatrix;
|
||||||
|
xRot.set(EulerF(mRotation.x, 0.0f, 0.0f));
|
||||||
|
zRot.set(EulerF(0.0f, 0.0f, mRotation.z));
|
||||||
|
|
||||||
|
cameraMatrix.mul(zRot, xRot);
|
||||||
|
cameraMatrix.getColumn(1, &ownerVec);
|
||||||
|
cameraMatrix.setColumn(3, pos - ownerVec * pos);
|
||||||
|
|
||||||
|
RotationF camRot = RotationF(cameraMatrix);
|
||||||
|
|
||||||
|
if (camRot != mCamera->getRotOffset())
|
||||||
|
mCamera->setRotation(camRot);
|
||||||
|
|
||||||
|
if (pos != mCamera->getPosOffset())
|
||||||
|
mCamera->setPosition(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,41 +20,55 @@
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "component/moreAdvancedComponent.h"
|
#ifndef CAMERA_ORBITER_COMPONENT_H
|
||||||
|
#define CAMERA_ORBITER_COMPONENT_H
|
||||||
|
|
||||||
// unitTest_runTests("Component/MoreAdvancedComponent");
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CAMERA_COMPONENT_H
|
||||||
|
#include "T3D/components/camera/cameraComponent.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
IMPLEMENT_CONOBJECT(MoreAdvancedComponent);
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
ConsoleDocClass( MoreAdvancedComponent,
|
class CameraOrbiterComponent : public Component
|
||||||
"@brief This is a slightly more advanced component which will be used to demonstrate "
|
|
||||||
"components which are dependent on other components.\n\n"
|
|
||||||
"Not intended for game development, for editors or internal use only.\n\n "
|
|
||||||
"@internal");
|
|
||||||
|
|
||||||
bool MoreAdvancedComponent::onComponentRegister( SimComponent *owner )
|
|
||||||
{
|
{
|
||||||
if( !Parent::onComponentRegister( owner ) )
|
typedef Component Parent;
|
||||||
return false;
|
|
||||||
|
|
||||||
// This will return the first interface of type SimpleComponent that is cached
|
F32 mMinOrbitDist;
|
||||||
// on the parent object.
|
F32 mMaxOrbitDist;
|
||||||
mSCInterface = owner->getInterface<SimpleComponentInterface>();
|
F32 mCurOrbitDist;
|
||||||
|
Point3F mPosition;
|
||||||
|
|
||||||
// If we can't find this interface, our component can't function, so false
|
F32 mMaxPitchAngle;
|
||||||
// will be returned, and this will signify, to the top-level component, that it
|
F32 mMinPitchAngle;
|
||||||
// should fail the onAdd call.
|
|
||||||
return ( mSCInterface != NULL );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MoreAdvancedComponent::testDependentInterface()
|
RotationF mRotation;
|
||||||
{
|
|
||||||
// These two requirements must be met in order for the test to proceed, so
|
|
||||||
// lets check them.
|
|
||||||
if( mSCInterface == NULL || !mSCInterface->isValid() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return mSCInterface->isFortyTwo( 42 );
|
CameraComponent* mCamera;
|
||||||
}
|
|
||||||
|
public:
|
||||||
|
CameraOrbiterComponent();
|
||||||
|
virtual ~CameraOrbiterComponent();
|
||||||
|
DECLARE_CONOBJECT(CameraOrbiterComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXAMPLEBEHAVIOR_H
|
||||||
582
Engine/source/T3D/components/collision/collisionComponent.cpp
Normal file
582
Engine/source/T3D/components/collision/collisionComponent.cpp
Normal file
|
|
@ -0,0 +1,582 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/collision/collisionComponent.h"
|
||||||
|
#include "T3D/components/collision/collisionComponent_ScriptBinding.h"
|
||||||
|
#include "T3D/components/physics/physicsBehavior.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "gfx/gfxDrawUtil.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
|
#include "T3D/physics/physicsBody.h"
|
||||||
|
#include "T3D/physics/physicsCollision.h"
|
||||||
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "collision/extrudedPolyList.h"
|
||||||
|
#include "math/mathIO.h"
|
||||||
|
#include "gfx/sim/debugDraw.h"
|
||||||
|
#include "collision/concretePolyList.h"
|
||||||
|
|
||||||
|
#include "T3D/trigger.h"
|
||||||
|
#include "opcode/Opcode.h"
|
||||||
|
#include "opcode/Ice/IceAABB.h"
|
||||||
|
#include "opcode/Ice/IcePoint.h"
|
||||||
|
#include "opcode/OPC_AABBTree.h"
|
||||||
|
#include "opcode/OPC_AABBCollider.h"
|
||||||
|
|
||||||
|
#include "math/mathUtils.h"
|
||||||
|
#include "materials/baseMatInstance.h"
|
||||||
|
#include "collision/vertexPolyList.h"
|
||||||
|
|
||||||
|
extern bool gEditingMission;
|
||||||
|
|
||||||
|
static bool sRenderColliders = false;
|
||||||
|
|
||||||
|
//Docs
|
||||||
|
ConsoleDocClass(CollisionComponent,
|
||||||
|
"@brief The Box Collider component uses a box or rectangular convex shape for collisions.\n\n"
|
||||||
|
|
||||||
|
"Colliders are individualized components that are similarly based off the CollisionInterface core.\n"
|
||||||
|
"They are basically the entire functionality of how Torque handles collisions compacted into a single component.\n"
|
||||||
|
"A collider will both collide against and be collided with, other entities.\n"
|
||||||
|
"Individual colliders will offer different shapes. This box collider will generate a box/rectangle convex, \n"
|
||||||
|
"while the mesh collider will take the owner Entity's rendered shape and do polysoup collision on it, etc.\n\n"
|
||||||
|
|
||||||
|
"The general flow of operations for how collisions happen is thus:\n"
|
||||||
|
" -When the component is added(or updated) prepCollision() is called.\n"
|
||||||
|
" This will set up our initial convex shape for usage later.\n\n"
|
||||||
|
|
||||||
|
" -When we update via processTick(), we first test if our entity owner is mobile.\n"
|
||||||
|
" If our owner isn't mobile(as in, they have no components that provide it a velocity to move)\n"
|
||||||
|
" then we skip doing our active collision checks. Collisions are checked by the things moving, as\n"
|
||||||
|
" opposed to being reactionary. If we're moving, we call updateWorkingCollisionSet().\n"
|
||||||
|
" updateWorkingCollisionSet() estimates our bounding space for our current ticket based on our position and velocity.\n"
|
||||||
|
" If our bounding space has changed since the last tick, we proceed to call updateWorkingList() on our convex.\n"
|
||||||
|
" This notifies any object in the bounding space that they may be collided with, so they will call buildConvex().\n"
|
||||||
|
" buildConvex() will set up our ConvexList with our collision convex info.\n\n"
|
||||||
|
|
||||||
|
" -When the component that is actually causing our movement, such as SimplePhysicsBehavior, updates, it will check collisions.\n"
|
||||||
|
" It will call checkCollisions() on us. checkCollisions() will first build a bounding shape for our convex, and test\n"
|
||||||
|
" if we can early out because we won't hit anything based on our starting point, velocity, and tick time.\n"
|
||||||
|
" If we don't early out, we proceed to call updateCollisions(). This builds an ExtrudePolyList, which is then extruded\n"
|
||||||
|
" based on our velocity. We then test our extruded polies on our working list of objects we build\n"
|
||||||
|
" up earlier via updateWorkingCollisionSet. Any collisions that happen here will be added to our mCollisionList.\n"
|
||||||
|
" Finally, we call handleCollisionList() on our collisionList, which then queues out the colliison notice\n"
|
||||||
|
" to the object(s) we collided with so they can do callbacks and the like. We also report back on if we did collide\n"
|
||||||
|
" to the physics component via our bool return in checkCollisions() so it can make the physics react accordingly.\n\n"
|
||||||
|
|
||||||
|
"One interesting point to note is the usage of mBlockColliding.\n"
|
||||||
|
"This is set so that it dictates the return on checkCollisions(). If set to false, it will ensure checkCollisions()\n"
|
||||||
|
"will return false, regardless if we actually collided. This is useful, because even if checkCollisions() returns false,\n"
|
||||||
|
"we still handle the collisions so the callbacks happen. This enables us to apply a collider to an object that doesn't block\n"
|
||||||
|
"objects, but does have callbacks, so it can act as a trigger, allowing for arbitrarily shaped triggers, as any collider can\n"
|
||||||
|
"act as a trigger volume(including MeshCollider).\n\n"
|
||||||
|
|
||||||
|
"@tsexample\n"
|
||||||
|
"new CollisionComponentInstance()\n"
|
||||||
|
"{\n"
|
||||||
|
" template = CollisionComponentTemplate;\n"
|
||||||
|
" colliderSize = \"1 1 2\";\n"
|
||||||
|
" blockColldingObject = \"1\";\n"
|
||||||
|
"};\n"
|
||||||
|
"@endtsexample\n"
|
||||||
|
|
||||||
|
"@see SimplePhysicsBehavior\n"
|
||||||
|
"@ingroup Collision\n"
|
||||||
|
"@ingroup Components\n"
|
||||||
|
);
|
||||||
|
//Docs
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
ImplementEnumType(CollisionMeshMeshType,
|
||||||
|
"Type of mesh data available in a shape.\n"
|
||||||
|
"@ingroup gameObjects")
|
||||||
|
{ CollisionComponent::None, "None", "No mesh data." },
|
||||||
|
{ CollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
|
||||||
|
{ CollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
|
||||||
|
{ CollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
|
||||||
|
EndImplementEnumType;
|
||||||
|
|
||||||
|
//
|
||||||
|
CollisionComponent::CollisionComponent() : Component()
|
||||||
|
{
|
||||||
|
mNetFlags.set(Ghostable | ScopeAlways);
|
||||||
|
|
||||||
|
mFriendlyName = "Collision(Component)";
|
||||||
|
|
||||||
|
mOwnerRenderInterface = NULL;
|
||||||
|
mOwnerPhysicsInterface = NULL;
|
||||||
|
|
||||||
|
mBlockColliding = true;
|
||||||
|
|
||||||
|
mCollisionType = CollisionMesh;
|
||||||
|
mLOSType = CollisionMesh;
|
||||||
|
mDecalType = CollisionMesh;
|
||||||
|
|
||||||
|
colisionMeshPrefix = StringTable->insert("Collision");
|
||||||
|
|
||||||
|
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
||||||
|
StaticShapeObjectType | VehicleObjectType |
|
||||||
|
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
||||||
|
|
||||||
|
mPhysicsRep = NULL;
|
||||||
|
mPhysicsWorld = NULL;
|
||||||
|
|
||||||
|
mTimeoutList = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionComponent::~CollisionComponent()
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < mFields.size(); ++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(CollisionComponent);
|
||||||
|
|
||||||
|
void CollisionComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||||
|
if (renderInterface)
|
||||||
|
{
|
||||||
|
renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
|
||||||
|
mOwnerRenderInterface = renderInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
//physicsInterface
|
||||||
|
PhysicsComponentInterface *physicsInterface = mOwner->getComponent<PhysicsComponentInterface>();
|
||||||
|
if (!physicsInterface)
|
||||||
|
{
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||||
|
if (renderInterface)
|
||||||
|
{
|
||||||
|
renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
|
||||||
|
mOwnerRenderInterface = renderInterface;
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
|
||||||
|
if (physicsInterface)
|
||||||
|
{
|
||||||
|
if (mPhysicsRep)
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId()) //?????????
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||||
|
if (renderInterface)
|
||||||
|
{
|
||||||
|
renderInterface->onShapeInstanceChanged.remove(this, &CollisionComponent::targetShapeChanged);
|
||||||
|
mOwnerRenderInterface = NULL;
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
//physicsInterface
|
||||||
|
PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
|
||||||
|
if (physicsInterface)
|
||||||
|
{
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::checkDependencies()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addGroup("Collision");
|
||||||
|
|
||||||
|
addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, CollisionComponent),
|
||||||
|
"The type of mesh data to use for collision queries.");
|
||||||
|
|
||||||
|
addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, CollisionComponent),
|
||||||
|
"The type of mesh data to use for collision queries.");
|
||||||
|
|
||||||
|
addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, CollisionComponent),
|
||||||
|
"The type of mesh data to use for collision queries.");
|
||||||
|
|
||||||
|
addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, CollisionComponent),
|
||||||
|
"The type of mesh data to use for collision queries.");
|
||||||
|
|
||||||
|
addField("BlockCollisions", TypeBool, Offset(mBlockColliding, CollisionComponent), "");
|
||||||
|
|
||||||
|
endGroup("Collision");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::inspectPostApply()
|
||||||
|
{
|
||||||
|
// Apply any transformations set in the editor
|
||||||
|
Parent::inspectPostApply();
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
setMaskBits(ColliderMask);
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 CollisionComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & (ColliderMask | InitialUpdateMask)))
|
||||||
|
{
|
||||||
|
stream->write((U32)mCollisionType);
|
||||||
|
stream->writeString(colisionMeshPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if (stream->readFlag()) // UpdateMask
|
||||||
|
{
|
||||||
|
U32 collisionType = CollisionMesh;
|
||||||
|
|
||||||
|
stream->read(&collisionType);
|
||||||
|
|
||||||
|
// Handle it if we have changed CollisionType's
|
||||||
|
if ((MeshType)collisionType != mCollisionType)
|
||||||
|
{
|
||||||
|
mCollisionType = (MeshType)collisionType;
|
||||||
|
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
char readBuffer[1024];
|
||||||
|
|
||||||
|
stream->readString(readBuffer);
|
||||||
|
colisionMeshPrefix = StringTable->insert(readBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::ownerTransformSet(MatrixF *mat)
|
||||||
|
{
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
|
||||||
|
{
|
||||||
|
prepCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::prepCollision()
|
||||||
|
{
|
||||||
|
if (!mOwner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Let the client know that the collision was updated
|
||||||
|
setMaskBits(ColliderMask);
|
||||||
|
|
||||||
|
mOwner->disableCollision();
|
||||||
|
|
||||||
|
if ((!PHYSICSMGR || mCollisionType == None) ||
|
||||||
|
(mOwnerRenderInterface == NULL && (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PhysicsCollision *colShape = NULL;
|
||||||
|
|
||||||
|
if (mCollisionType == Bounds)
|
||||||
|
{
|
||||||
|
MatrixF offset(true);
|
||||||
|
|
||||||
|
if (mOwnerRenderInterface && mOwnerRenderInterface->getShape())
|
||||||
|
offset.setPosition(mOwnerRenderInterface->getShape()->center);
|
||||||
|
|
||||||
|
colShape = PHYSICSMGR->createCollision();
|
||||||
|
colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
|
||||||
|
}
|
||||||
|
else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh /*&& !mOwner->getComponent<AnimatedMesh>()*/))
|
||||||
|
{
|
||||||
|
colShape = buildColShapes();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colShape)
|
||||||
|
{
|
||||||
|
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
|
||||||
|
if (mPhysicsRep)
|
||||||
|
{
|
||||||
|
if (mBlockColliding)
|
||||||
|
mPhysicsRep->init(colShape, 0, 0, mOwner, mPhysicsWorld);
|
||||||
|
else
|
||||||
|
mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER, mOwner, mPhysicsWorld);
|
||||||
|
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwner->enableCollision();
|
||||||
|
|
||||||
|
onCollisionChanged.trigger(colShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::processTick()
|
||||||
|
{
|
||||||
|
if (!isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//ProcessTick is where our collision testing begins!
|
||||||
|
|
||||||
|
//callback if we have a persisting contact
|
||||||
|
if (mContactInfo.contactObject)
|
||||||
|
{
|
||||||
|
if (mContactInfo.contactTimer > 0)
|
||||||
|
{
|
||||||
|
if (isMethod("updateContact"))
|
||||||
|
Con::executef(this, "updateContact");
|
||||||
|
|
||||||
|
if (mOwner->isMethod("updateContact"))
|
||||||
|
Con::executef(mOwner, "updateContact");
|
||||||
|
}
|
||||||
|
|
||||||
|
++mContactInfo.contactTimer;
|
||||||
|
}
|
||||||
|
else if (mContactInfo.contactTimer != 0)
|
||||||
|
mContactInfo.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionComponent::updatePhysics()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsCollision* CollisionComponent::getCollisionData()
|
||||||
|
{
|
||||||
|
if ((!PHYSICSMGR || mCollisionType == None) || mOwnerRenderInterface == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
PhysicsCollision *colShape = NULL;
|
||||||
|
if (mCollisionType == Bounds)
|
||||||
|
{
|
||||||
|
MatrixF offset(true);
|
||||||
|
offset.setPosition(mOwnerRenderInterface->getShape()->center);
|
||||||
|
colShape = PHYSICSMGR->createCollision();
|
||||||
|
colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
|
||||||
|
}
|
||||||
|
else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh/* && !mOwner->getComponent<AnimatedMesh>()*/))
|
||||||
|
{
|
||||||
|
colShape = buildColShapes();
|
||||||
|
//colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
|
||||||
|
}
|
||||||
|
/*else if (mCollisionType == VisibleMesh && !mOwner->getComponent<AnimatedMesh>())
|
||||||
|
{
|
||||||
|
//We don't have support for visible mesh collisions with animated meshes currently in the physics abstraction layer
|
||||||
|
//so we don't generate anything if we're set to use a visible mesh but have an animated mesh component.
|
||||||
|
colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
|
||||||
|
}*/
|
||||||
|
else if (mCollisionType == VisibleMesh/* && mOwner->getComponent<AnimatedMesh>()*/)
|
||||||
|
{
|
||||||
|
Con::printf("CollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return colShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionComponent::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
|
||||||
|
{
|
||||||
|
if (!mCollisionType == None)
|
||||||
|
{
|
||||||
|
if (mPhysicsWorld)
|
||||||
|
{
|
||||||
|
return mPhysicsWorld->castRay(start, end, info, Point3F::Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsCollision* CollisionComponent::buildColShapes()
|
||||||
|
{
|
||||||
|
PROFILE_SCOPE(CollisionComponent_buildColShapes);
|
||||||
|
|
||||||
|
PhysicsCollision *colShape = NULL;
|
||||||
|
U32 surfaceKey = 0;
|
||||||
|
|
||||||
|
TSShape* shape = mOwnerRenderInterface->getShape();
|
||||||
|
|
||||||
|
if (mCollisionType == VisibleMesh)
|
||||||
|
{
|
||||||
|
// Here we build triangle collision meshes from the
|
||||||
|
// visible detail levels.
|
||||||
|
|
||||||
|
// A negative subshape on the detail means we don't have geometry.
|
||||||
|
const TSShape::Detail &detail = shape->details[0];
|
||||||
|
if (detail.subShapeNum < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// We don't try to optimize the triangles we're given
|
||||||
|
// and assume the art was created properly for collision.
|
||||||
|
ConcretePolyList polyList;
|
||||||
|
polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
|
||||||
|
|
||||||
|
// Create the collision meshes.
|
||||||
|
S32 start = shape->subShapeFirstObject[detail.subShapeNum];
|
||||||
|
S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
|
||||||
|
for (S32 o = start; o < end; o++)
|
||||||
|
{
|
||||||
|
const TSShape::Object &object = shape->objects[o];
|
||||||
|
if (detail.objectDetailNum >= object.numMeshes)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// No mesh or no verts.... nothing to do.
|
||||||
|
TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
|
||||||
|
if (!mesh || mesh->mNumVerts == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Gather the mesh triangles.
|
||||||
|
polyList.clear();
|
||||||
|
mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
|
||||||
|
|
||||||
|
// Create the collision shape if we haven't already.
|
||||||
|
if (!colShape)
|
||||||
|
colShape = PHYSICSMGR->createCollision();
|
||||||
|
|
||||||
|
// Get the object space mesh transform.
|
||||||
|
MatrixF localXfm;
|
||||||
|
shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
|
||||||
|
|
||||||
|
colShape->addTriangleMesh(polyList.mVertexList.address(),
|
||||||
|
polyList.mVertexList.size(),
|
||||||
|
polyList.mIndexList.address(),
|
||||||
|
polyList.mIndexList.size() / 3,
|
||||||
|
localXfm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return what we built... if anything.
|
||||||
|
return colShape;
|
||||||
|
}
|
||||||
|
else if (mCollisionType == CollisionMesh)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Scan out the collision hulls...
|
||||||
|
//
|
||||||
|
// TODO: We need to support LOS collision for physics.
|
||||||
|
//
|
||||||
|
for (U32 i = 0; i < shape->details.size(); i++)
|
||||||
|
{
|
||||||
|
const TSShape::Detail &detail = shape->details[i];
|
||||||
|
const String &name = shape->names[detail.nameIndex];
|
||||||
|
|
||||||
|
// Is this a valid collision detail.
|
||||||
|
if (!dStrStartsWith(name, colisionMeshPrefix) || detail.subShapeNum < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Now go thru the meshes for this detail.
|
||||||
|
S32 start = shape->subShapeFirstObject[detail.subShapeNum];
|
||||||
|
S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
|
||||||
|
if (start >= end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (S32 o = start; o < end; o++)
|
||||||
|
{
|
||||||
|
const TSShape::Object &object = shape->objects[o];
|
||||||
|
const String &meshName = shape->names[object.nameIndex];
|
||||||
|
|
||||||
|
if (object.numMeshes <= detail.objectDetailNum)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// No mesh, a flat bounds, or no verts.... nothing to do.
|
||||||
|
TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
|
||||||
|
if (!mesh || mesh->getBounds().isEmpty() || mesh->mNumVerts == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We need the default mesh transform.
|
||||||
|
MatrixF localXfm;
|
||||||
|
shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
|
||||||
|
|
||||||
|
// We have some sort of collision shape... so allocate it.
|
||||||
|
if (!colShape)
|
||||||
|
colShape = PHYSICSMGR->createCollision();
|
||||||
|
|
||||||
|
// Any other mesh name we assume as a generic convex hull.
|
||||||
|
//
|
||||||
|
// Collect the verts using the vertex polylist which will
|
||||||
|
// filter out duplicates. This is importaint as the convex
|
||||||
|
// generators can sometimes fail with duplicate verts.
|
||||||
|
//
|
||||||
|
VertexPolyList polyList;
|
||||||
|
MatrixF meshMat(localXfm);
|
||||||
|
|
||||||
|
Point3F t = meshMat.getPosition();
|
||||||
|
t.convolve(mOwner->getScale());
|
||||||
|
meshMat.setPosition(t);
|
||||||
|
|
||||||
|
polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
|
||||||
|
mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
|
||||||
|
colShape->addConvex(polyList.getVertexList().address(),
|
||||||
|
polyList.getVertexList().size(),
|
||||||
|
meshMat);
|
||||||
|
} // objects
|
||||||
|
} // details
|
||||||
|
}
|
||||||
|
|
||||||
|
return colShape;
|
||||||
|
}
|
||||||
208
Engine/source/T3D/components/collision/collisionComponent.h
Normal file
208
Engine/source/T3D/components/collision/collisionComponent.h
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef COLLISION_COMPONENT_H
|
||||||
|
#define COLLISION_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef __RESOURCE_H__
|
||||||
|
#include "core/resource.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShape.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
#ifndef COLLISION_INTERFACES_H
|
||||||
|
#include "T3D/components/collision/collisionInterfaces.h"
|
||||||
|
#endif
|
||||||
|
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/render/renderComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/physics/physicsComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||||
|
#include "T3D/physics/physicsCommon.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||||
|
#include "T3D/physics/physicsWorld.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TSShapeInstance;
|
||||||
|
class SceneRenderState;
|
||||||
|
class CollisionComponent;
|
||||||
|
class PhysicsBody;
|
||||||
|
class PhysicsWorld;
|
||||||
|
|
||||||
|
class CollisionComponent : public Component,
|
||||||
|
public CollisionInterface,
|
||||||
|
public CastRayInterface
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
public:
|
||||||
|
enum MeshType
|
||||||
|
{
|
||||||
|
None = 0, ///< No mesh
|
||||||
|
Bounds = 1, ///< Bounding box of the shape
|
||||||
|
CollisionMesh = 2, ///< Specifically designated collision meshes
|
||||||
|
VisibleMesh = 3 ///< Rendered mesh polygons
|
||||||
|
};
|
||||||
|
|
||||||
|
PhysicsWorld* mPhysicsWorld;
|
||||||
|
PhysicsBody* mPhysicsRep;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MeshType mCollisionType;
|
||||||
|
MeshType mDecalType;
|
||||||
|
MeshType mLOSType;
|
||||||
|
|
||||||
|
Vector<S32> mCollisionDetails;
|
||||||
|
Vector<S32> mLOSDetails;
|
||||||
|
|
||||||
|
StringTableEntry colisionMeshPrefix;
|
||||||
|
|
||||||
|
RenderComponentInterface* mOwnerRenderInterface;
|
||||||
|
|
||||||
|
PhysicsComponentInterface* mOwnerPhysicsInterface;
|
||||||
|
|
||||||
|
//only really relevent for the collision mesh type
|
||||||
|
//if we note an animation component is added, we flag as being animated.
|
||||||
|
//This way, if we're using collision meshes, we can set it up to update their transforms
|
||||||
|
//as needed
|
||||||
|
bool mAnimated;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ColliderMask = Parent::NextFreeMask,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CollisionComponent();
|
||||||
|
virtual ~CollisionComponent();
|
||||||
|
DECLARE_CONOBJECT(CollisionComponent);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
virtual void ownerTransformSet(MatrixF *mat);
|
||||||
|
void targetShapeChanged(RenderComponentInterface* instanceInterface);
|
||||||
|
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
|
||||||
|
virtual void checkDependencies();
|
||||||
|
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
void inspectPostApply();
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
|
||||||
|
void prepCollision();
|
||||||
|
|
||||||
|
PhysicsCollision* buildColShapes();
|
||||||
|
|
||||||
|
void updatePhysics();
|
||||||
|
|
||||||
|
virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||||
|
|
||||||
|
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere){ return false; }
|
||||||
|
|
||||||
|
virtual PhysicsCollision* getCollisionData();
|
||||||
|
|
||||||
|
//Utility functions, mostly for script
|
||||||
|
Point3F getContactNormal() { return mContactInfo.contactNormal; }
|
||||||
|
bool hasContact()
|
||||||
|
{
|
||||||
|
if (mContactInfo.contactObject)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
S32 getCollisionCount()
|
||||||
|
{
|
||||||
|
return mCollisionList.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Point3F getCollisionNormal(S32 collisionIndex)
|
||||||
|
{
|
||||||
|
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||||
|
return Point3F::Zero;
|
||||||
|
|
||||||
|
return mCollisionList[collisionIndex].normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 getCollisionAngle(S32 collisionIndex, Point3F upVector)
|
||||||
|
{
|
||||||
|
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 getBestCollision(Point3F upVector)
|
||||||
|
{
|
||||||
|
S32 bestCollision = -1;
|
||||||
|
|
||||||
|
F32 bestAngle = 360.f;
|
||||||
|
S32 count = mCollisionList.getCount();
|
||||||
|
for (U32 i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
|
||||||
|
|
||||||
|
if (angle < bestAngle)
|
||||||
|
{
|
||||||
|
bestCollision = i;
|
||||||
|
bestAngle = angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCollision;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 getBestCollisionAngle(VectorF upVector)
|
||||||
|
{
|
||||||
|
S32 bestCol = getBestCollision(upVector);
|
||||||
|
|
||||||
|
if (bestCol == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return getCollisionAngle(bestCol, upVector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef CollisionComponent::MeshType CollisionMeshMeshType;
|
||||||
|
DefineEnumType(CollisionMeshMeshType);
|
||||||
|
|
||||||
|
#endif // COLLISION_COMPONENT_H
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/components/collision/collisionComponent.h"
|
||||||
|
#include "materials/baseMatInstance.h"
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getNumberOfContacts, S32, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
return object->getCollisionList()->getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getBestContact, S32, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getContactNormal, Point3F, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
if (object->getContactInfo())
|
||||||
|
{
|
||||||
|
if (object->getContactInfo()->contactObject)
|
||||||
|
{
|
||||||
|
return object->getContactInfo()->contactNormal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Point3F::Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getContactMaterial, S32, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
if (object->getContactInfo())
|
||||||
|
{
|
||||||
|
if (object->getContactInfo()->contactObject)
|
||||||
|
{
|
||||||
|
if (object->getContactInfo()->contactMaterial != NULL)
|
||||||
|
return object->getContactInfo()->contactMaterial->getMaterial()->getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getContactObject, S32, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
if (object->getContactInfo())
|
||||||
|
{
|
||||||
|
return object->getContactInfo()->contactObject != NULL ? object->getContactInfo()->contactObject->getId() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getContactPoint, Point3F, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
if (object->getContactInfo())
|
||||||
|
{
|
||||||
|
if (object->getContactInfo()->contactObject)
|
||||||
|
{
|
||||||
|
return object->getContactInfo()->contactPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Point3F::Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(CollisionComponent, getContactTime, S32, (), ,
|
||||||
|
"Gets the number of contacts this collider has hit.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
if (object->getContactInfo())
|
||||||
|
{
|
||||||
|
if (object->getContactInfo()->contactObject)
|
||||||
|
{
|
||||||
|
return object->getContactInfo()->contactTimer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CollisionComponent, hasContact, bool, (), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->hasContact();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CollisionComponent, getCollisionCount, S32, (), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getCollisionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CollisionComponent, getCollisionNormal, Point3F, (S32 collisionIndex), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getCollisionNormal(collisionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CollisionComponent, getCollisionAngle, F32, (S32 collisionIndex, VectorF upVector), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getCollisionAngle(collisionIndex, upVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CollisionComponent, getBestCollisionAngle, F32, (VectorF upVector), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getBestCollisionAngle(upVector);
|
||||||
|
}
|
||||||
258
Engine/source/T3D/components/collision/collisionInterfaces.cpp
Normal file
258
Engine/source/T3D/components/collision/collisionInterfaces.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/collision/collisionInterfaces.h"
|
||||||
|
#include "scene/sceneObject.h"
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/trigger.h"
|
||||||
|
#include "materials/baseMatInstance.h"
|
||||||
|
|
||||||
|
void CollisionInterface::handleCollisionList( CollisionList &collisionList, VectorF velocity )
|
||||||
|
{
|
||||||
|
Collision bestCol;
|
||||||
|
|
||||||
|
mCollisionList = collisionList;
|
||||||
|
|
||||||
|
for (U32 i=0; i < collisionList.getCount(); ++i)
|
||||||
|
{
|
||||||
|
Collision& colCheck = collisionList[i];
|
||||||
|
|
||||||
|
if (colCheck.object)
|
||||||
|
{
|
||||||
|
if (colCheck.object->getTypeMask() & PlayerObjectType)
|
||||||
|
{
|
||||||
|
handleCollision( colCheck, velocity );
|
||||||
|
}
|
||||||
|
else if (colCheck.object->getTypeMask() & TriggerObjectType)
|
||||||
|
{
|
||||||
|
// We've hit it's bounding box, that's close enough for triggers
|
||||||
|
Trigger* pTrigger = static_cast<Trigger*>(colCheck.object);
|
||||||
|
|
||||||
|
Component *comp = dynamic_cast<Component*>(this);
|
||||||
|
pTrigger->potentialEnterObject(comp->getOwner());
|
||||||
|
}
|
||||||
|
else if (colCheck.object->getTypeMask() & DynamicShapeObjectType)
|
||||||
|
{
|
||||||
|
Con::printf("HIT A GENERICALLY DYNAMIC OBJECT");
|
||||||
|
handleCollision(colCheck, velocity);
|
||||||
|
}
|
||||||
|
else if(colCheck.object->getTypeMask() & EntityObjectType)
|
||||||
|
{
|
||||||
|
Entity* ent = dynamic_cast<Entity*>(colCheck.object);
|
||||||
|
if (ent)
|
||||||
|
{
|
||||||
|
CollisionInterface *colObjectInterface = ent->getComponent<CollisionInterface>();
|
||||||
|
if (colObjectInterface)
|
||||||
|
{
|
||||||
|
//convert us to our component
|
||||||
|
Component *thisComp = dynamic_cast<Component*>(this);
|
||||||
|
if (thisComp)
|
||||||
|
{
|
||||||
|
colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner());
|
||||||
|
|
||||||
|
//TODO: properly do this
|
||||||
|
Collision oppositeCol = colCheck;
|
||||||
|
oppositeCol.object = thisComp->getOwner();
|
||||||
|
|
||||||
|
colObjectInterface->handleCollision(oppositeCol, velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handleCollision(colCheck, velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionInterface::handleCollision( Collision &col, VectorF velocity )
|
||||||
|
{
|
||||||
|
if (col.object && (mContactInfo.contactObject == NULL ||
|
||||||
|
col.object->getId() != mContactInfo.contactObject->getId()))
|
||||||
|
{
|
||||||
|
queueCollision(col.object, velocity - col.object->getVelocity());
|
||||||
|
|
||||||
|
//do the callbacks to script for this collision
|
||||||
|
Component *comp = dynamic_cast<Component*>(this);
|
||||||
|
if (comp->isMethod("onCollision"))
|
||||||
|
{
|
||||||
|
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
||||||
|
Con::executef(comp, "onCollision", col.object, col.normal, col.point, matId, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp->getOwner()->isMethod("onCollisionEvent"))
|
||||||
|
{
|
||||||
|
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
||||||
|
Con::executef(comp->getOwner(), "onCollisionEvent", col.object, col.normal, col.point, matId, velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionInterface::handleCollisionNotifyList()
|
||||||
|
{
|
||||||
|
//special handling for any collision components we should notify that a collision happened.
|
||||||
|
for (U32 i = 0; i < mCollisionNotifyList.size(); ++i)
|
||||||
|
{
|
||||||
|
//convert us to our component
|
||||||
|
Component *thisComp = dynamic_cast<Component*>(this);
|
||||||
|
if (thisComp)
|
||||||
|
{
|
||||||
|
mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mCollisionNotifyList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunker<CollisionInterface::CollisionTimeout> sCollisionTimeoutChunker;
|
||||||
|
CollisionInterface::CollisionTimeout* CollisionInterface::sFreeTimeoutList = 0;
|
||||||
|
|
||||||
|
void CollisionInterface::queueCollision( SceneObject *obj, const VectorF &vec)
|
||||||
|
{
|
||||||
|
// Add object to list of collisions.
|
||||||
|
SimTime time = Sim::getCurrentTime();
|
||||||
|
S32 num = obj->getId();
|
||||||
|
|
||||||
|
CollisionTimeout** adr = &mTimeoutList;
|
||||||
|
CollisionTimeout* ptr = mTimeoutList;
|
||||||
|
while (ptr)
|
||||||
|
{
|
||||||
|
if (ptr->objectNumber == num)
|
||||||
|
{
|
||||||
|
if (ptr->expireTime < time)
|
||||||
|
{
|
||||||
|
ptr->expireTime = time + CollisionTimeoutValue;
|
||||||
|
ptr->object = obj;
|
||||||
|
ptr->vector = vec;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Recover expired entries
|
||||||
|
if (ptr->expireTime < time)
|
||||||
|
{
|
||||||
|
CollisionTimeout* cur = ptr;
|
||||||
|
*adr = ptr->next;
|
||||||
|
ptr = ptr->next;
|
||||||
|
cur->next = sFreeTimeoutList;
|
||||||
|
sFreeTimeoutList = cur;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adr = &ptr->next;
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New entry for the object
|
||||||
|
if (sFreeTimeoutList != NULL)
|
||||||
|
{
|
||||||
|
ptr = sFreeTimeoutList;
|
||||||
|
sFreeTimeoutList = ptr->next;
|
||||||
|
ptr->next = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = sCollisionTimeoutChunker.alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr->object = obj;
|
||||||
|
ptr->objectNumber = obj->getId();
|
||||||
|
ptr->vector = vec;
|
||||||
|
ptr->expireTime = time + CollisionTimeoutValue;
|
||||||
|
ptr->next = mTimeoutList;
|
||||||
|
|
||||||
|
mTimeoutList = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionInterface::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
|
||||||
|
Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
|
||||||
|
{
|
||||||
|
Point3F end = start + velocity * time;
|
||||||
|
Point3F distance = end - start;
|
||||||
|
|
||||||
|
Box3F scaledBox = objectBox;
|
||||||
|
scaledBox.minExtents.convolve(objectScale);
|
||||||
|
scaledBox.maxExtents.convolve(objectScale);
|
||||||
|
|
||||||
|
if (mFabs(distance.x) < objectBox.len_x() &&
|
||||||
|
mFabs(distance.y) < objectBox.len_y() &&
|
||||||
|
mFabs(distance.z) < objectBox.len_z())
|
||||||
|
{
|
||||||
|
// We can potentially early out of this. If there are no polys in the clipped polylist at our
|
||||||
|
// end position, then we can bail, and just set start = end;
|
||||||
|
Box3F wBox = scaledBox;
|
||||||
|
wBox.minExtents += end;
|
||||||
|
wBox.maxExtents += end;
|
||||||
|
|
||||||
|
static EarlyOutPolyList eaPolyList;
|
||||||
|
eaPolyList.clear();
|
||||||
|
eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
|
||||||
|
eaPolyList.mPlaneList.clear();
|
||||||
|
eaPolyList.mPlaneList.setSize(6);
|
||||||
|
eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
|
||||||
|
eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
|
||||||
|
eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
|
||||||
|
eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
|
||||||
|
eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
|
||||||
|
eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
// Build list from convex states here...
|
||||||
|
CollisionWorkingList& rList = colWorkingList;
|
||||||
|
CollisionWorkingList* pList = rList.wLink.mNext;
|
||||||
|
while (pList != &rList)
|
||||||
|
{
|
||||||
|
Convex* pConvex = pList->mConvex;
|
||||||
|
|
||||||
|
if (pConvex->getObject()->getTypeMask() & collisionMask)
|
||||||
|
{
|
||||||
|
Box3F convexBox = pConvex->getBoundingBox();
|
||||||
|
|
||||||
|
if (wBox.isOverlapped(convexBox))
|
||||||
|
{
|
||||||
|
// No need to separate out the physical zones here, we want those
|
||||||
|
// to cause a fallthrough as well...
|
||||||
|
pConvex->getPolyList(&eaPolyList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pList = pList->wLink.mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eaPolyList.isEmpty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Collision* CollisionInterface::getCollision(S32 col)
|
||||||
|
{
|
||||||
|
if(col < mCollisionList.getCount() && col >= 0)
|
||||||
|
return &mCollisionList[col];
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
167
Engine/source/T3D/components/collision/collisionInterfaces.h
Normal file
167
Engine/source/T3D/components/collision/collisionInterfaces.h
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef COLLISION_INTERFACES_H
|
||||||
|
#define COLLISION_INTERFACES_H
|
||||||
|
|
||||||
|
#ifndef _CONVEX_H_
|
||||||
|
#include "collision/convex.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _COLLISION_H_
|
||||||
|
#include "collision/collision.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _EARLYOUTPOLYLIST_H_
|
||||||
|
#include "collision/earlyOutPolyList.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SIM_H_
|
||||||
|
#include "console/sim.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENECONTAINER_H_
|
||||||
|
#include "scene/sceneContainer.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||||
|
#include "T3D/physics/physicsCommon.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ContactInfo
|
||||||
|
{
|
||||||
|
bool contacted, move;
|
||||||
|
SceneObject *contactObject;
|
||||||
|
VectorF idealContactNormal;
|
||||||
|
VectorF contactNormal;
|
||||||
|
Point3F contactPoint;
|
||||||
|
F32 contactTime;
|
||||||
|
S32 contactTimer;
|
||||||
|
BaseMatInstance *contactMaterial;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
contacted=move=false;
|
||||||
|
contactObject = NULL;
|
||||||
|
contactNormal.set(0,0,0);
|
||||||
|
contactTime = 0.f;
|
||||||
|
contactTimer = 0;
|
||||||
|
idealContactNormal.set(0, 0, 1);
|
||||||
|
contactMaterial = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactInfo() { clear(); }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class CollisionInterface// : public Interface<CollisionInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// CollisionTimeout
|
||||||
|
// This struct lets us track our collisions and estimate when they've have timed out and we'll need to act on it.
|
||||||
|
struct CollisionTimeout
|
||||||
|
{
|
||||||
|
CollisionTimeout* next;
|
||||||
|
SceneObject* object;
|
||||||
|
U32 objectNumber;
|
||||||
|
SimTime expireTime;
|
||||||
|
VectorF vector;
|
||||||
|
};
|
||||||
|
|
||||||
|
Signal< void( SceneObject* ) > onCollisionSignal;
|
||||||
|
Signal< void( SceneObject* ) > onContactSignal;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CollisionTimeout* mTimeoutList;
|
||||||
|
static CollisionTimeout* sFreeTimeoutList;
|
||||||
|
|
||||||
|
CollisionList mCollisionList;
|
||||||
|
Vector<CollisionInterface*> mCollisionNotifyList;
|
||||||
|
|
||||||
|
ContactInfo mContactInfo;
|
||||||
|
|
||||||
|
Box3F mWorkingQueryBox;
|
||||||
|
|
||||||
|
U32 CollisionMoveMask;
|
||||||
|
|
||||||
|
Convex *mConvexList;
|
||||||
|
|
||||||
|
bool mBlockColliding;
|
||||||
|
|
||||||
|
void handleCollisionNotifyList();
|
||||||
|
|
||||||
|
void queueCollision( SceneObject *obj, const VectorF &vec);
|
||||||
|
|
||||||
|
/// checkEarlyOut
|
||||||
|
/// This function lets you trying and early out of any expensive collision checks by using simple extruded poly boxes representing our objects
|
||||||
|
/// If it returns true, we know we won't hit with the given parameters and can successfully early out. If it returns false, our test case collided
|
||||||
|
/// and we should do the full collision sim.
|
||||||
|
bool checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
|
||||||
|
Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// checkCollisions
|
||||||
|
// This is our main function for checking if a collision is happening based on the start point, velocity and time
|
||||||
|
// We do the bulk of the collision checking in here
|
||||||
|
//virtual bool checkCollisions( const F32 travelTime, Point3F *velocity, Point3F start )=0;
|
||||||
|
|
||||||
|
CollisionList *getCollisionList() { return &mCollisionList; }
|
||||||
|
|
||||||
|
void clearCollisionList() { mCollisionList.clear(); }
|
||||||
|
|
||||||
|
void clearCollisionNotifyList() { mCollisionNotifyList.clear(); }
|
||||||
|
|
||||||
|
Collision *getCollision(S32 col);
|
||||||
|
|
||||||
|
ContactInfo* getContactInfo() { return &mContactInfo; }
|
||||||
|
|
||||||
|
Convex *getConvexList() { return mConvexList; }
|
||||||
|
|
||||||
|
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
|
||||||
|
|
||||||
|
enum PublicConstants {
|
||||||
|
CollisionTimeoutValue = 250
|
||||||
|
};
|
||||||
|
|
||||||
|
bool doesBlockColliding() { return mBlockColliding; }
|
||||||
|
|
||||||
|
/// handleCollisionList
|
||||||
|
/// This basically takes in a CollisionList and calls handleCollision for each.
|
||||||
|
void handleCollisionList(CollisionList &collisionList, VectorF velocity);
|
||||||
|
|
||||||
|
/// handleCollision
|
||||||
|
/// This will take a collision and queue the collision info for the object so that in knows about the collision.
|
||||||
|
void handleCollision(Collision &col, VectorF velocity);
|
||||||
|
|
||||||
|
virtual PhysicsCollision* getCollisionData() = 0;
|
||||||
|
|
||||||
|
Signal< void(PhysicsCollision* collision) > onCollisionChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuildConvexInterface //: public Interface<CollisionInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void buildConvex(const Box3F& box, Convex* convex)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuildPolyListInterface// : public Interface<CollisionInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
619
Engine/source/T3D/components/collision/collisionTrigger.cpp
Normal file
619
Engine/source/T3D/components/collision/collisionTrigger.cpp
Normal file
|
|
@ -0,0 +1,619 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "T3D/components/collision/collisionTrigger.h"
|
||||||
|
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "collision/boxConvex.h"
|
||||||
|
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "math/mathIO.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "renderInstance/renderPassManager.h"
|
||||||
|
#include "gfx/gfxDrawUtil.h"
|
||||||
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
|
#include "T3D/physics/physicsBody.h"
|
||||||
|
#include "T3D/physics/physicsCollision.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool CollisionTrigger::smRenderCollisionTriggers = false;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(CollisionTrigger);
|
||||||
|
|
||||||
|
ConsoleDocClass(CollisionTrigger,
|
||||||
|
"@brief A CollisionTrigger is a volume of space that initiates script callbacks "
|
||||||
|
"when objects pass through the CollisionTrigger.\n\n"
|
||||||
|
|
||||||
|
"CollisionTriggerData provides the callbacks for the CollisionTrigger when an object enters, stays inside "
|
||||||
|
"or leaves the CollisionTrigger's volume.\n\n"
|
||||||
|
|
||||||
|
"@see CollisionTriggerData\n"
|
||||||
|
"@ingroup gameObjects\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK(CollisionTrigger, onAdd, void, (U32 objectId), (objectId),
|
||||||
|
"@brief Called when the CollisionTrigger is being created.\n\n"
|
||||||
|
"@param objectId the object id of the CollisionTrigger being created\n");
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK(CollisionTrigger, onRemove, void, (U32 objectId), (objectId),
|
||||||
|
"@brief Called just before the CollisionTrigger is deleted.\n\n"
|
||||||
|
"@param objectId the object id of the CollisionTrigger being deleted\n");
|
||||||
|
|
||||||
|
CollisionTrigger::CollisionTrigger()
|
||||||
|
{
|
||||||
|
// Don't ghost by default.
|
||||||
|
mNetFlags.set(Ghostable | ScopeAlways);
|
||||||
|
|
||||||
|
mTypeMask |= TriggerObjectType;
|
||||||
|
|
||||||
|
mObjScale.set(1, 1, 1);
|
||||||
|
mObjToWorld.identity();
|
||||||
|
mWorldToObj.identity();
|
||||||
|
|
||||||
|
mLastThink = 0;
|
||||||
|
mCurrTick = 0;
|
||||||
|
|
||||||
|
mConvexList = new Convex;
|
||||||
|
|
||||||
|
mPhysicsRep = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionTrigger::~CollisionTrigger()
|
||||||
|
{
|
||||||
|
delete mConvexList;
|
||||||
|
mConvexList = NULL;
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionTrigger::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
|
||||||
|
{
|
||||||
|
// Collide against bounding box
|
||||||
|
F32 st, et, fst = 0, fet = 1;
|
||||||
|
F32 *bmin = &mObjBox.minExtents.x;
|
||||||
|
F32 *bmax = &mObjBox.maxExtents.x;
|
||||||
|
F32 const *si = &start.x;
|
||||||
|
F32 const *ei = &end.x;
|
||||||
|
|
||||||
|
for (S32 i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (*si < *ei)
|
||||||
|
{
|
||||||
|
if (*si > *bmax || *ei < *bmin)
|
||||||
|
return false;
|
||||||
|
F32 di = *ei - *si;
|
||||||
|
st = (*si < *bmin) ? (*bmin - *si) / di : 0;
|
||||||
|
et = (*ei > *bmax) ? (*bmax - *si) / di : 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*ei > *bmax || *si < *bmin)
|
||||||
|
return false;
|
||||||
|
F32 di = *ei - *si;
|
||||||
|
st = (*si > *bmax) ? (*bmax - *si) / di : 0;
|
||||||
|
et = (*ei < *bmin) ? (*bmin - *si) / di : 1;
|
||||||
|
}
|
||||||
|
if (st > fst) fst = st;
|
||||||
|
if (et < fet) fet = et;
|
||||||
|
if (fet < fst)
|
||||||
|
return false;
|
||||||
|
bmin++; bmax++;
|
||||||
|
si++; ei++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->normal = start - end;
|
||||||
|
info->normal.normalizeSafe();
|
||||||
|
getTransform().mulV(info->normal);
|
||||||
|
|
||||||
|
info->t = fst;
|
||||||
|
info->object = this;
|
||||||
|
info->point.interpolate(start, end, fst);
|
||||||
|
info->material = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CollisionTrigger::consoleInit()
|
||||||
|
{
|
||||||
|
Con::addVariable("$CollisionTrigger::renderCollisionTriggers", TypeBool, &smRenderCollisionTriggers,
|
||||||
|
"@brief Forces all CollisionTrigger's to render.\n\n"
|
||||||
|
"Used by the Tools and debug render modes.\n"
|
||||||
|
"@ingroup gameObjects");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::initPersistFields()
|
||||||
|
{
|
||||||
|
addField("polyhedron", TypeTriggerPolyhedron, Offset(mCollisionTriggerPolyhedron, CollisionTrigger),
|
||||||
|
"@brief Defines a non-rectangular area for the CollisionTrigger.\n\n"
|
||||||
|
"Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral "
|
||||||
|
"CollisionTrigger area. The quadrilateral is defined as a corner point followed by three vectors "
|
||||||
|
"representing the edges extending from the corner.\n");
|
||||||
|
|
||||||
|
addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, CollisionTrigger), &setEnterCmd, &defaultProtectedGetFn,
|
||||||
|
"The command to execute when an object enters this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
|
||||||
|
addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, CollisionTrigger), &setLeaveCmd, &defaultProtectedGetFn,
|
||||||
|
"The command to execute when an object leaves this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
|
||||||
|
addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, CollisionTrigger), &setTickCmd, &defaultProtectedGetFn,
|
||||||
|
"The command to execute while an object is inside this CollisionTrigger. Maximum 1023 characters.");
|
||||||
|
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionTrigger::setEnterCmd(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
static_cast<CollisionTrigger*>(object)->setMaskBits(EnterCmdMask);
|
||||||
|
return true; // to update the actual field
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionTrigger::setLeaveCmd(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
static_cast<CollisionTrigger*>(object)->setMaskBits(LeaveCmdMask);
|
||||||
|
return true; // to update the actual field
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionTrigger::setTickCmd(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
static_cast<CollisionTrigger*>(object)->setMaskBits(TickCmdMask);
|
||||||
|
return true; // to update the actual field
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool CollisionTrigger::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
onAdd_callback(getId());
|
||||||
|
|
||||||
|
Polyhedron temp = mCollisionTriggerPolyhedron;
|
||||||
|
setTriggerPolyhedron(temp);
|
||||||
|
|
||||||
|
addToScene();
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
scriptOnAdd();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::onRemove()
|
||||||
|
{
|
||||||
|
onRemove_callback(getId());
|
||||||
|
|
||||||
|
mConvexList->nukeList();
|
||||||
|
|
||||||
|
removeFromScene();
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionTrigger::onNewDataBlock(GameBaseData *dptr, bool reload)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::onDeleteNotify(SimObject *obj)
|
||||||
|
{
|
||||||
|
GameBase* pScene = dynamic_cast<GameBase*>(obj);
|
||||||
|
|
||||||
|
if (pScene != NULL)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < mObjects.size(); i++)
|
||||||
|
{
|
||||||
|
if (pScene == mObjects[i])
|
||||||
|
{
|
||||||
|
mObjects.erase(i);
|
||||||
|
//onLeaveCollisionTrigger_callback(this, pScene);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent::onDeleteNotify(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::inspectPostApply()
|
||||||
|
{
|
||||||
|
setTriggerPolyhedron(mCollisionTriggerPolyhedron);
|
||||||
|
setMaskBits(PolyMask);
|
||||||
|
Parent::inspectPostApply();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CollisionTrigger::buildConvex(const Box3F& box, Convex* convex)
|
||||||
|
{
|
||||||
|
// These should really come out of a pool
|
||||||
|
mConvexList->collectGarbage();
|
||||||
|
|
||||||
|
Box3F realBox = box;
|
||||||
|
mWorldToObj.mul(realBox);
|
||||||
|
realBox.minExtents.convolveInverse(mObjScale);
|
||||||
|
realBox.maxExtents.convolveInverse(mObjScale);
|
||||||
|
|
||||||
|
if (realBox.isOverlapped(getObjBox()) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Just return a box convex for the entire shape...
|
||||||
|
Convex* cc = 0;
|
||||||
|
CollisionWorkingList& wl = convex->getWorkingList();
|
||||||
|
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
|
||||||
|
if (itr->mConvex->getType() == BoxConvexType &&
|
||||||
|
itr->mConvex->getObject() == this) {
|
||||||
|
cc = itr->mConvex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Create a new convex.
|
||||||
|
BoxConvex* cp = new BoxConvex;
|
||||||
|
mConvexList->registerObject(cp);
|
||||||
|
convex->addToWorkingList(cp);
|
||||||
|
cp->init(this);
|
||||||
|
|
||||||
|
mObjBox.getCenter(&cp->mCenter);
|
||||||
|
cp->mSize.x = mObjBox.len_x() / 2.0f;
|
||||||
|
cp->mSize.y = mObjBox.len_y() / 2.0f;
|
||||||
|
cp->mSize.z = mObjBox.len_z() / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CollisionTrigger::setTransform(const MatrixF & mat)
|
||||||
|
{
|
||||||
|
Parent::setTransform(mat);
|
||||||
|
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->setTransform(mat);
|
||||||
|
|
||||||
|
if (isServerObject()) {
|
||||||
|
MatrixF base(true);
|
||||||
|
base.scale(Point3F(1.0 / mObjScale.x,
|
||||||
|
1.0 / mObjScale.y,
|
||||||
|
1.0 / mObjScale.z));
|
||||||
|
base.mul(mWorldToObj);
|
||||||
|
mClippedList.setBaseTransform(base);
|
||||||
|
|
||||||
|
setMaskBits(TransformMask | ScaleMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::prepRenderImage(SceneRenderState *state)
|
||||||
|
{
|
||||||
|
// only render if selected or render flag is set
|
||||||
|
if (!smRenderCollisionTriggers && !isSelected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||||
|
ri->renderDelegate.bind(this, &CollisionTrigger::renderObject);
|
||||||
|
ri->type = RenderPassManager::RIT_Editor;
|
||||||
|
ri->translucentSort = true;
|
||||||
|
ri->defaultKey = 1;
|
||||||
|
state->getRenderPass()->addInst(ri);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::renderObject(ObjectRenderInst *ri,
|
||||||
|
SceneRenderState *state,
|
||||||
|
BaseMatInstance *overrideMat)
|
||||||
|
{
|
||||||
|
if (overrideMat)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GFXStateBlockDesc desc;
|
||||||
|
desc.setZReadWrite(true, false);
|
||||||
|
desc.setBlend(true);
|
||||||
|
|
||||||
|
// CollisionTrigger polyhedrons are set up with outward facing normals and CCW ordering
|
||||||
|
// so can't enable backface culling.
|
||||||
|
desc.setCullMode(GFXCullNone);
|
||||||
|
|
||||||
|
GFXTransformSaver saver;
|
||||||
|
|
||||||
|
MatrixF mat = getRenderTransform();
|
||||||
|
mat.scale(getScale());
|
||||||
|
|
||||||
|
GFX->multWorld(mat);
|
||||||
|
|
||||||
|
GFXDrawUtil *drawer = GFX->getDrawUtil();
|
||||||
|
|
||||||
|
drawer->drawPolyhedron(desc, mCollisionTriggerPolyhedron, ColorI(255, 192, 0, 45));
|
||||||
|
|
||||||
|
// Render wireframe.
|
||||||
|
|
||||||
|
desc.setFillModeWireframe();
|
||||||
|
drawer->drawPolyhedron(desc, mCollisionTriggerPolyhedron, ColorI::BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron)
|
||||||
|
{
|
||||||
|
mCollisionTriggerPolyhedron = rPolyhedron;
|
||||||
|
|
||||||
|
if (mCollisionTriggerPolyhedron.pointList.size() != 0) {
|
||||||
|
mObjBox.minExtents.set(1e10, 1e10, 1e10);
|
||||||
|
mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
|
||||||
|
for (U32 i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++) {
|
||||||
|
mObjBox.minExtents.setMin(mCollisionTriggerPolyhedron.pointList[i]);
|
||||||
|
mObjBox.maxExtents.setMax(mCollisionTriggerPolyhedron.pointList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mObjBox.minExtents.set(-0.5, -0.5, -0.5);
|
||||||
|
mObjBox.maxExtents.set(0.5, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixF xform = getTransform();
|
||||||
|
setTransform(xform);
|
||||||
|
|
||||||
|
mClippedList.clear();
|
||||||
|
mClippedList.mPlaneList = mCollisionTriggerPolyhedron.planeList;
|
||||||
|
// for (U32 i = 0; i < mClippedList.mPlaneList.size(); i++)
|
||||||
|
// mClippedList.mPlaneList[i].neg();
|
||||||
|
|
||||||
|
MatrixF base(true);
|
||||||
|
base.scale(Point3F(1.0 / mObjScale.x,
|
||||||
|
1.0 / mObjScale.y,
|
||||||
|
1.0 / mObjScale.z));
|
||||||
|
base.mul(mWorldToObj);
|
||||||
|
|
||||||
|
mClippedList.setBaseTransform(base);
|
||||||
|
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
if (PHYSICSMGR)
|
||||||
|
{
|
||||||
|
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
|
||||||
|
|
||||||
|
MatrixF colMat(true);
|
||||||
|
colMat.displace(Point3F(0, 0, mObjBox.getExtents().z * 0.5f * mObjScale.z));
|
||||||
|
|
||||||
|
colShape->addBox(mObjBox.getExtents() * 0.5f * mObjScale, colMat);
|
||||||
|
//MatrixF colMat( true );
|
||||||
|
//colMat.scale( mObjScale );
|
||||||
|
//colShape->addConvex( mCollisionTriggerPolyhedron.pointList.address(), mCollisionTriggerPolyhedron.pointList.size(), colMat );
|
||||||
|
|
||||||
|
PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world);
|
||||||
|
mPhysicsRep->setTransform(getTransform());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool CollisionTrigger::testObject(GameBase* enter)
|
||||||
|
{
|
||||||
|
if (mCollisionTriggerPolyhedron.pointList.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mClippedList.clear();
|
||||||
|
|
||||||
|
SphereF sphere;
|
||||||
|
sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
|
||||||
|
VectorF bv = mWorldBox.maxExtents - sphere.center;
|
||||||
|
sphere.radius = bv.len();
|
||||||
|
|
||||||
|
enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere);
|
||||||
|
return mClippedList.isEmpty() == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CollisionTrigger::potentialEnterObject(GameBase* enter)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < mObjects.size(); i++) {
|
||||||
|
if (mObjects[i] == enter)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testObject(enter) == true) {
|
||||||
|
mObjects.push_back(enter);
|
||||||
|
deleteNotify(enter);
|
||||||
|
|
||||||
|
if (!mEnterCommand.isEmpty())
|
||||||
|
{
|
||||||
|
String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand;
|
||||||
|
Con::evaluate(command.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
//onEnterCollisionTrigger_callback(this, enter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CollisionTrigger::processTick(const Move* move)
|
||||||
|
{
|
||||||
|
Parent::processTick(move);
|
||||||
|
|
||||||
|
//
|
||||||
|
if (mObjects.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mLastThink + 100 < mCurrTick)
|
||||||
|
{
|
||||||
|
mCurrTick = 0;
|
||||||
|
mLastThink = 0;
|
||||||
|
|
||||||
|
for (S32 i = S32(mObjects.size() - 1); i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (testObject(mObjects[i]) == false)
|
||||||
|
{
|
||||||
|
GameBase* remove = mObjects[i];
|
||||||
|
mObjects.erase(i);
|
||||||
|
clearNotify(remove);
|
||||||
|
|
||||||
|
if (!mLeaveCommand.isEmpty())
|
||||||
|
{
|
||||||
|
String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand;
|
||||||
|
Con::evaluate(command.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
//onLeaveCollisionTrigger_callback(this, remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mTickCommand.isEmpty())
|
||||||
|
Con::evaluate(mTickCommand.c_str());
|
||||||
|
|
||||||
|
//if (mObjects.size() != 0)
|
||||||
|
// onTickCollisionTrigger_callback(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCurrTick += TickMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
U32 CollisionTrigger::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||||
|
{
|
||||||
|
U32 i;
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & TransformMask))
|
||||||
|
{
|
||||||
|
stream->writeAffineTransform(mObjToWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the polyhedron
|
||||||
|
if (stream->writeFlag(mask & PolyMask))
|
||||||
|
{
|
||||||
|
stream->write(mCollisionTriggerPolyhedron.pointList.size());
|
||||||
|
for (i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++)
|
||||||
|
mathWrite(*stream, mCollisionTriggerPolyhedron.pointList[i]);
|
||||||
|
|
||||||
|
stream->write(mCollisionTriggerPolyhedron.planeList.size());
|
||||||
|
for (i = 0; i < mCollisionTriggerPolyhedron.planeList.size(); i++)
|
||||||
|
mathWrite(*stream, mCollisionTriggerPolyhedron.planeList[i]);
|
||||||
|
|
||||||
|
stream->write(mCollisionTriggerPolyhedron.edgeList.size());
|
||||||
|
for (i = 0; i < mCollisionTriggerPolyhedron.edgeList.size(); i++) {
|
||||||
|
const Polyhedron::Edge& rEdge = mCollisionTriggerPolyhedron.edgeList[i];
|
||||||
|
|
||||||
|
stream->write(rEdge.face[0]);
|
||||||
|
stream->write(rEdge.face[1]);
|
||||||
|
stream->write(rEdge.vertex[0]);
|
||||||
|
stream->write(rEdge.vertex[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & EnterCmdMask))
|
||||||
|
stream->writeLongString(CMD_SIZE - 1, mEnterCommand.c_str());
|
||||||
|
if (stream->writeFlag(mask & LeaveCmdMask))
|
||||||
|
stream->writeLongString(CMD_SIZE - 1, mLeaveCommand.c_str());
|
||||||
|
if (stream->writeFlag(mask & TickCmdMask))
|
||||||
|
stream->writeLongString(CMD_SIZE - 1, mTickCommand.c_str());
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionTrigger::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
U32 i, size;
|
||||||
|
|
||||||
|
// Transform
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
MatrixF temp;
|
||||||
|
stream->readAffineTransform(&temp);
|
||||||
|
setTransform(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the polyhedron
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
Polyhedron tempPH;
|
||||||
|
stream->read(&size);
|
||||||
|
tempPH.pointList.setSize(size);
|
||||||
|
for (i = 0; i < tempPH.pointList.size(); i++)
|
||||||
|
mathRead(*stream, &tempPH.pointList[i]);
|
||||||
|
|
||||||
|
stream->read(&size);
|
||||||
|
tempPH.planeList.setSize(size);
|
||||||
|
for (i = 0; i < tempPH.planeList.size(); i++)
|
||||||
|
mathRead(*stream, &tempPH.planeList[i]);
|
||||||
|
|
||||||
|
stream->read(&size);
|
||||||
|
tempPH.edgeList.setSize(size);
|
||||||
|
for (i = 0; i < tempPH.edgeList.size(); i++) {
|
||||||
|
Polyhedron::Edge& rEdge = tempPH.edgeList[i];
|
||||||
|
|
||||||
|
stream->read(&rEdge.face[0]);
|
||||||
|
stream->read(&rEdge.face[1]);
|
||||||
|
stream->read(&rEdge.vertex[0]);
|
||||||
|
stream->read(&rEdge.vertex[1]);
|
||||||
|
}
|
||||||
|
setTriggerPolyhedron(tempPH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
char buf[CMD_SIZE];
|
||||||
|
stream->readLongString(CMD_SIZE - 1, buf);
|
||||||
|
mEnterCommand = buf;
|
||||||
|
}
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
char buf[CMD_SIZE];
|
||||||
|
stream->readLongString(CMD_SIZE - 1, buf);
|
||||||
|
mLeaveCommand = buf;
|
||||||
|
}
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
char buf[CMD_SIZE];
|
||||||
|
stream->readLongString(CMD_SIZE - 1, buf);
|
||||||
|
mTickCommand = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ConsoleMethod( CollisionTrigger, getNumObjects, S32, 2, 2, "")
|
||||||
|
DefineEngineMethod(CollisionTrigger, getNumObjects, S32, (), ,
|
||||||
|
"@brief Get the number of objects that are within the CollisionTrigger's bounds.\n\n"
|
||||||
|
"@see getObject()\n")
|
||||||
|
{
|
||||||
|
return object->getNumCollisionTriggeringObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
//ConsoleMethod( CollisionTrigger, getObject, S32, 3, 3, "(int idx)")
|
||||||
|
DefineEngineMethod(CollisionTrigger, getObject, S32, (S32 index), ,
|
||||||
|
"@brief Retrieve the requested object that is within the CollisionTrigger's bounds.\n\n"
|
||||||
|
"@param index Index of the object to get (range is 0 to getNumObjects()-1)\n"
|
||||||
|
"@returns The SimObjectID of the object, or -1 if the requested index is invalid.\n"
|
||||||
|
"@see getNumObjects()\n")
|
||||||
|
{
|
||||||
|
if (index >= object->getNumCollisionTriggeringObjects() || index < 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return object->getObject(U32(index))->getId();
|
||||||
|
}
|
||||||
145
Engine/source/T3D/components/collision/collisionTrigger.h
Normal file
145
Engine/source/T3D/components/collision/collisionTrigger.h
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef _H_CollisionTrigger
|
||||||
|
#define _H_CollisionTrigger
|
||||||
|
|
||||||
|
#ifndef _GAMEBASE_H_
|
||||||
|
#include "T3D/gameBase/gameBase.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _EARLYOUTPOLYLIST_H_
|
||||||
|
#include "collision/earlyOutPolyList.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MPOLYHEDRON_H_
|
||||||
|
#include "math/mPolyhedron.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TRIGGER_H_
|
||||||
|
#include "T3D/trigger.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Convex;
|
||||||
|
class PhysicsBody;
|
||||||
|
class TriggerPolyhedronType;
|
||||||
|
|
||||||
|
class CollisionTrigger : public GameBase
|
||||||
|
{
|
||||||
|
typedef GameBase Parent;
|
||||||
|
|
||||||
|
/// CollisionTrigger polyhedron with *outward* facing normals and CCW ordered
|
||||||
|
/// vertices.
|
||||||
|
Polyhedron mCollisionTriggerPolyhedron;
|
||||||
|
|
||||||
|
EarlyOutPolyList mClippedList;
|
||||||
|
Vector<GameBase*> mObjects;
|
||||||
|
|
||||||
|
PhysicsBody *mPhysicsRep;
|
||||||
|
|
||||||
|
U32 mLastThink;
|
||||||
|
U32 mCurrTick;
|
||||||
|
Convex *mConvexList;
|
||||||
|
|
||||||
|
String mEnterCommand;
|
||||||
|
String mLeaveCommand;
|
||||||
|
String mTickCommand;
|
||||||
|
|
||||||
|
enum CollisionTriggerUpdateBits
|
||||||
|
{
|
||||||
|
TransformMask = Parent::NextFreeMask << 0,
|
||||||
|
PolyMask = Parent::NextFreeMask << 1,
|
||||||
|
EnterCmdMask = Parent::NextFreeMask << 2,
|
||||||
|
LeaveCmdMask = Parent::NextFreeMask << 3,
|
||||||
|
TickCmdMask = Parent::NextFreeMask << 4,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const U32 CMD_SIZE = 1024;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static bool smRenderCollisionTriggers;
|
||||||
|
bool testObject(GameBase* enter);
|
||||||
|
void processTick(const Move *move);
|
||||||
|
|
||||||
|
void buildConvex(const Box3F& box, Convex* convex);
|
||||||
|
|
||||||
|
static bool setEnterCmd(void *object, const char *index, const char *data);
|
||||||
|
static bool setLeaveCmd(void *object, const char *index, const char *data);
|
||||||
|
static bool setTickCmd(void *object, const char *index, const char *data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CollisionTrigger();
|
||||||
|
~CollisionTrigger();
|
||||||
|
|
||||||
|
// SimObject
|
||||||
|
DECLARE_CONOBJECT(CollisionTrigger);
|
||||||
|
|
||||||
|
DECLARE_CALLBACK(void, onAdd, (U32 objectId));
|
||||||
|
DECLARE_CALLBACK(void, onRemove, (U32 objectId));
|
||||||
|
|
||||||
|
static void consoleInit();
|
||||||
|
static void initPersistFields();
|
||||||
|
bool onAdd();
|
||||||
|
void onRemove();
|
||||||
|
void onDeleteNotify(SimObject*);
|
||||||
|
void inspectPostApply();
|
||||||
|
|
||||||
|
// NetObject
|
||||||
|
U32 packUpdate(NetConnection *conn, U32 mask, BitStream* stream);
|
||||||
|
void unpackUpdate(NetConnection *conn, BitStream* stream);
|
||||||
|
|
||||||
|
// SceneObject
|
||||||
|
void setTransform(const MatrixF &mat);
|
||||||
|
void prepRenderImage(SceneRenderState* state);
|
||||||
|
|
||||||
|
// GameBase
|
||||||
|
bool onNewDataBlock(GameBaseData *dptr, bool reload);
|
||||||
|
|
||||||
|
// CollisionTrigger
|
||||||
|
void setTriggerPolyhedron(const Polyhedron&);
|
||||||
|
|
||||||
|
void potentialEnterObject(GameBase*);
|
||||||
|
U32 getNumCollisionTriggeringObjects() const;
|
||||||
|
GameBase* getObject(const U32);
|
||||||
|
const Vector<GameBase*>& getObjects() const { return mObjects; }
|
||||||
|
|
||||||
|
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
|
||||||
|
|
||||||
|
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline U32 CollisionTrigger::getNumCollisionTriggeringObjects() const
|
||||||
|
{
|
||||||
|
return mObjects.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GameBase* CollisionTrigger::getObject(const U32 index)
|
||||||
|
{
|
||||||
|
AssertFatal(index < getNumCollisionTriggeringObjects(), "Error, out of range object index");
|
||||||
|
|
||||||
|
return mObjects[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _H_CollisionTrigger
|
||||||
|
|
||||||
640
Engine/source/T3D/components/component.cpp
Normal file
640
Engine/source/T3D/components/component.cpp
Normal file
|
|
@ -0,0 +1,640 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/simBase.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "console/consoleInternal.h"
|
||||||
|
|
||||||
|
#define DECLARE_NATIVE_COMPONENT( ComponentType ) \
|
||||||
|
Component* staticComponentTemplate = new ComponentType; \
|
||||||
|
Sim::gNativeComponentSet->addObject(staticComponentTemplate);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Component::Component()
|
||||||
|
{
|
||||||
|
mFriendlyName = StringTable->lookup("");
|
||||||
|
mFromResource = StringTable->lookup("");
|
||||||
|
mComponentType = StringTable->lookup("");
|
||||||
|
mComponentGroup = StringTable->lookup("");
|
||||||
|
mNetworkType = StringTable->lookup("");
|
||||||
|
mTemplateName = StringTable->lookup("");
|
||||||
|
//mDependency = StringTable->lookup("");
|
||||||
|
|
||||||
|
mNetworked = false;
|
||||||
|
|
||||||
|
|
||||||
|
// [tom, 1/12/2007] We manage the memory for the description since it
|
||||||
|
// could be loaded from a file and thus massive. This is accomplished with
|
||||||
|
// protected fields, but since they still call Con::getData() the field
|
||||||
|
// needs to always be valid. This is pretty lame.
|
||||||
|
mDescription = new char[1];
|
||||||
|
((char *)mDescription)[0] = 0;
|
||||||
|
|
||||||
|
mOwner = NULL;
|
||||||
|
|
||||||
|
mCanSaveFieldDictionary = false;
|
||||||
|
|
||||||
|
mNetFlags.set(Ghostable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component::~Component()
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < mFields.size(); ++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(Component);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Component::initPersistFields()
|
||||||
|
{
|
||||||
|
addGroup("Component");
|
||||||
|
addField("componentType", TypeCaseString, Offset(mComponentType, Component), "The type of behavior.", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
addField("networkType", TypeCaseString, Offset(mNetworkType, Component), "The type of behavior.", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
addField("friendlyName", TypeCaseString, Offset(mFriendlyName, Component), "Human friendly name of this behavior", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
addProtectedField("description", TypeCaseString, Offset(mDescription, Component), &setDescription, &getDescription,
|
||||||
|
"The description of this behavior which can be set to a \"string\" or a fileName\n", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
|
||||||
|
addField("networked", TypeBool, Offset(mNetworked, Component), "Is this behavior ghosted to clients?", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
|
||||||
|
addProtectedField("Owner", TypeSimObjectPtr, Offset(mOwner, Component), &setOwner, &defaultProtectedGetFn, "", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
|
||||||
|
//addField("hidden", TypeBool, Offset(mHidden, Component), "Flags if this behavior is shown in the editor or not", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||||
|
addProtectedField("enabled", TypeBool, Offset(mEnabled, Component), &_setEnabled, &defaultProtectedGetFn, "");
|
||||||
|
endGroup("Component");
|
||||||
|
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
//clear out irrelevent fields
|
||||||
|
removeField("name");
|
||||||
|
//removeField("internalName");
|
||||||
|
removeField("parentGroup");
|
||||||
|
//removeField("class");
|
||||||
|
removeField("superClass");
|
||||||
|
removeField("hidden");
|
||||||
|
removeField("canSave");
|
||||||
|
removeField("canSaveDynamicFields");
|
||||||
|
removeField("persistentId");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Component::_setEnabled(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
Component *c = static_cast<Component*>(object);
|
||||||
|
|
||||||
|
c->mEnabled = dAtob(data);
|
||||||
|
c->setMaskBits(EnableMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool Component::setDescription(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
Component *bT = static_cast<Component *>(object);
|
||||||
|
SAFE_DELETE_ARRAY(bT->mDescription);
|
||||||
|
bT->mDescription = bT->getDescriptionText(data);
|
||||||
|
|
||||||
|
// We return false since we don't want the console to mess with the data
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * Component::getDescription(void* obj, const char* data)
|
||||||
|
{
|
||||||
|
Component *object = static_cast<Component *>(obj);
|
||||||
|
|
||||||
|
return object->mDescription ? object->mDescription : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
bool Component::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setMaskBits(UpdateMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::onRemove()
|
||||||
|
{
|
||||||
|
onDataSet.removeAll();
|
||||||
|
|
||||||
|
if (mOwner)
|
||||||
|
{
|
||||||
|
//notify our removal to the owner, so we have no loose ends
|
||||||
|
mOwner->removeComponent(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::onComponentAdd()
|
||||||
|
{
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
if (isMethod("onAdd"))
|
||||||
|
Con::executef(this, "onAdd");
|
||||||
|
}
|
||||||
|
|
||||||
|
mEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::onComponentRemove()
|
||||||
|
{
|
||||||
|
mEnabled = false;
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
if (isMethod("onRemove"))
|
||||||
|
Con::executef(this, "onRemove");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOwner)
|
||||||
|
{
|
||||||
|
mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
|
||||||
|
mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
|
||||||
|
mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwner = NULL;
|
||||||
|
setDataField("owner", NULL, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::setOwner(Entity* owner)
|
||||||
|
{
|
||||||
|
//first, catch if we have an existing owner, and we're changing from it
|
||||||
|
if (mOwner && mOwner != owner)
|
||||||
|
{
|
||||||
|
mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
|
||||||
|
mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
|
||||||
|
mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
|
||||||
|
|
||||||
|
mOwner->removeComponent(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwner = owner;
|
||||||
|
|
||||||
|
if (mOwner != NULL)
|
||||||
|
{
|
||||||
|
mOwner->onComponentAdded.notify(this, &Component::componentAddedToOwner);
|
||||||
|
mOwner->onComponentRemoved.notify(this, &Component::componentRemovedFromOwner);
|
||||||
|
mOwner->onTransformSet.notify(this, &Component::ownerTransformSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
setMaskBits(OwnerMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::ownerTransformSet(MatrixF *mat)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (mask & OwnerMask)
|
||||||
|
{
|
||||||
|
if (mOwner != NULL)
|
||||||
|
{
|
||||||
|
S32 ghostIndex = con->getGhostIndex(mOwner);
|
||||||
|
|
||||||
|
if (ghostIndex == -1)
|
||||||
|
{
|
||||||
|
stream->writeFlag(false);
|
||||||
|
retMask |= OwnerMask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream->writeFlag(true);
|
||||||
|
stream->writeFlag(true);
|
||||||
|
stream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream->writeFlag(true);
|
||||||
|
stream->writeFlag(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stream->writeFlag(false);
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & EnableMask))
|
||||||
|
{
|
||||||
|
stream->writeFlag(mEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
//we have an owner object, so fetch it
|
||||||
|
S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
|
||||||
|
|
||||||
|
Entity *e = dynamic_cast<Entity*>(con->resolveGhost(gIndex));
|
||||||
|
if (e)
|
||||||
|
e->addComponent(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//it's being nulled out
|
||||||
|
setOwner(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->readFlag())
|
||||||
|
{
|
||||||
|
mEnabled = stream->readFlag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::packToStream(Stream &stream, U32 tabStop, S32 behaviorID, U32 flags /* = 0 */)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
writeFields(stream, tabStop);
|
||||||
|
|
||||||
|
// Write out the fields which the behavior template knows about
|
||||||
|
for (int i = 0; i < getComponentFieldCount(); i++)
|
||||||
|
{
|
||||||
|
ComponentField *field = getComponentField(i);
|
||||||
|
const char *objFieldValue = getDataField(field->mFieldName, NULL);
|
||||||
|
|
||||||
|
// If the field holds the same value as the template's default value than it
|
||||||
|
// will get initialized by the template, and so it won't be included just
|
||||||
|
// to try to keep the object files looking as non-horrible as possible.
|
||||||
|
if (dStrcmp(field->mDefaultValue, objFieldValue) != 0)
|
||||||
|
{
|
||||||
|
dSprintf(buffer, sizeof(buffer), "%s = \"%s\";\n", field->mFieldName, (dStrlen(objFieldValue) > 0 ? objFieldValue : "0"));
|
||||||
|
|
||||||
|
stream.writeTabs(tabStop);
|
||||||
|
stream.write(dStrlen(buffer), buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::processTick()
|
||||||
|
{
|
||||||
|
if (isServerObject() && mEnabled)
|
||||||
|
{
|
||||||
|
if (mOwner != NULL && isMethod("Update"))
|
||||||
|
Con::executef(this, "Update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::setDataField(StringTableEntry slotName, const char *array, const char *value)
|
||||||
|
{
|
||||||
|
Parent::setDataField(slotName, array, value);
|
||||||
|
|
||||||
|
onDataSet.trigger(this, slotName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//catch any behavior field updates
|
||||||
|
void Component::onStaticModified(const char* slotName, const char* newValue)
|
||||||
|
{
|
||||||
|
Parent::onStaticModified(slotName, newValue);
|
||||||
|
|
||||||
|
//If we don't have an owner yet, then this is probably the initial setup, so we don't need the console callbacks yet.
|
||||||
|
if (!mOwner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
onDataSet.trigger(this, slotName, newValue);
|
||||||
|
|
||||||
|
checkComponentFieldModified(slotName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::onDynamicModified(const char* slotName, const char* newValue)
|
||||||
|
{
|
||||||
|
Parent::onDynamicModified(slotName, newValue);
|
||||||
|
|
||||||
|
//If we don't have an owner yet, then this is probably the initial setup, so we don't need the console callbacks yet.
|
||||||
|
if (!mOwner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
checkComponentFieldModified(slotName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::checkComponentFieldModified(const char* slotName, const char* newValue)
|
||||||
|
{
|
||||||
|
StringTableEntry slotNameEntry = StringTable->insert(slotName);
|
||||||
|
|
||||||
|
//find if it's a behavior field
|
||||||
|
for (int i = 0; i < mFields.size(); i++)
|
||||||
|
{
|
||||||
|
ComponentField *field = getComponentField(i);
|
||||||
|
if (field->mFieldName == slotNameEntry)
|
||||||
|
{
|
||||||
|
//we have a match, do the script callback that we updated a field
|
||||||
|
if (isMethod("onInspectorUpdate"))
|
||||||
|
Con::executef(this, "onInspectorUpdate", slotName);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
void Component::addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue /* = NULL */, const char *userData /* = NULL */, /*const char* dependency /* = NULL *//*,*/ bool hidden /* = false */)
|
||||||
|
{
|
||||||
|
StringTableEntry stFieldName = StringTable->insert(fieldName);
|
||||||
|
|
||||||
|
for (S32 i = 0; i < mFields.size(); ++i)
|
||||||
|
{
|
||||||
|
if (mFields[i].mFieldName == stFieldName)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentField field;
|
||||||
|
field.mFieldName = stFieldName;
|
||||||
|
|
||||||
|
//find the field type
|
||||||
|
S32 fieldTypeMask = -1;
|
||||||
|
StringTableEntry fieldType = StringTable->insert(type);
|
||||||
|
|
||||||
|
if (fieldType == StringTable->insert("int"))
|
||||||
|
fieldTypeMask = TypeS32;
|
||||||
|
else if (fieldType == StringTable->insert("float"))
|
||||||
|
fieldTypeMask = TypeF32;
|
||||||
|
else if (fieldType == StringTable->insert("vector"))
|
||||||
|
fieldTypeMask = TypePoint3F;
|
||||||
|
else if (fieldType == StringTable->insert("material"))
|
||||||
|
fieldTypeMask = TypeMaterialName;
|
||||||
|
else if (fieldType == StringTable->insert("image"))
|
||||||
|
fieldTypeMask = TypeImageFilename;
|
||||||
|
else if (fieldType == StringTable->insert("shape"))
|
||||||
|
fieldTypeMask = TypeShapeFilename;
|
||||||
|
else if (fieldType == StringTable->insert("bool"))
|
||||||
|
fieldTypeMask = TypeBool;
|
||||||
|
else if (fieldType == StringTable->insert("object"))
|
||||||
|
fieldTypeMask = TypeSimObjectPtr;
|
||||||
|
else
|
||||||
|
fieldTypeMask = TypeString;
|
||||||
|
|
||||||
|
field.mFieldType = fieldTypeMask;
|
||||||
|
|
||||||
|
field.mUserData = StringTable->insert(userData ? userData : "");
|
||||||
|
field.mDefaultValue = StringTable->insert(defaultValue ? defaultValue : "");
|
||||||
|
field.mFieldDescription = getDescriptionText(desc);
|
||||||
|
|
||||||
|
field.mGroup = mComponentGroup;
|
||||||
|
|
||||||
|
field.mHidden = hidden;
|
||||||
|
|
||||||
|
mFields.push_back(field);
|
||||||
|
|
||||||
|
//Before we set this, we need to do a test to see if this field was already set, like from the mission file or a taml file
|
||||||
|
const char* curFieldData = getDataField(field.mFieldName, NULL);
|
||||||
|
|
||||||
|
if (dStrIsEmpty(curFieldData))
|
||||||
|
setDataField(field.mFieldName, NULL, field.mDefaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentField* Component::getComponentField(const char *fieldName)
|
||||||
|
{
|
||||||
|
StringTableEntry stFieldName = StringTable->insert(fieldName);
|
||||||
|
|
||||||
|
for (S32 i = 0; i < mFields.size(); ++i)
|
||||||
|
{
|
||||||
|
if (mFields[i].mFieldName == stFieldName)
|
||||||
|
return &mFields[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const char * Component::getDescriptionText(const char *desc)
|
||||||
|
{
|
||||||
|
if (desc == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *newDesc;
|
||||||
|
|
||||||
|
// [tom, 1/12/2007] If it isn't a file, just do it the easy way
|
||||||
|
if (!Platform::isFile(desc))
|
||||||
|
{
|
||||||
|
newDesc = new char[dStrlen(desc) + 1];
|
||||||
|
dStrcpy(newDesc, desc);
|
||||||
|
|
||||||
|
return newDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStream str;
|
||||||
|
str.open(desc, Torque::FS::File::Read);
|
||||||
|
|
||||||
|
Stream *stream = &str;
|
||||||
|
if (stream == NULL){
|
||||||
|
str.close();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 size = stream->getStreamSize();
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
newDesc = new char[size + 1];
|
||||||
|
if (stream->read(size, (void *)newDesc))
|
||||||
|
newDesc[size] = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SAFE_DELETE_ARRAY(newDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str.close();
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
return newDesc;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
void Component::beginFieldGroup(const char* groupName)
|
||||||
|
{
|
||||||
|
if (dStrcmp(mComponentGroup, ""))
|
||||||
|
{
|
||||||
|
Con::errorf("Component: attempting to begin new field group with a group already begun!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mComponentGroup = StringTable->insert(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::endFieldGroup()
|
||||||
|
{
|
||||||
|
mComponentGroup = StringTable->insert("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::addDependency(StringTableEntry name)
|
||||||
|
{
|
||||||
|
mDependencies.push_back_unique(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Console Methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
ConsoleMethod(Component, beginGroup, void, 3, 3, "(groupName)\n"
|
||||||
|
"Starts the grouping for following fields being added to be grouped into\n"
|
||||||
|
"@param groupName The name of this group\n"
|
||||||
|
"@param desc The Description of this field\n"
|
||||||
|
"@param type The DataType for this field (default, int, float, Point2F, bool, enum, Object, keybind, color)\n"
|
||||||
|
"@param defaultValue The Default value for this field\n"
|
||||||
|
"@param userData An extra data field that can be used for custom data on a per-field basis<br>Usage for default types<br>"
|
||||||
|
"-enum: a TAB separated list of possible values<br>"
|
||||||
|
"-object: the T2D object type that are valid choices for the field. The object types observe inheritance, so if you have a t2dSceneObject field you will be able to choose t2dStaticSrpites, t2dAnimatedSprites, etc.\n"
|
||||||
|
"@return Nothing\n")
|
||||||
|
{
|
||||||
|
object->beginFieldGroup(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, endGroup, void, 2, 2, "()\n"
|
||||||
|
"Ends the grouping for prior fields being added to be grouped into\n"
|
||||||
|
"@param groupName The name of this group\n"
|
||||||
|
"@param desc The Description of this field\n"
|
||||||
|
"@param type The DataType for this field (default, int, float, Point2F, bool, enum, Object, keybind, color)\n"
|
||||||
|
"@param defaultValue The Default value for this field\n"
|
||||||
|
"@param userData An extra data field that can be used for custom data on a per-field basis<br>Usage for default types<br>"
|
||||||
|
"-enum: a TAB separated list of possible values<br>"
|
||||||
|
"-object: the T2D object type that are valid choices for the field. The object types observe inheritance, so if you have a t2dSceneObject field you will be able to choose t2dStaticSrpites, t2dAnimatedSprites, etc.\n"
|
||||||
|
"@return Nothing\n")
|
||||||
|
{
|
||||||
|
object->endFieldGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineConsoleMethod(Component, addComponentField, void, (String fieldName, String fieldDesc, String fieldType, String defValue, String userData, bool hidden),
|
||||||
|
("", "", "", "", "", false),
|
||||||
|
"Get the number of static fields on the object.\n"
|
||||||
|
"@return The number of static fields defined on the object.")
|
||||||
|
{
|
||||||
|
object->addComponentField(fieldName, fieldDesc, fieldType, defValue, userData, hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, getComponentFieldCount, S32, 2, 2, "() - Get the number of ComponentField's on this object\n"
|
||||||
|
"@return Returns the number of BehaviorFields as a nonnegative integer\n")
|
||||||
|
{
|
||||||
|
return object->getComponentFieldCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// [tom, 1/12/2007] Field accessors split into multiple methods to allow space
|
||||||
|
// for long descriptions and type data.
|
||||||
|
|
||||||
|
ConsoleMethod(Component, getComponentField, const char *, 3, 3, "(int index) - Gets a Tab-Delimited list of information about a ComponentField specified by Index\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return FieldName, FieldType and FieldDefaultValue, each separated by a TAB character.\n")
|
||||||
|
{
|
||||||
|
ComponentField *field = object->getComponentField(dAtoi(argv[2]));
|
||||||
|
if (field == NULL)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
char *buf = Con::getReturnBuffer(1024);
|
||||||
|
dSprintf(buf, 1024, "%s\t%s\t%s\t%s", field->mFieldName, field->mFieldType, field->mDefaultValue, field->mGroup);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, setComponentield, const char *, 3, 3, "(int index) - Gets a Tab-Delimited list of information about a ComponentField specified by Index\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return FieldName, FieldType and FieldDefaultValue, each separated by a TAB character.\n")
|
||||||
|
{
|
||||||
|
ComponentField *field = object->getComponentField(dAtoi(argv[2]));
|
||||||
|
if (field == NULL)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
char *buf = Con::getReturnBuffer(1024);
|
||||||
|
dSprintf(buf, 1024, "%s\t%s\t%s", field->mFieldName, field->mFieldType, field->mDefaultValue);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, getBehaviorFieldUserData, const char *, 3, 3, "(int index) - Gets the UserData associated with a field by index in the field list\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return Returns a string representing the user data of this field\n")
|
||||||
|
{
|
||||||
|
ComponentField *field = object->getComponentField(dAtoi(argv[2]));
|
||||||
|
if (field == NULL)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return field->mUserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, getComponentFieldDescription, const char *, 3, 3, "(int index) - Gets a field description by index\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return Returns a string representing the description of this field\n")
|
||||||
|
{
|
||||||
|
ComponentField *field = object->getComponentField(dAtoi(argv[2]));
|
||||||
|
if (field == NULL)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return field->mFieldDescription ? field->mFieldDescription : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, addDependency, void, 3, 3, "(string behaviorName) - Gets a field description by index\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return Returns a string representing the description of this field\n")
|
||||||
|
{
|
||||||
|
object->addDependency(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Component, setDirty, void, 2, 2, "() - Gets a field description by index\n"
|
||||||
|
"@param index The index of the behavior\n"
|
||||||
|
"@return Returns a string representing the description of this field\n")
|
||||||
|
{
|
||||||
|
object->setMaskBits(Component::OwnerMask);
|
||||||
|
}
|
||||||
197
Engine/source/T3D/components/component.h
Normal file
197
Engine/source/T3D/components/component.h
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#define COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef _NETOBJECT_H_
|
||||||
|
#include "sim/netObject.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Entity;
|
||||||
|
|
||||||
|
struct ComponentField
|
||||||
|
{
|
||||||
|
StringTableEntry mFieldName;
|
||||||
|
StringTableEntry mFieldDescription;
|
||||||
|
|
||||||
|
S32 mFieldType;
|
||||||
|
StringTableEntry mUserData;
|
||||||
|
|
||||||
|
StringTableEntry mDefaultValue;
|
||||||
|
|
||||||
|
StringTableEntry mGroup;
|
||||||
|
|
||||||
|
StringTableEntry mDependency;
|
||||||
|
|
||||||
|
bool mHidden;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class Component : public NetObject, public UpdateInterface
|
||||||
|
{
|
||||||
|
typedef NetObject Parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StringTableEntry mFriendlyName;
|
||||||
|
StringTableEntry mDescription;
|
||||||
|
|
||||||
|
StringTableEntry mFromResource;
|
||||||
|
StringTableEntry mComponentGroup;
|
||||||
|
StringTableEntry mComponentType;
|
||||||
|
StringTableEntry mNetworkType;
|
||||||
|
StringTableEntry mTemplateName;
|
||||||
|
|
||||||
|
Vector<StringTableEntry> mDependencies;
|
||||||
|
Vector<ComponentField> mFields;
|
||||||
|
|
||||||
|
bool mNetworked;
|
||||||
|
|
||||||
|
U32 componentIdx;
|
||||||
|
|
||||||
|
Entity* mOwner;
|
||||||
|
bool mHidden;
|
||||||
|
bool mEnabled;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Component();
|
||||||
|
virtual ~Component();
|
||||||
|
DECLARE_CONOBJECT(Component);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void packToStream(Stream &stream, U32 tabStop, S32 behaviorID, U32 flags = 0);
|
||||||
|
|
||||||
|
//This is called when we are added to an entity
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
//This is called when we are removed from an entity
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
|
||||||
|
//This is called when a different component is added to our owner entity
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
//This is called when a different component is removed from our owner entity
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual void ownerTransformSet(MatrixF *mat);
|
||||||
|
|
||||||
|
void setOwner(Entity* pOwner);
|
||||||
|
inline Entity *getOwner() { return mOwner ? mOwner : NULL; }
|
||||||
|
static bool setOwner(void *object, const char *index, const char *data) { return true; }
|
||||||
|
|
||||||
|
bool isEnabled() { return mEnabled; }
|
||||||
|
void setEnabled(bool toggle) { mEnabled = toggle; setMaskBits(EnableMask); }
|
||||||
|
|
||||||
|
bool isActive() { return mEnabled && mOwner != NULL; }
|
||||||
|
|
||||||
|
static bool _setEnabled(void *object, const char *index, const char *data);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
virtual void interpolateTick(F32 dt){}
|
||||||
|
virtual void advanceTime(F32 dt){}
|
||||||
|
|
||||||
|
/// @name Adding Named Fields
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Adds a named field to a Component that can specify a description, data type, default value and userData
|
||||||
|
///
|
||||||
|
/// @param fieldName The name of the Field
|
||||||
|
/// @param desc The Description of the Field
|
||||||
|
/// @param type The Type of field that this is, example 'Text' or 'Bool'
|
||||||
|
/// @param defaultValue The Default value of this field
|
||||||
|
/// @param userData An extra optional field that can be used for user data
|
||||||
|
void addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue = NULL, const char *userData = NULL, bool hidden = false);
|
||||||
|
|
||||||
|
/// Returns the number of ComponentField's on this template
|
||||||
|
inline S32 getComponentFieldCount() { return mFields.size(); };
|
||||||
|
|
||||||
|
/// Gets a ComponentField by its index in the mFields vector
|
||||||
|
/// @param idx The index of the field in the mField vector
|
||||||
|
inline ComponentField *getComponentField(S32 idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || idx >= mFields.size())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &mFields[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentField *getComponentField(const char* fieldName);
|
||||||
|
|
||||||
|
const char* getComponentType() { return mComponentType; }
|
||||||
|
|
||||||
|
const char *getDescriptionText(const char *desc);
|
||||||
|
|
||||||
|
const char *getName() { return mTemplateName; }
|
||||||
|
|
||||||
|
const char *getFriendlyName() { return mFriendlyName; }
|
||||||
|
|
||||||
|
bool isNetworked() { return mNetworked; }
|
||||||
|
|
||||||
|
void beginFieldGroup(const char* groupName);
|
||||||
|
void endFieldGroup();
|
||||||
|
|
||||||
|
void addDependency(StringTableEntry name);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name Description
|
||||||
|
/// @{
|
||||||
|
static bool setDescription(void *object, const char *index, const char *data);
|
||||||
|
static const char* getDescription(void* obj, const char* data);
|
||||||
|
|
||||||
|
/// @Primary usage functions
|
||||||
|
/// @These are used by the various engine-based behaviors to integrate with the component classes
|
||||||
|
enum NetMaskBits
|
||||||
|
{
|
||||||
|
InitialUpdateMask = BIT(0),
|
||||||
|
OwnerMask = BIT(1),
|
||||||
|
UpdateMask = BIT(2),
|
||||||
|
EnableMask = BIT(3),
|
||||||
|
NextFreeMask = BIT(4)
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
Signal< void(SimObject*, String, String) > onDataSet;
|
||||||
|
virtual void setDataField(StringTableEntry slotName, const char *array, const char *value);
|
||||||
|
|
||||||
|
virtual void onStaticModified(const char* slotName, const char* newValue); ///< Called when a static field is modified.
|
||||||
|
virtual void onDynamicModified(const char* slotName, const char*newValue = NULL); ///< Called when a dynamic field is modified.
|
||||||
|
|
||||||
|
/// This is what we actually use to check if the modified field is one of our behavior fields. If it is, we update and make the correct callbacks
|
||||||
|
void checkComponentFieldModified(const char* slotName, const char* newValue);
|
||||||
|
|
||||||
|
virtual void checkDependencies(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COMPONENT_H
|
||||||
101
Engine/source/T3D/components/coreInterfaces.h
Normal file
101
Engine/source/T3D/components/coreInterfaces.h
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#define CORE_INTERFACES_H
|
||||||
|
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Vector<T*> all;
|
||||||
|
|
||||||
|
Interface()
|
||||||
|
{
|
||||||
|
all.push_back((T*)this);
|
||||||
|
}
|
||||||
|
virtual ~Interface()
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < all.size(); i++)
|
||||||
|
{
|
||||||
|
if (all[i] == (T*)this)
|
||||||
|
{
|
||||||
|
all.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename T> Vector<T*> Interface<T>::all(0);
|
||||||
|
|
||||||
|
//Basically a file for generic interfaces that many behaviors may make use of
|
||||||
|
class SetTransformInterface// : public Interface<SetTransformInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void setTransform( MatrixF transform );
|
||||||
|
virtual void setTransform( Point3F pos, EulerF rot );
|
||||||
|
};
|
||||||
|
|
||||||
|
class UpdateInterface : public Interface<UpdateInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void processTick(){}
|
||||||
|
virtual void interpolateTick(F32 dt){}
|
||||||
|
virtual void advanceTime(F32 dt){}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BehaviorFieldInterface// : public Interface<BehaviorFieldInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void onFieldChange(const char* fieldName, const char* newValue){};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CameraInterface// : public Interface<CameraInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool getCameraTransform(F32* pos,MatrixF* mat)=0;
|
||||||
|
virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)=0;
|
||||||
|
virtual Frustum getFrustum()=0;
|
||||||
|
virtual F32 getCameraFov()=0;
|
||||||
|
virtual void setCameraFov(F32 fov)=0;
|
||||||
|
|
||||||
|
virtual bool isValidCameraFov(F32 fov)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRayInterface// : public Interface<CastRayInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EditorInspectInterface// : public Interface<EditorInspectInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void onInspect()=0;
|
||||||
|
virtual void onEndInspect()=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
434
Engine/source/T3D/components/game/stateMachine.cpp
Normal file
434
Engine/source/T3D/components/game/stateMachine.cpp
Normal file
|
|
@ -0,0 +1,434 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/game/stateMachine.h"
|
||||||
|
|
||||||
|
StateMachine::StateMachine()
|
||||||
|
{
|
||||||
|
mStateStartTime = -1;
|
||||||
|
mStateTime = 0;
|
||||||
|
|
||||||
|
mStartingState = "";
|
||||||
|
|
||||||
|
mCurCreateState = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMachine::~StateMachine()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::loadStateMachineFile()
|
||||||
|
{
|
||||||
|
if (!mXMLReader)
|
||||||
|
{
|
||||||
|
SimXMLDocument *xmlrdr = new SimXMLDocument();
|
||||||
|
xmlrdr->registerObject();
|
||||||
|
|
||||||
|
mXMLReader = xmlrdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasStartState = false;
|
||||||
|
|
||||||
|
if (!dStrIsEmpty(mStateMachineFile))
|
||||||
|
{
|
||||||
|
//use our xml reader to parse the file!
|
||||||
|
SimXMLDocument *reader = mXMLReader.getObject();
|
||||||
|
if (!reader->loadFile(mStateMachineFile))
|
||||||
|
Con::errorf("Could not load state machine file: &s", mStateMachineFile);
|
||||||
|
|
||||||
|
if (!reader->pushFirstChildElement("StateMachine"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//find our starting state
|
||||||
|
if (reader->pushFirstChildElement("StartingState"))
|
||||||
|
{
|
||||||
|
mStartingState = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
hasStartState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
readStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasStartState)
|
||||||
|
mCurrentState = getStateByName(mStartingState);
|
||||||
|
|
||||||
|
mStateStartTime = -1;
|
||||||
|
mStateTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::readStates()
|
||||||
|
{
|
||||||
|
SimXMLDocument *reader = mXMLReader.getObject();
|
||||||
|
|
||||||
|
//iterate through our states now!
|
||||||
|
if (reader->pushFirstChildElement("State"))
|
||||||
|
{
|
||||||
|
//get our first state
|
||||||
|
State firstState;
|
||||||
|
|
||||||
|
readStateName(&firstState, reader);
|
||||||
|
readStateScriptFunction(&firstState, reader);
|
||||||
|
|
||||||
|
readTransitions(firstState);
|
||||||
|
|
||||||
|
mStates.push_back(firstState);
|
||||||
|
|
||||||
|
//now, iterate the siblings
|
||||||
|
while (reader->nextSiblingElement("State"))
|
||||||
|
{
|
||||||
|
State newState;
|
||||||
|
readStateName(&newState, reader);
|
||||||
|
readStateScriptFunction(&newState, reader);
|
||||||
|
|
||||||
|
readTransitions(newState);
|
||||||
|
|
||||||
|
mStates.push_back(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::readTransitions(State ¤tState)
|
||||||
|
{
|
||||||
|
SimXMLDocument *reader = mXMLReader.getObject();
|
||||||
|
|
||||||
|
//iterate through our states now!
|
||||||
|
if (reader->pushFirstChildElement("Transition"))
|
||||||
|
{
|
||||||
|
//get our first state
|
||||||
|
StateTransition firstTransition;
|
||||||
|
|
||||||
|
readTransitonTarget(&firstTransition, reader);
|
||||||
|
|
||||||
|
readConditions(firstTransition);
|
||||||
|
|
||||||
|
currentState.mTransitions.push_back(firstTransition);
|
||||||
|
|
||||||
|
//now, iterate the siblings
|
||||||
|
while (reader->nextSiblingElement("Transition"))
|
||||||
|
{
|
||||||
|
StateTransition newTransition;
|
||||||
|
readTransitonTarget(&newTransition, reader);
|
||||||
|
|
||||||
|
readConditions(newTransition);
|
||||||
|
|
||||||
|
currentState.mTransitions.push_back(newTransition);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader->popElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::readConditions(StateTransition ¤tTransition)
|
||||||
|
{
|
||||||
|
SimXMLDocument *reader = mXMLReader.getObject();
|
||||||
|
|
||||||
|
//iterate through our states now!
|
||||||
|
if (reader->pushFirstChildElement("Rule"))
|
||||||
|
{
|
||||||
|
//get our first state
|
||||||
|
StateTransition::Condition firstCondition;
|
||||||
|
StateField firstField;
|
||||||
|
bool fieldRead = false;
|
||||||
|
|
||||||
|
readFieldName(&firstField, reader);
|
||||||
|
firstCondition.field = firstField;
|
||||||
|
|
||||||
|
readFieldComparitor(&firstCondition, reader);
|
||||||
|
|
||||||
|
readFieldValue(&firstCondition.field, reader);
|
||||||
|
|
||||||
|
currentTransition.mTransitionRules.push_back(firstCondition);
|
||||||
|
|
||||||
|
//now, iterate the siblings
|
||||||
|
while (reader->nextSiblingElement("Transition"))
|
||||||
|
{
|
||||||
|
StateTransition::Condition newCondition;
|
||||||
|
StateField newField;
|
||||||
|
|
||||||
|
readFieldName(&newField, reader);
|
||||||
|
newCondition.field = newField;
|
||||||
|
|
||||||
|
readFieldComparitor(&newCondition, reader);
|
||||||
|
|
||||||
|
readFieldValue(&newCondition.field, reader);
|
||||||
|
|
||||||
|
currentTransition.mTransitionRules.push_back(newCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader->popElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 StateMachine::parseComparitor(const char* comparitorName)
|
||||||
|
{
|
||||||
|
S32 targetType = -1;
|
||||||
|
|
||||||
|
if (!dStrcmp("GreaterThan", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::GeaterThan;
|
||||||
|
else if (!dStrcmp("GreaterOrEqual", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::GreaterOrEqual;
|
||||||
|
else if (!dStrcmp("LessThan", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::LessThan;
|
||||||
|
else if (!dStrcmp("LessOrEqual", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::LessOrEqual;
|
||||||
|
else if (!dStrcmp("Equals", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::Equals;
|
||||||
|
else if (!dStrcmp("True", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::True;
|
||||||
|
else if (!dStrcmp("False", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::False;
|
||||||
|
else if (!dStrcmp("Negative", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::Negative;
|
||||||
|
else if (!dStrcmp("Positive", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::Positive;
|
||||||
|
else if (!dStrcmp("DoesNotEqual", comparitorName))
|
||||||
|
targetType = StateMachine::StateTransition::Condition::DoesNotEqual;
|
||||||
|
|
||||||
|
return targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update()
|
||||||
|
{
|
||||||
|
//we always check if there's a timout transition, as that's the most generic transition possible.
|
||||||
|
F32 curTime = Sim::getCurrentTime();
|
||||||
|
|
||||||
|
if (mStateStartTime == -1)
|
||||||
|
mStateStartTime = curTime;
|
||||||
|
|
||||||
|
mStateTime = curTime - mStateStartTime;
|
||||||
|
|
||||||
|
char buffer[64];
|
||||||
|
dSprintf(buffer, sizeof(buffer), "%g", mStateTime);
|
||||||
|
|
||||||
|
checkTransitions("stateTime", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::checkTransitions(const char* slotName, const char* newValue)
|
||||||
|
{
|
||||||
|
//because we use our current state's fields as dynamic fields on the instance
|
||||||
|
//we'll want to catch any fields being set so we can treat changes as transition triggers if
|
||||||
|
//any of the transitions on this state call for it
|
||||||
|
|
||||||
|
//One example would be in order to implement burst fire on a weapon state machine.
|
||||||
|
//The behavior instance has a dynamic variable set up like: GunStateMachine.burstShotCount = 0;
|
||||||
|
|
||||||
|
//We also have a transition in our fire state, as: GunStateMachine.addTransition("FireState", "burstShotCount", "DoneShooting", 3);
|
||||||
|
//What that does is for our fire state, we check the dynamicField burstShotCount if it's equal or greater than 3. If it is, we perform the transition.
|
||||||
|
|
||||||
|
//As state fields are handled as dynamicFields for the instance, regular dynamicFields are processed as well as state fields. So we can use the regular
|
||||||
|
//dynamic fields for our transitions, to act as 'global' variables that are state-agnostic. Alternately, we can use state-specific fields, such as a transition
|
||||||
|
//like this:
|
||||||
|
//GunStateMachine.addTransition("IdleState", "Fidget", "Timeout", ">=", 5000);
|
||||||
|
|
||||||
|
//That uses the the timeout field, which is reset each time the state changes, and so state-specific, to see if it's been 5 seconds. If it has been, we transition
|
||||||
|
//to our fidget state
|
||||||
|
|
||||||
|
//so, lets check our current transitions
|
||||||
|
//now that we have the type, check our transitions!
|
||||||
|
for (U32 t = 0; t < mCurrentState.mTransitions.size(); t++)
|
||||||
|
{
|
||||||
|
//if (!dStrcmp(mCurrentState.mTransitions[t]., slotName))
|
||||||
|
{
|
||||||
|
//found a transition looking for this variable, so do work
|
||||||
|
//first, figure out what data type thie field is
|
||||||
|
//S32 type = getVariableType(newValue);
|
||||||
|
|
||||||
|
bool fail = false;
|
||||||
|
bool match = false;
|
||||||
|
S32 ruleCount = mCurrentState.mTransitions[t].mTransitionRules.size();
|
||||||
|
|
||||||
|
for (U32 r = 0; r < ruleCount; r++)
|
||||||
|
{
|
||||||
|
const char* fieldName = mCurrentState.mTransitions[t].mTransitionRules[r].field.name;
|
||||||
|
if (!dStrcmp(fieldName, slotName))
|
||||||
|
{
|
||||||
|
match = true;
|
||||||
|
//now, check the value with the comparitor and see if we do the transition.
|
||||||
|
if (!passComparitorCheck(newValue, mCurrentState.mTransitions[t].mTransitionRules[r]))
|
||||||
|
{
|
||||||
|
fail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we do have a transition rule for this field, and we didn't fail on the condition, go ahead and switch states
|
||||||
|
if (match && !fail)
|
||||||
|
{
|
||||||
|
setState(mCurrentState.mTransitions[t].mStateTarget);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StateMachine::passComparitorCheck(const char* var, StateTransition::Condition transitionRule)
|
||||||
|
{
|
||||||
|
F32 num = dAtof(var);
|
||||||
|
switch (transitionRule.field.fieldType)
|
||||||
|
{
|
||||||
|
case StateField::Type::VectorType:
|
||||||
|
switch (transitionRule.triggerComparitor)
|
||||||
|
{
|
||||||
|
case StateTransition::Condition::Equals:
|
||||||
|
case StateTransition::Condition::GeaterThan:
|
||||||
|
case StateTransition::Condition::GreaterOrEqual:
|
||||||
|
case StateTransition::Condition::LessThan:
|
||||||
|
case StateTransition::Condition::LessOrEqual:
|
||||||
|
case StateTransition::Condition::DoesNotEqual:
|
||||||
|
//do
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
case StateField::Type::StringType:
|
||||||
|
switch (transitionRule.triggerComparitor)
|
||||||
|
{
|
||||||
|
case StateTransition::Condition::Equals:
|
||||||
|
if (!dStrcmp(var, transitionRule.field.triggerStringVal))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::DoesNotEqual:
|
||||||
|
if (dStrcmp(var, transitionRule.field.triggerStringVal))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
case StateField::Type::BooleanType:
|
||||||
|
switch (transitionRule.triggerComparitor)
|
||||||
|
{
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::True:
|
||||||
|
if (dAtob(var))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::False:
|
||||||
|
if (dAtob(var))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
case StateField::Type::NumberType:
|
||||||
|
switch (transitionRule.triggerComparitor)
|
||||||
|
{
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::Equals:
|
||||||
|
if (num == transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::GeaterThan:
|
||||||
|
if (num > transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::GreaterOrEqual:
|
||||||
|
if (num >= transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::LessThan:
|
||||||
|
if (num < transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::LessOrEqual:
|
||||||
|
if (num <= transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::DoesNotEqual:
|
||||||
|
if (num != transitionRule.field.triggerNumVal)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::Positive:
|
||||||
|
if (num > 0)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
case StateTransition::Condition::TriggerValueTarget::Negative:
|
||||||
|
if (num < 0)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::setState(const char* stateName, bool clearFields)
|
||||||
|
{
|
||||||
|
State oldState = mCurrentState;
|
||||||
|
StringTableEntry sName = StringTable->insert(stateName);
|
||||||
|
for (U32 i = 0; i < mStates.size(); i++)
|
||||||
|
{
|
||||||
|
//if(!dStrcmp(mStates[i]->stateName, stateName))
|
||||||
|
if (!dStrcmp(mStates[i].stateName,sName))
|
||||||
|
{
|
||||||
|
mCurrentState = mStates[i];
|
||||||
|
mStateStartTime = Sim::getCurrentTime();
|
||||||
|
|
||||||
|
onStateChanged.trigger(this, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* StateMachine::getStateByIndex(S32 index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && mStates.size() > index)
|
||||||
|
return mStates[index].stateName;
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMachine::State& StateMachine::getStateByName(const char* name)
|
||||||
|
{
|
||||||
|
StringTableEntry stateName = StringTable->insert(name);
|
||||||
|
|
||||||
|
for (U32 i = 0; i < mStates.size(); i++)
|
||||||
|
{
|
||||||
|
if (!dStrcmp(stateName, mStates[i].stateName))
|
||||||
|
return mStates[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 StateMachine::findFieldByName(const char* name)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < mFields.size(); i++)
|
||||||
|
{
|
||||||
|
if (!dStrcmp(mFields[i].name, name))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
259
Engine/source/T3D/components/game/stateMachine.h
Normal file
259
Engine/source/T3D/components/game/stateMachine.h
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef STATE_MACHINE_H
|
||||||
|
#define STATE_MACHINE_H
|
||||||
|
|
||||||
|
#ifndef _SIMBASE_H_
|
||||||
|
#include "console/simBase.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _OBJECTTYPES_H_
|
||||||
|
#include "T3D/objectTypes.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MMATH_H_
|
||||||
|
#include "math/mMath.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _XMLDOC_H_
|
||||||
|
#include "console/SimXMLDocument.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class StateMachine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct StateField
|
||||||
|
{
|
||||||
|
StringTableEntry name;
|
||||||
|
|
||||||
|
bool triggerBoolVal;
|
||||||
|
float triggerNumVal;
|
||||||
|
Point3F triggerVectorVal;
|
||||||
|
String triggerStringVal;
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
BooleanType = 0,
|
||||||
|
NumberType,
|
||||||
|
VectorType,
|
||||||
|
StringType
|
||||||
|
}fieldType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UniqueReference
|
||||||
|
{
|
||||||
|
SimObject* referenceObj;
|
||||||
|
const char* referenceVar;
|
||||||
|
const char* uniqueName;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateTransition
|
||||||
|
{
|
||||||
|
struct Condition
|
||||||
|
{
|
||||||
|
enum TriggerValueTarget
|
||||||
|
{
|
||||||
|
Equals = 0,
|
||||||
|
GeaterThan,
|
||||||
|
LessThan,
|
||||||
|
GreaterOrEqual,
|
||||||
|
LessOrEqual,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
Positive,
|
||||||
|
Negative,
|
||||||
|
DoesNotEqual
|
||||||
|
};
|
||||||
|
|
||||||
|
StateField field;
|
||||||
|
|
||||||
|
TriggerValueTarget triggerComparitor;
|
||||||
|
|
||||||
|
UniqueReference *valUniqueRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringTableEntry mName;
|
||||||
|
StringTableEntry mStateTarget;
|
||||||
|
Vector<Condition> mTransitionRules;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
Vector<StateTransition> mTransitions;
|
||||||
|
|
||||||
|
StringTableEntry stateName;
|
||||||
|
|
||||||
|
StringTableEntry callbackName;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringTableEntry mStateMachineFile;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Vector<State> mStates;
|
||||||
|
|
||||||
|
Vector<StateField> mFields;
|
||||||
|
|
||||||
|
Vector<UniqueReference> mUniqueReferences;
|
||||||
|
|
||||||
|
State mCurrentState;
|
||||||
|
|
||||||
|
F32 mStateStartTime;
|
||||||
|
F32 mStateTime;
|
||||||
|
|
||||||
|
StringTableEntry mStartingState;
|
||||||
|
|
||||||
|
State *mCurCreateSuperState;
|
||||||
|
State *mCurCreateState;
|
||||||
|
|
||||||
|
SimObjectPtr<SimXMLDocument> mXMLReader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateMachine();
|
||||||
|
virtual ~StateMachine();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
void loadStateMachineFile();
|
||||||
|
void readStates();
|
||||||
|
void readTransitions(State ¤tState);
|
||||||
|
void readConditions(StateTransition &newTransition);
|
||||||
|
|
||||||
|
void setState(const char* stateName, bool clearFields = true);
|
||||||
|
|
||||||
|
const char* getCurrentStateName() { return mCurrentState.stateName; }
|
||||||
|
State& getCurrentState() {
|
||||||
|
return mCurrentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 getStateCount() { return mStates.size(); }
|
||||||
|
const char* getStateByIndex(S32 index);
|
||||||
|
State& getStateByName(const char* name);
|
||||||
|
|
||||||
|
void checkTransitions(const char* slotName, const char* newValue);
|
||||||
|
|
||||||
|
bool passComparitorCheck(const char* var, StateTransition::Condition transitionRule);
|
||||||
|
|
||||||
|
S32 findFieldByName(const char* name);
|
||||||
|
|
||||||
|
S32 getFieldsCount() { return mFields.size(); }
|
||||||
|
|
||||||
|
StateField getField(U32 index)
|
||||||
|
{
|
||||||
|
if (index <= mFields.size())
|
||||||
|
return mFields[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Signal< void(StateMachine*, S32 stateIdx) > onStateChanged;
|
||||||
|
|
||||||
|
//
|
||||||
|
inline bool readStateName(State* state, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("Name"))
|
||||||
|
{
|
||||||
|
state->stateName = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool readStateScriptFunction(State* state, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("ScriptFunction"))
|
||||||
|
{
|
||||||
|
state->callbackName = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool readTransitonTarget(StateTransition* transition, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("StateTarget"))
|
||||||
|
{
|
||||||
|
transition->mStateTarget = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
inline bool readFieldName(StateField* newField, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("FieldName"))
|
||||||
|
{
|
||||||
|
newField->name = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool readFieldComparitor(StateTransition::Condition* condition, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("Comparitor"))
|
||||||
|
{
|
||||||
|
S32 compIdx = parseComparitor(reader->getData());
|
||||||
|
condition->triggerComparitor = static_cast<StateTransition::Condition::TriggerValueTarget>(compIdx);
|
||||||
|
reader->popElement();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool readFieldValue(StateField* field, SimXMLDocument* reader)
|
||||||
|
{
|
||||||
|
if (reader->pushFirstChildElement("NumValue"))
|
||||||
|
{
|
||||||
|
field->fieldType = StateField::NumberType;
|
||||||
|
field->triggerNumVal = dAtof(reader->getData());
|
||||||
|
reader->popElement();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (reader->pushFirstChildElement("StringValue"))
|
||||||
|
{
|
||||||
|
field->fieldType = StateField::StringType;
|
||||||
|
field->triggerStringVal = reader->getData();
|
||||||
|
reader->popElement();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (reader->pushFirstChildElement("BoolValue"))
|
||||||
|
{
|
||||||
|
field->fieldType = StateField::BooleanType;
|
||||||
|
field->triggerBoolVal = dAtob(reader->getData());
|
||||||
|
reader->popElement();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
S32 parseComparitor(const char* comparitorName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
215
Engine/source/T3D/components/game/stateMachineComponent.cpp
Normal file
215
Engine/source/T3D/components/game/stateMachineComponent.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/game/stateMachineComponent.h"
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( StateMachineComponent, onStateChange, void, (), (),
|
||||||
|
"@brief Called when we collide with another object.\n\n"
|
||||||
|
"@param obj The ShapeBase object\n"
|
||||||
|
"@param collObj The object we collided with\n"
|
||||||
|
"@param vec Collision impact vector\n"
|
||||||
|
"@param len Length of the impact vector\n" );
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
StateMachineComponent::StateMachineComponent() : Component()
|
||||||
|
{
|
||||||
|
mFriendlyName = "State Machine";
|
||||||
|
mComponentType = "Game";
|
||||||
|
|
||||||
|
mDescription = getDescriptionText("A generic state machine.");
|
||||||
|
|
||||||
|
mStateMachineFile = "";
|
||||||
|
|
||||||
|
//doesn't need to be networked
|
||||||
|
mNetworked = false;
|
||||||
|
mNetFlags.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMachineComponent::~StateMachineComponent()
|
||||||
|
{
|
||||||
|
for(S32 i = 0;i < mFields.size();++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(StateMachineComponent);
|
||||||
|
|
||||||
|
bool StateMachineComponent::onAdd()
|
||||||
|
{
|
||||||
|
if(! Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Register for the resource change signal.
|
||||||
|
ResourceManager::get().getChangedSignal().notify(this, &StateMachineComponent::_onResourceChanged);
|
||||||
|
|
||||||
|
mStateMachine.onStateChanged.notify(this, &StateMachineComponent::onStateChanged);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 StateMachineComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||||
|
void StateMachineComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addProtectedField("stateMachineFile", TypeFilename, Offset(mStateMachineFile, StateMachineComponent),
|
||||||
|
&_setSMFile, &defaultProtectedGetFn, "The sim time of when we started this state");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StateMachineComponent::_setSMFile(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
StateMachineComponent* smComp = static_cast<StateMachineComponent*>(object);
|
||||||
|
if (smComp)
|
||||||
|
{
|
||||||
|
smComp->setStateMachineFile(data);
|
||||||
|
smComp->loadStateMachineFile();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::_onResourceChanged(const Torque::Path &path)
|
||||||
|
{
|
||||||
|
if (path != Torque::Path(mStateMachineFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadStateMachineFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::loadStateMachineFile()
|
||||||
|
{
|
||||||
|
if (!dStrIsEmpty(mStateMachineFile))
|
||||||
|
{
|
||||||
|
mStateMachine.mStateMachineFile = mStateMachineFile;
|
||||||
|
mStateMachine.loadStateMachineFile();
|
||||||
|
|
||||||
|
//now that it's loaded, we need to parse the SM's fields and set them as script vars on ourselves
|
||||||
|
S32 smFieldCount = mStateMachine.getFieldsCount();
|
||||||
|
|
||||||
|
for (U32 i = 0; i < smFieldCount; i++)
|
||||||
|
{
|
||||||
|
StateMachine::StateField field = mStateMachine.getField(i);
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
|
||||||
|
if (field.fieldType == StateMachine::StateField::BooleanType)
|
||||||
|
{
|
||||||
|
dSprintf(buffer, sizeof(buffer), "%b", field.triggerBoolVal);
|
||||||
|
setDataField(field.name, NULL, buffer);
|
||||||
|
}
|
||||||
|
else if (field.fieldType == StateMachine::StateField::NumberType)
|
||||||
|
{
|
||||||
|
dSprintf(buffer, sizeof(buffer), "%g", field.triggerNumVal);
|
||||||
|
setDataField(field.name, NULL, buffer);
|
||||||
|
}
|
||||||
|
else if (field.fieldType == StateMachine::StateField::StringType)
|
||||||
|
{
|
||||||
|
setDataField(field.name, NULL, field.triggerStringVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::processTick()
|
||||||
|
{
|
||||||
|
if (!isServerObject() || !isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mStateMachine.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::onDynamicModified( const char* slotName, const char* newValue )
|
||||||
|
{
|
||||||
|
Parent::onDynamicModified(slotName, newValue);
|
||||||
|
|
||||||
|
StringTableEntry fieldName = StringTable->insert(slotName);
|
||||||
|
mStateMachine.checkTransitions(fieldName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::onStaticModified( const char* slotName, const char* newValue )
|
||||||
|
{
|
||||||
|
Parent::onStaticModified(slotName, newValue);
|
||||||
|
|
||||||
|
StringTableEntry fieldName = StringTable->insert(slotName);
|
||||||
|
mStateMachine.checkTransitions(fieldName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachineComponent::onStateChanged(StateMachine* sm, S32 stateIdx)
|
||||||
|
{
|
||||||
|
//do a script callback, if we have one
|
||||||
|
//check if we have a function for that, and then also check if our owner does
|
||||||
|
StringTableEntry callbackName = mStateMachine.getCurrentState().callbackName;
|
||||||
|
|
||||||
|
if (isMethod(callbackName))
|
||||||
|
Con::executef(this, callbackName);
|
||||||
|
|
||||||
|
if (mOwner->isMethod(callbackName))
|
||||||
|
Con::executef(mOwner, callbackName);
|
||||||
|
}
|
||||||
81
Engine/source/T3D/components/game/stateMachineComponent.h
Normal file
81
Engine/source/T3D/components/game/stateMachineComponent.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef STATE_MACHINE_COMPONENT_H
|
||||||
|
#define STATE_MACHINE_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef STATE_MACHINE_H
|
||||||
|
#include "T3D/components/game/stateMachine.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class StateMachineComponent : public Component
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateMachine mStateMachine;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StringTableEntry mStateMachineFile;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateMachineComponent();
|
||||||
|
virtual ~StateMachineComponent();
|
||||||
|
DECLARE_CONOBJECT(StateMachineComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
|
||||||
|
void _onResourceChanged(const Torque::Path &path);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
|
||||||
|
virtual void onDynamicModified(const char* slotName, const char* newValue);
|
||||||
|
virtual void onStaticModified(const char* slotName, const char* newValue);
|
||||||
|
|
||||||
|
virtual void loadStateMachineFile();
|
||||||
|
|
||||||
|
void setStateMachineFile(const char* fileName) { mStateMachineFile = StringTable->insert(fileName); }
|
||||||
|
|
||||||
|
static bool _setSMFile(void *object, const char *index, const char *data);
|
||||||
|
|
||||||
|
void onStateChanged(StateMachine* sm, S32 stateIdx);
|
||||||
|
|
||||||
|
//Callbacks
|
||||||
|
DECLARE_CALLBACK(void, onStateChange, ());
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
359
Engine/source/T3D/components/game/triggerComponent.cpp
Normal file
359
Engine/source/T3D/components/game/triggerComponent.cpp
Normal file
|
|
@ -0,0 +1,359 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Torque Game Engine
|
||||||
|
// Copyright (C) GarageGames.com, Inc.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "T3D/components/game/triggerComponent.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#include "math/mathUtils.h"
|
||||||
|
#include "collision/concretePolyList.h"
|
||||||
|
#include "collision/clippedPolyList.h"
|
||||||
|
|
||||||
|
#include "gfx/sim/debugDraw.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( TriggerComponent, onEnterViewCmd, void,
|
||||||
|
( Entity* cameraEnt, bool firstTimeSeeing ), ( cameraEnt, firstTimeSeeing ),
|
||||||
|
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||||
|
|
||||||
|
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||||
|
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( TriggerComponent, onExitViewCmd, void,
|
||||||
|
( Entity* cameraEnt ), ( cameraEnt ),
|
||||||
|
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||||
|
|
||||||
|
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||||
|
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( TriggerComponent, onUpdateInViewCmd, void,
|
||||||
|
( Entity* cameraEnt ), ( cameraEnt ),
|
||||||
|
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||||
|
|
||||||
|
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||||
|
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||||
|
|
||||||
|
IMPLEMENT_CALLBACK( TriggerComponent, onUpdateOutOfViewCmd, void,
|
||||||
|
( Entity* cameraEnt ), ( cameraEnt ),
|
||||||
|
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||||
|
|
||||||
|
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||||
|
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TriggerComponent::TriggerComponent() : Component()
|
||||||
|
{
|
||||||
|
mObjectList.clear();
|
||||||
|
|
||||||
|
mVisible = false;
|
||||||
|
|
||||||
|
mFriendlyName = "Trigger";
|
||||||
|
mComponentType = "Trigger";
|
||||||
|
|
||||||
|
mDescription = getDescriptionText("Calls trigger events when a client starts and stops seeing it. Also ticks while visible to clients.");
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerComponent::~TriggerComponent()
|
||||||
|
{
|
||||||
|
for(S32 i = 0;i < mFields.size();++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(TriggerComponent);
|
||||||
|
|
||||||
|
|
||||||
|
bool TriggerComponent::onAdd()
|
||||||
|
{
|
||||||
|
if(! Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||||
|
void TriggerComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||||
|
|
||||||
|
if(colInt)
|
||||||
|
{
|
||||||
|
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||||
|
|
||||||
|
if(colInt)
|
||||||
|
{
|
||||||
|
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||||
|
|
||||||
|
if (colInt)
|
||||||
|
{
|
||||||
|
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId()) //?????????
|
||||||
|
return;
|
||||||
|
|
||||||
|
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||||
|
|
||||||
|
if (colInt)
|
||||||
|
{
|
||||||
|
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("visibile", TypeBool, Offset( mVisible, TriggerComponent ), "" );
|
||||||
|
|
||||||
|
addField("onEnterViewCmd", TypeCommand, Offset(mEnterCommand, TriggerComponent), "");
|
||||||
|
addField("onExitViewCmd", TypeCommand, Offset(mOnExitCommand, TriggerComponent), "");
|
||||||
|
addField("onUpdateInViewCmd", TypeCommand, Offset(mOnUpdateInViewCmd, TriggerComponent), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 TriggerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::potentialEnterObject(SceneObject *collider)
|
||||||
|
{
|
||||||
|
if(testObject(collider))
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for(U32 i=0; i < mObjectList.size(); i++)
|
||||||
|
{
|
||||||
|
if(mObjectList[i]->getId() == collider->getId())
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
mObjectList.push_back(collider);
|
||||||
|
|
||||||
|
if (!mEnterCommand.isEmpty())
|
||||||
|
{
|
||||||
|
String command = String("%obj = ") + collider->getIdString() + ";" +
|
||||||
|
String("%this = ") + getIdString() + ";" + mEnterCommand;
|
||||||
|
Con::evaluate(command.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
//onEnterTrigger_callback(this, enter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TriggerComponent::testObject(SceneObject* enter)
|
||||||
|
{
|
||||||
|
//First, test to early out
|
||||||
|
Box3F enterBox = enter->getWorldBox();
|
||||||
|
|
||||||
|
//if(!mOwner->getWorldBox().intersect(enterBox) || !)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
//We're still here, so we should do actual work
|
||||||
|
//We're going to be
|
||||||
|
ConcretePolyList mClippedList;
|
||||||
|
|
||||||
|
SphereF sphere;
|
||||||
|
sphere.center = (mOwner->getWorldBox().minExtents + mOwner->getWorldBox().maxExtents) * 0.5;
|
||||||
|
VectorF bv = mOwner->getWorldBox().maxExtents - sphere.center;
|
||||||
|
sphere.radius = bv.len();
|
||||||
|
|
||||||
|
Entity* enterEntity = dynamic_cast<Entity*>(enter);
|
||||||
|
if(enterEntity)
|
||||||
|
{
|
||||||
|
//quick early out. If the bounds don't overlap, it cannot be colliding or inside
|
||||||
|
if (!mOwner->getWorldBox().isOverlapped(enterBox))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//check if the entity has a collision shape
|
||||||
|
CollisionInterface *cI = enterEntity->getComponent<CollisionInterface>();
|
||||||
|
if (cI)
|
||||||
|
{
|
||||||
|
cI->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
|
||||||
|
|
||||||
|
if (!mClippedList.isEmpty())
|
||||||
|
{
|
||||||
|
//well, it's clipped with, or inside, our bounds
|
||||||
|
//now to test the clipped list against our own collision mesh
|
||||||
|
CollisionInterface *myCI = mOwner->getComponent<CollisionInterface>();
|
||||||
|
|
||||||
|
//wait, how would we NOT have this?
|
||||||
|
if (myCI)
|
||||||
|
{
|
||||||
|
//anywho, build our list and then we'll check intersections
|
||||||
|
ClippedPolyList myList;
|
||||||
|
|
||||||
|
MatrixF ownerTransform = mOwner->getTransform();
|
||||||
|
myList.setTransform(&ownerTransform, mOwner->getScale());
|
||||||
|
myList.setObject(mOwner);
|
||||||
|
|
||||||
|
myCI->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
|
||||||
|
|
||||||
|
bool test = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mClippedList.isEmpty() == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::processTick()
|
||||||
|
{
|
||||||
|
Parent::processTick();
|
||||||
|
|
||||||
|
if (!isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//get our list of active clients, and see if they have cameras, if they do, build a frustum and see if we exist inside that
|
||||||
|
mVisible = false;
|
||||||
|
if(isServerObject())
|
||||||
|
{
|
||||||
|
for(U32 i=0; i < mObjectList.size(); i++)
|
||||||
|
{
|
||||||
|
if(!testObject(mObjectList[i]))
|
||||||
|
{
|
||||||
|
if (!mOnExitCommand.isEmpty())
|
||||||
|
{
|
||||||
|
String command = String("%obj = ") + mObjectList[i]->getIdString() + ";" +
|
||||||
|
String("%this = ") + getIdString() + ";" + mOnExitCommand;
|
||||||
|
Con::evaluate(command.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
mObjectList.erase(i);
|
||||||
|
//mDataBlock->onLeaveTrigger_callback( this, remove );
|
||||||
|
//onLeaveTrigger_callback(this, remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (!mTickCommand.isEmpty())
|
||||||
|
Con::evaluate(mTickCommand.c_str());
|
||||||
|
|
||||||
|
if (mObjects.size() != 0)
|
||||||
|
onTickTrigger_callback(this);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::visualizeFrustums(F32 renderTimeMS)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GameConnection* TriggerComponent::getConnection(S32 connectionID)
|
||||||
|
{
|
||||||
|
for(NetConnection *conn = NetConnection::getConnectionList(); conn; conn = conn->getNext())
|
||||||
|
{
|
||||||
|
GameConnection* gameConn = dynamic_cast<GameConnection*>(conn);
|
||||||
|
|
||||||
|
if (!gameConn || (gameConn && gameConn->isAIControlled()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(connectionID == gameConn->getId())
|
||||||
|
return gameConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::addClient(S32 clientID)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerComponent::removeClient(S32 clientID)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod( TriggerComponent, addClient, void,
|
||||||
|
( S32 clientID ), ( -1 ),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)" )
|
||||||
|
{
|
||||||
|
if(clientID == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
object->addClient( clientID );
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod( TriggerComponent, removeClient, void,
|
||||||
|
( S32 clientID ), ( -1 ),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)" )
|
||||||
|
{
|
||||||
|
if(clientID == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
object->removeClient( clientID );
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod( TriggerComponent, visualizeFrustums, void,
|
||||||
|
(F32 renderTime), (1000),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)" )
|
||||||
|
{
|
||||||
|
object->visualizeFrustums(renderTime);
|
||||||
|
}
|
||||||
74
Engine/source/T3D/components/game/triggerComponent.h
Normal file
74
Engine/source/T3D/components/game/triggerComponent.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Torque Game Engine
|
||||||
|
// Copyright (C) GarageGames.com, Inc.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _TRIGGER_COMPONENT_H_
|
||||||
|
#define _TRIGGER_COMPONENT_H_
|
||||||
|
|
||||||
|
#ifndef _COMPONENT_H_
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ENTITY_H_
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _COLLISION_INTERFACES_H_
|
||||||
|
#include "T3D/components/collision/collisionInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class TriggerComponent : public Component
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Vector<SceneObject*> mObjectList;
|
||||||
|
|
||||||
|
bool mVisible;
|
||||||
|
|
||||||
|
String mEnterCommand;
|
||||||
|
String mOnExitCommand;
|
||||||
|
String mOnUpdateInViewCmd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TriggerComponent();
|
||||||
|
virtual ~TriggerComponent();
|
||||||
|
DECLARE_CONOBJECT(TriggerComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
void potentialEnterObject(SceneObject *collider);
|
||||||
|
|
||||||
|
bool testObject(SceneObject* enter);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
|
||||||
|
GameConnection* getConnection(S32 connectionID);
|
||||||
|
|
||||||
|
void addClient(S32 clientID);
|
||||||
|
void removeClient(S32 clientID);
|
||||||
|
|
||||||
|
void visualizeFrustums(F32 renderTimeMS);
|
||||||
|
|
||||||
|
DECLARE_CALLBACK(void, onEnterViewCmd, (Entity* cameraEnt, bool firstTimeSeeing));
|
||||||
|
DECLARE_CALLBACK(void, onExitViewCmd, (Entity* cameraEnt));
|
||||||
|
DECLARE_CALLBACK(void, onUpdateInViewCmd, (Entity* cameraEnt));
|
||||||
|
DECLARE_CALLBACK(void, onUpdateOutOfViewCmd, (Entity* cameraEnt));
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _EXAMPLEBEHAVIOR_H_
|
||||||
368
Engine/source/T3D/components/physics/physicsBehavior.cpp
Normal file
368
Engine/source/T3D/components/physics/physicsBehavior.cpp
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/physics/physicsBehavior.h"
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "T3D/containerQuery.h"
|
||||||
|
#include "math/mathIO.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
PhysicsComponent::PhysicsComponent() : Component()
|
||||||
|
{
|
||||||
|
addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
|
||||||
|
addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
|
||||||
|
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
|
||||||
|
addComponentField("mass", "The mass of the object", "float", "1", "");
|
||||||
|
|
||||||
|
mStatic = false;
|
||||||
|
mAtRest = false;
|
||||||
|
mAtRestCounter = 0;
|
||||||
|
|
||||||
|
mGravity = VectorF(0, 0, 0);
|
||||||
|
mVelocity = VectorF(0, 0, 0);
|
||||||
|
mDrag = 0.7f;
|
||||||
|
mMass = 1.f;
|
||||||
|
|
||||||
|
mGravityMod = 1.f;
|
||||||
|
|
||||||
|
csmAtRestTimer = 64;
|
||||||
|
sAtRestVelocity = 0.15f;
|
||||||
|
|
||||||
|
mDelta.pos = Point3F(0, 0, 0);
|
||||||
|
mDelta.posVec = Point3F(0, 0, 0);
|
||||||
|
mDelta.warpTicks = mDelta.warpCount = 0;
|
||||||
|
mDelta.dt = 1;
|
||||||
|
mDelta.move = NullMove;
|
||||||
|
mPredictionCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsComponent::~PhysicsComponent()
|
||||||
|
{
|
||||||
|
for(S32 i = 0;i < mFields.size();++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(PhysicsComponent);
|
||||||
|
|
||||||
|
void PhysicsComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
// Initialize interpolation vars.
|
||||||
|
mDelta.rot[1] = mDelta.rot[0] = QuatF(mOwner->getTransform());
|
||||||
|
mDelta.pos = mOwner->getPosition();
|
||||||
|
mDelta.posVec = Point3F(0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("gravity", TypePoint3F, Offset(mGravity, PhysicsComponent));
|
||||||
|
addField("velocity", TypePoint3F, Offset(mVelocity, PhysicsComponent));
|
||||||
|
addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if(stream->writeFlag(mask & VelocityMask))
|
||||||
|
mathWrite( *stream, mVelocity );
|
||||||
|
|
||||||
|
if(stream->writeFlag(mask & UpdateMask))
|
||||||
|
{
|
||||||
|
stream->writeFlag(mStatic);
|
||||||
|
stream->writeFlag(mAtRest);
|
||||||
|
stream->writeInt(mAtRestCounter,8);
|
||||||
|
|
||||||
|
mathWrite( *stream, mGravity );
|
||||||
|
|
||||||
|
stream->writeFloat(mDrag, 12);
|
||||||
|
//stream->writeFloat(mMass, 12);
|
||||||
|
|
||||||
|
stream->writeFloat(mGravityMod, 12);
|
||||||
|
}
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if(stream->readFlag())
|
||||||
|
mathRead( *stream, &mVelocity );
|
||||||
|
|
||||||
|
if(stream->readFlag())
|
||||||
|
{
|
||||||
|
mStatic = stream->readFlag();
|
||||||
|
mAtRest = stream->readFlag();
|
||||||
|
mAtRestCounter = stream->readInt(8);
|
||||||
|
|
||||||
|
mathRead( *stream, &mGravity );
|
||||||
|
|
||||||
|
mDrag = stream->readFloat(12);
|
||||||
|
//mMass = stream->readFloat(12);
|
||||||
|
|
||||||
|
mGravityMod = stream->readFloat(12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void PhysicsComponent::interpolateTick(F32 dt)
|
||||||
|
{
|
||||||
|
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
||||||
|
//Point3F rot = mDelta.rot + mDelta.rotVec * dt;
|
||||||
|
|
||||||
|
setRenderPosition(pos,dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void PhysicsComponent::updateContainer()
|
||||||
|
{
|
||||||
|
PROFILE_SCOPE( PhysicsBehaviorInstance_updateContainer );
|
||||||
|
|
||||||
|
// Update container drag and buoyancy properties
|
||||||
|
|
||||||
|
// Set default values.
|
||||||
|
//mDrag = mDataBlock->drag;
|
||||||
|
//mBuoyancy = 0.0f;
|
||||||
|
//mGravityMod = 1.0;
|
||||||
|
//mAppliedForce.set(0,0,0);
|
||||||
|
|
||||||
|
ContainerQueryInfo info;
|
||||||
|
info.box = mOwner->getWorldBox();
|
||||||
|
info.mass = mMass;
|
||||||
|
|
||||||
|
mOwner->getContainer()->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
|
||||||
|
|
||||||
|
//mWaterCoverage = info.waterCoverage;
|
||||||
|
//mLiquidType = info.liquidType;
|
||||||
|
//mLiquidHeight = info.waterHeight;
|
||||||
|
//setCurrentWaterObject( info.waterObject );
|
||||||
|
|
||||||
|
// This value might be useful as a datablock value,
|
||||||
|
// This is what allows the player to stand in shallow water (below this coverage)
|
||||||
|
// without jiggling from buoyancy
|
||||||
|
if (info.waterCoverage >= 0.25f)
|
||||||
|
{
|
||||||
|
// water viscosity is used as drag for in water.
|
||||||
|
// ShapeBaseData drag is used for drag outside of water.
|
||||||
|
// Combine these two components to calculate this ShapeBase object's
|
||||||
|
// current drag.
|
||||||
|
mDrag = ( info.waterCoverage * info.waterViscosity ) +
|
||||||
|
( 1.0f - info.waterCoverage ) * mDrag;
|
||||||
|
//mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mAppliedForce = info.appliedForce;
|
||||||
|
mGravityMod = info.gravityScale;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
void PhysicsComponent::_updatePhysics()
|
||||||
|
{
|
||||||
|
/*SAFE_DELETE( mOwner->mPhysicsRep );
|
||||||
|
|
||||||
|
if ( !PHYSICSMGR )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mDataBlock->simpleServerCollision)
|
||||||
|
{
|
||||||
|
// We only need the trigger on the server.
|
||||||
|
if ( isServerObject() )
|
||||||
|
{
|
||||||
|
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
|
||||||
|
colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
|
||||||
|
|
||||||
|
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
|
||||||
|
mPhysicsRep->setTransform( getTransform() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( !mShapeInstance )
|
||||||
|
return;
|
||||||
|
|
||||||
|
PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
|
||||||
|
|
||||||
|
if ( colShape )
|
||||||
|
{
|
||||||
|
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
|
||||||
|
mPhysicsRep->setTransform( getTransform() );
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsBody *PhysicsComponent::getPhysicsRep()
|
||||||
|
{
|
||||||
|
/*if(mOwner)
|
||||||
|
{
|
||||||
|
Entity* ac = dynamic_cast<Entity*>(mOwner);
|
||||||
|
if(ac)
|
||||||
|
return ac->mPhysicsRep;
|
||||||
|
}*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
void PhysicsComponent::setTransform(const MatrixF& mat)
|
||||||
|
{
|
||||||
|
mOwner->setTransform(mat);
|
||||||
|
|
||||||
|
if (!mStatic)
|
||||||
|
{
|
||||||
|
mAtRest = false;
|
||||||
|
mAtRestCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( getPhysicsRep() )
|
||||||
|
getPhysicsRep()->setTransform( mOwner->getTransform() );
|
||||||
|
|
||||||
|
setMaskBits(UpdateMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::setPosition(const Point3F& pos)
|
||||||
|
{
|
||||||
|
MatrixF mat = mOwner->getTransform();
|
||||||
|
if (mOwner->isMounted()) {
|
||||||
|
// Use transform from mounted object
|
||||||
|
//mOwner->getObjectMount()->getMountTransform( mOwner->getMountNode(), mMount.xfm, &mat );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mat.setColumn(3,pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwner->setTransform(mat);
|
||||||
|
|
||||||
|
if ( getPhysicsRep() )
|
||||||
|
getPhysicsRep()->setTransform( mat );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
|
||||||
|
{
|
||||||
|
MatrixF mat = mOwner->getRenderTransform();
|
||||||
|
if (mOwner->isMounted()) {
|
||||||
|
// Use transform from mounted object
|
||||||
|
//mOwner->getObjectMount()->getMountRenderTransform( dt, mOwner->getMountNode(), mMount.xfm, &mat );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mat.setColumn(3,pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
mOwner->setRenderTransform(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::updateVelocity(const F32 dt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::setVelocity(const VectorF& vel)
|
||||||
|
{
|
||||||
|
mVelocity = vel;
|
||||||
|
|
||||||
|
mAtRest = false;
|
||||||
|
mAtRestCounter = 0;
|
||||||
|
setMaskBits(VelocityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::getVelocity(const Point3F& r, Point3F* v)
|
||||||
|
{
|
||||||
|
*v = mVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::getOriginVector(const Point3F &p,Point3F* r)
|
||||||
|
{
|
||||||
|
*r = p - mOwner->getObjBox().getCenter();
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 PhysicsComponent::getZeroImpulse(const Point3F& r,const Point3F& normal)
|
||||||
|
{
|
||||||
|
Point3F a,b,c;
|
||||||
|
|
||||||
|
//set up our inverse matrix
|
||||||
|
MatrixF iv,qmat;
|
||||||
|
MatrixF inverse = MatrixF::Identity;
|
||||||
|
qmat = mOwner->getTransform();
|
||||||
|
iv.mul(qmat,inverse);
|
||||||
|
qmat.transpose();
|
||||||
|
inverse.mul(iv,qmat);
|
||||||
|
|
||||||
|
mCross(r, normal, &a);
|
||||||
|
inverse.mulV(a, &b);
|
||||||
|
mCross(b, r, &c);
|
||||||
|
|
||||||
|
return 1 / ((1/mMass) + mDot(c, normal));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::accumulateForce(F32 dt, Point3F force)
|
||||||
|
{
|
||||||
|
mVelocity += force * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::applyImpulse(const Point3F&,const VectorF& vec)
|
||||||
|
{
|
||||||
|
// Items ignore angular velocity
|
||||||
|
VectorF vel;
|
||||||
|
vel.x = vec.x / mMass;
|
||||||
|
vel.y = vec.y / mMass;
|
||||||
|
vel.z = vec.z / mMass;
|
||||||
|
setVelocity(mVelocity + vel);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod( PhysicsComponent, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
object->applyImpulse(pos,vel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
135
Engine/source/T3D/components/physics/physicsBehavior.h
Normal file
135
Engine/source/T3D/components/physics/physicsBehavior.h
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Torque Game Engine
|
||||||
|
// Copyright (C) GarageGames.com, Inc.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef _PHYSICSBEHAVIOR_H_
|
||||||
|
#define _PHYSICSBEHAVIOR_H_
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
|
||||||
|
#ifndef __RESOURCE_H__
|
||||||
|
#include "core/resource.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShape.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _ENTITY_H_
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _CONVEX_H_
|
||||||
|
#include "collision/convex.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _BOXCONVEX_H_
|
||||||
|
#include "collision/boxConvex.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _RIGID_H_
|
||||||
|
#include "T3D/rigid.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICS_PHYSICSBODY_H_
|
||||||
|
#include "T3D/physics/physicsBody.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _RENDER_COMPONENT_INTERFACE_H_
|
||||||
|
#include "T3D/components/render/renderComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TSShapeInstance;
|
||||||
|
class SceneRenderState;
|
||||||
|
class PhysicsBody;
|
||||||
|
class PhysicsBehaviorInstance;
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class PhysicsComponent : public Component
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool mStatic;
|
||||||
|
bool mAtRest;
|
||||||
|
S32 mAtRestCounter;
|
||||||
|
|
||||||
|
VectorF mGravity;
|
||||||
|
VectorF mVelocity;
|
||||||
|
F32 mDrag;
|
||||||
|
F32 mMass;
|
||||||
|
|
||||||
|
F32 mGravityMod;
|
||||||
|
|
||||||
|
S32 csmAtRestTimer;
|
||||||
|
F32 sAtRestVelocity; // Min speed after collisio
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum MaskBits {
|
||||||
|
PositionMask = Parent::NextFreeMask << 0,
|
||||||
|
FreezeMask = Parent::NextFreeMask << 1,
|
||||||
|
ForceMoveMask = Parent::NextFreeMask << 2,
|
||||||
|
VelocityMask = Parent::NextFreeMask << 3,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateDelta
|
||||||
|
{
|
||||||
|
Move move; ///< Last move from server
|
||||||
|
F32 dt; ///< Last interpolation time
|
||||||
|
// Interpolation data
|
||||||
|
Point3F pos;
|
||||||
|
Point3F posVec;
|
||||||
|
QuatF rot[2];
|
||||||
|
// Warp data
|
||||||
|
S32 warpTicks; ///< Number of ticks to warp
|
||||||
|
S32 warpCount; ///< Current pos in warp
|
||||||
|
Point3F warpOffset;
|
||||||
|
QuatF warpRot[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
StateDelta mDelta;
|
||||||
|
S32 mPredictionCount; ///< Number of ticks to predict
|
||||||
|
|
||||||
|
public:
|
||||||
|
PhysicsComponent();
|
||||||
|
virtual ~PhysicsComponent();
|
||||||
|
DECLARE_CONOBJECT(PhysicsComponent);
|
||||||
|
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void interpolateTick(F32 dt);
|
||||||
|
virtual void updatePos(const U32 /*mask*/, const F32 dt){}
|
||||||
|
virtual void _updatePhysics();
|
||||||
|
virtual PhysicsBody *getPhysicsRep();
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
|
||||||
|
void updateContainer();
|
||||||
|
|
||||||
|
virtual void updateVelocity(const F32 dt);
|
||||||
|
virtual Point3F getVelocity() { return mVelocity; }
|
||||||
|
virtual void getOriginVector(const Point3F &p, Point3F* r);
|
||||||
|
virtual void getVelocity(const Point3F& r, Point3F* v);
|
||||||
|
virtual void setVelocity(const VectorF& vel);
|
||||||
|
virtual void setTransform(const MatrixF& mat);
|
||||||
|
virtual void setPosition(const Point3F& pos);
|
||||||
|
void setRenderPosition(const Point3F& pos, F32 dt);
|
||||||
|
|
||||||
|
virtual void applyImpulse(const Point3F&, const VectorF& vec);
|
||||||
|
virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
|
||||||
|
virtual void accumulateForce(F32 dt, Point3F force);
|
||||||
|
|
||||||
|
//Rigid Body Collision Conveinence Hooks
|
||||||
|
virtual bool updateCollision(F32 dt, Rigid& ns, CollisionList &cList) { return false; }
|
||||||
|
virtual bool resolveContacts(Rigid& ns, CollisionList& cList, F32 dt) { return false; }
|
||||||
|
//virtual bool resolveCollision(Rigid& ns, CollisionList& cList) { return false; }
|
||||||
|
virtual bool resolveCollision(const Point3F& p, const Point3F &normal) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _COMPONENT_H_
|
||||||
|
|
@ -20,36 +20,30 @@
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifndef _MOREADVANCEDCOMPONENT_H_
|
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||||
#define _MOREADVANCEDCOMPONENT_H_
|
#define PHYSICS_COMPONENT_INTERFACE_H
|
||||||
|
|
||||||
#ifndef _SIMPLECOMPONENT_H_
|
#ifndef CORE_INTERFACES_H
|
||||||
#include "component/simpleComponent.h"
|
#include "T3D/components/coreInterfaces.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// This is a slightly more advanced component which will be used to demonstrate
|
class PhysicsComponentInterface : public Interface<PhysicsComponentInterface>
|
||||||
/// components which are dependent on other components.
|
|
||||||
class MoreAdvancedComponent : public SimComponent
|
|
||||||
{
|
{
|
||||||
typedef SimComponent Parent;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// This component is going to be dependent on a SimpleComponentInterface being
|
VectorF mVelocity;
|
||||||
// queried off of it's parent object. This will store that interface that
|
F32 mMass;
|
||||||
// will get queried during onComponentRegister()
|
|
||||||
SimpleComponentInterface *mSCInterface;
|
F32 mGravityMod;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DECLARE_CONOBJECT(MoreAdvancedComponent);
|
void updateForces();
|
||||||
|
|
||||||
// Firstly, take a look at the documentation for this function in simComponent.h.
|
VectorF getVelocity() { return mVelocity; }
|
||||||
// We will be overloading this method to query the component heirarchy for our
|
void setVelocity(VectorF vel) { mVelocity = vel; }
|
||||||
// dependent interface, as noted above.
|
|
||||||
virtual bool onComponentRegister( SimComponent *owner );
|
|
||||||
|
|
||||||
// This function will try to execute a function through the interface that this
|
F32 getMass() { return mMass; }
|
||||||
// component is dependent on.
|
|
||||||
virtual bool testDependentInterface();
|
Signal< void(VectorF normal, Vector<SceneObject*> overlappedObjects) > onPhysicsCollision;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -0,0 +1,865 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/physics/playerControllerComponent.h"
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "collision/collision.h"
|
||||||
|
#include "T3D/physics/physicsPlayer.h"
|
||||||
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
|
#include "T3D/components/collision/collisionInterfaces.h"
|
||||||
|
#include "T3D/trigger.h"
|
||||||
|
#include "T3D/components/collision/collisionTrigger.h"
|
||||||
|
|
||||||
|
// Movement constants
|
||||||
|
static F32 sVerticalStepDot = 0.173f; // 80
|
||||||
|
static F32 sMinFaceDistance = 0.01f;
|
||||||
|
static F32 sTractionDistance = 0.04f;
|
||||||
|
static F32 sNormalElasticity = 0.01f;
|
||||||
|
static U32 sMoveRetryCount = 5;
|
||||||
|
static F32 sMaxImpulseVelocity = 200.0f;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Callbacks
|
||||||
|
IMPLEMENT_CALLBACK(PlayerControllerComponent, updateMove, void, (PlayerControllerComponent* obj), (obj),
|
||||||
|
"Called when the player updates it's movement, only called if object is set to callback in script(doUpdateMove).\n"
|
||||||
|
"@param obj the Player object\n");
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
PlayerControllerComponent::PlayerControllerComponent() : Component()
|
||||||
|
{
|
||||||
|
addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
|
||||||
|
addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
|
||||||
|
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
|
||||||
|
addComponentField("mass", "The mass of the object", "float", "1", "");
|
||||||
|
|
||||||
|
mBuoyancy = 0.f;
|
||||||
|
mFriction = 0.3f;
|
||||||
|
mElasticity = 0.4f;
|
||||||
|
mMaxVelocity = 3000.f;
|
||||||
|
mVelocity = VectorF::Zero;
|
||||||
|
mContactTimer = 0;
|
||||||
|
mSticky = false;
|
||||||
|
|
||||||
|
mFalling = false;
|
||||||
|
mSwimming = false;
|
||||||
|
mInWater = false;
|
||||||
|
|
||||||
|
mDelta.pos = mDelta.posVec = Point3F::Zero;
|
||||||
|
mDelta.warpTicks = mDelta.warpCount = 0;
|
||||||
|
mDelta.rot[0].identity();
|
||||||
|
mDelta.rot[1].identity();
|
||||||
|
mDelta.dt = 1;
|
||||||
|
|
||||||
|
mUseDirectMoveInput = false;
|
||||||
|
|
||||||
|
mFriendlyName = "Player Controller";
|
||||||
|
mComponentType = "Physics";
|
||||||
|
|
||||||
|
mDescription = getDescriptionText("A general-purpose physics player controller.");
|
||||||
|
|
||||||
|
//mNetFlags.set(Ghostable | ScopeAlways);
|
||||||
|
|
||||||
|
mMass = 9.0f; // from ShapeBase
|
||||||
|
mDrag = 1.0f; // from ShapeBase
|
||||||
|
|
||||||
|
maxStepHeight = 1.0f;
|
||||||
|
moveSurfaceAngle = 60.0f;
|
||||||
|
contactSurfaceAngle = 85.0f;
|
||||||
|
|
||||||
|
fallingSpeedThreshold = -10.0f;
|
||||||
|
|
||||||
|
horizMaxSpeed = 80.0f;
|
||||||
|
horizMaxAccel = 100.0f;
|
||||||
|
horizResistSpeed = 38.0f;
|
||||||
|
horizResistFactor = 1.0f;
|
||||||
|
|
||||||
|
upMaxSpeed = 80.0f;
|
||||||
|
upMaxAccel = 100.0f;
|
||||||
|
upResistSpeed = 38.0f;
|
||||||
|
upResistFactor = 1.0f;
|
||||||
|
|
||||||
|
// Air control
|
||||||
|
airControl = 0.0f;
|
||||||
|
|
||||||
|
//Grav mod
|
||||||
|
mGravityMod = 1;
|
||||||
|
|
||||||
|
mInputVelocity = Point3F(0, 0, 0);
|
||||||
|
|
||||||
|
mPhysicsRep = NULL;
|
||||||
|
mPhysicsWorld = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerControllerComponent::~PlayerControllerComponent()
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < mFields.size(); ++i)
|
||||||
|
{
|
||||||
|
ComponentField &field = mFields[i];
|
||||||
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE_ARRAY(mDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(PlayerControllerComponent);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool PlayerControllerComponent::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
updatePhysics();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
|
||||||
|
if (collisionInterface)
|
||||||
|
{
|
||||||
|
collisionInterface->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
|
||||||
|
mOwnerCollisionInterface = collisionInterface;
|
||||||
|
updatePhysics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
if (comp->getId() == getId()) //?????????
|
||||||
|
return;
|
||||||
|
|
||||||
|
//test if this is a shape component!
|
||||||
|
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
|
||||||
|
if (collisionInterface)
|
||||||
|
{
|
||||||
|
collisionInterface->onCollisionChanged.remove(this, &PlayerControllerComponent::updatePhysics);
|
||||||
|
mOwnerCollisionInterface = NULL;
|
||||||
|
updatePhysics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::updatePhysics(PhysicsCollision *collision)
|
||||||
|
{
|
||||||
|
if (!PHYSICSMGR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
|
||||||
|
//first, clear the old physRep
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
mPhysicsRep = PHYSICSMGR->createPlayer();
|
||||||
|
|
||||||
|
F32 runSurfaceCos = mCos(mDegToRad(moveSurfaceAngle));
|
||||||
|
|
||||||
|
Point3F ownerBounds = mOwner->getObjBox().getExtents() * mOwner->getScale();
|
||||||
|
|
||||||
|
mPhysicsRep->init("", ownerBounds, runSurfaceCos, maxStepHeight, mOwner, mPhysicsWorld);
|
||||||
|
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
addField("inputVelocity", TypePoint3F, Offset(mInputVelocity, PlayerControllerComponent), "");
|
||||||
|
addField("useDirectMoveInput", TypePoint3F, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 PlayerControllerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void PlayerControllerComponent::processTick()
|
||||||
|
{
|
||||||
|
Parent::processTick();
|
||||||
|
|
||||||
|
if (!isServerObject() || !isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Warp to catch up to server
|
||||||
|
if (mDelta.warpCount < mDelta.warpTicks)
|
||||||
|
{
|
||||||
|
mDelta.warpCount++;
|
||||||
|
|
||||||
|
// Set new pos.
|
||||||
|
mDelta.pos = mOwner->getPosition();
|
||||||
|
mDelta.pos += mDelta.warpOffset;
|
||||||
|
mDelta.rot[0] = mDelta.rot[1];
|
||||||
|
mDelta.rot[1].interpolate(mDelta.warpRot[0], mDelta.warpRot[1], F32(mDelta.warpCount) / mDelta.warpTicks);
|
||||||
|
|
||||||
|
MatrixF trans;
|
||||||
|
mDelta.rot[1].setMatrix(&trans);
|
||||||
|
trans.setPosition(mDelta.pos);
|
||||||
|
|
||||||
|
mOwner->setTransform(trans);
|
||||||
|
|
||||||
|
// Pos backstepping
|
||||||
|
mDelta.posVec.x = -mDelta.warpOffset.x;
|
||||||
|
mDelta.posVec.y = -mDelta.warpOffset.y;
|
||||||
|
mDelta.posVec.z = -mDelta.warpOffset.z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Save current rigid state interpolation
|
||||||
|
mDelta.posVec = mOwner->getPosition();
|
||||||
|
mDelta.rot[0] = mOwner->getTransform();
|
||||||
|
|
||||||
|
updateMove();
|
||||||
|
updatePos(TickSec);
|
||||||
|
|
||||||
|
// Wrap up interpolation info
|
||||||
|
mDelta.pos = mOwner->getPosition();
|
||||||
|
mDelta.posVec -= mOwner->getPosition();
|
||||||
|
mDelta.rot[1] = mOwner->getTransform();
|
||||||
|
|
||||||
|
// Update container database
|
||||||
|
setTransform(mOwner->getTransform());
|
||||||
|
|
||||||
|
setMaskBits(VelocityMask);
|
||||||
|
setMaskBits(PositionMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::interpolateTick(F32 dt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::ownerTransformSet(MatrixF *mat)
|
||||||
|
{
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::setTransform(const MatrixF& mat)
|
||||||
|
{
|
||||||
|
mOwner->setTransform(mat);
|
||||||
|
|
||||||
|
setMaskBits(UpdateMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void PlayerControllerComponent::updateMove()
|
||||||
|
{
|
||||||
|
if (!PHYSICSMGR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Move *move = &mOwner->lastMove;
|
||||||
|
|
||||||
|
//If we're not set to use mUseDirectMoveInput, then we allow for an override in the form of mInputVelocity
|
||||||
|
if (!mUseDirectMoveInput)
|
||||||
|
{
|
||||||
|
move->x = mInputVelocity.x;
|
||||||
|
move->y = mInputVelocity.y;
|
||||||
|
move->z = mInputVelocity.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is waterCoverage high enough to be 'swimming'?
|
||||||
|
{
|
||||||
|
bool swimming = mOwner->getContainerInfo().waterCoverage > 0.65f/* && canSwim()*/;
|
||||||
|
|
||||||
|
if (swimming != mSwimming)
|
||||||
|
{
|
||||||
|
mSwimming = swimming;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update current orientation
|
||||||
|
bool doStandardMove = true;
|
||||||
|
GameConnection* con = mOwner->getControllingClient();
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXTENDED_MOVE
|
||||||
|
// Work with an absolute rotation from the ExtendedMove class?
|
||||||
|
if (con && con->getControlSchemeAbsoluteRotation())
|
||||||
|
{
|
||||||
|
doStandardMove = false;
|
||||||
|
const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
|
||||||
|
U32 emoveIndex = smExtendedMoveHeadPosRotIndex;
|
||||||
|
if (emoveIndex >= ExtendedMove::MaxPositionsRotations)
|
||||||
|
emoveIndex = 0;
|
||||||
|
|
||||||
|
if (emove->EulerBasedRotation[emoveIndex])
|
||||||
|
{
|
||||||
|
// Head pitch
|
||||||
|
mHead.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
|
||||||
|
|
||||||
|
// Do we also include the relative yaw value?
|
||||||
|
if (con->getControlSchemeAddPitchToAbsRot())
|
||||||
|
{
|
||||||
|
F32 x = move->pitch;
|
||||||
|
if (x > M_PI_F)
|
||||||
|
x -= M_2PI_F;
|
||||||
|
|
||||||
|
mHead.x += x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain the range of mHead.x
|
||||||
|
while (mHead.x < -M_PI_F)
|
||||||
|
mHead.x += M_2PI_F;
|
||||||
|
while (mHead.x > M_PI_F)
|
||||||
|
mHead.x -= M_2PI_F;
|
||||||
|
|
||||||
|
// Rotate (heading) head or body?
|
||||||
|
if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
|
||||||
|
{
|
||||||
|
// Rotate head
|
||||||
|
mHead.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
|
||||||
|
|
||||||
|
// Do we also include the relative yaw value?
|
||||||
|
if (con->getControlSchemeAddYawToAbsRot())
|
||||||
|
{
|
||||||
|
F32 z = move->yaw;
|
||||||
|
if (z > M_PI_F)
|
||||||
|
z -= M_2PI_F;
|
||||||
|
|
||||||
|
mHead.z += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain the range of mHead.z
|
||||||
|
while (mHead.z < 0.0f)
|
||||||
|
mHead.z += M_2PI_F;
|
||||||
|
while (mHead.z > M_2PI_F)
|
||||||
|
mHead.z -= M_2PI_F;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rotate body
|
||||||
|
mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
|
||||||
|
|
||||||
|
// Do we also include the relative yaw value?
|
||||||
|
if (con->getControlSchemeAddYawToAbsRot())
|
||||||
|
{
|
||||||
|
F32 z = move->yaw;
|
||||||
|
if (z > M_PI_F)
|
||||||
|
z -= M_2PI_F;
|
||||||
|
|
||||||
|
mRot.z += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain the range of mRot.z
|
||||||
|
while (mRot.z < 0.0f)
|
||||||
|
mRot.z += M_2PI_F;
|
||||||
|
while (mRot.z > M_2PI_F)
|
||||||
|
mRot.z -= M_2PI_F;
|
||||||
|
}
|
||||||
|
mLastAbsoluteYaw = emove->rotZ[emoveIndex];
|
||||||
|
mLastAbsolutePitch = emove->rotX[emoveIndex];
|
||||||
|
|
||||||
|
// Head bank
|
||||||
|
mHead.y = emove->rotY[emoveIndex];
|
||||||
|
|
||||||
|
// Constrain the range of mHead.y
|
||||||
|
while (mHead.y > M_PI_F)
|
||||||
|
mHead.y -= M_2PI_F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MatrixF zRot;
|
||||||
|
zRot.set(EulerF(0.0f, 0.0f, mOwner->getRotation().asEulerF().z));
|
||||||
|
|
||||||
|
// Desired move direction & speed
|
||||||
|
VectorF moveVec;
|
||||||
|
F32 moveSpeed = mInputVelocity.len();
|
||||||
|
|
||||||
|
zRot.getColumn(0, &moveVec);
|
||||||
|
moveVec *= move->x;
|
||||||
|
VectorF tv;
|
||||||
|
zRot.getColumn(1, &tv);
|
||||||
|
moveVec += tv * move->y;
|
||||||
|
|
||||||
|
// Acceleration due to gravity
|
||||||
|
VectorF acc(mPhysicsWorld->getGravity() * mGravityMod * TickSec);
|
||||||
|
|
||||||
|
// Determine ground contact normal. Only look for contacts if
|
||||||
|
// we can move and aren't mounted.
|
||||||
|
mContactInfo.contactNormal = VectorF::Zero;
|
||||||
|
mContactInfo.jump = false;
|
||||||
|
mContactInfo.run = false;
|
||||||
|
|
||||||
|
bool jumpSurface = false, runSurface = false;
|
||||||
|
if (!mOwner->isMounted())
|
||||||
|
findContact(&mContactInfo.run, &mContactInfo.jump, &mContactInfo.contactNormal);
|
||||||
|
if (mContactInfo.jump)
|
||||||
|
mJumpSurfaceNormal = mContactInfo.contactNormal;
|
||||||
|
|
||||||
|
// If we don't have a runSurface but we do have a contactNormal,
|
||||||
|
// then we are standing on something that is too steep.
|
||||||
|
// Deflect the force of gravity by the normal so we slide.
|
||||||
|
// We could also try aligning it to the runSurface instead,
|
||||||
|
// but this seems to work well.
|
||||||
|
if (!mContactInfo.run && !mContactInfo.contactNormal.isZero())
|
||||||
|
acc = (acc - 2 * mContactInfo.contactNormal * mDot(acc, mContactInfo.contactNormal));
|
||||||
|
|
||||||
|
// Acceleration on run surface
|
||||||
|
if (mContactInfo.run && !mSwimming)
|
||||||
|
{
|
||||||
|
mContactTimer = 0;
|
||||||
|
|
||||||
|
VectorF pv = moveVec;
|
||||||
|
|
||||||
|
// Adjust the player's requested dir. to be parallel
|
||||||
|
// to the contact surface.
|
||||||
|
F32 pvl = pv.len();
|
||||||
|
|
||||||
|
// Convert to acceleration
|
||||||
|
if (pvl)
|
||||||
|
pv *= moveSpeed / pvl;
|
||||||
|
VectorF runAcc = pv - (mVelocity + acc);
|
||||||
|
F32 runSpeed = runAcc.len();
|
||||||
|
|
||||||
|
// Clamp acceleration, player also accelerates faster when
|
||||||
|
// in his hard landing recover state.
|
||||||
|
F32 maxAcc;
|
||||||
|
|
||||||
|
maxAcc = (horizMaxAccel / mMass) * TickSec;
|
||||||
|
|
||||||
|
if (runSpeed > maxAcc)
|
||||||
|
runAcc *= maxAcc / runSpeed;
|
||||||
|
|
||||||
|
acc += runAcc;
|
||||||
|
}
|
||||||
|
else if (!mSwimming && airControl > 0.0f)
|
||||||
|
{
|
||||||
|
VectorF pv;
|
||||||
|
pv = moveVec;
|
||||||
|
F32 pvl = pv.len();
|
||||||
|
|
||||||
|
if (pvl)
|
||||||
|
pv *= moveSpeed / pvl;
|
||||||
|
|
||||||
|
VectorF runAcc = pv - (mVelocity + acc);
|
||||||
|
runAcc.z = 0;
|
||||||
|
runAcc.x = runAcc.x * airControl;
|
||||||
|
runAcc.y = runAcc.y * airControl;
|
||||||
|
F32 runSpeed = runAcc.len();
|
||||||
|
|
||||||
|
// We don't test for sprinting when performing air control
|
||||||
|
F32 maxAcc = (horizMaxAccel / mMass) * TickSec * 0.3f;
|
||||||
|
|
||||||
|
if (runSpeed > maxAcc)
|
||||||
|
runAcc *= maxAcc / runSpeed;
|
||||||
|
|
||||||
|
acc += runAcc;
|
||||||
|
|
||||||
|
// There are no special air control animations
|
||||||
|
// so... increment this unless you really want to
|
||||||
|
// play the run anims in the air.
|
||||||
|
mContactTimer++;
|
||||||
|
}
|
||||||
|
else if (mSwimming)
|
||||||
|
{
|
||||||
|
// Remove acc into contact surface (should only be gravity)
|
||||||
|
// Clear out floating point acc errors, this will allow
|
||||||
|
// the player to "rest" on the ground.
|
||||||
|
F32 vd = -mDot(acc, mContactInfo.contactNormal);
|
||||||
|
if (vd > 0.0f)
|
||||||
|
{
|
||||||
|
VectorF dv = mContactInfo.contactNormal * (vd + 0.002f);
|
||||||
|
acc += dv;
|
||||||
|
if (acc.len() < 0.0001f)
|
||||||
|
acc.set(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the head pitch and add it to the moveVec
|
||||||
|
// This more accurate swim vector calc comes from Matt Fairfax
|
||||||
|
MatrixF xRot, zRot;
|
||||||
|
xRot.set(EulerF(mOwner->getRotation().asEulerF().x, 0, 0));
|
||||||
|
zRot.set(EulerF(0, 0, mOwner->getRotation().asEulerF().z));
|
||||||
|
MatrixF rot;
|
||||||
|
rot.mul(zRot, xRot);
|
||||||
|
rot.getColumn(0, &moveVec);
|
||||||
|
|
||||||
|
moveVec *= move->x;
|
||||||
|
VectorF tv;
|
||||||
|
rot.getColumn(1, &tv);
|
||||||
|
moveVec += tv * move->y;
|
||||||
|
rot.getColumn(2, &tv);
|
||||||
|
moveVec += tv * move->z;
|
||||||
|
|
||||||
|
// Force a 0 move if there is no energy, and only drain
|
||||||
|
// move energy if we're moving.
|
||||||
|
VectorF swimVec = moveVec;
|
||||||
|
|
||||||
|
// If we are swimming but close enough to the shore/ground
|
||||||
|
// we can still have a surface-normal. In this case align the
|
||||||
|
// velocity to the normal to make getting out of water easier.
|
||||||
|
|
||||||
|
moveVec.normalize();
|
||||||
|
F32 isSwimUp = mDot(moveVec, mContactInfo.contactNormal);
|
||||||
|
|
||||||
|
if (!mContactInfo.contactNormal.isZero() && isSwimUp < 0.1f)
|
||||||
|
{
|
||||||
|
F32 pvl = swimVec.len();
|
||||||
|
|
||||||
|
if (pvl)
|
||||||
|
{
|
||||||
|
VectorF nn;
|
||||||
|
mCross(swimVec, VectorF(0.0f, 0.0f, 1.0f), &nn);
|
||||||
|
nn *= 1.0f / pvl;
|
||||||
|
VectorF cv = mContactInfo.contactNormal;
|
||||||
|
cv -= nn * mDot(nn, cv);
|
||||||
|
swimVec -= cv * mDot(swimVec, cv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 swimVecLen = swimVec.len();
|
||||||
|
|
||||||
|
// Convert to acceleration.
|
||||||
|
if (swimVecLen)
|
||||||
|
swimVec *= moveSpeed / swimVecLen;
|
||||||
|
VectorF swimAcc = swimVec - (mVelocity + acc);
|
||||||
|
F32 swimSpeed = swimAcc.len();
|
||||||
|
|
||||||
|
// Clamp acceleration.
|
||||||
|
F32 maxAcc = (horizMaxAccel / mMass) * TickSec;
|
||||||
|
if (swimSpeed > maxAcc)
|
||||||
|
swimAcc *= maxAcc / swimSpeed;
|
||||||
|
|
||||||
|
acc += swimAcc;
|
||||||
|
|
||||||
|
mContactTimer++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mContactTimer++;
|
||||||
|
|
||||||
|
// Add in force from physical zones...
|
||||||
|
acc += (mOwner->getContainerInfo().appliedForce / mMass) * TickSec;
|
||||||
|
|
||||||
|
// Adjust velocity with all the move & gravity acceleration
|
||||||
|
// TG: I forgot why doesn't the TickSec multiply happen here...
|
||||||
|
mVelocity += acc;
|
||||||
|
|
||||||
|
// apply horizontal air resistance
|
||||||
|
|
||||||
|
F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y);
|
||||||
|
|
||||||
|
if (hvel > horizResistSpeed)
|
||||||
|
{
|
||||||
|
F32 speedCap = hvel;
|
||||||
|
if (speedCap > horizMaxSpeed)
|
||||||
|
speedCap = horizMaxSpeed;
|
||||||
|
speedCap -= horizResistFactor * TickSec * (speedCap - horizResistSpeed);
|
||||||
|
F32 scale = speedCap / hvel;
|
||||||
|
mVelocity.x *= scale;
|
||||||
|
mVelocity.y *= scale;
|
||||||
|
}
|
||||||
|
if (mVelocity.z > upResistSpeed)
|
||||||
|
{
|
||||||
|
if (mVelocity.z > upMaxSpeed)
|
||||||
|
mVelocity.z = upMaxSpeed;
|
||||||
|
mVelocity.z -= upResistFactor * TickSec * (mVelocity.z - upResistSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply drag
|
||||||
|
mVelocity -= mVelocity * mDrag * TickSec;
|
||||||
|
|
||||||
|
// Clamp very small velocity to zero
|
||||||
|
if (mVelocity.isZero())
|
||||||
|
mVelocity = Point3F::Zero;
|
||||||
|
|
||||||
|
// If we are not touching anything and have sufficient -z vel,
|
||||||
|
// we are falling.
|
||||||
|
if (mContactInfo.run)
|
||||||
|
{
|
||||||
|
mFalling = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorF vel;
|
||||||
|
mOwner->getWorldToObj().mulV(mVelocity, &vel);
|
||||||
|
mFalling = vel.z < fallingSpeedThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter/Leave Liquid
|
||||||
|
if (!mInWater && mOwner->getContainerInfo().waterCoverage > 0.0f)
|
||||||
|
{
|
||||||
|
mInWater = true;
|
||||||
|
}
|
||||||
|
else if (mInWater && mOwner->getContainerInfo().waterCoverage <= 0.0f)
|
||||||
|
{
|
||||||
|
mInWater = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::updatePos(const F32 travelTime)
|
||||||
|
{
|
||||||
|
if (!PHYSICSMGR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PROFILE_SCOPE(PlayerControllerComponent_UpdatePos);
|
||||||
|
|
||||||
|
Point3F newPos;
|
||||||
|
|
||||||
|
Collision col;
|
||||||
|
dMemset(&col, 0, sizeof(col));
|
||||||
|
|
||||||
|
static CollisionList collisionList;
|
||||||
|
collisionList.clear();
|
||||||
|
|
||||||
|
newPos = mPhysicsRep->move(mVelocity * travelTime, collisionList);
|
||||||
|
|
||||||
|
bool haveCollisions = false;
|
||||||
|
bool wasFalling = mFalling;
|
||||||
|
if (collisionList.getCount() > 0)
|
||||||
|
{
|
||||||
|
mFalling = false;
|
||||||
|
haveCollisions = true;
|
||||||
|
|
||||||
|
//TODO: clean this up so the phys component doesn't have to tell the col interface to do this
|
||||||
|
CollisionInterface* colInterface = mOwner->getComponent<CollisionInterface>();
|
||||||
|
if (colInterface)
|
||||||
|
{
|
||||||
|
colInterface->handleCollisionList(collisionList, mVelocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveCollisions)
|
||||||
|
{
|
||||||
|
// Pick the collision that most closely matches our direction
|
||||||
|
VectorF velNormal = mVelocity;
|
||||||
|
velNormal.normalizeSafe();
|
||||||
|
const Collision *collision = &collisionList[0];
|
||||||
|
F32 collisionDot = mDot(velNormal, collision->normal);
|
||||||
|
const Collision *cp = collision + 1;
|
||||||
|
const Collision *ep = collision + collisionList.getCount();
|
||||||
|
for (; cp != ep; cp++)
|
||||||
|
{
|
||||||
|
F32 dp = mDot(velNormal, cp->normal);
|
||||||
|
if (dp < collisionDot)
|
||||||
|
{
|
||||||
|
collisionDot = dp;
|
||||||
|
collision = cp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify our velocity based on collisions
|
||||||
|
for (U32 i = 0; i<collisionList.getCount(); ++i)
|
||||||
|
{
|
||||||
|
F32 bd = -mDot(mVelocity, collisionList[i].normal);
|
||||||
|
VectorF dv = collisionList[i].normal * (bd + sNormalElasticity);
|
||||||
|
mVelocity += dv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the last collision for use later on. The handle collision
|
||||||
|
// code only expects a single collision object.
|
||||||
|
if (collisionList.getCount() > 0)
|
||||||
|
col = collisionList[collisionList.getCount() - 1];
|
||||||
|
|
||||||
|
// We'll handle any player-to-player collision, and the last collision
|
||||||
|
// with other obejct types.
|
||||||
|
for (U32 i = 0; i<collisionList.getCount(); ++i)
|
||||||
|
{
|
||||||
|
Collision& colCheck = collisionList[i];
|
||||||
|
if (colCheck.object)
|
||||||
|
{
|
||||||
|
col = colCheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixF newMat;
|
||||||
|
newMat.setPosition(newPos);
|
||||||
|
mPhysicsRep->setTransform(newMat);
|
||||||
|
|
||||||
|
mOwner->setPosition(newPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void PlayerControllerComponent::setVelocity(const VectorF& vel)
|
||||||
|
{
|
||||||
|
mVelocity = vel;
|
||||||
|
|
||||||
|
// Clamp against the maximum velocity.
|
||||||
|
if (mMaxVelocity > 0)
|
||||||
|
{
|
||||||
|
F32 len = mVelocity.magnitudeSafe();
|
||||||
|
if (len > mMaxVelocity)
|
||||||
|
{
|
||||||
|
Point3F excess = mVelocity * (1.0f - (mMaxVelocity / len));
|
||||||
|
mVelocity -= excess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaskBits(VelocityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::findContact(bool *run, bool *jump, VectorF *contactNormal)
|
||||||
|
{
|
||||||
|
SceneObject *contactObject = NULL;
|
||||||
|
|
||||||
|
Vector<SceneObject*> overlapObjects;
|
||||||
|
|
||||||
|
mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
|
||||||
|
|
||||||
|
F32 vd = (*contactNormal).z;
|
||||||
|
*run = vd > mCos(mDegToRad(moveSurfaceAngle));
|
||||||
|
*jump = vd > mCos(mDegToRad(contactSurfaceAngle));
|
||||||
|
|
||||||
|
// Check for triggers
|
||||||
|
for (U32 i = 0; i < overlapObjects.size(); i++)
|
||||||
|
{
|
||||||
|
SceneObject *obj = overlapObjects[i];
|
||||||
|
U32 objectMask = obj->getTypeMask();
|
||||||
|
|
||||||
|
// Check: triggers, corpses and items...
|
||||||
|
//
|
||||||
|
if (objectMask & TriggerObjectType)
|
||||||
|
{
|
||||||
|
if (Trigger* pTrigger = dynamic_cast<Trigger*>(obj))
|
||||||
|
{
|
||||||
|
pTrigger->potentialEnterObject(mOwner);
|
||||||
|
}
|
||||||
|
else if (CollisionTrigger* pTriggerEx = dynamic_cast<CollisionTrigger*>(obj))
|
||||||
|
{
|
||||||
|
if (pTriggerEx)
|
||||||
|
pTriggerEx->potentialEnterObject(mOwner);
|
||||||
|
}
|
||||||
|
//Add any other custom classes and the sort here that should be filtered against
|
||||||
|
/*else if (TriggerExample* pTriggerEx = dynamic_cast<TriggerExample*>(obj))
|
||||||
|
{
|
||||||
|
if (pTriggerEx)
|
||||||
|
pTriggerEx->potentialEnterObject(mOwner);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mContactInfo.contacted = contactObject != NULL;
|
||||||
|
mContactInfo.contactObject = contactObject;
|
||||||
|
|
||||||
|
if (mContactInfo.contacted)
|
||||||
|
mContactInfo.contactNormal = *contactNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerControllerComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
|
||||||
|
{
|
||||||
|
|
||||||
|
AssertFatal(!mIsNaN(vec), "Player::applyImpulse() - The vector is NaN!");
|
||||||
|
|
||||||
|
// Players ignore angular velocity
|
||||||
|
VectorF vel;
|
||||||
|
vel.x = vec.x / getMass();
|
||||||
|
vel.y = vec.y / getMass();
|
||||||
|
vel.z = vec.z / getMass();
|
||||||
|
|
||||||
|
// Make sure the impulse isn't too bigg
|
||||||
|
F32 len = vel.magnitudeSafe();
|
||||||
|
if (len > sMaxImpulseVelocity)
|
||||||
|
{
|
||||||
|
Point3F excess = vel * (1.0f - (sMaxImpulseVelocity / len));
|
||||||
|
vel -= excess;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVelocity(mVelocity + vel);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(PlayerControllerComponent, applyImpulse, bool, (Point3F pos, VectorF vel), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
object->applyImpulse(pos, vel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(PlayerControllerComponent, getContactNormal, Point3F, (), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getContactNormal();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(PlayerControllerComponent, getContactObject, SceneObject*, (), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->getContactObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(PlayerControllerComponent, isContacted, bool, (), ,
|
||||||
|
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||||
|
|
||||||
|
"@param pos impulse world position\n"
|
||||||
|
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||||
|
"@return Always true\n"
|
||||||
|
|
||||||
|
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||||
|
{
|
||||||
|
return object->isContacted();
|
||||||
|
}
|
||||||
212
Engine/source/T3D/components/physics/playerControllerComponent.h
Normal file
212
Engine/source/T3D/components/physics/playerControllerComponent.h
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef PLAYER_CONTORLLER_COMPONENT_H
|
||||||
|
#define PLAYER_CONTORLLER_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef PHYSICSBEHAVIOR_H
|
||||||
|
#include "T3D/components/physics/physicsBehavior.h"
|
||||||
|
#endif
|
||||||
|
#ifndef __RESOURCE_H__
|
||||||
|
#include "core/resource.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShape.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _CONVEX_H_
|
||||||
|
#include "collision/convex.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _BOXCONVEX_H_
|
||||||
|
#include "collision/boxConvex.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||||
|
#include "T3D/physics/physicsCommon.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||||
|
#include "T3D/physics/physicsWorld.h"
|
||||||
|
#endif
|
||||||
|
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/physics/physicsComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
#ifndef COLLISION_INTERFACES_H
|
||||||
|
#include "T3D/components/collision/collisionInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SceneRenderState;
|
||||||
|
class PhysicsWorld;
|
||||||
|
class PhysicsPlayer;
|
||||||
|
class SimplePhysicsBehaviorInstance;
|
||||||
|
class CollisionInterface;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class PlayerControllerComponent : public Component,
|
||||||
|
public PhysicsComponentInterface
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
enum MaskBits {
|
||||||
|
VelocityMask = Parent::NextFreeMask << 0,
|
||||||
|
PositionMask = Parent::NextFreeMask << 1,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateDelta
|
||||||
|
{
|
||||||
|
Move move; ///< Last move from server
|
||||||
|
F32 dt; ///< Last interpolation time
|
||||||
|
// Interpolation data
|
||||||
|
Point3F pos;
|
||||||
|
Point3F posVec;
|
||||||
|
QuatF rot[2];
|
||||||
|
// Warp data
|
||||||
|
S32 warpTicks; ///< Number of ticks to warp
|
||||||
|
S32 warpCount; ///< Current pos in warp
|
||||||
|
Point3F warpOffset;
|
||||||
|
QuatF warpRot[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
StateDelta mDelta;
|
||||||
|
|
||||||
|
PhysicsPlayer *mPhysicsRep;
|
||||||
|
PhysicsWorld *mPhysicsWorld;
|
||||||
|
|
||||||
|
CollisionInterface* mOwnerCollisionInterface;
|
||||||
|
|
||||||
|
struct ContactInfo
|
||||||
|
{
|
||||||
|
bool contacted, jump, run;
|
||||||
|
SceneObject *contactObject;
|
||||||
|
VectorF contactNormal;
|
||||||
|
F32 contactTime;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
contacted = jump = run = false;
|
||||||
|
contactObject = NULL;
|
||||||
|
contactNormal.set(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactInfo() { clear(); }
|
||||||
|
|
||||||
|
} mContactInfo;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
F32 mDrag;
|
||||||
|
F32 mBuoyancy;
|
||||||
|
F32 mFriction;
|
||||||
|
F32 mElasticity;
|
||||||
|
F32 mMaxVelocity;
|
||||||
|
bool mSticky;
|
||||||
|
|
||||||
|
bool mFalling;
|
||||||
|
bool mSwimming;
|
||||||
|
bool mInWater;
|
||||||
|
|
||||||
|
S32 mContactTimer; ///< Ticks since last contact
|
||||||
|
|
||||||
|
U32 mIntegrationCount;
|
||||||
|
|
||||||
|
Point3F mJumpSurfaceNormal; ///< Normal of the surface the player last jumped on
|
||||||
|
|
||||||
|
F32 maxStepHeight; ///< Maximum height the player can step up
|
||||||
|
F32 moveSurfaceAngle; ///< Maximum angle from vertical in degrees the player can run up
|
||||||
|
F32 contactSurfaceAngle; ///< Maximum angle from vertical in degrees we consider having real 'contact'
|
||||||
|
|
||||||
|
F32 horizMaxSpeed; ///< Max speed attainable in the horizontal
|
||||||
|
F32 horizMaxAccel;
|
||||||
|
F32 horizResistSpeed; ///< Speed at which resistance will take place
|
||||||
|
F32 horizResistFactor; ///< Factor of resistance once horizResistSpeed has been reached
|
||||||
|
|
||||||
|
F32 upMaxSpeed; ///< Max vertical speed attainable
|
||||||
|
F32 upMaxAccel;
|
||||||
|
F32 upResistSpeed; ///< Speed at which resistance will take place
|
||||||
|
F32 upResistFactor; ///< Factor of resistance once upResistSpeed has been reached
|
||||||
|
|
||||||
|
F32 fallingSpeedThreshold; ///< Downward speed at which we consider the player falling
|
||||||
|
|
||||||
|
// Air control
|
||||||
|
F32 airControl;
|
||||||
|
|
||||||
|
Point3F mInputVelocity;
|
||||||
|
|
||||||
|
bool mUseDirectMoveInput;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayerControllerComponent();
|
||||||
|
virtual ~PlayerControllerComponent();
|
||||||
|
DECLARE_CONOBJECT(PlayerControllerComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual void ownerTransformSet(MatrixF *mat);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
void updatePhysics(PhysicsCollision *collision = NULL);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
virtual void interpolateTick(F32 dt);
|
||||||
|
virtual void updatePos(const F32 dt);
|
||||||
|
void updateMove();
|
||||||
|
|
||||||
|
virtual VectorF getVelocity() { return mVelocity; }
|
||||||
|
virtual void setVelocity(const VectorF& vel);
|
||||||
|
virtual void setTransform(const MatrixF& mat);
|
||||||
|
|
||||||
|
void findContact(bool *run, bool *jump, VectorF *contactNormal);
|
||||||
|
Point3F getContactNormal() { return mContactInfo.contactNormal; }
|
||||||
|
SceneObject* getContactObject() { return mContactInfo.contactObject; }
|
||||||
|
bool isContacted() { return mContactInfo.contacted; }
|
||||||
|
|
||||||
|
//
|
||||||
|
void applyImpulse(const Point3F &pos, const VectorF &vec);
|
||||||
|
|
||||||
|
//This is a weird artifact of the PhysicsReps. We want the collision component to be privvy to any events that happen
|
||||||
|
//so when the physics components do a findContact test during their update, they'll have a signal collision components
|
||||||
|
//can be listening to to update themselves with that info
|
||||||
|
Signal< void(SceneObject*) > onContactSignal;
|
||||||
|
|
||||||
|
//
|
||||||
|
DECLARE_CALLBACK(void, updateMove, (PlayerControllerComponent* obj));
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _COMPONENT_H_
|
||||||
467
Engine/source/T3D/components/physics/rigidBodyComponent.cpp
Normal file
467
Engine/source/T3D/components/physics/rigidBodyComponent.cpp
Normal file
|
|
@ -0,0 +1,467 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "T3D/components/physics/rigidBodyComponent.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "T3D/physics/physicsBody.h"
|
||||||
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
|
#include "T3D/physics/physicsWorld.h"
|
||||||
|
#include "T3D/physics/physicsCollision.h"
|
||||||
|
#include "T3D/components/collision/collisionComponent.h"
|
||||||
|
|
||||||
|
bool RigidBodyComponent::smNoCorrections = false;
|
||||||
|
bool RigidBodyComponent::smNoSmoothing = false;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
RigidBodyComponent::RigidBodyComponent() : Component()
|
||||||
|
{
|
||||||
|
mMass = 20;
|
||||||
|
mDynamicFriction = 1;
|
||||||
|
mStaticFriction = 0.1f;
|
||||||
|
mRestitution = 10;
|
||||||
|
mLinearDamping = 0;
|
||||||
|
mAngularDamping = 0;
|
||||||
|
mLinearSleepThreshold = 1;
|
||||||
|
mAngularSleepThreshold = 1;
|
||||||
|
mWaterDampingScale = 0.1f;
|
||||||
|
mBuoyancyDensity = 1;
|
||||||
|
|
||||||
|
mSimType = SimType_ServerOnly;
|
||||||
|
|
||||||
|
mPhysicsRep = NULL;
|
||||||
|
mResetPos = MatrixF::Identity;
|
||||||
|
|
||||||
|
mOwnerColComponent = NULL;
|
||||||
|
|
||||||
|
mFriendlyName = "RigidBody(Component)";
|
||||||
|
}
|
||||||
|
|
||||||
|
RigidBodyComponent::~RigidBodyComponent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(RigidBodyComponent);
|
||||||
|
|
||||||
|
bool RigidBodyComponent::onAdd()
|
||||||
|
{
|
||||||
|
if(! Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
}
|
||||||
|
void RigidBodyComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||||
|
void RigidBodyComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
storeRestorePos();
|
||||||
|
PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||||
|
if (colComp)
|
||||||
|
{
|
||||||
|
colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
|
||||||
|
updatePhysics(colComp->getCollisionData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
updatePhysics();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
PhysicsPlugin::getPhysicsResetSignal().remove(this, &RigidBodyComponent::_onPhysicsReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||||
|
if (colComp)
|
||||||
|
{
|
||||||
|
colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::componentAddedToOwner(Component *comp)
|
||||||
|
{
|
||||||
|
CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
|
||||||
|
if (colComp)
|
||||||
|
{
|
||||||
|
colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
|
||||||
|
updatePhysics(colComp->getCollisionData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::componentRemovedFromOwner(Component *comp)
|
||||||
|
{
|
||||||
|
//test if this is a shape component!
|
||||||
|
CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
|
||||||
|
if (colComp)
|
||||||
|
{
|
||||||
|
colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
|
||||||
|
updatePhysics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::ownerTransformSet(MatrixF *mat)
|
||||||
|
{
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::updatePhysics(PhysicsCollision* collision)
|
||||||
|
{
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
if (!PHYSICSMGR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
|
||||||
|
if (!collision)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
|
||||||
|
mPhysicsRep->init(collision, mMass, 0, mOwner, mWorld);
|
||||||
|
|
||||||
|
mPhysicsRep->setMaterial(mRestitution, mDynamicFriction, mStaticFriction);
|
||||||
|
|
||||||
|
mPhysicsRep->setDamping(mLinearDamping, mAngularDamping);
|
||||||
|
mPhysicsRep->setSleepThreshold(mLinearSleepThreshold, mAngularSleepThreshold);
|
||||||
|
|
||||||
|
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||||
|
|
||||||
|
// The reset position is the transform on the server
|
||||||
|
// at creation time... its not used on the client.
|
||||||
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
storeRestorePos();
|
||||||
|
PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 RigidBodyComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & StateMask))
|
||||||
|
{
|
||||||
|
// This will encode the position relative to the control
|
||||||
|
// object position.
|
||||||
|
//
|
||||||
|
// This will compress the position to as little as 6.25
|
||||||
|
// bytes if the position is within about 30 meters of the
|
||||||
|
// control object.
|
||||||
|
//
|
||||||
|
// Worst case its a full 12 bytes + 2 bits if the position
|
||||||
|
// is more than 500 meters from the control object.
|
||||||
|
//
|
||||||
|
stream->writeCompressedPoint(mState.position);
|
||||||
|
|
||||||
|
// Use only 3.5 bytes to send the orientation.
|
||||||
|
stream->writeQuat(mState.orientation, 9);
|
||||||
|
|
||||||
|
// If the server object has been set to sleep then
|
||||||
|
// we don't need to send any velocity.
|
||||||
|
if (!stream->writeFlag(mState.sleeping))
|
||||||
|
{
|
||||||
|
// This gives me ~0.015f resolution in velocity magnitude
|
||||||
|
// while only costing me 1 bit of the velocity is zero length,
|
||||||
|
// <5 bytes in normal cases, and <8 bytes if the velocity is
|
||||||
|
// greater than 1000.
|
||||||
|
AssertWarn(mState.linVelocity.len() < 1000.0f,
|
||||||
|
"PhysicsShape::packUpdate - The linVelocity is out of range!");
|
||||||
|
stream->writeVector(mState.linVelocity, 1000.0f, 16, 9);
|
||||||
|
|
||||||
|
// For angular velocity we get < 0.01f resolution in magnitude
|
||||||
|
// with the most common case being under 4 bytes.
|
||||||
|
AssertWarn(mState.angVelocity.len() < 10.0f,
|
||||||
|
"PhysicsShape::packUpdate - The angVelocity is out of range!");
|
||||||
|
stream->writeVector(mState.angVelocity, 10.0f, 10, 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if (stream->readFlag()) // StateMask
|
||||||
|
{
|
||||||
|
PhysicsState state;
|
||||||
|
|
||||||
|
// Read the encoded and compressed position... commonly only 6.25 bytes.
|
||||||
|
stream->readCompressedPoint(&state.position);
|
||||||
|
|
||||||
|
// Read the compressed quaternion... 3.5 bytes.
|
||||||
|
stream->readQuat(&state.orientation, 9);
|
||||||
|
|
||||||
|
state.sleeping = stream->readFlag();
|
||||||
|
if (!state.sleeping)
|
||||||
|
{
|
||||||
|
stream->readVector(&state.linVelocity, 1000.0f, 16, 9);
|
||||||
|
stream->readVector(&state.angVelocity, 10.0f, 10, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!smNoCorrections && mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
// Set the new state on the physics object immediately.
|
||||||
|
mPhysicsRep->applyCorrection(state.getTransform());
|
||||||
|
|
||||||
|
mPhysicsRep->setSleeping(state.sleeping);
|
||||||
|
if (!state.sleeping)
|
||||||
|
{
|
||||||
|
mPhysicsRep->setLinVelocity(state.linVelocity);
|
||||||
|
mPhysicsRep->setAngVelocity(state.angVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPhysicsRep->getState(&mState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no physics object then just set the
|
||||||
|
// new state... the tick will take care of the
|
||||||
|
// interpolation and extrapolation.
|
||||||
|
if (!mPhysicsRep || !mPhysicsRep->isDynamic())
|
||||||
|
mState = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::processTick()
|
||||||
|
{
|
||||||
|
Parent::processTick();
|
||||||
|
|
||||||
|
if (!mPhysicsRep || !PHYSICSMGR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Note that unlike TSStatic, the serverside PhysicsShape does not
|
||||||
|
// need to play the ambient animation because even if the animation were
|
||||||
|
// to move collision shapes it would not affect the physx representation.
|
||||||
|
|
||||||
|
PROFILE_START(RigidBodyComponent_ProcessTick);
|
||||||
|
|
||||||
|
if (!mPhysicsRep->isDynamic())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// SINGLE PLAYER HACK!!!!
|
||||||
|
if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
|
||||||
|
{
|
||||||
|
RigidBodyComponent *servObj = (RigidBodyComponent*)getServerObject();
|
||||||
|
mOwner->setTransform(servObj->mState.getTransform());
|
||||||
|
mRenderState[0] = servObj->mRenderState[0];
|
||||||
|
mRenderState[1] = servObj->mRenderState[1];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the last render state.
|
||||||
|
mRenderState[0] = mRenderState[1];
|
||||||
|
|
||||||
|
// If the last render state doesn't match the last simulation
|
||||||
|
// state then we got a correction and need to
|
||||||
|
Point3F errorDelta = mRenderState[1].position - mState.position;
|
||||||
|
const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing;
|
||||||
|
|
||||||
|
const bool wasSleeping = mState.sleeping;
|
||||||
|
|
||||||
|
// Get the new physics state.
|
||||||
|
mPhysicsRep->getState(&mState);
|
||||||
|
updateContainerForces();
|
||||||
|
|
||||||
|
// Smooth the correction back into the render state.
|
||||||
|
mRenderState[1] = mState;
|
||||||
|
if (doSmoothing)
|
||||||
|
{
|
||||||
|
F32 correction = mClampF(errorDelta.len() / 20.0f, 0.1f, 0.9f);
|
||||||
|
mRenderState[1].position.interpolate(mState.position, mRenderState[0].position, correction);
|
||||||
|
mRenderState[1].orientation.interpolate(mState.orientation, mRenderState[0].orientation, correction);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if any collisions occured
|
||||||
|
findContact();
|
||||||
|
|
||||||
|
// If we haven't been sleeping then update our transform
|
||||||
|
// and set ourselves as dirty for the next client update.
|
||||||
|
if (!wasSleeping || !mState.sleeping)
|
||||||
|
{
|
||||||
|
// Set the transform on the parent so that
|
||||||
|
// the physics object isn't moved.
|
||||||
|
mOwner->setTransform(mState.getTransform());
|
||||||
|
|
||||||
|
// If we're doing server simulation then we need
|
||||||
|
// to send the client a state update.
|
||||||
|
if (isServerObject() && mPhysicsRep && !smNoCorrections &&
|
||||||
|
!PHYSICSMGR->isSinglePlayer() // SINGLE PLAYER HACK!!!!
|
||||||
|
)
|
||||||
|
setMaskBits(StateMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
PROFILE_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::findContact()
|
||||||
|
{
|
||||||
|
SceneObject *contactObject = NULL;
|
||||||
|
|
||||||
|
VectorF *contactNormal = new VectorF(0, 0, 0);
|
||||||
|
|
||||||
|
Vector<SceneObject*> overlapObjects;
|
||||||
|
|
||||||
|
mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
|
||||||
|
|
||||||
|
if (!overlapObjects.empty())
|
||||||
|
{
|
||||||
|
//fire our signal that the physics sim said collisions happened
|
||||||
|
onPhysicsCollision.trigger(*contactNormal, overlapObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::_onPhysicsReset(PhysicsResetEvent reset)
|
||||||
|
{
|
||||||
|
if (reset == PhysicsResetEvent_Store)
|
||||||
|
mResetPos = mOwner->getTransform();
|
||||||
|
|
||||||
|
else if (reset == PhysicsResetEvent_Restore)
|
||||||
|
{
|
||||||
|
mOwner->setTransform(mResetPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::storeRestorePos()
|
||||||
|
{
|
||||||
|
mResetPos = mOwner->getTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
|
||||||
|
{
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
mPhysicsRep->applyImpulse(pos, vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude)
|
||||||
|
{
|
||||||
|
if (!mPhysicsRep || !mPhysicsRep->isDynamic())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Find a better approximation of the
|
||||||
|
// force vector using the object box.
|
||||||
|
|
||||||
|
VectorF force = mOwner->getWorldBox().getCenter() - origin;
|
||||||
|
F32 dist = force.magnitudeSafe();
|
||||||
|
force.normalize();
|
||||||
|
|
||||||
|
if (dist == 0.0f)
|
||||||
|
force *= magnitude;
|
||||||
|
else
|
||||||
|
force *= mClampF(radius / dist, 0.0f, 1.0f) * magnitude;
|
||||||
|
|
||||||
|
mPhysicsRep->applyImpulse(origin, force);
|
||||||
|
|
||||||
|
// TODO: There is no simple way to really sync this sort of an
|
||||||
|
// event with the client.
|
||||||
|
//
|
||||||
|
// The best is to send the current physics snapshot, calculate the
|
||||||
|
// time difference from when this event occured and the time when the
|
||||||
|
// client recieves it, and then extrapolate where it should be.
|
||||||
|
//
|
||||||
|
// Even then its impossible to be absolutely sure its synced.
|
||||||
|
//
|
||||||
|
// Bottom line... you shouldn't use physics over the network like this.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidBodyComponent::updateContainerForces()
|
||||||
|
{
|
||||||
|
PROFILE_SCOPE(RigidBodyComponent_updateContainerForces);
|
||||||
|
|
||||||
|
// If we're not simulating don't update forces.
|
||||||
|
PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
if (!world || !world->isEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ContainerQueryInfo info;
|
||||||
|
info.box = mOwner->getWorldBox();
|
||||||
|
info.mass = mMass;
|
||||||
|
|
||||||
|
// Find and retreive physics info from intersecting WaterObject(s)
|
||||||
|
mOwner->getContainer()->findObjects(mOwner->getWorldBox(), WaterObjectType | PhysicalZoneObjectType, findRouter, &info);
|
||||||
|
|
||||||
|
// Calculate buoyancy and drag
|
||||||
|
F32 angDrag = mAngularDamping;
|
||||||
|
F32 linDrag = mLinearDamping;
|
||||||
|
F32 buoyancy = 0.0f;
|
||||||
|
Point3F cmass = mPhysicsRep->getCMassPosition();
|
||||||
|
|
||||||
|
F32 density = mBuoyancyDensity;
|
||||||
|
if (density > 0.0f)
|
||||||
|
{
|
||||||
|
if (info.waterCoverage > 0.0f)
|
||||||
|
{
|
||||||
|
F32 waterDragScale = info.waterViscosity * mWaterDampingScale;
|
||||||
|
F32 powCoverage = mPow(info.waterCoverage, 0.25f);
|
||||||
|
|
||||||
|
angDrag = mLerp(angDrag, angDrag * waterDragScale, powCoverage);
|
||||||
|
linDrag = mLerp(linDrag, linDrag * waterDragScale, powCoverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
buoyancy = (info.waterDensity / density) * mPow(info.waterCoverage, 2.0f);
|
||||||
|
|
||||||
|
// A little hackery to prevent oscillation
|
||||||
|
// Based on this blog post:
|
||||||
|
// (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html)
|
||||||
|
// JCF: disabled!
|
||||||
|
Point3F buoyancyForce = buoyancy * -world->getGravity() * TickSec * mMass;
|
||||||
|
mPhysicsRep->applyImpulse(cmass, buoyancyForce);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the dampening as the container might have changed.
|
||||||
|
mPhysicsRep->setDamping(linDrag, angDrag);
|
||||||
|
|
||||||
|
// Apply physical zone forces.
|
||||||
|
if (!info.appliedForce.isZero())
|
||||||
|
mPhysicsRep->applyImpulse(cmass, info.appliedForce);
|
||||||
|
}
|
||||||
183
Engine/source/T3D/components/physics/rigidBodyComponent.h
Normal file
183
Engine/source/T3D/components/physics/rigidBodyComponent.h
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef RIGID_BODY_COMPONENT_H
|
||||||
|
#define RIGID_BODY_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||||
|
#include "T3D/physics/physicsCommon.h"
|
||||||
|
#endif
|
||||||
|
#ifndef COLLISION_COMPONENT_H
|
||||||
|
#include "T3D/components/collision/collisionComponent.h"
|
||||||
|
#endif
|
||||||
|
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/physics/physicsComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class PhysicsBody;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class RigidBodyComponent : public Component, public PhysicsComponentInterface
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
enum SimType
|
||||||
|
{
|
||||||
|
/// This physics representation only exists on the client
|
||||||
|
/// world and the server only does ghosting.
|
||||||
|
SimType_ClientOnly,
|
||||||
|
|
||||||
|
/// The physics representation only exists on the server world
|
||||||
|
/// and the client gets delta updates for rendering.
|
||||||
|
SimType_ServerOnly,
|
||||||
|
|
||||||
|
/// The physics representation exists on the client and the server
|
||||||
|
/// worlds with corrections occuring when the client gets out of sync.
|
||||||
|
SimType_ClientServer,
|
||||||
|
|
||||||
|
/// The bits used to pack the SimType field.
|
||||||
|
SimType_Bits = 3,
|
||||||
|
|
||||||
|
} mSimType;
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
/// The current physics state.
|
||||||
|
PhysicsState mState;
|
||||||
|
|
||||||
|
/// The previous and current render states.
|
||||||
|
PhysicsState mRenderState[2];
|
||||||
|
|
||||||
|
/// The abstracted physics actor.
|
||||||
|
PhysicsBody *mPhysicsRep;
|
||||||
|
|
||||||
|
PhysicsWorld *mWorld;
|
||||||
|
|
||||||
|
/// The starting position to place the shape when
|
||||||
|
/// the level begins or is reset.
|
||||||
|
MatrixF mResetPos;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
/// If true then no corrections are sent from the server
|
||||||
|
/// and/or applied from the client.
|
||||||
|
///
|
||||||
|
/// This is only ment for debugging.
|
||||||
|
///
|
||||||
|
static bool smNoCorrections;
|
||||||
|
|
||||||
|
/// If true then no smoothing is done on the client when
|
||||||
|
/// applying server corrections.
|
||||||
|
///
|
||||||
|
/// This is only ment for debugging.
|
||||||
|
///
|
||||||
|
static bool smNoSmoothing;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mMass;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mDynamicFriction;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mStaticFriction;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mRestitution;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mLinearDamping;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mAngularDamping;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mLinearSleepThreshold;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mAngularSleepThreshold;
|
||||||
|
|
||||||
|
// A scale applied to the normal linear and angular damping
|
||||||
|
// when the object enters a water volume.
|
||||||
|
F32 mWaterDampingScale;
|
||||||
|
|
||||||
|
// The density of this object used for water buoyancy effects.
|
||||||
|
F32 mBuoyancyDensity;
|
||||||
|
|
||||||
|
CollisionComponent* mOwnerColComponent;
|
||||||
|
|
||||||
|
enum MaskBits {
|
||||||
|
PositionMask = Parent::NextFreeMask << 0,
|
||||||
|
FreezeMask = Parent::NextFreeMask << 1,
|
||||||
|
StateMask = Parent::NextFreeMask << 2,
|
||||||
|
VelocityMask = Parent::NextFreeMask << 3,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
RigidBodyComponent();
|
||||||
|
virtual ~RigidBodyComponent();
|
||||||
|
DECLARE_CONOBJECT(RigidBodyComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
|
||||||
|
virtual void componentAddedToOwner(Component *comp);
|
||||||
|
virtual void componentRemovedFromOwner(Component *comp);
|
||||||
|
|
||||||
|
virtual void ownerTransformSet(MatrixF *mat);
|
||||||
|
|
||||||
|
inline F32 getMass() { return mMass; }
|
||||||
|
Point3F getVelocity() const { return mState.linVelocity; }
|
||||||
|
void applyImpulse(const Point3F &pos, const VectorF &vec);
|
||||||
|
void applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude);
|
||||||
|
|
||||||
|
void updateContainerForces();
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
virtual void processTick();
|
||||||
|
|
||||||
|
void findContact();
|
||||||
|
|
||||||
|
/// Save the current transform as where we return to when a physics reset
|
||||||
|
/// event occurs. This is automatically set in onAdd but some manipulators
|
||||||
|
/// such as Prefab need to make use of this.
|
||||||
|
void storeRestorePos();
|
||||||
|
|
||||||
|
void updatePhysics(PhysicsCollision *collision = NULL);
|
||||||
|
|
||||||
|
void _onPhysicsReset(PhysicsResetEvent reset);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RIGID_BODY_COMPONENT_H_
|
||||||
530
Engine/source/T3D/components/render/meshComponent.cpp
Normal file
530
Engine/source/T3D/components/render/meshComponent.cpp
Normal file
|
|
@ -0,0 +1,530 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "platform/platform.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "T3D/components/render/meshComponent.h"
|
||||||
|
#include "core/util/safeDelete.h"
|
||||||
|
#include "core/resourceManager.h"
|
||||||
|
#include "core/stream/fileStream.h"
|
||||||
|
#include "console/consoleTypes.h"
|
||||||
|
#include "console/consoleObject.h"
|
||||||
|
#include "core/stream/bitStream.h"
|
||||||
|
#include "sim/netConnection.h"
|
||||||
|
#include "gfx/gfxTransformSaver.h"
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "lighting/lightQuery.h"
|
||||||
|
#include "scene/sceneManager.h"
|
||||||
|
#include "gfx/bitmap/ddsFile.h"
|
||||||
|
#include "gfx/bitmap/ddsUtils.h"
|
||||||
|
#include "gfx/gfxTextureManager.h"
|
||||||
|
#include "materials/materialFeatureTypes.h"
|
||||||
|
#include "renderInstance/renderImposterMgr.h"
|
||||||
|
#include "util/imposterCapture.h"
|
||||||
|
#include "gfx/sim/debugDraw.h"
|
||||||
|
#include "gfx/gfxDrawUtil.h"
|
||||||
|
#include "materials/materialManager.h"
|
||||||
|
#include "materials/matInstance.h"
|
||||||
|
#include "core/strings/findMatch.h"
|
||||||
|
#include "T3D/components/render/meshComponent_ScriptBinding.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor/Destructor
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
MeshComponent::MeshComponent() : Component()
|
||||||
|
{
|
||||||
|
mShapeName = StringTable->insert("");
|
||||||
|
mShapeAsset = StringTable->insert("");
|
||||||
|
mShapeInstance = NULL;
|
||||||
|
|
||||||
|
mChangingMaterials.clear();
|
||||||
|
|
||||||
|
mMaterials.clear();
|
||||||
|
|
||||||
|
mFriendlyName = "Mesh Component";
|
||||||
|
mComponentType = "Render";
|
||||||
|
|
||||||
|
mDescription = getDescriptionText("Causes the object to render a non-animating 3d shape using the file provided.");
|
||||||
|
|
||||||
|
mNetworked = true;
|
||||||
|
mNetFlags.set(Ghostable | ScopeAlways);
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshComponent::~MeshComponent(){}
|
||||||
|
|
||||||
|
IMPLEMENT_CO_NETOBJECT_V1(MeshComponent);
|
||||||
|
|
||||||
|
//==========================================================================================
|
||||||
|
void MeshComponent::boneObject::addObject(SimObject* object)
|
||||||
|
{
|
||||||
|
SceneObject* sc = dynamic_cast<SceneObject*>(object);
|
||||||
|
|
||||||
|
if(sc && mOwner)
|
||||||
|
{
|
||||||
|
if(TSShape* shape = mOwner->getShape())
|
||||||
|
{
|
||||||
|
S32 nodeID = shape->findNode(mBoneName);
|
||||||
|
|
||||||
|
//we may have a offset on the shape's center
|
||||||
|
//so make sure we accomodate for that when setting up the mount offsets
|
||||||
|
MatrixF mat = mOwner->getNodeTransform(nodeID);
|
||||||
|
|
||||||
|
mOwner->getOwner()->mountObject(sc, nodeID, mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeshComponent::onAdd()
|
||||||
|
{
|
||||||
|
if(! Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Register for the resource change signal.
|
||||||
|
ResourceManager::get().getChangedSignal().notify( this, &MeshComponent::_onResourceChanged );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onComponentAdd()
|
||||||
|
{
|
||||||
|
Parent::onComponentAdd();
|
||||||
|
|
||||||
|
//get the default shape, if any
|
||||||
|
updateShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onRemove()
|
||||||
|
{
|
||||||
|
Parent::onRemove();
|
||||||
|
|
||||||
|
mMeshAsset.clear();
|
||||||
|
|
||||||
|
SAFE_DELETE(mShapeInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onComponentRemove()
|
||||||
|
{
|
||||||
|
if(mOwner)
|
||||||
|
{
|
||||||
|
Point3F pos = mOwner->getPosition(); //store our center pos
|
||||||
|
mOwner->setObjectBox(Box3F(Point3F(-1,-1,-1), Point3F(1,1,1)));
|
||||||
|
mOwner->setPosition(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent::onComponentRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::initPersistFields()
|
||||||
|
{
|
||||||
|
Parent::initPersistFields();
|
||||||
|
|
||||||
|
//create a hook to our internal variables
|
||||||
|
addGroup("Model");
|
||||||
|
addProtectedField("MeshAsset", TypeAssetId, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn,
|
||||||
|
"The asset Id used for the mesh.", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
|
||||||
|
endGroup("Model");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeshComponent::_setMesh(void *object, const char *index, const char *data)
|
||||||
|
{
|
||||||
|
MeshComponent *rbI = static_cast<MeshComponent*>(object);
|
||||||
|
|
||||||
|
// Sanity!
|
||||||
|
AssertFatal(data != NULL, "Cannot use a NULL asset Id.");
|
||||||
|
|
||||||
|
return rbI->setMeshAsset(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeshComponent::_setShape( void *object, const char *index, const char *data )
|
||||||
|
{
|
||||||
|
MeshComponent *rbI = static_cast<MeshComponent*>(object);
|
||||||
|
rbI->mShapeName = StringTable->insert(data);
|
||||||
|
rbI->updateShape(); //make sure we force the update to resize the owner bounds
|
||||||
|
rbI->setMaskBits(ShapeMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeshComponent::setMeshAsset(const char* assetName)
|
||||||
|
{
|
||||||
|
// Fetch the asset Id.
|
||||||
|
mMeshAssetId = StringTable->insert(assetName);
|
||||||
|
|
||||||
|
mMeshAsset = mMeshAssetId;
|
||||||
|
|
||||||
|
if (mMeshAsset.isNull())
|
||||||
|
{
|
||||||
|
Con::errorf("[MeshComponent] Failed to load mesh asset.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mShapeName = mMeshAssetId;
|
||||||
|
mShapeAsset = mShapeName;
|
||||||
|
updateShape(); //make sure we force the update to resize the owner bounds
|
||||||
|
setMaskBits(ShapeMask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::_onResourceChanged( const Torque::Path &path )
|
||||||
|
{
|
||||||
|
if ( path != Torque::Path( mShapeName ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
updateShape();
|
||||||
|
setMaskBits(ShapeMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::inspectPostApply()
|
||||||
|
{
|
||||||
|
Parent::inspectPostApply();
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 MeshComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
{
|
||||||
|
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||||
|
|
||||||
|
if (!mOwner || con->getGhostIndex(mOwner) == -1)
|
||||||
|
{
|
||||||
|
stream->writeFlag(false);
|
||||||
|
stream->writeFlag(false);
|
||||||
|
|
||||||
|
if (mask & ShapeMask)
|
||||||
|
retMask |= ShapeMask;
|
||||||
|
if (mask & MaterialMask)
|
||||||
|
retMask |= MaterialMask;
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->writeFlag(mask & ShapeMask))
|
||||||
|
{
|
||||||
|
stream->writeString(mShapeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->writeFlag( mask & MaterialMask ))
|
||||||
|
{
|
||||||
|
stream->writeInt(mChangingMaterials.size(), 16);
|
||||||
|
|
||||||
|
for(U32 i=0; i < mChangingMaterials.size(); i++)
|
||||||
|
{
|
||||||
|
stream->writeInt(mChangingMaterials[i].slot, 16);
|
||||||
|
|
||||||
|
NetStringHandle matNameStr = mChangingMaterials[i].matName.c_str();
|
||||||
|
con->packNetStringHandleU(stream, matNameStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
mChangingMaterials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
|
{
|
||||||
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
|
if(stream->readFlag())
|
||||||
|
{
|
||||||
|
mShapeName = stream->readSTString();
|
||||||
|
setMeshAsset(mShapeName);
|
||||||
|
updateShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stream->readFlag())
|
||||||
|
{
|
||||||
|
mChangingMaterials.clear();
|
||||||
|
U32 materialCount = stream->readInt(16);
|
||||||
|
|
||||||
|
for(U32 i=0; i < materialCount; i++)
|
||||||
|
{
|
||||||
|
matMap newMatMap;
|
||||||
|
newMatMap.slot = stream->readInt(16);
|
||||||
|
newMatMap.matName = String(con->unpackNetStringHandleU(stream).getString());
|
||||||
|
|
||||||
|
mChangingMaterials.push_back(newMatMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMaterials();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::prepRenderImage( SceneRenderState *state )
|
||||||
|
{
|
||||||
|
if (!mEnabled || !mOwner || !mShapeInstance)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Point3F cameraOffset;
|
||||||
|
mOwner->getRenderTransform().getColumn(3, &cameraOffset);
|
||||||
|
cameraOffset -= state->getDiffuseCameraPosition();
|
||||||
|
F32 dist = cameraOffset.len();
|
||||||
|
if (dist < 0.01f)
|
||||||
|
dist = 0.01f;
|
||||||
|
|
||||||
|
Point3F objScale = getOwner()->getScale();
|
||||||
|
F32 invScale = (1.0f / getMax(getMax(objScale.x, objScale.y), objScale.z));
|
||||||
|
|
||||||
|
mShapeInstance->setDetailFromDistance(state, dist * invScale);
|
||||||
|
|
||||||
|
if (mShapeInstance->getCurrentDetail() < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GFXTransformSaver saver;
|
||||||
|
|
||||||
|
// Set up our TS render state.
|
||||||
|
TSRenderState rdata;
|
||||||
|
rdata.setSceneState(state);
|
||||||
|
rdata.setFadeOverride(1.0f);
|
||||||
|
rdata.setOriginSort(false);
|
||||||
|
|
||||||
|
// We might have some forward lit materials
|
||||||
|
// so pass down a query to gather lights.
|
||||||
|
LightQuery query;
|
||||||
|
query.init(mOwner->getWorldSphere());
|
||||||
|
rdata.setLightQuery(&query);
|
||||||
|
|
||||||
|
MatrixF mat = mOwner->getRenderTransform();
|
||||||
|
Point3F renderPos = mat.getPosition();
|
||||||
|
EulerF renderRot = mat.toEuler();
|
||||||
|
mat.scale(objScale);
|
||||||
|
GFX->setWorldMatrix(mat);
|
||||||
|
|
||||||
|
mShapeInstance->render(rdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::updateShape()
|
||||||
|
{
|
||||||
|
bool isServer = isServerObject();
|
||||||
|
|
||||||
|
if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
|
||||||
|
{
|
||||||
|
if (mMeshAsset == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mShape = mMeshAsset->getShape();
|
||||||
|
|
||||||
|
if (!mShape)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setupShape();
|
||||||
|
|
||||||
|
//Do this on both the server and client
|
||||||
|
S32 materialCount = mShape->materialList->getMaterialNameList().size();
|
||||||
|
|
||||||
|
if(isServerObject())
|
||||||
|
{
|
||||||
|
//we need to update the editor
|
||||||
|
for (U32 i = 0; i < mFields.size(); i++)
|
||||||
|
{
|
||||||
|
//find any with the materialslot title and clear them out
|
||||||
|
if (FindMatch::isMatch("MaterialSlot*", mFields[i].mFieldName, false))
|
||||||
|
{
|
||||||
|
setDataField(mFields[i].mFieldName, NULL, "");
|
||||||
|
mFields.erase(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//next, get a listing of our materials in the shape, and build our field list for them
|
||||||
|
char matFieldName[128];
|
||||||
|
|
||||||
|
if(materialCount > 0)
|
||||||
|
mComponentGroup = StringTable->insert("Materials");
|
||||||
|
|
||||||
|
for(U32 i=0; i < materialCount; i++)
|
||||||
|
{
|
||||||
|
String materialname = mShape->materialList->getMaterialName(i);
|
||||||
|
if(materialname == String("ShapeBounds"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dSprintf(matFieldName, 128, "MaterialSlot%d", i);
|
||||||
|
|
||||||
|
addComponentField(matFieldName, "A material used in the shape file", "TypeAssetId", materialname, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(materialCount > 0)
|
||||||
|
mComponentGroup = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mOwner != NULL)
|
||||||
|
{
|
||||||
|
Point3F min, max, pos;
|
||||||
|
pos = mOwner->getPosition();
|
||||||
|
|
||||||
|
mOwner->getWorldToObj().mulP(pos);
|
||||||
|
|
||||||
|
min = mShape->bounds.minExtents;
|
||||||
|
max = mShape->bounds.maxExtents;
|
||||||
|
|
||||||
|
mShapeBounds.set(min, max);
|
||||||
|
|
||||||
|
mOwner->setObjectBox(Box3F(min, max));
|
||||||
|
|
||||||
|
if( mOwner->getSceneManager() != NULL )
|
||||||
|
mOwner->getSceneManager()->notifyObjectDirty( mOwner );
|
||||||
|
}
|
||||||
|
|
||||||
|
//finally, notify that our shape was changed
|
||||||
|
onShapeInstanceChanged.trigger(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::setupShape()
|
||||||
|
{
|
||||||
|
mShapeInstance = new TSShapeInstance(mShape, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::updateMaterials()
|
||||||
|
{
|
||||||
|
if (mChangingMaterials.empty() || !mShape)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TSMaterialList* pMatList = mShapeInstance->getMaterialList();
|
||||||
|
pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
|
||||||
|
|
||||||
|
const Vector<String> &materialNames = pMatList->getMaterialNameList();
|
||||||
|
for ( S32 i = 0; i < materialNames.size(); i++ )
|
||||||
|
{
|
||||||
|
const String &pName = materialNames[i];
|
||||||
|
|
||||||
|
for(U32 m=0; m < mChangingMaterials.size(); m++)
|
||||||
|
{
|
||||||
|
if(mChangingMaterials[m].slot == i)
|
||||||
|
{
|
||||||
|
pMatList->renameMaterial( i, mChangingMaterials[m].matName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mChangingMaterials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the material instances
|
||||||
|
mShapeInstance->initMaterialList();
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
|
||||||
|
{
|
||||||
|
if (mShape)
|
||||||
|
{
|
||||||
|
S32 nodeCount = getShape()->nodes.size();
|
||||||
|
|
||||||
|
if(nodeIdx >= 0 && nodeIdx < nodeCount)
|
||||||
|
{
|
||||||
|
//animate();
|
||||||
|
MatrixF mountTransform = mShapeInstance->mNodeTransforms[nodeIdx];
|
||||||
|
mountTransform.mul(mOwner->getRenderTransform());
|
||||||
|
|
||||||
|
return mountTransform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MatrixF::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 MeshComponent::getNodeByName(String nodeName)
|
||||||
|
{
|
||||||
|
if (mShape)
|
||||||
|
{
|
||||||
|
S32 nodeIdx = getShape()->findNode(nodeName);
|
||||||
|
|
||||||
|
return nodeIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeshComponent::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::mountObjectToNode(SceneObject* objB, String node, MatrixF txfm)
|
||||||
|
{
|
||||||
|
const char* test;
|
||||||
|
test = node.c_str();
|
||||||
|
if(dIsdigit(test[0]))
|
||||||
|
{
|
||||||
|
getOwner()->mountObject(objB, dAtoi(node), txfm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(TSShape* shape = getShape())
|
||||||
|
{
|
||||||
|
S32 idx = shape->findNode(node);
|
||||||
|
getOwner()->mountObject(objB, idx, txfm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onDynamicModified(const char* slotName, const char* newValue)
|
||||||
|
{
|
||||||
|
if(FindMatch::isMatch( "materialslot*", slotName, false ))
|
||||||
|
{
|
||||||
|
if(!getShape())
|
||||||
|
return;
|
||||||
|
|
||||||
|
S32 slot = -1;
|
||||||
|
String outStr( String::GetTrailingNumber( slotName, slot ) );
|
||||||
|
|
||||||
|
if(slot == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for(U32 i=0; i < mChangingMaterials.size(); i++)
|
||||||
|
{
|
||||||
|
if(mChangingMaterials[i].slot == slot)
|
||||||
|
{
|
||||||
|
mChangingMaterials[i].matName = String(newValue);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!found)
|
||||||
|
{
|
||||||
|
matMap newMatMap;
|
||||||
|
newMatMap.slot = slot;
|
||||||
|
newMatMap.matName = String(newValue);
|
||||||
|
|
||||||
|
mChangingMaterials.push_back(newMatMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaskBits(MaterialMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent::onDynamicModified(slotName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::changeMaterial(U32 slot, const char* newMat)
|
||||||
|
{
|
||||||
|
|
||||||
|
char fieldName[512];
|
||||||
|
|
||||||
|
//update our respective field
|
||||||
|
dSprintf(fieldName, 512, "materialSlot%d", slot);
|
||||||
|
setDataField(fieldName, NULL, newMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onInspect()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshComponent::onEndInspect()
|
||||||
|
{
|
||||||
|
}
|
||||||
183
Engine/source/T3D/components/render/meshComponent.h
Normal file
183
Engine/source/T3D/components/render/meshComponent.h
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef STATIC_MESH_COMPONENT_H
|
||||||
|
#define STATIC_MESH_COMPONENT_H
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef __RESOURCE_H__
|
||||||
|
#include "core/resource.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShape.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SCENERENDERSTATE_H_
|
||||||
|
#include "scene/sceneRenderState.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MBOX_H_
|
||||||
|
#include "math/mBox.h"
|
||||||
|
#endif
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _NETSTRINGTABLE_H_
|
||||||
|
#include "sim/netStringTable.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||||
|
#include "T3D/components/render/renderComponentInterface.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _ASSET_PTR_H_
|
||||||
|
#include "assets/assetPtr.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _SHAPE_ASSET_H_
|
||||||
|
#include "T3D/assets/ShapeAsset.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _GFXVERTEXFORMAT_H_
|
||||||
|
#include "gfx/gfxVertexFormat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TSShapeInstance;
|
||||||
|
class SceneRenderState;
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
class MeshComponent : public Component,
|
||||||
|
public RenderComponentInterface,
|
||||||
|
public CastRayRenderedInterface,
|
||||||
|
public EditorInspectInterface
|
||||||
|
{
|
||||||
|
typedef Component Parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ShapeMask = Parent::NextFreeMask,
|
||||||
|
MaterialMask = Parent::NextFreeMask << 1,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
StringTableEntry mShapeName;
|
||||||
|
StringTableEntry mShapeAsset;
|
||||||
|
TSShape* mShape;
|
||||||
|
Box3F mShapeBounds;
|
||||||
|
Point3F mCenterOffset;
|
||||||
|
|
||||||
|
struct matMap
|
||||||
|
{
|
||||||
|
String matName;
|
||||||
|
U32 slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<matMap> mChangingMaterials;
|
||||||
|
Vector<matMap> mMaterials;
|
||||||
|
|
||||||
|
class boneObject : public SimGroup
|
||||||
|
{
|
||||||
|
MeshComponent *mOwner;
|
||||||
|
public:
|
||||||
|
boneObject(MeshComponent *owner){ mOwner = owner; }
|
||||||
|
|
||||||
|
StringTableEntry mBoneName;
|
||||||
|
S32 mItemID;
|
||||||
|
|
||||||
|
virtual void addObject(SimObject *obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<boneObject*> mNodesList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StringTableEntry mMeshAssetId;
|
||||||
|
AssetPtr<ShapeAsset> mMeshAsset;
|
||||||
|
|
||||||
|
TSShapeInstance* mShapeInstance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MeshComponent();
|
||||||
|
virtual ~MeshComponent();
|
||||||
|
DECLARE_CONOBJECT(MeshComponent);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
virtual void inspectPostApply();
|
||||||
|
|
||||||
|
virtual void prepRenderImage(SceneRenderState *state);
|
||||||
|
|
||||||
|
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||||
|
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||||
|
|
||||||
|
Box3F getShapeBounds() { return mShapeBounds; }
|
||||||
|
|
||||||
|
virtual MatrixF getNodeTransform(S32 nodeIdx);
|
||||||
|
S32 getNodeByName(String nodeName);
|
||||||
|
|
||||||
|
void setupShape();
|
||||||
|
void updateShape();
|
||||||
|
void updateMaterials();
|
||||||
|
|
||||||
|
virtual void onComponentRemove();
|
||||||
|
virtual void onComponentAdd();
|
||||||
|
|
||||||
|
static bool _setMesh(void *object, const char *index, const char *data);
|
||||||
|
static bool _setShape(void *object, const char *index, const char *data);
|
||||||
|
const char* _getShape(void *object, const char *data);
|
||||||
|
|
||||||
|
bool setMeshAsset(const char* assetName);
|
||||||
|
|
||||||
|
virtual TSShape* getShape() { if (mMeshAsset) return mMeshAsset->getShape(); else return NULL; }
|
||||||
|
virtual TSShapeInstance* getShapeInstance() { return mShapeInstance; }
|
||||||
|
|
||||||
|
Resource<TSShape> getShapeResource() { return mMeshAsset->getShapeResource(); }
|
||||||
|
|
||||||
|
void _onResourceChanged(const Torque::Path &path);
|
||||||
|
|
||||||
|
virtual bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info);
|
||||||
|
|
||||||
|
void mountObjectToNode(SceneObject* objB, String node, MatrixF txfm);
|
||||||
|
|
||||||
|
virtual void onDynamicModified(const char* slotName, const char* newValue);
|
||||||
|
|
||||||
|
void changeMaterial(U32 slot, const char* newMat);
|
||||||
|
|
||||||
|
virtual void onInspect();
|
||||||
|
virtual void onEndInspect();
|
||||||
|
|
||||||
|
virtual Vector<MatrixF> getNodeTransforms()
|
||||||
|
{
|
||||||
|
Vector<MatrixF> bob;
|
||||||
|
return bob;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setNodeTransforms(Vector<MatrixF> transforms)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/components/render/meshComponent.h"
|
||||||
|
#include "scene/sceneObject.h"
|
||||||
|
#include "math/mTransform.h"
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, getShapeBounds, Box3F, (), ,
|
||||||
|
"@brief Get the cobject we're in contact with.\n\n"
|
||||||
|
|
||||||
|
"The controlling client is the one that will send moves to us to act on.\n"
|
||||||
|
|
||||||
|
"@return the ID of the controlling GameConnection, or 0 if this object is not "
|
||||||
|
"controlled by any client.\n"
|
||||||
|
|
||||||
|
"@see GameConnection\n")
|
||||||
|
{
|
||||||
|
return object->getShapeBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, mountObject, bool,
|
||||||
|
(SceneObject* objB, String node, TransformF txfm), (MatrixF::Identity),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)")
|
||||||
|
{
|
||||||
|
if (objB)
|
||||||
|
{
|
||||||
|
//BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
|
||||||
|
//for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
|
||||||
|
object->mountObjectToNode(objB, node, /*MatrixF::Identity*/txfm.getMatrix());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, getNodeTransform, TransformF,
|
||||||
|
(S32 node), (-1),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)")
|
||||||
|
{
|
||||||
|
if (node != -1)
|
||||||
|
{
|
||||||
|
//BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
|
||||||
|
//for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
|
||||||
|
//object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
|
||||||
|
MatrixF mat = object->getNodeTransform(node);
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TransformF::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, getNodeEulerRot, EulerF,
|
||||||
|
(S32 node, bool radToDeg), (-1, true),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)")
|
||||||
|
{
|
||||||
|
if (node != -1)
|
||||||
|
{
|
||||||
|
//BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
|
||||||
|
//for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
|
||||||
|
//object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
|
||||||
|
MatrixF mat = object->getNodeTransform(node);
|
||||||
|
|
||||||
|
EulerF eul = mat.toEuler();
|
||||||
|
if (radToDeg)
|
||||||
|
eul = EulerF(mRadToDeg(eul.x), mRadToDeg(eul.y), mRadToDeg(eul.z));
|
||||||
|
|
||||||
|
return eul;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EulerF(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, getNodePosition, Point3F,
|
||||||
|
(S32 node), (-1),
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)")
|
||||||
|
{
|
||||||
|
if (node != -1)
|
||||||
|
{
|
||||||
|
//BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
|
||||||
|
//for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
|
||||||
|
//object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
|
||||||
|
MatrixF mat = object->getNodeTransform(node);
|
||||||
|
|
||||||
|
return mat.getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Point3F(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, getNodeByName, S32,
|
||||||
|
(String nodeName), ,
|
||||||
|
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||||
|
|
||||||
|
"@param objB Object to mount onto us\n"
|
||||||
|
"@param slot Mount slot ID\n"
|
||||||
|
"@param txfm (optional) mount offset transform\n"
|
||||||
|
"@return true if successful, false if failed (objB is not valid)")
|
||||||
|
{
|
||||||
|
if (!nodeName.isEmpty())
|
||||||
|
{
|
||||||
|
//BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
|
||||||
|
//for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
|
||||||
|
//object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
|
||||||
|
S32 node = object->getNodeByName(nodeName);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(MeshComponent, changeMaterial, void, (U32 slot, const char* newMat), (0, ""),
|
||||||
|
"@brief Change one of the materials on the shape.\n\n")
|
||||||
|
{
|
||||||
|
object->changeMaterial(slot, newMat);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||||
|
#define RENDER_COMPONENT_INTERFACE_H
|
||||||
|
|
||||||
|
#ifndef _TSSHAPE_H_
|
||||||
|
#include "ts/tsShape.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _TSSHAPEINSTANCE_H_
|
||||||
|
#include "ts/tsShapeInstance.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CORE_INTERFACES_H
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class RenderComponentInterface : public Interface < RenderComponentInterface >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void prepRenderImage(SceneRenderState *state) = 0;
|
||||||
|
|
||||||
|
virtual TSShape* getShape() = 0;
|
||||||
|
|
||||||
|
Signal< void(RenderComponentInterface*) > onShapeChanged;
|
||||||
|
|
||||||
|
virtual TSShapeInstance* getShapeInstance() = 0;
|
||||||
|
|
||||||
|
virtual MatrixF getNodeTransform(S32 nodeIdx) = 0;
|
||||||
|
|
||||||
|
virtual Vector<MatrixF> getNodeTransforms() = 0;
|
||||||
|
|
||||||
|
virtual void setNodeTransforms(Vector<MatrixF> transforms) = 0;
|
||||||
|
|
||||||
|
Signal< void(RenderComponentInterface*) > onShapeInstanceChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRayRenderedInterface// : public Interface<CastRayRenderedInterface>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
1939
Engine/source/T3D/entity.cpp
Normal file
1939
Engine/source/T3D/entity.cpp
Normal file
File diff suppressed because it is too large
Load diff
283
Engine/source/T3D/entity.h
Normal file
283
Engine/source/T3D/entity.h
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ENTITY_H
|
||||||
|
#define ENTITY_H
|
||||||
|
|
||||||
|
#ifndef _GAMEBASE_H_
|
||||||
|
#include "T3D/gameBase/gameBase.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _MOVEMANAGER_H_
|
||||||
|
#include "T3D/gameBase/moveManager.h"
|
||||||
|
#endif
|
||||||
|
#ifndef COMPONENT_H
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
#ifndef _CONTAINERQUERY_H_
|
||||||
|
#include "T3D/containerQuery.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Component;
|
||||||
|
|
||||||
|
//**************************************************************************
|
||||||
|
// Entity
|
||||||
|
//**************************************************************************
|
||||||
|
class Entity : public GameBase
|
||||||
|
{
|
||||||
|
typedef GameBase Parent;
|
||||||
|
friend class Component;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Point3F mPos;
|
||||||
|
RotationF mRot;
|
||||||
|
|
||||||
|
Vector<Component*> mComponents;
|
||||||
|
|
||||||
|
Vector<Component*> mToLoadComponents;
|
||||||
|
|
||||||
|
bool mStartComponentUpdate;
|
||||||
|
|
||||||
|
ContainerQueryInfo containerInfo;
|
||||||
|
|
||||||
|
bool mInitialized;
|
||||||
|
|
||||||
|
Signal< void(Component*) > onComponentAdded;
|
||||||
|
Signal< void(Component*) > onComponentRemoved;
|
||||||
|
|
||||||
|
Signal< void(MatrixF*) > onTransformSet;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void processTick(const Move* move);
|
||||||
|
virtual void advanceTime(F32 dt);
|
||||||
|
virtual void interpolateTick(F32 delta);
|
||||||
|
|
||||||
|
void prepRenderImage(SceneRenderState *state);
|
||||||
|
|
||||||
|
virtual bool onAdd();
|
||||||
|
virtual void onRemove();
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct StateDelta
|
||||||
|
{
|
||||||
|
Move move; ///< Last move from server
|
||||||
|
F32 dt; ///< Last interpolation time
|
||||||
|
// Interpolation data
|
||||||
|
Point3F pos;
|
||||||
|
Point3F posVec;
|
||||||
|
QuatF rot[2];
|
||||||
|
// Warp data
|
||||||
|
S32 warpTicks; ///< Number of ticks to warp
|
||||||
|
S32 warpCount; ///< Current pos in warp
|
||||||
|
Point3F warpOffset;
|
||||||
|
QuatF warpRot[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MaskBits
|
||||||
|
{
|
||||||
|
TransformMask = Parent::NextFreeMask << 0,
|
||||||
|
BoundsMask = Parent::NextFreeMask << 1,
|
||||||
|
ComponentsMask = Parent::NextFreeMask << 2,
|
||||||
|
NoWarpMask = Parent::NextFreeMask << 3,
|
||||||
|
NextFreeMask = Parent::NextFreeMask << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
StateDelta mDelta;
|
||||||
|
S32 mPredictionCount; ///< Number of ticks to predict
|
||||||
|
|
||||||
|
Move lastMove;
|
||||||
|
|
||||||
|
//
|
||||||
|
Entity();
|
||||||
|
~Entity();
|
||||||
|
|
||||||
|
static void initPersistFields();
|
||||||
|
virtual void onPostAdd();
|
||||||
|
|
||||||
|
virtual void setTransform(const MatrixF &mat);
|
||||||
|
virtual void setRenderTransform(const MatrixF &mat);
|
||||||
|
|
||||||
|
void setTransform(Point3F position, RotationF rotation);
|
||||||
|
|
||||||
|
void setRenderTransform(Point3F position, RotationF rotation);
|
||||||
|
|
||||||
|
virtual MatrixF getTransform();
|
||||||
|
virtual Point3F getPosition() const { return mPos; }
|
||||||
|
|
||||||
|
//void setTransform(Point3F position, RotationF rot);
|
||||||
|
|
||||||
|
//void setRotation(RotationF rotation);
|
||||||
|
|
||||||
|
void setRotation(RotationF rotation) {
|
||||||
|
mRot = rotation;
|
||||||
|
setMaskBits(TransformMask);
|
||||||
|
};
|
||||||
|
RotationF getRotation() { return mRot; }
|
||||||
|
|
||||||
|
void setMountOffset(Point3F posOffset);
|
||||||
|
void setMountRotation(EulerF rotOffset);
|
||||||
|
|
||||||
|
//static bool _setEulerRotation( void *object, const char *index, const char *data );
|
||||||
|
static bool _setPosition(void *object, const char *index, const char *data);
|
||||||
|
static const char * _getPosition(void* obj, const char* data);
|
||||||
|
|
||||||
|
static bool _setRotation(void *object, const char *index, const char *data);
|
||||||
|
static const char * _getRotation(void* obj, const char* data);
|
||||||
|
|
||||||
|
virtual void getMountTransform(S32 index, const MatrixF &xfm, MatrixF *outMat);
|
||||||
|
virtual void getRenderMountTransform(F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat);
|
||||||
|
|
||||||
|
void setForwardVector(VectorF newForward, VectorF upVector = VectorF::Zero);
|
||||||
|
|
||||||
|
virtual void mountObject(SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity);
|
||||||
|
void mountObject(SceneObject* objB, MatrixF txfm);
|
||||||
|
void onMount(SceneObject *obj, S32 node);
|
||||||
|
void onUnmount(SceneObject *obj, S32 node);
|
||||||
|
|
||||||
|
// NetObject
|
||||||
|
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||||
|
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||||
|
|
||||||
|
void setComponentsDirty();
|
||||||
|
void setComponentDirty(Component *comp, bool forceUpdate = false);
|
||||||
|
|
||||||
|
//Components
|
||||||
|
virtual bool deferAddingComponents() const { return true; }
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T* getComponent();
|
||||||
|
template <class T>
|
||||||
|
Vector<T*> getComponents();
|
||||||
|
|
||||||
|
Component* getComponent(String componentType);
|
||||||
|
|
||||||
|
U32 getComponentCount() const
|
||||||
|
{
|
||||||
|
return mComponents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setObjectBox(Box3F objBox);
|
||||||
|
|
||||||
|
void resetWorldBox() { Parent::resetWorldBox(); }
|
||||||
|
void resetObjectBox() { Parent::resetObjectBox(); }
|
||||||
|
void resetRenderWorldBox() { Parent::resetRenderWorldBox(); }
|
||||||
|
|
||||||
|
//function redirects for collisions
|
||||||
|
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||||
|
bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||||
|
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
|
||||||
|
virtual void buildConvex(const Box3F& box, Convex* convex);
|
||||||
|
|
||||||
|
Signal< void(SimObject*, String, String) > onDataSet;
|
||||||
|
virtual void setDataField(StringTableEntry slotName, const char *array, const char *value);
|
||||||
|
virtual void onStaticModified(const char* slotName, const char* newValue);
|
||||||
|
|
||||||
|
//void pushEvent(const char* eventName, Vector<const char*> eventParams);
|
||||||
|
|
||||||
|
void updateContainer();
|
||||||
|
|
||||||
|
ContainerQueryInfo getContainerInfo() { return containerInfo; }
|
||||||
|
|
||||||
|
//camera stuff
|
||||||
|
virtual void getCameraTransform(F32* pos, MatrixF* mat);
|
||||||
|
virtual void onCameraScopeQuery(NetConnection* connection, CameraScopeQuery* query);
|
||||||
|
|
||||||
|
//Heirarchy stuff
|
||||||
|
virtual void addObject(SimObject* object);
|
||||||
|
virtual void removeObject(SimObject* object);
|
||||||
|
|
||||||
|
virtual SimObject* findObjectByInternalName(StringTableEntry internalName, bool searchChildren);
|
||||||
|
|
||||||
|
//component stuff
|
||||||
|
bool addComponent(Component *comp);
|
||||||
|
bool removeComponent(Component *comp, bool deleteComponent);
|
||||||
|
void clearComponents(bool deleteComponents = true);
|
||||||
|
Component* getComponent(const U32 index) const;
|
||||||
|
|
||||||
|
void onInspect();
|
||||||
|
void onEndInspect();
|
||||||
|
|
||||||
|
virtual void write(Stream &stream, U32 tabStop, U32 flags);
|
||||||
|
|
||||||
|
// TamlChildren
|
||||||
|
virtual U32 getTamlChildCount(void) const
|
||||||
|
{
|
||||||
|
U32 componentCount = getComponentCount();
|
||||||
|
U32 childSize = (U32)size();
|
||||||
|
return componentCount + childSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual SimObject* getTamlChild(const U32 childIndex) const;
|
||||||
|
|
||||||
|
virtual void addTamlChild(SimObject* pSimObject)
|
||||||
|
{
|
||||||
|
// Sanity!
|
||||||
|
AssertFatal(pSimObject != NULL, "SimSet::addTamlChild() - Cannot add a NULL child object.");
|
||||||
|
|
||||||
|
addObject(pSimObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box3F getObjectBox() { return mObjBox; }
|
||||||
|
MatrixF getWorldToObj() { return mWorldToObj; }
|
||||||
|
MatrixF getObjToWorld() { return mObjToWorld; }
|
||||||
|
|
||||||
|
DECLARE_CONOBJECT(Entity);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T *Entity::getComponent()
|
||||||
|
{
|
||||||
|
U32 componentCount = getComponentCount();
|
||||||
|
for (U32 i = 0; i < componentCount; i++)
|
||||||
|
{
|
||||||
|
T* t = dynamic_cast<T *>(mComponents[i]);
|
||||||
|
|
||||||
|
if (t)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Vector<T*> Entity::getComponents()
|
||||||
|
{
|
||||||
|
Vector<T*> foundObjects;
|
||||||
|
|
||||||
|
T *curObj;
|
||||||
|
Component* comp;
|
||||||
|
|
||||||
|
// Loop through our child objects.
|
||||||
|
for (U32 i = 0; i < mComponents.size(); i++)
|
||||||
|
{
|
||||||
|
curObj = dynamic_cast<T*>(mComponents[i]);
|
||||||
|
|
||||||
|
// Add this child object if appropriate.
|
||||||
|
if (curObj)
|
||||||
|
foundObjects.push_back(curObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundObjects;
|
||||||
|
}
|
||||||
|
#endif //ENTITY_H
|
||||||
|
|
@ -117,7 +117,7 @@ void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect)
|
||||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return;
|
return;
|
||||||
ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
GameBase* control = dynamic_cast<GameBase*>(conn->getCameraObject());
|
||||||
if (!control || !(control->getTypeMask() & ObjectMask) || !conn->isFirstPerson())
|
if (!control || !(control->getTypeMask() & ObjectMask) || !conn->isFirstPerson())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@
|
||||||
#include "console/engineAPI.h"
|
#include "console/engineAPI.h"
|
||||||
#include "math/mTransform.h"
|
#include "math/mTransform.h"
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef TORQUE_HIFI_NET
|
#ifdef TORQUE_HIFI_NET
|
||||||
#include "T3D/gameBase/hifi/hifiMoveList.h"
|
#include "T3D/gameBase/hifi/hifiMoveList.h"
|
||||||
#elif defined TORQUE_EXTENDED_MOVE
|
#elif defined TORQUE_EXTENDED_MOVE
|
||||||
|
|
@ -551,7 +556,9 @@ void GameConnection::setControlObject(GameBase *obj)
|
||||||
obj->setControllingClient(this);
|
obj->setControllingClient(this);
|
||||||
|
|
||||||
// Update the camera's FOV to match the new control object
|
// Update the camera's FOV to match the new control object
|
||||||
setControlCameraFov( obj->getCameraFov() );
|
//but only if we don't have a specific camera object
|
||||||
|
if (!mCameraObject)
|
||||||
|
setControlCameraFov(obj->getCameraFov());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Okay, set our control object.
|
// Okay, set our control object.
|
||||||
|
|
@ -729,7 +736,21 @@ bool GameConnection::getControlCameraFov(F32 * fov)
|
||||||
}
|
}
|
||||||
if (cObj)
|
if (cObj)
|
||||||
{
|
{
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
if (Entity* ent = dynamic_cast<Entity*>(cObj))
|
||||||
|
{
|
||||||
|
if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
|
||||||
|
{
|
||||||
|
*fov = camInterface->getCameraFov();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
*fov = cObj->getCameraFov();
|
*fov = cObj->getCameraFov();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
*fov = cObj->getCameraFov();
|
||||||
|
#endif
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -747,7 +768,26 @@ bool GameConnection::isValidControlCameraFov(F32 fov)
|
||||||
obj = obj->getControlObject();
|
obj = obj->getControlObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
return cObj ? cObj->isValidCameraFov(fov) : NULL;
|
if (cObj)
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
if (Entity* ent = dynamic_cast<Entity*>(cObj))
|
||||||
|
{
|
||||||
|
if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
|
||||||
|
{
|
||||||
|
return camInterface->isValidCameraFov(fov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cObj->isValidCameraFov(fov);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return cObj->isValidCameraFov(fov);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameConnection::setControlCameraFov(F32 fov)
|
bool GameConnection::setControlCameraFov(F32 fov)
|
||||||
|
|
@ -762,9 +802,32 @@ bool GameConnection::setControlCameraFov(F32 fov)
|
||||||
}
|
}
|
||||||
if (cObj)
|
if (cObj)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
F32 newFov = 90.f;
|
||||||
|
if (Entity* ent = dynamic_cast<Entity*>(cObj))
|
||||||
|
{
|
||||||
|
if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
|
||||||
|
{
|
||||||
|
camInterface->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
||||||
|
newFov = camInterface->getCameraFov();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Con::errorf("Attempted to setControlCameraFov, but we don't have a camera!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// allow shapebase to clamp fov to its datablock values
|
||||||
|
cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
||||||
|
newFov = cObj->getCameraFov();
|
||||||
|
}
|
||||||
|
#else
|
||||||
// allow shapebase to clamp fov to its datablock values
|
// allow shapebase to clamp fov to its datablock values
|
||||||
cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
||||||
F32 newFov = cObj->getCameraFov();
|
F32 newFov = cObj->getCameraFov();
|
||||||
|
#endif
|
||||||
|
|
||||||
// server fov of client has 1degree resolution
|
// server fov of client has 1degree resolution
|
||||||
if( S32(newFov) != S32(mCameraFov) || newFov != fov )
|
if( S32(newFov) != S32(mCameraFov) || newFov != fov )
|
||||||
|
|
@ -1147,10 +1210,17 @@ void GameConnection::readPacket(BitStream *bstream)
|
||||||
|
|
||||||
if (bstream->readFlag())
|
if (bstream->readFlag())
|
||||||
{
|
{
|
||||||
|
bool callScript = false;
|
||||||
|
if (mCameraObject.isNull())
|
||||||
|
callScript = true;
|
||||||
|
|
||||||
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
|
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
|
||||||
GameBase* obj = dynamic_cast<GameBase*>(resolveGhost(gIndex));
|
GameBase* obj = dynamic_cast<GameBase*>(resolveGhost(gIndex));
|
||||||
setCameraObject(obj);
|
setCameraObject(obj);
|
||||||
obj->readPacketData(this, bstream);
|
obj->readPacketData(this, bstream);
|
||||||
|
|
||||||
|
if (callScript)
|
||||||
|
initialControlSet_callback();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
setCameraObject(0);
|
setCameraObject(0);
|
||||||
|
|
@ -1727,6 +1797,13 @@ DefineEngineMethod( GameConnection, transmitDataBlocks, void, (S32 sequence),,
|
||||||
// Ensure that the client knows that the datablock send is done...
|
// Ensure that the client knows that the datablock send is done...
|
||||||
object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
|
object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iCount == 0)
|
||||||
|
{
|
||||||
|
//if we have no datablocks to send, we still need to be able to complete the level load process
|
||||||
|
//so fire off our callback anyways
|
||||||
|
object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@
|
||||||
#include "platform/profiler.h"
|
#include "platform/profiler.h"
|
||||||
#include "console/consoleTypes.h"
|
#include "console/consoleTypes.h"
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
ProcessObject::ProcessObject()
|
ProcessObject::ProcessObject()
|
||||||
|
|
@ -268,6 +272,13 @@ void ProcessList::advanceObjects()
|
||||||
onTickObject(pobj);
|
onTickObject(pobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
for (U32 i = 0; i < UpdateInterface::all.size(); i++)
|
||||||
|
{
|
||||||
|
UpdateInterface::all[i]->processTick();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mTotalTicks++;
|
mTotalTicks++;
|
||||||
|
|
||||||
PROFILE_END();
|
PROFILE_END();
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@
|
||||||
#include "T3D/gameBase/std/stdMoveList.h"
|
#include "T3D/gameBase/std/stdMoveList.h"
|
||||||
#include "T3D/fx/cameraFXMgr.h"
|
#include "T3D/fx/cameraFXMgr.h"
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
#include "T3D/components/coreInterfaces.h"
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
MODULE_BEGIN( ProcessList )
|
MODULE_BEGIN( ProcessList )
|
||||||
|
|
||||||
MODULE_INIT
|
MODULE_INIT
|
||||||
|
|
@ -132,6 +137,18 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
|
||||||
obj = obj->mProcessLink.next;
|
obj = obj->mProcessLink.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
for (U32 i = 0; i < UpdateInterface::all.size(); i++)
|
||||||
|
{
|
||||||
|
Component *comp = dynamic_cast<Component*>(UpdateInterface::all[i]);
|
||||||
|
|
||||||
|
if (!comp->isClientObject() || !comp->isActive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UpdateInterface::all[i]->interpolateTick(mLastDelta);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Inform objects of total elapsed delta so they can advance
|
// Inform objects of total elapsed delta so they can advance
|
||||||
// client side animations.
|
// client side animations.
|
||||||
F32 dt = F32(timeDelta) / 1000;
|
F32 dt = F32(timeDelta) / 1000;
|
||||||
|
|
@ -146,6 +163,21 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
|
||||||
obj = obj->mProcessLink.next;
|
obj = obj->mProcessLink.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
for (U32 i = 0; i < UpdateInterface::all.size(); i++)
|
||||||
|
{
|
||||||
|
Component *comp = dynamic_cast<Component*>(UpdateInterface::all[i]);
|
||||||
|
|
||||||
|
if (comp)
|
||||||
|
{
|
||||||
|
if (!comp->isClientObject() || !comp->isActive())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateInterface::all[i]->advanceTime(dt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ bool GameProcessCameraQuery(CameraQuery *query)
|
||||||
|
|
||||||
if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
|
if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
|
||||||
{
|
{
|
||||||
query->object = dynamic_cast<ShapeBase*>(connection->getControlObject());
|
query->object = dynamic_cast<GameBase*>(connection->getCameraObject());
|
||||||
query->nearPlane = gClientSceneGraph->getNearClip();
|
query->nearPlane = gClientSceneGraph->getNearClip();
|
||||||
|
|
||||||
// Scale the normal visible distance by the performance
|
// Scale the normal visible distance by the performance
|
||||||
|
|
|
||||||
|
|
@ -190,12 +190,14 @@ void LightAnimData::AnimValue<COUNT>::updateKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
template<U32 COUNT>
|
template<U32 COUNT>
|
||||||
bool LightAnimData::AnimValue<COUNT>::animate( F32 time, F32 *output )
|
bool LightAnimData::AnimValue<COUNT>::animate(F32 time, F32 *output, bool multiply)
|
||||||
{
|
{
|
||||||
F32 scaledTime, lerpFactor, valueRange, keyFrameLerp;
|
F32 scaledTime, lerpFactor, valueRange, keyFrameLerp;
|
||||||
U32 posFrom, posTo;
|
U32 posFrom, posTo;
|
||||||
S32 keyFrameFrom, keyFrameTo;
|
S32 keyFrameFrom, keyFrameTo;
|
||||||
F32 initialValue = *output;
|
F32 initialValue = *output;
|
||||||
|
if (!multiply)
|
||||||
|
initialValue = 1;
|
||||||
|
|
||||||
bool wasAnimated = false;
|
bool wasAnimated = false;
|
||||||
|
|
||||||
|
|
@ -305,6 +307,6 @@ void LightAnimData::animate( LightInfo *lightInfo, LightAnimState *state )
|
||||||
lightInfo->setColor( color );
|
lightInfo->setColor( color );
|
||||||
|
|
||||||
F32 brightness = state->brightness;
|
F32 brightness = state->brightness;
|
||||||
mBrightness.animate( time, &brightness );
|
mBrightness.animate( time, &brightness, true );
|
||||||
lightInfo->setBrightness( brightness );
|
lightInfo->setBrightness( brightness );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ public:
|
||||||
/// Performs the animation returning the results in the output if
|
/// Performs the animation returning the results in the output if
|
||||||
/// the time scale is greater than zero.
|
/// the time scale is greater than zero.
|
||||||
/// @return Returns true if the animation was performed.
|
/// @return Returns true if the animation was performed.
|
||||||
bool animate( F32 time, F32 *output );
|
bool animate(F32 time, F32 *output, bool multiply = false);
|
||||||
|
|
||||||
/// Called when the key string is changed to update the
|
/// Called when the key string is changed to update the
|
||||||
/// key length and time scale.
|
/// key length and time scale.
|
||||||
|
|
|
||||||
|
|
@ -147,21 +147,25 @@ enum SceneObjectTypes
|
||||||
/// @see PhysicalZone
|
/// @see PhysicalZone
|
||||||
PhysicalZoneObjectType = BIT( 22 ),
|
PhysicalZoneObjectType = BIT( 22 ),
|
||||||
|
|
||||||
|
EntityObjectType = BIT(23),
|
||||||
/// @}
|
/// @}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SceneObjectTypeMasks
|
enum SceneObjectTypeMasks
|
||||||
{
|
{
|
||||||
STATIC_COLLISION_TYPEMASK = StaticShapeObjectType,
|
STATIC_COLLISION_TYPEMASK = (StaticShapeObjectType |
|
||||||
|
EntityObjectType),
|
||||||
|
|
||||||
DAMAGEABLE_TYPEMASK = ( PlayerObjectType |
|
DAMAGEABLE_TYPEMASK = ( PlayerObjectType |
|
||||||
|
EntityObjectType |
|
||||||
VehicleObjectType ),
|
VehicleObjectType ),
|
||||||
|
|
||||||
/// Typemask for objects that should be rendered into shadow passes.
|
/// Typemask for objects that should be rendered into shadow passes.
|
||||||
/// These should be all objects that are either meant to receive or cast
|
/// These should be all objects that are either meant to receive or cast
|
||||||
/// shadows or both.
|
/// shadows or both.
|
||||||
SHADOW_TYPEMASK = ( StaticShapeObjectType |
|
SHADOW_TYPEMASK = ( StaticShapeObjectType |
|
||||||
DynamicShapeObjectType ),
|
DynamicShapeObjectType |
|
||||||
|
EntityObjectType),
|
||||||
|
|
||||||
/// Typemask for objects that should be subjected to more fine-grained
|
/// Typemask for objects that should be subjected to more fine-grained
|
||||||
/// culling tests. Anything that is trivial rendering stuff or doesn't
|
/// culling tests. Anything that is trivial rendering stuff or doesn't
|
||||||
|
|
@ -172,6 +176,7 @@ enum SceneObjectTypeMasks
|
||||||
CULLING_INCLUDE_TYPEMASK = ( GameBaseObjectType | // Includes most other renderable types; but broader than we ideally want.
|
CULLING_INCLUDE_TYPEMASK = ( GameBaseObjectType | // Includes most other renderable types; but broader than we ideally want.
|
||||||
StaticShapeObjectType |
|
StaticShapeObjectType |
|
||||||
DynamicShapeObjectType |
|
DynamicShapeObjectType |
|
||||||
|
EntityObjectType |
|
||||||
ZoneObjectType ), // This improves the result of zone traversals.
|
ZoneObjectType ), // This improves the result of zone traversals.
|
||||||
|
|
||||||
/// Mask for objects that should be specifically excluded from zone culling.
|
/// Mask for objects that should be specifically excluded from zone culling.
|
||||||
|
|
@ -185,7 +190,9 @@ enum SceneObjectTypeMasks
|
||||||
StaticShapeObjectType |
|
StaticShapeObjectType |
|
||||||
DynamicShapeObjectType |
|
DynamicShapeObjectType |
|
||||||
LightObjectType | // Flares.
|
LightObjectType | // Flares.
|
||||||
GameBaseObjectType ),
|
GameBaseObjectType |
|
||||||
|
TriggerObjectType |
|
||||||
|
EntityObjectType),
|
||||||
|
|
||||||
/// Typemask to use for rendering when inside the editor.
|
/// Typemask to use for rendering when inside the editor.
|
||||||
EDITOR_RENDER_TYPEMASK = U32( -1 ),
|
EDITOR_RENDER_TYPEMASK = U32( -1 ),
|
||||||
|
|
|
||||||
|
|
@ -378,3 +378,89 @@ void BtBody::setSimulationEnabled( bool enabled )
|
||||||
|
|
||||||
mIsEnabled = enabled;
|
mIsEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BtBody::findContact(SceneObject **contactObject,
|
||||||
|
VectorF *contactNormal,
|
||||||
|
Vector<SceneObject*> *outOverlapObjects) const
|
||||||
|
{
|
||||||
|
AssertFatal(mActor, "BtPlayer::findContact - The controller is null!");
|
||||||
|
|
||||||
|
VectorF normal;
|
||||||
|
F32 maxDot = -1.0f;
|
||||||
|
|
||||||
|
// Go thru the contact points... get the first contact.
|
||||||
|
//mWorld->getDynamicsWorld()->computeOverlappingPairs();
|
||||||
|
btOverlappingPairCache *pairCache = mWorld->getDynamicsWorld()->getBroadphase()->getOverlappingPairCache();
|
||||||
|
|
||||||
|
btBroadphasePairArray& pairArray = pairCache->getOverlappingPairArray();
|
||||||
|
U32 numPairs = pairArray.size();
|
||||||
|
btManifoldArray manifoldArray;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < numPairs; i++)
|
||||||
|
{
|
||||||
|
const btBroadphasePair &pair = pairArray[i];
|
||||||
|
|
||||||
|
btBroadphasePair *collisionPair = pairCache->findPair(pair.m_pProxy0, pair.m_pProxy1);
|
||||||
|
if (!collisionPair || !collisionPair->m_algorithm)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
btCollisionObject *other = (btCollisionObject*)pair.m_pProxy0->m_clientObject;
|
||||||
|
if (other == mActor)
|
||||||
|
other = (btCollisionObject*)pair.m_pProxy1->m_clientObject;
|
||||||
|
|
||||||
|
// AssertFatal(!outOverlapObjects->contains(PhysicsUserData::getObject(other->getUserPointer())),
|
||||||
|
// "Got multiple pairs of the same object!");
|
||||||
|
outOverlapObjects->push_back(PhysicsUserData::getObject(other->getUserPointer()));
|
||||||
|
|
||||||
|
if (other->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
manifoldArray.clear();
|
||||||
|
collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
|
||||||
|
|
||||||
|
for (U32 j = 0; j < manifoldArray.size(); j++)
|
||||||
|
{
|
||||||
|
btPersistentManifold *manifold = manifoldArray[j];
|
||||||
|
btScalar directionSign = manifold->getBody0() == mActor ? 1.0f : -1.0f;
|
||||||
|
|
||||||
|
for (U32 p = 0; p < manifold->getNumContacts(); p++)
|
||||||
|
{
|
||||||
|
const btManifoldPoint &pt = manifold->getContactPoint(p);
|
||||||
|
|
||||||
|
// Test the normal... is it the most vertical one we got?
|
||||||
|
normal = btCast<Point3F>(pt.m_normalWorldOnB * directionSign);
|
||||||
|
F32 dot = mDot(normal, VectorF(0, 0, 1));
|
||||||
|
if (dot > maxDot)
|
||||||
|
{
|
||||||
|
maxDot = dot;
|
||||||
|
|
||||||
|
btCollisionObject *colObject = (btCollisionObject*)collisionPair->m_pProxy0->m_clientObject;
|
||||||
|
*contactObject = PhysicsUserData::getObject(colObject->getUserPointer());
|
||||||
|
*contactNormal = normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BtBody::moveKinematicTo(const MatrixF &transform)
|
||||||
|
{
|
||||||
|
AssertFatal(mActor, "BtBody::moveKinematicTo - The actor is null!");
|
||||||
|
|
||||||
|
U32 bodyflags = mActor->getCollisionFlags();
|
||||||
|
const bool isKinematic = bodyflags & BF_KINEMATIC;
|
||||||
|
if (!isKinematic)
|
||||||
|
{
|
||||||
|
Con::errorf("BtBody::moveKinematicTo is only for kinematic bodies.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCenterOfMass)
|
||||||
|
{
|
||||||
|
MatrixF xfm;
|
||||||
|
xfm.mul(transform, *mCenterOfMass);
|
||||||
|
mActor->setCenterOfMassTransform(btCast<btTransform>(xfm));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mActor->setCenterOfMassTransform(btCast<btTransform>(transform));
|
||||||
|
}
|
||||||
|
|
@ -111,6 +111,10 @@ public:
|
||||||
F32 staticFriction );
|
F32 staticFriction );
|
||||||
virtual void applyCorrection( const MatrixF &xfm );
|
virtual void applyCorrection( const MatrixF &xfm );
|
||||||
virtual void applyImpulse( const Point3F &origin, const Point3F &force );
|
virtual void applyImpulse( const Point3F &origin, const Point3F &force );
|
||||||
|
|
||||||
|
virtual void findContact(SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects) const;
|
||||||
|
virtual void moveKinematicTo(const MatrixF &xfm);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _T3D_PHYSICS_BTBODY_H_
|
#endif // _T3D_PHYSICS_BTBODY_H_
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ void BtPlayer::init( const char *type,
|
||||||
mObject = obj;
|
mObject = obj;
|
||||||
mWorld = (BtWorld*)world;
|
mWorld = (BtWorld*)world;
|
||||||
|
|
||||||
|
mSlopeAngle = runSurfaceCos;
|
||||||
mStepHeight = stepHeight;
|
mStepHeight = stepHeight;
|
||||||
|
|
||||||
//if ( dStricmp( type, "Capsule" ) == 0 )
|
//if ( dStricmp( type, "Capsule" ) == 0 )
|
||||||
|
|
@ -102,6 +103,17 @@ Point3F BtPlayer::move( const VectorF &disp, CollisionList &outCol )
|
||||||
{
|
{
|
||||||
AssertFatal( mGhostObject, "BtPlayer::move - The controller is null!" );
|
AssertFatal( mGhostObject, "BtPlayer::move - The controller is null!" );
|
||||||
|
|
||||||
|
if (!mWorld->isEnabled())
|
||||||
|
{
|
||||||
|
btTransform currentTrans = mGhostObject->getWorldTransform();
|
||||||
|
btVector3 currentPos = currentTrans.getOrigin();
|
||||||
|
|
||||||
|
Point3F returnPos = btCast<Point3F>(currentPos);
|
||||||
|
|
||||||
|
returnPos.z -= mOriginOffset;
|
||||||
|
return returnPos;
|
||||||
|
}
|
||||||
|
|
||||||
// First recover from any penetrations from the previous tick.
|
// First recover from any penetrations from the previous tick.
|
||||||
U32 numPenetrationLoops = 0;
|
U32 numPenetrationLoops = 0;
|
||||||
bool touchingContact = false;
|
bool touchingContact = false;
|
||||||
|
|
@ -305,16 +317,9 @@ bool BtPlayer::_sweep( btVector3 *inOutCurrPos, const btVector3 &disp, Collision
|
||||||
col.normal = btCast<Point3F>( callback.m_hitNormalWorld );
|
col.normal = btCast<Point3F>( callback.m_hitNormalWorld );
|
||||||
col.object = PhysicsUserData::getObject( callback.m_hitCollisionObject->getUserPointer() );
|
col.object = PhysicsUserData::getObject( callback.m_hitCollisionObject->getUserPointer() );
|
||||||
|
|
||||||
if (disp.z() < 0.0f)
|
F32 vd = col.normal.z;
|
||||||
{
|
if (vd < mSlopeAngle)
|
||||||
// We're sweeping down as part of the stepping routine. In this
|
return false;
|
||||||
// case we want to have the collision normal only point in the opposite direction.
|
|
||||||
// i.e. up If we include the sideways part of the normal then the Player class
|
|
||||||
// velocity calculations using this normal will affect the player's forwards
|
|
||||||
// momentum. This is especially noticable on stairs as the rounded bottom of
|
|
||||||
// the capsule slides up the corner of a stair.
|
|
||||||
col.normal.set(0.0f, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,10 @@ protected:
|
||||||
///
|
///
|
||||||
F32 mOriginOffset;
|
F32 mOriginOffset;
|
||||||
|
|
||||||
|
///
|
||||||
|
F32 mSlopeAngle;
|
||||||
|
///
|
||||||
|
|
||||||
///
|
///
|
||||||
F32 mStepHeight;
|
F32 mStepHeight;
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,14 @@ public:
|
||||||
|
|
||||||
///
|
///
|
||||||
virtual void applyImpulse( const Point3F &origin, const Point3F &force ) = 0;
|
virtual void applyImpulse( const Point3F &origin, const Point3F &force ) = 0;
|
||||||
|
|
||||||
|
virtual void findContact(SceneObject **contactObject,
|
||||||
|
VectorF *contactNormal,
|
||||||
|
Vector<SceneObject*> *outOverlapObjects) const = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
virtual void moveKinematicTo(const MatrixF &xfm) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -417,3 +417,73 @@ void Px3Body::applyImpulse( const Point3F &origin, const Point3F &force )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Px3Body::findContact(SceneObject **contactObject,
|
||||||
|
VectorF *contactNormal,
|
||||||
|
Vector<SceneObject*> *outOverlapObjects) const
|
||||||
|
{
|
||||||
|
// Calculate the sweep motion...
|
||||||
|
F32 halfCapSize = mOriginOffset;
|
||||||
|
F32 halfSmallCapSize = halfCapSize * 0.8f;
|
||||||
|
F32 diff = halfCapSize - halfSmallCapSize;
|
||||||
|
|
||||||
|
F32 distance = diff + mSkinWidth + 0.01f;
|
||||||
|
physx::PxVec3 dir(0, 0, -1);
|
||||||
|
|
||||||
|
physx::PxScene *scene = mWorld->getScene();
|
||||||
|
physx::PxHitFlags hitFlags(physx::PxHitFlag::eDEFAULT);
|
||||||
|
physx::PxQueryFilterData filterData(physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::eSTATIC);
|
||||||
|
filterData.data.word0 = PX3_DEFAULT;
|
||||||
|
physx::PxSweepHit sweepHit;
|
||||||
|
physx::PxRigidDynamic *actor = mController->getActor();
|
||||||
|
physx::PxU32 shapeIndex;
|
||||||
|
|
||||||
|
bool hit = physx::PxRigidBodyExt::linearSweepSingle(*actor, *scene, dir, distance, hitFlags, sweepHit, shapeIndex, filterData);
|
||||||
|
if (hit)
|
||||||
|
{
|
||||||
|
PhysicsUserData *data = PhysicsUserData::cast(sweepHit.actor->userData);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
*contactObject = data->getObject();
|
||||||
|
*contactNormal = px3Cast<Point3F>(sweepHit.normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlapped objects ( triggers )
|
||||||
|
|
||||||
|
if (!outOverlapObjects)
|
||||||
|
return;
|
||||||
|
|
||||||
|
filterData.data.word0 = PX3_TRIGGER;
|
||||||
|
|
||||||
|
const physx::PxU32 bufferSize = 10;
|
||||||
|
physx::PxOverlapBufferN<bufferSize> hitBuffer;
|
||||||
|
hit = scene->overlap(mGeometry, actor->getGlobalPose(), hitBuffer, filterData);
|
||||||
|
if (hit)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < hitBuffer.nbTouches; i++)
|
||||||
|
{
|
||||||
|
PhysicsUserData *data = PhysicsUserData::cast(hitBuffer.touches[i].actor->userData);
|
||||||
|
if (data)
|
||||||
|
outOverlapObjects->push_back(data->getObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}void Px3Body::moveKinematicTo(const MatrixF &transform)
|
||||||
|
{
|
||||||
|
AssertFatal(mActor, "Px3Body::moveKinematicTo - The actor is null!");
|
||||||
|
|
||||||
|
const bool isKinematic = mBodyFlags & BF_KINEMATIC;
|
||||||
|
if (!isKinematic)
|
||||||
|
{
|
||||||
|
Con::errorf("Px3Body::moveKinematicTo is only for kinematic bodies.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWorld->lockScene();
|
||||||
|
|
||||||
|
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
|
||||||
|
actor->setKinematicTarget(px3Cast<physx::PxTransform>(transform));
|
||||||
|
|
||||||
|
mWorld->unlockScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,11 @@ public:
|
||||||
F32 staticFriction );
|
F32 staticFriction );
|
||||||
virtual void applyCorrection( const MatrixF &xfm );
|
virtual void applyCorrection( const MatrixF &xfm );
|
||||||
virtual void applyImpulse( const Point3F &origin, const Point3F &force );
|
virtual void applyImpulse( const Point3F &origin, const Point3F &force );
|
||||||
|
|
||||||
|
virtual void findContact(SceneObject **contactObject, VectorF *contactNormal,
|
||||||
|
Vector<SceneObject*> *outOverlapObjects) const;
|
||||||
|
virtual void moveKinematicTo(const MatrixF &xfm);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _PX3BODY_H_
|
#endif // _PX3BODY_H_
|
||||||
|
|
|
||||||
|
|
@ -329,3 +329,33 @@ Box3F Px3Player::getWorldBounds()
|
||||||
return px3Cast<Box3F>( bounds );
|
return px3Cast<Box3F>( bounds );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Px3Player::testSpacials(const Point3F &nPos, const Point3F &nSize) const
|
||||||
|
{
|
||||||
|
F32 offset = nSize.z * 0.5f;
|
||||||
|
F32 radius = getMax(nSize.x, nSize.y) * 0.5f - mSkinWidth;
|
||||||
|
F32 height = (nSize.z - (radius * 2.0f)) * 0.5f;
|
||||||
|
height -= mSkinWidth * 2.0f;
|
||||||
|
physx::PxCapsuleGeometry geom(radius, height);
|
||||||
|
|
||||||
|
physx::PxVec3 pos(nPos.x, nPos.y, nPos.z + offset);
|
||||||
|
physx::PxQuat orientation(Float_HalfPi, physx::PxVec3(0.0f, 1.0f, 0.0f));
|
||||||
|
|
||||||
|
physx::PxOverlapBuffer hit;
|
||||||
|
physx::PxQueryFilterData queryFilter(physx::PxQueryFlag::eANY_HIT | physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC);
|
||||||
|
queryFilter.data.word0 = PX3_DEFAULT;
|
||||||
|
bool hasHit = mWorld->getScene()->overlap(geom, physx::PxTransform(pos, orientation), hit, queryFilter);
|
||||||
|
|
||||||
|
return !hasHit; // Return true if there are no overlapping objects
|
||||||
|
}
|
||||||
|
|
||||||
|
void Px3Player::setSpacials(const Point3F &nPos, const Point3F &nSize)
|
||||||
|
{
|
||||||
|
mOriginOffset = nSize.z * 0.5f;
|
||||||
|
F32 radius = getMax(nSize.x, nSize.y) * 0.5f - mSkinWidth;
|
||||||
|
F32 height = nSize.z - (radius * 2.0f);
|
||||||
|
height -= mSkinWidth * 2.0f;
|
||||||
|
|
||||||
|
mWorld->releaseWriteLock();
|
||||||
|
mController->resize(height);
|
||||||
|
px3GetFirstShape(mController->getActor())->getCapsuleGeometry(mGeometry);
|
||||||
|
}
|
||||||
|
|
@ -94,8 +94,8 @@ public:
|
||||||
PhysicsWorld *world );
|
PhysicsWorld *world );
|
||||||
virtual Point3F move( const VectorF &displacement, CollisionList &outCol );
|
virtual Point3F move( const VectorF &displacement, CollisionList &outCol );
|
||||||
virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) const;
|
virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) const;
|
||||||
virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; }
|
virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const;
|
||||||
virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {}
|
virtual void setSpacials( const Point3F &nPos, const Point3F &nSize );
|
||||||
virtual void enableCollision();
|
virtual void enableCollision();
|
||||||
virtual void disableCollision();
|
virtual void disableCollision();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ Px3World::Px3World(): mScene( NULL ),
|
||||||
mIsEnabled( false ),
|
mIsEnabled( false ),
|
||||||
mEditorTimeScale( 1.0f ),
|
mEditorTimeScale( 1.0f ),
|
||||||
mAccumulator( 0 ),
|
mAccumulator( 0 ),
|
||||||
mControllerManager( NULL )
|
mControllerManager(NULL),
|
||||||
|
mIsSceneLocked(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,6 +336,62 @@ void Px3World::releaseWriteLock()
|
||||||
//AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" );
|
//AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Px3World::lockScenes()
|
||||||
|
{
|
||||||
|
Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
|
||||||
|
|
||||||
|
if (world)
|
||||||
|
world->lockScene();
|
||||||
|
|
||||||
|
world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
|
||||||
|
|
||||||
|
if (world)
|
||||||
|
world->lockScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Px3World::unlockScenes()
|
||||||
|
{
|
||||||
|
Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
|
||||||
|
|
||||||
|
if (world)
|
||||||
|
world->unlockScene();
|
||||||
|
|
||||||
|
world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
|
||||||
|
|
||||||
|
if (world)
|
||||||
|
world->unlockScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Px3World::lockScene()
|
||||||
|
{
|
||||||
|
if (!mScene)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mIsSceneLocked)
|
||||||
|
{
|
||||||
|
Con::printf("Px3World: Attempting to lock a scene that is already locked.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mScene->lockWrite();
|
||||||
|
mIsSceneLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Px3World::unlockScene()
|
||||||
|
{
|
||||||
|
if (!mScene)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!mIsSceneLocked)
|
||||||
|
{
|
||||||
|
Con::printf("Px3World: Attempting to unlock a scene that is not locked.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mScene->unlockWrite();
|
||||||
|
mIsSceneLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
|
bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ protected:
|
||||||
bool mIsEnabled;
|
bool mIsEnabled;
|
||||||
bool mIsSimulating;
|
bool mIsSimulating;
|
||||||
bool mIsServer;
|
bool mIsServer;
|
||||||
|
bool mIsSceneLocked;
|
||||||
U32 mTickCount;
|
U32 mTickCount;
|
||||||
ProcessList *mProcessList;
|
ProcessList *mProcessList;
|
||||||
F32 mEditorTimeScale;
|
F32 mEditorTimeScale;
|
||||||
|
|
@ -96,11 +97,15 @@ public:
|
||||||
void releaseWriteLock();
|
void releaseWriteLock();
|
||||||
bool isServer(){return mIsServer;}
|
bool isServer(){return mIsServer;}
|
||||||
physx::PxController* createController( physx::PxControllerDesc &desc );
|
physx::PxController* createController( physx::PxControllerDesc &desc );
|
||||||
|
void lockScene();
|
||||||
|
void unlockScene();
|
||||||
//static
|
//static
|
||||||
static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL );
|
static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL );
|
||||||
static void releaseWriteLocks();
|
static void releaseWriteLocks();
|
||||||
static physx::PxCooking *getCooking();
|
static physx::PxCooking *getCooking();
|
||||||
static void setTiming(F32 stepTime,U32 maxIterations);
|
static void setTiming(F32 stepTime,U32 maxIterations);
|
||||||
|
static void lockScenes();
|
||||||
|
static void unlockScenes();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _PX3WORLD_H_
|
#endif // _PX3WORLD_H_
|
||||||
|
|
|
||||||
|
|
@ -4881,17 +4881,18 @@ DefineEngineMethod( ShapeBase, getTargetCount, S32, (),,
|
||||||
|
|
||||||
"@see getTargetName()\n")
|
"@see getTargetName()\n")
|
||||||
{
|
{
|
||||||
ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
|
ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
|
||||||
if(obj)
|
if(obj)
|
||||||
{
|
{
|
||||||
// Try to use the client object (so we get the reskinned targets in the Material Editor)
|
// Try to use the client object (so we get the reskinned targets in the Material Editor)
|
||||||
if ((ShapeBase*)obj->getClientObject())
|
if ((ShapeBase*)obj->getClientObject())
|
||||||
obj = (ShapeBase*)obj->getClientObject();
|
obj = (ShapeBase*)obj->getClientObject();
|
||||||
|
|
||||||
return obj->getShapeInstance()->getTargetCount();
|
if (obj->getShapeInstance() != NULL)
|
||||||
|
return obj->getShapeInstance()->getTargetCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),,
|
DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),,
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@
|
||||||
#include "gfx/primBuilder.h"
|
#include "gfx/primBuilder.h"
|
||||||
#include "gfx/gfxDrawUtil.h"
|
#include "gfx/gfxDrawUtil.h"
|
||||||
#include "materials/materialDefinition.h"
|
#include "materials/materialDefinition.h"
|
||||||
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
|
#include "T3D/physics/physicsBody.h"
|
||||||
|
#include "T3D/physics/physicsCollision.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -203,7 +206,8 @@ VehicleData::VehicleData()
|
||||||
dMemset(waterSound, 0, sizeof(waterSound));
|
dMemset(waterSound, 0, sizeof(waterSound));
|
||||||
|
|
||||||
collDamageThresholdVel = 20;
|
collDamageThresholdVel = 20;
|
||||||
collDamageMultiplier = 0.05f;
|
collDamageMultiplier = 0.05f;
|
||||||
|
enablePhysicsRep = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -315,6 +319,7 @@ void VehicleData::packData(BitStream* stream)
|
||||||
stream->write(softSplashSoundVel);
|
stream->write(softSplashSoundVel);
|
||||||
stream->write(medSplashSoundVel);
|
stream->write(medSplashSoundVel);
|
||||||
stream->write(hardSplashSoundVel);
|
stream->write(hardSplashSoundVel);
|
||||||
|
stream->write(enablePhysicsRep);
|
||||||
|
|
||||||
// write the water sound profiles
|
// write the water sound profiles
|
||||||
for(i = 0; i < MaxSounds; i++)
|
for(i = 0; i < MaxSounds; i++)
|
||||||
|
|
@ -411,6 +416,7 @@ void VehicleData::unpackData(BitStream* stream)
|
||||||
stream->read(&softSplashSoundVel);
|
stream->read(&softSplashSoundVel);
|
||||||
stream->read(&medSplashSoundVel);
|
stream->read(&medSplashSoundVel);
|
||||||
stream->read(&hardSplashSoundVel);
|
stream->read(&hardSplashSoundVel);
|
||||||
|
stream->read(&enablePhysicsRep);
|
||||||
|
|
||||||
// write the water sound profiles
|
// write the water sound profiles
|
||||||
for(i = 0; i < MaxSounds; i++)
|
for(i = 0; i < MaxSounds; i++)
|
||||||
|
|
@ -465,6 +471,11 @@ void VehicleData::unpackData(BitStream* stream)
|
||||||
|
|
||||||
void VehicleData::initPersistFields()
|
void VehicleData::initPersistFields()
|
||||||
{
|
{
|
||||||
|
addGroup("Physics");
|
||||||
|
addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, VehicleData),
|
||||||
|
"@brief Creates a representation of the object in the physics plugin.\n");
|
||||||
|
endGroup("Physics");
|
||||||
|
|
||||||
addField( "jetForce", TypeF32, Offset(jetForce, VehicleData),
|
addField( "jetForce", TypeF32, Offset(jetForce, VehicleData),
|
||||||
"@brief Additional force applied to the vehicle when it is jetting.\n\n"
|
"@brief Additional force applied to the vehicle when it is jetting.\n\n"
|
||||||
"For WheeledVehicles, the force is applied in the forward direction. For "
|
"For WheeledVehicles, the force is applied in the forward direction. For "
|
||||||
|
|
@ -682,6 +693,8 @@ Vehicle::Vehicle()
|
||||||
mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
|
mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
|
||||||
mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
|
mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
|
||||||
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
||||||
|
|
||||||
|
mPhysicsRep = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 Vehicle::getCollisionMask()
|
U32 Vehicle::getCollisionMask()
|
||||||
|
|
@ -695,6 +708,25 @@ Point3F Vehicle::getVelocity() const
|
||||||
return mRigid.linVelocity;
|
return mRigid.linVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Vehicle::_createPhysics()
|
||||||
|
{
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
|
if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TSShape *shape = mShapeInstance->getShape();
|
||||||
|
PhysicsCollision *colShape = NULL;
|
||||||
|
colShape = shape->buildColShape(false, getScale());
|
||||||
|
|
||||||
|
if (colShape)
|
||||||
|
{
|
||||||
|
PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
|
mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world);
|
||||||
|
mPhysicsRep->setTransform(getTransform());
|
||||||
|
}
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
bool Vehicle::onAdd()
|
bool Vehicle::onAdd()
|
||||||
|
|
@ -776,11 +808,15 @@ bool Vehicle::onAdd()
|
||||||
mConvex.box.maxExtents.convolve(mObjScale);
|
mConvex.box.maxExtents.convolve(mObjScale);
|
||||||
mConvex.findNodeTransform();
|
mConvex.findNodeTransform();
|
||||||
|
|
||||||
|
_createPhysics();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vehicle::onRemove()
|
void Vehicle::onRemove()
|
||||||
{
|
{
|
||||||
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
U32 i=0;
|
U32 i=0;
|
||||||
for( i=0; i<VehicleData::VC_NUM_DUST_EMITTERS; i++ )
|
for( i=0; i<VehicleData::VC_NUM_DUST_EMITTERS; i++ )
|
||||||
{
|
{
|
||||||
|
|
@ -880,6 +916,11 @@ void Vehicle::processTick(const Move* move)
|
||||||
setPosition(mRigid.linPosition, mRigid.angPosition);
|
setPosition(mRigid.linPosition, mRigid.angPosition);
|
||||||
setMaskBits(PositionMask);
|
setMaskBits(PositionMask);
|
||||||
updateContainer();
|
updateContainer();
|
||||||
|
|
||||||
|
//TODO: Only update when position has actually changed
|
||||||
|
//no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->moveKinematicTo(getTransform());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,8 @@ struct VehicleData: public ShapeBaseData
|
||||||
F32 splashFreqMod;
|
F32 splashFreqMod;
|
||||||
F32 splashVelEpsilon;
|
F32 splashVelEpsilon;
|
||||||
|
|
||||||
|
bool enablePhysicsRep;
|
||||||
|
|
||||||
//
|
//
|
||||||
VehicleData();
|
VehicleData();
|
||||||
bool preload(bool server, String &errorStr);
|
bool preload(bool server, String &errorStr);
|
||||||
|
|
@ -142,6 +144,7 @@ struct VehicleData: public ShapeBaseData
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
class PhysicsBody;
|
||||||
|
|
||||||
class Vehicle: public ShapeBase
|
class Vehicle: public ShapeBase
|
||||||
{
|
{
|
||||||
|
|
@ -177,6 +180,8 @@ class Vehicle: public ShapeBase
|
||||||
Point3F cameraRotVec;
|
Point3F cameraRotVec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PhysicsBody *mPhysicsRep;
|
||||||
|
|
||||||
StateDelta mDelta;
|
StateDelta mDelta;
|
||||||
S32 mPredictionCount; ///< Number of ticks to predict
|
S32 mPredictionCount; ///< Number of ticks to predict
|
||||||
VehicleData* mDataBlock;
|
VehicleData* mDataBlock;
|
||||||
|
|
@ -262,6 +267,8 @@ public:
|
||||||
bool onAdd();
|
bool onAdd();
|
||||||
void onRemove();
|
void onRemove();
|
||||||
|
|
||||||
|
void _createPhysics();
|
||||||
|
|
||||||
/// Interpolates between move ticks @see processTick
|
/// Interpolates between move ticks @see processTick
|
||||||
/// @param dt Change in time between the last call and this call to the function
|
/// @param dt Change in time between the last call and this call to the function
|
||||||
void interpolateTick(F32 dt);
|
void interpolateTick(F32 dt);
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ AssetManager::AssetManager() :
|
||||||
mMaxLoadedPrivateAssetsCount( 0 ),
|
mMaxLoadedPrivateAssetsCount( 0 ),
|
||||||
mAcquiredReferenceCount( 0 ),
|
mAcquiredReferenceCount( 0 ),
|
||||||
mEchoInfo( false ),
|
mEchoInfo( false ),
|
||||||
mIgnoreAutoUnload( false )
|
mIgnoreAutoUnload( true )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "component/simComponent.h"
|
|
||||||
#include "component/componentInterface.h"
|
|
||||||
#include "core/strings/findMatch.h"
|
|
||||||
#include "core/stringTable.h"
|
|
||||||
|
|
||||||
bool ComponentInterfaceCache::add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface )
|
|
||||||
{
|
|
||||||
if( ( mInterfaceList.size() == 0 ) || ( enumerate( NULL, type, name, owner ) == 0 ) )
|
|
||||||
{
|
|
||||||
mInterfaceList.increment();
|
|
||||||
// CodeReview [tom, 3/9/2007] Seems silly to keep calling last(), why not cache the var? Yes, I know I am pedantic.
|
|
||||||
mInterfaceList.last().type = ( type == NULL ? NULL : StringTable->insert( type ) );
|
|
||||||
mInterfaceList.last().name = ( name == NULL ? NULL : StringTable->insert( name ) );
|
|
||||||
mInterfaceList.last().owner = owner;
|
|
||||||
mInterfaceList.last().iface = cinterface;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void ComponentInterfaceCache::clear()
|
|
||||||
{
|
|
||||||
mInterfaceList.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
U32 ComponentInterfaceCache::enumerate( ComponentInterfaceList *list, const char *type /* = NULL */,
|
|
||||||
const char *name /* = NULL */, const SimComponent *owner /* = NULL */, bool notOwner /* = false */ ) const
|
|
||||||
{
|
|
||||||
U32 numMatches = 0;
|
|
||||||
|
|
||||||
for( _InterfaceEntryItr i = mInterfaceList.begin(); i != mInterfaceList.end(); i++ )
|
|
||||||
{
|
|
||||||
// Early out if limiting results by component owner
|
|
||||||
if( owner != NULL && (
|
|
||||||
( (*i).owner == owner && notOwner ) ||
|
|
||||||
( (*i).owner != owner && !notOwner ) ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Match the type, short circuit if type == NULL
|
|
||||||
if( type == NULL || FindMatch::isMatch( type, (*i).type ) )
|
|
||||||
{
|
|
||||||
// Match the name
|
|
||||||
if( name == NULL || FindMatch::isMatch( name, (*i).name ) )
|
|
||||||
{
|
|
||||||
numMatches++;
|
|
||||||
|
|
||||||
if( list != NULL )
|
|
||||||
list->push_back( (*i).iface );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numMatches;
|
|
||||||
}
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef _COMPONENTINTERFACE_H_
|
|
||||||
#define _COMPONENTINTERFACE_H_
|
|
||||||
|
|
||||||
#ifndef _TVECTOR_H_
|
|
||||||
#include "core/util/tVector.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _SIMOBJECT_H_
|
|
||||||
#include "console/simObject.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "core/util/safeDelete.h"
|
|
||||||
|
|
||||||
|
|
||||||
class SimComponent;
|
|
||||||
|
|
||||||
|
|
||||||
// CodeReview [patw, 2, 13, 2007] The issue I have not addressed in this class is
|
|
||||||
// interface locking. I think that we want to do this, for sure, but I also want
|
|
||||||
// to keep it as light-weight as possible. For the most part, there should only
|
|
||||||
// ever be one thing doing something with a component at one time, but I can see
|
|
||||||
// many situations where this wouldn't be the case. When we decide to address
|
|
||||||
// the issues of locking, I believe it should be done here, at the ComponentInterface
|
|
||||||
// level. I would like lock functionality to be as centralized as possible, and
|
|
||||||
// so this is the place for it. The functionality is critical for safe useage of
|
|
||||||
// the ComponentProperty class, so implementation here would also be ideal.
|
|
||||||
|
|
||||||
// CodeReview [patw, 2, 14, 2007] This really should be a ref-counted object
|
|
||||||
class ComponentInterface
|
|
||||||
{
|
|
||||||
friend class SimComponent;
|
|
||||||
private:
|
|
||||||
SimObjectPtr<SimComponent> mOwner; ///< SimComponent will directly modify this value
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Default constructor
|
|
||||||
ComponentInterface() : mOwner(NULL) {};
|
|
||||||
|
|
||||||
/// Destructor
|
|
||||||
virtual ~ComponentInterface()
|
|
||||||
{
|
|
||||||
mOwner = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will return true if the interface is valid
|
|
||||||
virtual bool isValid() const
|
|
||||||
{
|
|
||||||
return mOwner != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the owner of this interface
|
|
||||||
SimComponent *getOwner() { return mOwner; }
|
|
||||||
const SimComponent *getOwner() const { return mOwner; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef VectorPtr<ComponentInterface *> ComponentInterfaceList;
|
|
||||||
typedef VectorPtr<ComponentInterface *>::iterator ComponentInterfaceListIterator;
|
|
||||||
|
|
||||||
// These two asserts I found myself wanting a lot when doing interface methods
|
|
||||||
#ifdef TORQUE_ENABLE_ASSERTS
|
|
||||||
# define VALID_INTERFACE_ASSERT(OwningClassType) \
|
|
||||||
AssertFatal( isValid(), "Interface validity check failed." ); \
|
|
||||||
AssertFatal( dynamic_cast<const OwningClassType *>( getOwner() ) != NULL, avar( "Owner is not an instance of %s", #OwningClassType ) )
|
|
||||||
#else
|
|
||||||
# define VALID_INTERFACE_ASSERT(OwningClassType)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// This class is designed to wrap an existing class or type easily to allow
|
|
||||||
/// a SimComponent to expose a property with custom processing code in an efficient
|
|
||||||
/// and safe way. Specialized templates could be written which include validation
|
|
||||||
/// on sets, and processing on gets.
|
|
||||||
///
|
|
||||||
/// This class has a lot of "blow your leg off" potential, if you have bad aim.
|
|
||||||
/// I think that a lot of very intuitive functionality can be gained from using
|
|
||||||
/// this properly, however when implementing a specialized template, be mindful
|
|
||||||
/// of what you are doing, and
|
|
||||||
|
|
||||||
// CodeReview [patw, 2, 13, 2007] I am very interested in making this as thin as
|
|
||||||
// possible. I really like the possibilities that it exposes as far as exposing
|
|
||||||
// "properties" to other components. I want it to be performant, however, so
|
|
||||||
// if anyone has notes on this, mark up the source, e-mail me, whatever.
|
|
||||||
template<class T>
|
|
||||||
class ComponentProperty : public ComponentInterface
|
|
||||||
{
|
|
||||||
typedef ComponentInterface Parent;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
T *mValuePtr;
|
|
||||||
|
|
||||||
// ComponentInterface Overrides
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Override this to add a check for valid memory.
|
|
||||||
virtual bool isValid() const
|
|
||||||
{
|
|
||||||
return ( mValuePtr != NULL ) && Parent::isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operator overloads
|
|
||||||
public:
|
|
||||||
/// Dereferencing a value interface will allow get to do any processing and
|
|
||||||
/// return the reference to that
|
|
||||||
const T &operator*()
|
|
||||||
{
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assignment operator will invoke set.
|
|
||||||
const T &operator=( const T &lval )
|
|
||||||
{
|
|
||||||
return set( lval );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructors/Destructors, specialize these if needed
|
|
||||||
public:
|
|
||||||
/// Default Constructor.
|
|
||||||
ComponentProperty() : mValuePtr( NULL )
|
|
||||||
{
|
|
||||||
mValuePtr = new T;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy constructor
|
|
||||||
ComponentProperty( const T © )
|
|
||||||
{
|
|
||||||
ComponentProperty();
|
|
||||||
|
|
||||||
// CodeReview [patw, 2, 13, 2007] So, the reasoning here is that I want to
|
|
||||||
// use the functionality that a specialized template implements in the set
|
|
||||||
// method. See the notes on the set method implementation.
|
|
||||||
set( copy );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destructor, destroy memory
|
|
||||||
virtual ~ComponentProperty()
|
|
||||||
{
|
|
||||||
SAFE_DELETE( mValuePtr );
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the ComponentProperty interface that specializations of the class
|
|
||||||
// will be interested in.
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// Get the value associated with this interface. Processing code can be done
|
|
||||||
/// here for specialized implementations.
|
|
||||||
virtual const T &get() // 'const' is intentionally not used as a modifier here
|
|
||||||
{
|
|
||||||
return *mValuePtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the value associated with this interface. Validation/processing code
|
|
||||||
/// can be done here. The copy-constructor uses the set method to do it's copy
|
|
||||||
/// so be mindful of that, or specialize the copy-constructor.
|
|
||||||
virtual const T &set( const T &t )
|
|
||||||
{
|
|
||||||
// CodeReview [patw, 2, 13, 2007] So I am using the = operator here. Do you
|
|
||||||
// guys think that this should be the default behavior? I am trying to keep
|
|
||||||
// everything as object friendly as possible, so I figured I'd use this.
|
|
||||||
*mValuePtr = t;
|
|
||||||
return *mValuePtr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This class is just designed to isolate the functionality of querying for, and
|
|
||||||
/// managing interfaces.
|
|
||||||
class ComponentInterfaceCache
|
|
||||||
{
|
|
||||||
// CodeReview [patw, 2, 14, 2007] When we move this whole system to Juggernaught
|
|
||||||
// we may want to consider making safe pointers for ComponentInterfaces. Not
|
|
||||||
// sure why I put this note here.
|
|
||||||
private:
|
|
||||||
struct _InterfaceEntry
|
|
||||||
{
|
|
||||||
ComponentInterface *iface;
|
|
||||||
StringTableEntry type;
|
|
||||||
StringTableEntry name;
|
|
||||||
const SimComponent *owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<_InterfaceEntry> mInterfaceList;
|
|
||||||
typedef Vector<_InterfaceEntry>::const_iterator _InterfaceEntryItr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Add an interface to the cache. This function will return true if the interface
|
|
||||||
/// is added successfully. An interface will not be added successfully if an entry
|
|
||||||
/// in the list with the same values for 'type' and 'name' is present in the list.
|
|
||||||
///
|
|
||||||
/// @param type Type of the interface being added. If NULL is passed, it will match any type string queried.
|
|
||||||
/// @param name Name of interface being added. If NULL is passed, it will match any name string queried.
|
|
||||||
/// @param owner The owner of the ComponentInterface being cached
|
|
||||||
/// @param cinterface The ComponentInterface being cached
|
|
||||||
virtual bool add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface );
|
|
||||||
|
|
||||||
/// Clear the interface cache. This does not perform any operations on the contents
|
|
||||||
/// of the list.
|
|
||||||
virtual void clear();
|
|
||||||
|
|
||||||
/// Query the list for all of the interfaces it stores references to that match
|
|
||||||
/// the 'type' and 'name' parameters. The results of the query will be appended
|
|
||||||
/// to the list specified. Pattern matching is done using core/findMatch.h; for
|
|
||||||
/// more information on matching, see that code/header pair. Passing NULL for
|
|
||||||
/// one of these fields will match all values for that field. The return value
|
|
||||||
/// for the method will be the number of interfaces which match the query.
|
|
||||||
///
|
|
||||||
/// @param list The list that this method will append search results on to. It is possible to pass NULL here and just receive the return value.
|
|
||||||
/// @param type An expression which the 'type' field on an added object must match to be included in results
|
|
||||||
/// @param name An expression which the 'name' field on an added object must match to be included in results
|
|
||||||
/// @param owner Limit results to components owned/not-owned by this SimComponent (see next param)
|
|
||||||
/// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner'
|
|
||||||
virtual U32 enumerate( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,209 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "component/dynamicConsoleMethodComponent.h"
|
|
||||||
#include "console/stringStack.h"
|
|
||||||
|
|
||||||
extern StringStack STR;
|
|
||||||
extern ConsoleValueStack CSTK;
|
|
||||||
|
|
||||||
IMPLEMENT_CO_NETOBJECT_V1(DynamicConsoleMethodComponent);
|
|
||||||
|
|
||||||
ConsoleDocClass( DynamicConsoleMethodComponent,
|
|
||||||
"@brief Console object used for calling methods defined in script, from within other classes.\n\n"
|
|
||||||
"Not intended for game development, for editors or internal use only.\n\n "
|
|
||||||
"@internal");
|
|
||||||
|
|
||||||
//-----------------------------------------------------------
|
|
||||||
// Function name: SimComponent::handlesConsoleMethod
|
|
||||||
// Summary:
|
|
||||||
//-----------------------------------------------------------
|
|
||||||
bool DynamicConsoleMethodComponent::handlesConsoleMethod( const char *fname, S32 *routingId )
|
|
||||||
{
|
|
||||||
// CodeReview: Host object is now given priority over components for method
|
|
||||||
// redirection. [6/23/2007 Pat]
|
|
||||||
|
|
||||||
// On this object?
|
|
||||||
if( isMethod( fname ) )
|
|
||||||
{
|
|
||||||
*routingId = -1; // -1 denotes method on object
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
// Inject Method.
|
|
||||||
injectMethodCall( fname );
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on this objects components?
|
|
||||||
S32 nI = 0;
|
|
||||||
VectorPtr<SimComponent*> &componentList = lockComponentList();
|
|
||||||
for( SimComponentIterator nItr = componentList.begin(); nItr != componentList.end(); nItr++, nI++ )
|
|
||||||
{
|
|
||||||
SimObject *pComponent = dynamic_cast<SimObject*>(*nItr);
|
|
||||||
if( pComponent != NULL && pComponent->isMethod( fname ) )
|
|
||||||
{
|
|
||||||
*routingId = -2; // -2 denotes method on component
|
|
||||||
unlockComponentList();
|
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
// Inject Method.
|
|
||||||
injectMethodCall( fname );
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unlockComponentList();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *DynamicConsoleMethodComponent::callMethod( S32 argc, const char* methodName, ... )
|
|
||||||
{
|
|
||||||
const char *argv[128];
|
|
||||||
methodName = StringTable->insert( methodName );
|
|
||||||
|
|
||||||
argc++;
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, methodName);
|
|
||||||
for(S32 i = 0; i < argc; i++)
|
|
||||||
argv[i+2] = va_arg(args, const char *);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
// FIXME: the following seems a little excessive. I wonder why it's needed?
|
|
||||||
argv[0] = methodName;
|
|
||||||
argv[1] = methodName;
|
|
||||||
argv[2] = methodName;
|
|
||||||
|
|
||||||
StringStackConsoleWrapper argsw(argc, argv);
|
|
||||||
|
|
||||||
return callMethodArgList( argsw.count() , argsw );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
/// Inject Method Call.
|
|
||||||
void DynamicConsoleMethodComponent::injectMethodCall( const char* method )
|
|
||||||
{
|
|
||||||
// Get Call Method.
|
|
||||||
StringTableEntry callMethod = StringTable->insert( method );
|
|
||||||
|
|
||||||
// Find Call Method Metric.
|
|
||||||
callMethodMetricType::Iterator itr = mCallMethodMetrics.find( callMethod );
|
|
||||||
|
|
||||||
// Did we find the method?
|
|
||||||
if ( itr == mCallMethodMetrics.end() )
|
|
||||||
{
|
|
||||||
// No, so set the call count to initially be 1.
|
|
||||||
itr = mCallMethodMetrics.insert( callMethod, 1 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Increment Call Count.
|
|
||||||
itr->value++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis /* = true */ )
|
|
||||||
{
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
injectMethodCall( argv[0] );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return _callMethod( argc, argv, callThis );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call all components that implement methodName giving them a chance to operate
|
|
||||||
// Components are called in reverse order of addition
|
|
||||||
const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, ConsoleValueRef argv[], bool callThis /* = true */ )
|
|
||||||
{
|
|
||||||
// Set Owner
|
|
||||||
SimObject *pThis = dynamic_cast<SimObject *>( this );
|
|
||||||
AssertFatal( pThis, "DynamicConsoleMethodComponent::callMethod : this should always exist!" );
|
|
||||||
|
|
||||||
const char *cbName = StringTable->insert(argv[0]);
|
|
||||||
|
|
||||||
if( getComponentCount() > 0 )
|
|
||||||
{
|
|
||||||
lockComponentList();
|
|
||||||
for( S32 i = getComponentCount() - 1; i >= 0; i-- )
|
|
||||||
//for( SimComponentIterator nItr = componentList.end(); nItr != componentList.begin(); nItr-- )
|
|
||||||
{
|
|
||||||
argv[0] = cbName;
|
|
||||||
|
|
||||||
SimComponent *pComponent = dynamic_cast<SimComponent *>( getComponent( i ) );
|
|
||||||
AssertFatal( pComponent, "DynamicConsoleMethodComponent::callMethod - NULL component in list!" );
|
|
||||||
|
|
||||||
DynamicConsoleMethodComponent *pThisComponent = dynamic_cast<DynamicConsoleMethodComponent*>( pComponent );
|
|
||||||
AssertFatal( pThisComponent, "DynamicConsoleMethodComponent::callMethod - Non DynamicConsoleMethodComponent component attempting to callback!");
|
|
||||||
|
|
||||||
// Prevent stack corruption
|
|
||||||
STR.pushFrame();
|
|
||||||
CSTK.pushFrame();
|
|
||||||
// --
|
|
||||||
|
|
||||||
// Only call on first depth components
|
|
||||||
// Should isMethod check these calls? [11/22/2006 justind]
|
|
||||||
if(pComponent->isEnabled())
|
|
||||||
Con::execute( pThisComponent, argc, argv );
|
|
||||||
|
|
||||||
// Prevent stack corruption
|
|
||||||
STR.popFrame();
|
|
||||||
CSTK.popFrame();
|
|
||||||
// --
|
|
||||||
|
|
||||||
// Bail if this was the first element
|
|
||||||
//if( nItr == componentList.begin() )
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
unlockComponentList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent stack corruption
|
|
||||||
STR.pushFrame();
|
|
||||||
CSTK.pushFrame();
|
|
||||||
// --
|
|
||||||
|
|
||||||
// Set Owner Field
|
|
||||||
const char* result = "";
|
|
||||||
if(callThis)
|
|
||||||
result = Con::execute( pThis, argc, argv, true ); // true - exec method onThisOnly, not on DCMCs
|
|
||||||
|
|
||||||
// Prevent stack corruption
|
|
||||||
STR.popFrame();
|
|
||||||
CSTK.popFrame();
|
|
||||||
// --
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleMethod( DynamicConsoleMethodComponent, callMethod, void, 3, 64 , "(methodName, argi) Calls script defined method\n"
|
|
||||||
"@param methodName The method's name as a string\n"
|
|
||||||
"@param argi Any arguments to pass to the method\n"
|
|
||||||
"@return No return value"
|
|
||||||
"@note %obj.callMethod( %methodName, %arg1, %arg2, ... );\n")
|
|
||||||
|
|
||||||
{
|
|
||||||
object->callMethodArgList( argc - 1, argv + 2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_
|
|
||||||
#define _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_
|
|
||||||
|
|
||||||
#ifndef _SIMCOMPONENT_H_
|
|
||||||
#include "component/simComponent.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _CONSOLEINTERNAL_H_
|
|
||||||
#include "console/consoleInternal.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _ICALLMETHOD_H_
|
|
||||||
#include "console/ICallMethod.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
#ifndef _TDICTIONARY_H_
|
|
||||||
#include "core/util/tDictionary.h"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class DynamicConsoleMethodComponent : public SimComponent, public ICallMethod
|
|
||||||
{
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
public:
|
|
||||||
typedef Map<StringTableEntry, S32> callMethodMetricType;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef SimComponent Parent;
|
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
// Call Method Debug Stat.
|
|
||||||
callMethodMetricType mCallMethodMetrics;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// Internal callMethod : Actually does component notification and script method execution
|
|
||||||
/// @attention This method does some magic to the argc argv to make Con::execute act properly
|
|
||||||
/// as such it's internal and should not be exposed or used except by this class
|
|
||||||
virtual const char* _callMethod( U32 argc, ConsoleValueRef argv[], bool callThis = true );
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
/// Call Method Metrics.
|
|
||||||
const callMethodMetricType& getCallMethodMetrics( void ) const { return mCallMethodMetrics; };
|
|
||||||
|
|
||||||
/// Inject Method Call.
|
|
||||||
void injectMethodCall( const char* method );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Call Method
|
|
||||||
virtual const char* callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis = true );
|
|
||||||
|
|
||||||
/// Call Method format string
|
|
||||||
const char* callMethod( S32 argc, const char* methodName, ... );
|
|
||||||
|
|
||||||
// query for console method data
|
|
||||||
virtual bool handlesConsoleMethod(const char * fname, S32 * routingId);
|
|
||||||
|
|
||||||
DECLARE_CONOBJECT(DynamicConsoleMethodComponent);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,452 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "platform/platform.h"
|
|
||||||
#include "console/simObject.h"
|
|
||||||
#include "console/consoleTypes.h"
|
|
||||||
#include "component/simComponent.h"
|
|
||||||
#include "core/stream/stream.h"
|
|
||||||
#include "console/engineAPI.h"
|
|
||||||
|
|
||||||
SimComponent::SimComponent() : mOwner( NULL )
|
|
||||||
{
|
|
||||||
mComponentList.clear();
|
|
||||||
mMutex = Mutex::createMutex();
|
|
||||||
|
|
||||||
mEnabled = true;
|
|
||||||
mTemplate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SimComponent::~SimComponent()
|
|
||||||
{
|
|
||||||
Mutex::destroyMutex( mMutex );
|
|
||||||
mMutex = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
IMPLEMENT_CO_NETOBJECT_V1(SimComponent);
|
|
||||||
|
|
||||||
ConsoleDocClass( SimComponent,
|
|
||||||
"@brief Legacy component system, soon to be deprecated.\n\n"
|
|
||||||
"Not intended for game development, for editors or internal use only.\n\n "
|
|
||||||
"@internal");
|
|
||||||
|
|
||||||
bool SimComponent::onAdd()
|
|
||||||
{
|
|
||||||
if( !Parent::onAdd() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Register
|
|
||||||
_registerInterfaces( this );
|
|
||||||
|
|
||||||
if( !_registerComponents( this ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//Con::executef( this, 1, "onAdd" );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimComponent::_registerInterfaces( SimComponent *owner )
|
|
||||||
{
|
|
||||||
// First call this to expose the interfaces that this component will cache
|
|
||||||
// before examining the list of subcomponents
|
|
||||||
registerInterfaces( owner );
|
|
||||||
|
|
||||||
// Early out to avoid mutex lock and such
|
|
||||||
if( !hasComponents() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
VectorPtr<SimComponent *> &components = lockComponentList();
|
|
||||||
for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
|
|
||||||
{
|
|
||||||
(*i)->mOwner = owner;
|
|
||||||
|
|
||||||
// Tell the component itself to register it's interfaces
|
|
||||||
(*i)->registerInterfaces( owner );
|
|
||||||
|
|
||||||
(*i)->mOwner = NULL; // This tests to see if the object's onComponentRegister call will call up to the parent.
|
|
||||||
|
|
||||||
// Recurse
|
|
||||||
(*i)->_registerInterfaces( owner );
|
|
||||||
}
|
|
||||||
|
|
||||||
unlockComponentList();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SimComponent::_registerComponents( SimComponent *owner )
|
|
||||||
{
|
|
||||||
// This method will return true if the object contains no components. See the
|
|
||||||
// documentation for SimComponent::onComponentRegister for more information
|
|
||||||
// on this behavior.
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
// If this doesn't contain components, don't even lock the list.
|
|
||||||
if( hasComponents() )
|
|
||||||
{
|
|
||||||
VectorPtr<SimComponent *> &components = lockComponentList();
|
|
||||||
for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
|
|
||||||
{
|
|
||||||
if( !(*i)->onComponentRegister( owner ) )
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertFatal( (*i)->mOwner == owner, "Component failed to call parent onComponentRegister!" );
|
|
||||||
|
|
||||||
// Recurse
|
|
||||||
if( !(*i)->_registerComponents( owner ) )
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unlockComponentList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimComponent::_unregisterComponents()
|
|
||||||
{
|
|
||||||
if( !hasComponents() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
VectorPtr<SimComponent *> &components = lockComponentList();
|
|
||||||
for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
|
|
||||||
{
|
|
||||||
(*i)->onComponentUnRegister();
|
|
||||||
|
|
||||||
AssertFatal( (*i)->mOwner == NULL, "Component failed to call parent onUnRegister" );
|
|
||||||
|
|
||||||
// Recurse
|
|
||||||
(*i)->_unregisterComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
unlockComponentList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimComponent::onRemove()
|
|
||||||
{
|
|
||||||
//Con::executef(this, 1, "onRemove");
|
|
||||||
|
|
||||||
_unregisterComponents();
|
|
||||||
|
|
||||||
// Delete all components
|
|
||||||
VectorPtr<SimComponent *>&componentList = lockComponentList();
|
|
||||||
while(componentList.size() > 0)
|
|
||||||
{
|
|
||||||
SimComponent *c = componentList[0];
|
|
||||||
componentList.erase( componentList.begin() );
|
|
||||||
|
|
||||||
if( c->isProperlyAdded() )
|
|
||||||
c->deleteObject();
|
|
||||||
else if( !c->isRemoved() && !c->isDeleted() )
|
|
||||||
delete c;
|
|
||||||
// else, something else is deleting this, don't mess with it
|
|
||||||
}
|
|
||||||
unlockComponentList();
|
|
||||||
|
|
||||||
Parent::onRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SimComponent::processArguments(S32 argc, ConsoleValueRef *argv)
|
|
||||||
{
|
|
||||||
for(S32 i = 0; i < argc; i++)
|
|
||||||
{
|
|
||||||
SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
|
|
||||||
if(obj)
|
|
||||||
addComponent(obj);
|
|
||||||
else
|
|
||||||
Con::printf("SimComponent::processArguments - Invalid Component Object \"%s\"", (const char*)argv[i]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void SimComponent::initPersistFields()
|
|
||||||
{
|
|
||||||
addGroup("Component");
|
|
||||||
|
|
||||||
addProtectedField( "Template", TypeBool, Offset(mTemplate, SimComponent),
|
|
||||||
&setIsTemplate, &defaultProtectedGetFn,
|
|
||||||
"Places the object in a component set for later use in new levels." );
|
|
||||||
|
|
||||||
endGroup("Component");
|
|
||||||
|
|
||||||
// Call Parent.
|
|
||||||
Parent::initPersistFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
bool SimComponent::getInterfaces( ComponentInterfaceList *list, const char *type /* = NULL */, const char *name /* = NULL */,
|
|
||||||
const SimComponent *owner /* = NULL */, bool notOwner /* = false */ )
|
|
||||||
{
|
|
||||||
AssertFatal( list != NULL, "Passing NULL for a list is not supported functionality for SimComponents." );
|
|
||||||
return ( mInterfaceCache.enumerate( list, type, name, owner, notOwner ) > 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SimComponent::registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface )
|
|
||||||
{
|
|
||||||
if( mInterfaceCache.add( type, name, interfaceOwner, cinterface ) )
|
|
||||||
{
|
|
||||||
cinterface->mOwner = interfaceOwner;
|
|
||||||
|
|
||||||
// Recurse
|
|
||||||
if( mOwner != NULL )
|
|
||||||
return mOwner->registerCachedInterface( type, name, interfaceOwner, cinterface );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// So this is not a good assert, because it will get triggered due to the recursive
|
|
||||||
// nature of interface caching. I want to keep it here, though, just so nobody
|
|
||||||
// else thinks, "Oh I'll add an assert here."
|
|
||||||
//
|
|
||||||
//AssertFatal( false, avar( "registerCachedInterface failed, probably because interface with type '%s', name '%s' and owner with SimObjectId '%d' already exists",
|
|
||||||
// type, name, interfaceOwner->getId() ) );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Component Management
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SimComponent::addComponentFromField( void* obj, const char* data )
|
|
||||||
{
|
|
||||||
SimComponent *pComponent = dynamic_cast<SimComponent*>( Sim::findObject( data ) );
|
|
||||||
if( pComponent != NULL )
|
|
||||||
static_cast<SimComponent*>(obj)->addComponent( pComponent );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Component to this one
|
|
||||||
bool SimComponent::addComponent( SimComponent *component )
|
|
||||||
{
|
|
||||||
AssertFatal( dynamic_cast<SimObject*>(component), "SimComponent - Cannot add non SimObject derived components!" );
|
|
||||||
|
|
||||||
MutexHandle mh;
|
|
||||||
if( mh.lock( mMutex, true ) )
|
|
||||||
{
|
|
||||||
for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ )
|
|
||||||
{
|
|
||||||
SimComponent *pComponent = dynamic_cast<SimComponent*>(*nItr);
|
|
||||||
AssertFatal( pComponent, "SimComponent::addComponent - NULL component in list!" );
|
|
||||||
if( pComponent == component )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(component->onComponentAdd(this))
|
|
||||||
{
|
|
||||||
component->mOwner = this;
|
|
||||||
mComponentList.push_back( component );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove Component from this one
|
|
||||||
bool SimComponent::removeComponent( SimComponent *component )
|
|
||||||
{
|
|
||||||
MutexHandle mh;
|
|
||||||
if( mh.lock( mMutex, true ) )
|
|
||||||
{
|
|
||||||
for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ )
|
|
||||||
{
|
|
||||||
SimComponent *pComponent = dynamic_cast<SimComponent*>(*nItr);
|
|
||||||
AssertFatal( pComponent, "SimComponent::removeComponent - NULL component in list!" );
|
|
||||||
if( pComponent == component )
|
|
||||||
{
|
|
||||||
AssertFatal( component->mOwner == this, "Somehow we contain a component who doesn't think we are it's owner." );
|
|
||||||
(*nItr)->onComponentRemove(this);
|
|
||||||
component->mOwner = NULL;
|
|
||||||
mComponentList.erase( nItr );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SimComponent::onComponentAdd(SimComponent *target)
|
|
||||||
{
|
|
||||||
Con::executef(this, "onComponentAdd", Con::getIntArg(target->getId()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimComponent::onComponentRemove(SimComponent *target)
|
|
||||||
{
|
|
||||||
Con::executef(this, "onComponentRemove", Con::getIntArg(target->getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ComponentInterface *SimComponent::getInterface(const char *type /* = NULL */, const char *name /* = NULL */,
|
|
||||||
const SimComponent *owner /* = NULL */, bool notOwner /* = false */)
|
|
||||||
{
|
|
||||||
ComponentInterfaceList iLst;
|
|
||||||
|
|
||||||
if( getInterfaces( &iLst, type, name, owner, notOwner ) )
|
|
||||||
return iLst[0];
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SimComponent::writeField(StringTableEntry fieldname, const char* value)
|
|
||||||
{
|
|
||||||
if (!Parent::writeField(fieldname, value))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( fieldname == StringTable->insert("owner") )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimComponent::write(Stream &stream, U32 tabStop, U32 flags /* = 0 */)
|
|
||||||
{
|
|
||||||
MutexHandle handle;
|
|
||||||
handle.lock(mMutex); // When this goes out of scope, it will unlock it
|
|
||||||
|
|
||||||
// export selected only?
|
|
||||||
if((flags & SelectedOnly) && !isSelected())
|
|
||||||
{
|
|
||||||
for(U32 i = 0; i < mComponentList.size(); i++)
|
|
||||||
mComponentList[i]->write(stream, tabStop, flags);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeTabs(tabStop);
|
|
||||||
char buffer[1024];
|
|
||||||
dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : "");
|
|
||||||
stream.write(dStrlen(buffer), buffer);
|
|
||||||
writeFields(stream, tabStop + 1);
|
|
||||||
|
|
||||||
if(mComponentList.size())
|
|
||||||
{
|
|
||||||
stream.write(2, "\r\n");
|
|
||||||
|
|
||||||
stream.writeTabs(tabStop+1);
|
|
||||||
stream.writeLine((U8 *)"// Note: This is a list of behaviors, not arbitrary SimObjects as in a SimGroup or SimSet.\r\n");
|
|
||||||
|
|
||||||
for(U32 i = 0; i < mComponentList.size(); i++)
|
|
||||||
mComponentList[i]->write(stream, tabStop + 1, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeTabs(tabStop);
|
|
||||||
stream.write(4, "};\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Console Methods
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ConsoleMethod( SimComponent, addComponents, bool, 3, 64, "%obj.addComponents( %compObjName, %compObjName2, ... );\n"
|
|
||||||
"Adds additional components to current list.\n"
|
|
||||||
"@param Up to 62 component names\n"
|
|
||||||
"@return Returns true on success, false otherwise.")
|
|
||||||
{
|
|
||||||
for(S32 i = 2; i < argc; i++)
|
|
||||||
{
|
|
||||||
SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
|
|
||||||
if(obj)
|
|
||||||
object->addComponent(obj);
|
|
||||||
else
|
|
||||||
Con::printf("SimComponent::addComponents - Invalid Component Object \"%s\"", (const char*)argv[i]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleMethod( SimComponent, removeComponents, bool, 3, 64, "%obj.removeComponents( %compObjName, %compObjName2, ... );\n"
|
|
||||||
"Removes components by name from current list.\n"
|
|
||||||
"@param objNamex Up to 62 component names\n"
|
|
||||||
"@return Returns true on success, false otherwise.")
|
|
||||||
{
|
|
||||||
for(S32 i = 2; i < argc; i++)
|
|
||||||
{
|
|
||||||
SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
|
|
||||||
if(obj)
|
|
||||||
object->removeComponent(obj);
|
|
||||||
else
|
|
||||||
Con::printf("SimComponent::removeComponents - Invalid Component Object \"%s\"", (const char*)argv[i]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod( SimComponent, getComponentCount, S32, (), , "() Get the current component count\n"
|
|
||||||
"@return The number of components in the list as an integer")
|
|
||||||
{
|
|
||||||
return object->getComponentCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod( SimComponent, getComponent, S32, (S32 idx), , "(idx) Get the component corresponding to the given index.\n"
|
|
||||||
"@param idx An integer index value corresponding to the desired component.\n"
|
|
||||||
"@return The id of the component at the given index as an integer")
|
|
||||||
{
|
|
||||||
if(idx < 0 || idx >= object->getComponentCount())
|
|
||||||
{
|
|
||||||
Con::errorf("SimComponent::getComponent - Invalid index %d", idx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SimComponent *c = object->getComponent(idx);
|
|
||||||
return c ? c->getId() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod(SimComponent, setEnabled, void, (bool enabled), , "(enabled) Sets or unsets the enabled flag\n"
|
|
||||||
"@param enabled Boolean value\n"
|
|
||||||
"@return No return value")
|
|
||||||
{
|
|
||||||
object->setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod(SimComponent, isEnabled, bool, (), , "() Check whether SimComponent is currently enabled\n"
|
|
||||||
"@return true if enabled and false if not")
|
|
||||||
{
|
|
||||||
return object->isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod(SimComponent, setIsTemplate, void, (bool templateFlag), , "(template) Sets or unsets the template flag\n"
|
|
||||||
"@param template Boolean value\n"
|
|
||||||
"@return No return value")
|
|
||||||
{
|
|
||||||
object->setIsTemplate(templateFlag);
|
|
||||||
}
|
|
||||||
|
|
||||||
DefineConsoleMethod(SimComponent, getIsTemplate, bool, (), , "() Check whether SimComponent is currently a template\n"
|
|
||||||
"@return true if is a template and false if not")
|
|
||||||
{
|
|
||||||
return object->getIsTemplate();
|
|
||||||
}
|
|
||||||
|
|
@ -1,256 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef _SIMCOMPONENT_H_
|
|
||||||
#define _SIMCOMPONENT_H_
|
|
||||||
|
|
||||||
#ifndef _TVECTOR_H_
|
|
||||||
#include "core/util/tVector.h"
|
|
||||||
#endif
|
|
||||||
#ifndef _STRINGTABLE_H_
|
|
||||||
#include "core/stringTable.h"
|
|
||||||
#endif
|
|
||||||
#ifndef _NETOBJECT_H_
|
|
||||||
#include "sim/netObject.h"
|
|
||||||
#endif
|
|
||||||
#ifndef _COMPONENTINTERFACE_H_
|
|
||||||
#include "component/componentInterface.h"
|
|
||||||
#endif
|
|
||||||
#ifndef _PLATFORM_THREADS_MUTEX_H_
|
|
||||||
#include "platform/threads/mutex.h"
|
|
||||||
#endif
|
|
||||||
#ifndef _STRINGFUNCTIONS_H_
|
|
||||||
#include "core/strings/stringFunctions.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Forward refs
|
|
||||||
class Stream;
|
|
||||||
class ComponentInterface;
|
|
||||||
class ComponentInterfaceCache;
|
|
||||||
|
|
||||||
class SimComponent : public NetObject
|
|
||||||
{
|
|
||||||
typedef NetObject Parent;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VectorPtr<SimComponent *> mComponentList; ///< The Component List
|
|
||||||
void *mMutex; ///< Component List Mutex
|
|
||||||
|
|
||||||
SimObjectPtr<SimComponent> mOwner; ///< The component which owns this one.
|
|
||||||
|
|
||||||
/// This is called internally to instruct the component to iterate over it's
|
|
||||||
// list of components and recursively call _registerInterfaces on their lists
|
|
||||||
// of components.
|
|
||||||
void _registerInterfaces( SimComponent *owner );
|
|
||||||
|
|
||||||
bool _registerComponents( SimComponent *owner );
|
|
||||||
void _unregisterComponents();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ComponentInterfaceCache mInterfaceCache; ///< Stores the interfaces exposed by this component.
|
|
||||||
|
|
||||||
bool mEnabled;
|
|
||||||
|
|
||||||
bool mTemplate;
|
|
||||||
|
|
||||||
// Non-const getOwner for derived classes
|
|
||||||
SimComponent *_getOwner() { return mOwner; }
|
|
||||||
|
|
||||||
/// Returns a const reference to private mComponentList
|
|
||||||
typedef VectorPtr<SimComponent *>::iterator SimComponentIterator;
|
|
||||||
VectorPtr<SimComponent *> &lockComponentList()
|
|
||||||
{
|
|
||||||
Mutex::lockMutex( mMutex );
|
|
||||||
return mComponentList;
|
|
||||||
};
|
|
||||||
|
|
||||||
void unlockComponentList()
|
|
||||||
{
|
|
||||||
Mutex::unlockMutex( mMutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// onComponentRegister is called on each component by it's owner. If a component
|
|
||||||
/// has no owner, onComponentRegister will not be called on it. The purpose
|
|
||||||
/// of onComponentRegister is to allow a component to check for any external
|
|
||||||
/// interfaces, or other dependencies which it needs to function. If any component
|
|
||||||
/// in a component hierarchy returns false from it's onComponentRegister call
|
|
||||||
/// the entire hierarchy is invalid, and SimObject::onAdd will fail on the
|
|
||||||
/// top-level component. To put it another way, if a component contains other
|
|
||||||
/// components, it will be registered successfully with Sim iff each subcomponent
|
|
||||||
/// returns true from onComponentRegister. If a component does not contain
|
|
||||||
/// other components, it will not receive an onComponentRegister call.
|
|
||||||
///
|
|
||||||
/// Overloads of this method must pass the call along to their parent, as is
|
|
||||||
/// shown in the example below.
|
|
||||||
///
|
|
||||||
/// @code
|
|
||||||
/// bool FooComponent::onComponentRegister( SimComponent *owner )
|
|
||||||
/// {
|
|
||||||
/// if( !Parent::onComponentRegister( owner ) )
|
|
||||||
/// return false;
|
|
||||||
/// ...
|
|
||||||
/// }
|
|
||||||
/// @endcode
|
|
||||||
virtual bool onComponentRegister( SimComponent *owner )
|
|
||||||
{
|
|
||||||
mOwner = owner;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// onUnregister is called when the owner is unregistering. Your object should
|
|
||||||
/// do cleanup here, as well as pass a call up the chain to the parent.
|
|
||||||
virtual void onComponentUnRegister()
|
|
||||||
{
|
|
||||||
mOwner = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// registerInterfaces is called on each component as it's owner is registering
|
|
||||||
/// it's interfaces. This is called before onComponentRegister, and should be used to
|
|
||||||
/// register all interfaces exposed by your component, as well as all callbacks
|
|
||||||
/// needed by your component.
|
|
||||||
virtual void registerInterfaces( SimComponent *owner )
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
DECLARE_CONOBJECT(SimComponent);
|
|
||||||
|
|
||||||
/// Constructor
|
|
||||||
/// Add this component
|
|
||||||
SimComponent();
|
|
||||||
|
|
||||||
/// Destructor
|
|
||||||
/// Remove this component and destroy child references
|
|
||||||
virtual ~SimComponent();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual bool onAdd();
|
|
||||||
virtual void onRemove();
|
|
||||||
|
|
||||||
static void initPersistFields();
|
|
||||||
|
|
||||||
virtual bool processArguments(S32 argc, ConsoleValueRef *argv);
|
|
||||||
|
|
||||||
bool isEnabled() const { return mEnabled; }
|
|
||||||
|
|
||||||
void setEnabled( bool value ) { mEnabled = value; }
|
|
||||||
|
|
||||||
/// Will return true if this object contains components.
|
|
||||||
bool hasComponents() const { return ( mComponentList.size() > 0 ); };
|
|
||||||
|
|
||||||
/// The component which owns this object
|
|
||||||
const SimComponent *getOwner() const { return mOwner; };
|
|
||||||
|
|
||||||
// Component Information
|
|
||||||
inline virtual StringTableEntry getComponentName() { return StringTable->insert( getClassName() ); };
|
|
||||||
|
|
||||||
/// Protected 'Component' Field setter that will add a component to the list.
|
|
||||||
static bool addComponentFromField(void* obj, const char* data);
|
|
||||||
|
|
||||||
/// Add Component to this one
|
|
||||||
virtual bool addComponent( SimComponent *component );
|
|
||||||
|
|
||||||
/// Remove Component from this one
|
|
||||||
virtual bool removeComponent( SimComponent *component );
|
|
||||||
|
|
||||||
/// Clear Child components of this one
|
|
||||||
virtual bool clearComponents() { mComponentList.clear(); return true; };
|
|
||||||
|
|
||||||
virtual bool onComponentAdd(SimComponent *target);
|
|
||||||
virtual void onComponentRemove(SimComponent *target);
|
|
||||||
|
|
||||||
S32 getComponentCount() { return mComponentList.size(); }
|
|
||||||
SimComponent *getComponent(S32 idx) { return mComponentList[idx]; }
|
|
||||||
|
|
||||||
SimComponentIterator find(SimComponentIterator first, SimComponentIterator last, SimComponent *value)
|
|
||||||
{
|
|
||||||
return ::find(first, last, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool setIsTemplate( void *object, const char *index, const char *data )
|
|
||||||
{ static_cast<SimComponent*>(object)->setIsTemplate( dAtob( data ) ); return false; };
|
|
||||||
virtual void setIsTemplate( const bool pTemplate ) { mTemplate = pTemplate; }
|
|
||||||
bool getIsTemplate() const { return mTemplate; }
|
|
||||||
|
|
||||||
virtual void write(Stream &stream, U32 tabStop, U32 flags = 0);
|
|
||||||
virtual bool writeField(StringTableEntry fieldname, const char* value);
|
|
||||||
|
|
||||||
|
|
||||||
/// getInterfaces allows the caller to enumerate the interfaces exposed by
|
|
||||||
/// this component. This method can be overwritten to expose interfaces
|
|
||||||
/// which are not cached on the object, before passing the call to the Parent.
|
|
||||||
/// This can be used delay interface creation until it is queried for, instead
|
|
||||||
/// of creating it on initialization, and caching it. Returns false if no results
|
|
||||||
/// were found
|
|
||||||
///
|
|
||||||
/// @param list The list that this method will append search results on to.
|
|
||||||
/// @param type An expression which the 'type' field on an added object must match to be included in results
|
|
||||||
/// @param name An expression which the 'name' field on an added object must match to be included in results
|
|
||||||
/// @param owner Limit results to components owned/not-owned by this SimComponent (see next param)
|
|
||||||
/// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner'
|
|
||||||
virtual bool getInterfaces( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ); // const omission intentional
|
|
||||||
|
|
||||||
|
|
||||||
/// These two methods allow for easy query of component interfaces if you know
|
|
||||||
/// exactly what you are looking for, and don't mind being passed back the first
|
|
||||||
/// matching result.
|
|
||||||
ComponentInterface *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false );
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false );
|
|
||||||
|
|
||||||
/// Add an interface to the cache. This function will return true if the interface
|
|
||||||
/// is added successfully. An interface will not be added successfully if an entry
|
|
||||||
/// in this components cache with the same values for 'type' and 'name' is present.
|
|
||||||
///
|
|
||||||
/// @param type Type of the interface being added. If NULL is passed, it will match any type string queried.
|
|
||||||
/// @param name Name of interface being added. If NULL is passed, it will match any name string queried.
|
|
||||||
/// @param interfaceOwner The component which owns the interface being cached
|
|
||||||
/// @param cinterface The ComponentInterface being cached
|
|
||||||
bool registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface );
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T *SimComponent::getInterface( const char *type /* = NULL */, const char *name /* = NULL */,
|
|
||||||
const SimComponent *owner /* = NULL */, bool notOwner /* = false */ )
|
|
||||||
{
|
|
||||||
ComponentInterfaceList iLst;
|
|
||||||
|
|
||||||
if( getInterfaces( &iLst, type, name, owner, notOwner ) )
|
|
||||||
{
|
|
||||||
ComponentInterfaceListIterator itr = iLst.begin();
|
|
||||||
|
|
||||||
while( dynamic_cast<T *>( *itr ) == NULL )
|
|
||||||
itr++;
|
|
||||||
|
|
||||||
if( itr != iLst.end() )
|
|
||||||
return static_cast<T *>( *itr );
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _SIMCOMPONENT_H_
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2012 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef _SIMPLECOMPONENT_H_
|
|
||||||
#define _SIMPLECOMPONENT_H_
|
|
||||||
|
|
||||||
#ifndef _SIMCOMPONENT_H_
|
|
||||||
#include "component/simComponent.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _COMPONENTINTERFACE_H_
|
|
||||||
#include "component/componentInterface.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// This is a very simple interface. Interfaces provide ways for components to
|
|
||||||
/// interact with each-other, and query each-other for functionality. It makes it
|
|
||||||
/// possible for two components to be interdependent on one another, as well. An
|
|
||||||
/// interface should make accessor calls to it's owner for functionality, and
|
|
||||||
/// generally be as thin of a layer as possible.
|
|
||||||
class SimpleComponentInterface : public ComponentInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool isFortyTwo( const U32 test );
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
/// The purpose of this component is to provide a minimalistic component that
|
|
||||||
/// exposes a simple, cached interface
|
|
||||||
class SimpleComponent : public SimComponent
|
|
||||||
{
|
|
||||||
typedef SimComponent Parent;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SimpleComponentInterface mSCInterface;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Components are still SimObjects, and need to be declared and implemented
|
|
||||||
// with the standard macros
|
|
||||||
DECLARE_CONOBJECT(SimpleComponent);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// SimComponent overloads.
|
|
||||||
|
|
||||||
// This method is called on each component as it's parent is getting onAdd
|
|
||||||
// called. The purpose of overloading this method is to expose cached interfaces
|
|
||||||
// before onComponentRegister is called, so that other components can depend on the
|
|
||||||
// interfaces you expose in order to register properly. This functionality
|
|
||||||
// will be demonstrated in a more advanced example.
|
|
||||||
virtual void registerInterfaces( SimComponent *owner )
|
|
||||||
{
|
|
||||||
// While it is not imperative that we pass this call to the Parent in this
|
|
||||||
// example, if there existed a class-heirarchy of components, it would be
|
|
||||||
// critical, so for good practice, call up to the Parent.
|
|
||||||
Parent::registerInterfaces( owner );
|
|
||||||
|
|
||||||
// Now we should go ahead and register our cached interface. What we are doing
|
|
||||||
// is telling the component which contains this component (if it exists)
|
|
||||||
// all of the interfaces that we expose. When this call is made, it will
|
|
||||||
// recurse up the owner list.
|
|
||||||
//
|
|
||||||
// For example, there exists components A, B, and C.
|
|
||||||
// A owns B, and B owns C.
|
|
||||||
//
|
|
||||||
// If C exposes a cached interface, it will expose it via registerCachedInterface
|
|
||||||
// when registerInterfaces is recursively called. It will add it's interface to
|
|
||||||
// it's cache list, and then pass the register call up to it's parent. The parent
|
|
||||||
// will also cache the interface, and continue to pass the cache call up the
|
|
||||||
// child->parent chain until there exists no parent.
|
|
||||||
//
|
|
||||||
// The result is that, if C exposes an interface 'foo', and A owns B, and
|
|
||||||
// B owns C, an interface request for 'foo' given to component A will result
|
|
||||||
// in 'foo' being returned, even though A does not expose 'foo'. This makes
|
|
||||||
// it possible for a component to query it's owner for an interface, and
|
|
||||||
// not care where that interface is exposed. It also allows for game code
|
|
||||||
// to work with any SimComponent and query that component for any interface
|
|
||||||
// it wants without knowing or caring exactly where it is coming from.
|
|
||||||
//
|
|
||||||
// registerCachedInterface returns a boolean value if it was successful.
|
|
||||||
// Success results in the caching of this interface throughout the full
|
|
||||||
// child->parent chain. An interface will be added to a cache list
|
|
||||||
// successfully iff there exists no entry in that list that has matching
|
|
||||||
// values for 'type', 'name' and 'owner'.
|
|
||||||
owner->registerCachedInterface(
|
|
||||||
// The first parameter is the 'type' of the interface, this is not to be
|
|
||||||
// confused with any kind of existing console, or c++ type. It is simply
|
|
||||||
// a string which is can be set to any value
|
|
||||||
"example",
|
|
||||||
|
|
||||||
// The next parameter is the 'name' of the interface. This is also a string
|
|
||||||
// which can be set to any value
|
|
||||||
"isfortytwo",
|
|
||||||
|
|
||||||
// The owner of the interface. Note that the value being assigned here
|
|
||||||
// is this instance of SimpleComponent, and not the 'owner' argument
|
|
||||||
// of the function registerInterfaces that we are calling from.
|
|
||||||
this,
|
|
||||||
|
|
||||||
// And finally the interface; a pointer to an object with type ComponentInterface
|
|
||||||
&mSCInterface );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Specific functionality
|
|
||||||
|
|
||||||
/// This is the test method, it will return true if the number provided
|
|
||||||
/// is forty two
|
|
||||||
bool isFortyTwo( const U32 test ) const
|
|
||||||
{
|
|
||||||
return ( test == 42 );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Interface implementation
|
|
||||||
//
|
|
||||||
// Since interfaces themselves implement very little functionality, it is a good
|
|
||||||
// idea to inline them if at all possible. Interdependent components will be using
|
|
||||||
// these interfaces constantly, and so putting as thin of a layer between the
|
|
||||||
// functionality they expose, and the functionality the component implements is
|
|
||||||
// a good design practice.
|
|
||||||
inline bool SimpleComponentInterface::isFortyTwo( const U32 test )
|
|
||||||
{
|
|
||||||
// This code block will test for a valid owner in a debug build before
|
|
||||||
// performing operations on it's owner. It is worth noting that the
|
|
||||||
// ComponentInterface::isValid() method can be overridden to include
|
|
||||||
// validation specific to your interface and/or component.
|
|
||||||
AssertFatal( isValid(), "SimpleComponentInterface has not been registered properly by the component which exposes it." );
|
|
||||||
|
|
||||||
// This is a sanity check. The owner of this interface should have the type
|
|
||||||
// SimpleComponent, otherwise this won't work. (See further interface examples
|
|
||||||
// for some ways around this)
|
|
||||||
AssertFatal( dynamic_cast<SimpleComponent *>( getOwner() ) != NULL, "Owner of SimpleComponentInterface is not a SimpleComponent" );
|
|
||||||
|
|
||||||
// Component interfaces rely on being registered to set their mOwner
|
|
||||||
// field. This field is initialized to NULL, and then gets set by
|
|
||||||
// SimComponent when the interface is registered.
|
|
||||||
return static_cast<const SimpleComponent *>( getOwner() )->isFortyTwo( test );
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2014 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef TORQUE_TESTS_ENABLED
|
|
||||||
#include "testing/unitTesting.h"
|
|
||||||
#include "component/moreAdvancedComponent.h"
|
|
||||||
|
|
||||||
TEST(MoreAdvancedComponent, MoreAdvancedComponent)
|
|
||||||
{
|
|
||||||
// Create component instances and compose them.
|
|
||||||
SimComponent *parentComponent = new SimComponent();
|
|
||||||
SimpleComponent *simpleComponent = new SimpleComponent();
|
|
||||||
MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent();
|
|
||||||
// CodeReview note that the interface pointer isn't initialized in a ctor
|
|
||||||
// on the components, so it's bad memory against which you might
|
|
||||||
// be checking in testDependentInterface [3/3/2007 justind]
|
|
||||||
parentComponent->addComponent( simpleComponent );
|
|
||||||
parentComponent->addComponent( moreAdvComponent );
|
|
||||||
|
|
||||||
simpleComponent->registerObject();
|
|
||||||
moreAdvComponent->registerObject();
|
|
||||||
|
|
||||||
// Put a break-point here, follow the onAdd call, and observe the order in
|
|
||||||
// which the SimComponent::onAdd function executes. You will see the interfaces
|
|
||||||
// get cached, and the dependent interface query being made.
|
|
||||||
parentComponent->registerObject();
|
|
||||||
|
|
||||||
// If the MoreAdvancedComponent found an interface, than the parentComponent
|
|
||||||
// should have returned true, from onAdd, and should therefore be registered
|
|
||||||
// properly with the Sim
|
|
||||||
EXPECT_TRUE( parentComponent->isProperlyAdded() )
|
|
||||||
<< "Parent component not properly added!";
|
|
||||||
|
|
||||||
// Now lets test the interface. You can step through this, as well.
|
|
||||||
EXPECT_TRUE( moreAdvComponent->testDependentInterface() )
|
|
||||||
<< "Dependent interface test failed.";
|
|
||||||
|
|
||||||
// CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind]
|
|
||||||
//
|
|
||||||
// Clean up
|
|
||||||
parentComponent->removeComponent( simpleComponent );
|
|
||||||
parentComponent->removeComponent( moreAdvComponent );
|
|
||||||
|
|
||||||
parentComponent->deleteObject();
|
|
||||||
moreAdvComponent->deleteObject();
|
|
||||||
simpleComponent->deleteObject();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2014 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef TORQUE_TESTS_ENABLED
|
|
||||||
#include "testing/unitTesting.h"
|
|
||||||
#include "component/simComponent.h"
|
|
||||||
|
|
||||||
class CachedInterfaceExampleComponent : public SimComponent
|
|
||||||
{
|
|
||||||
typedef SimComponent Parent;
|
|
||||||
|
|
||||||
ComponentProperty<U32> mMyId;
|
|
||||||
static U32 smNumInstances;
|
|
||||||
ComponentProperty<U32> *mpU32; // CodeReview [patw, 2, 17, 2007] Make ref objects when this is in Jugg
|
|
||||||
|
|
||||||
public:
|
|
||||||
DECLARE_CONOBJECT( CachedInterfaceExampleComponent );
|
|
||||||
|
|
||||||
CachedInterfaceExampleComponent() : mpU32( NULL )
|
|
||||||
{
|
|
||||||
mMyId = ( ( 1 << 24 ) | smNumInstances++ );
|
|
||||||
}
|
|
||||||
virtual ~CachedInterfaceExampleComponent()
|
|
||||||
{
|
|
||||||
smNumInstances--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
virtual void registerInterfaces( SimComponent *owner )
|
|
||||||
{
|
|
||||||
// Register a cached interface for this
|
|
||||||
owner->registerCachedInterface( NULL, "aU32", this, &mMyId );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool onComponentRegister( SimComponent *owner )
|
|
||||||
{
|
|
||||||
// Call up to the parent first
|
|
||||||
if( !Parent::onComponentRegister( owner ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// We want to get an interface from another object in our containing component
|
|
||||||
// to simulate component interdependency.
|
|
||||||
ComponentInterfaceList list;
|
|
||||||
|
|
||||||
// Enumerate the interfaces on the owner, only ignore interfaces that this object owns
|
|
||||||
if( !owner->getInterfaces( &list, NULL, "aU32", this, true ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Sanity check before just assigning all willy-nilly
|
|
||||||
for( ComponentInterfaceListIterator i = list.begin(); i != list.end(); i++ )
|
|
||||||
{
|
|
||||||
mpU32 = dynamic_cast<ComponentProperty<U32> *>( (*i) );
|
|
||||||
|
|
||||||
if( mpU32 != NULL )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface
|
|
||||||
// for this functionality later
|
|
||||||
void unit_test()
|
|
||||||
{
|
|
||||||
EXPECT_TRUE( mpU32 != NULL )
|
|
||||||
<< "Pointer to dependent interface is NULL";
|
|
||||||
if( mpU32 )
|
|
||||||
{
|
|
||||||
EXPECT_TRUE( *(*mpU32) & ( 1 << 24 ) )
|
|
||||||
<< "Pointer to interface data is bogus.";
|
|
||||||
EXPECT_TRUE( *(*mpU32) != *mMyId )
|
|
||||||
<< "Two of me have the same ID, bad!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CONOBJECT( CachedInterfaceExampleComponent );
|
|
||||||
U32 CachedInterfaceExampleComponent::smNumInstances = 0;
|
|
||||||
|
|
||||||
ConsoleDocClass( CachedInterfaceExampleComponent,
|
|
||||||
"@brief Legacy from older component system.\n\n"
|
|
||||||
"Not intended for game development, for editors or internal use only.\n\n "
|
|
||||||
"@internal");
|
|
||||||
|
|
||||||
TEST(SimComponent, Composition)
|
|
||||||
{
|
|
||||||
SimComponent *testComponent = new SimComponent();
|
|
||||||
CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent();
|
|
||||||
CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent();
|
|
||||||
|
|
||||||
// Register sub-components
|
|
||||||
EXPECT_TRUE( componentA->registerObject() )
|
|
||||||
<< "Failed to register componentA";
|
|
||||||
EXPECT_TRUE( componentB->registerObject() )
|
|
||||||
<< "Failed to register componentB";
|
|
||||||
|
|
||||||
// Add the components
|
|
||||||
EXPECT_TRUE( testComponent->addComponent( componentA ) )
|
|
||||||
<< "Failed to add component a to testComponent";
|
|
||||||
EXPECT_TRUE( testComponent->addComponent( componentB ) )
|
|
||||||
<< "Failed to add component b to testComponent";
|
|
||||||
|
|
||||||
EXPECT_EQ( componentA->getOwner(), testComponent )
|
|
||||||
<< "testComponent did not properly set the mOwner field of componentA to NULL.";
|
|
||||||
EXPECT_EQ( componentB->getOwner(), testComponent )
|
|
||||||
<< "testComponent did not properly set the mOwner field of componentB to NULL.";
|
|
||||||
|
|
||||||
// Register the object with the simulation, kicking off the interface registration
|
|
||||||
ASSERT_TRUE( testComponent->registerObject() )
|
|
||||||
<< "Failed to register testComponent";
|
|
||||||
|
|
||||||
{
|
|
||||||
SCOPED_TRACE("componentA");
|
|
||||||
componentA->unit_test();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
SCOPED_TRACE("componentB");
|
|
||||||
componentB->unit_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
testComponent->deleteObject();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Copyright (c) 2014 GarageGames, LLC
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef TORQUE_TESTS_ENABLED
|
|
||||||
#include "testing/unitTesting.h"
|
|
||||||
#include "component/simpleComponent.h"
|
|
||||||
|
|
||||||
TEST(SimpleComponent, SimpleComponent)
|
|
||||||
{
|
|
||||||
// When instantiating, and working with a SimObject in C++ code, such as
|
|
||||||
// a unit test, you *may not* allocate a SimObject off of the stack.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// SimpleComponent sc;
|
|
||||||
// is a stack allocation. This memory is allocated off of the program stack
|
|
||||||
// when the function is called. SimObject deletion is done via SimObject::deleteObject()
|
|
||||||
// and the last command of this method is 'delete this;' That command will
|
|
||||||
// cause an assert if it is called on stack-allocated memory. Therefor, when
|
|
||||||
// instantiating SimObjects in C++ code, it is imperitive that you keep in
|
|
||||||
// mind that if any script calls 'delete()' on that SimObject, or any other
|
|
||||||
// C++ code calls 'deleteObject()' on that SimObject, it will crash.
|
|
||||||
SimpleComponent *sc = new SimpleComponent();
|
|
||||||
|
|
||||||
// SimObject::registerObject must be called on a SimObject before it is
|
|
||||||
// fully 'hooked in' to the engine.
|
|
||||||
//
|
|
||||||
// Tracing execution of this function will let you see onAdd get called on
|
|
||||||
// the component, and you will see it cache the interface we exposed.
|
|
||||||
sc->registerObject();
|
|
||||||
|
|
||||||
// It is *not* required that a component always be owned by a component (obviously)
|
|
||||||
// however I am using an owner so that you can trace execution of recursive
|
|
||||||
// calls to cache interfaces and such.
|
|
||||||
SimComponent *testOwner = new SimComponent();
|
|
||||||
|
|
||||||
// Add the test component to it's owner. This will set the 'mOwner' field
|
|
||||||
// of 'sc' to the address of 'testOwner'
|
|
||||||
testOwner->addComponent( sc );
|
|
||||||
|
|
||||||
// If you step-into this registerObject the same way as the previous one,
|
|
||||||
// you will be able to see the recursive caching of the exposed interface.
|
|
||||||
testOwner->registerObject();
|
|
||||||
|
|
||||||
// Now to prove that object composition is working properly, lets ask
|
|
||||||
// both of these components for their interface lists...
|
|
||||||
|
|
||||||
// The ComponentInterfaceList is a typedef for type 'VectorPtr<ComponentInterface *>'
|
|
||||||
// and it will be used by getInterfaces() to store the results of the interface
|
|
||||||
// query. This is the "complete" way to obtain an interface, and it is too
|
|
||||||
// heavy-weight for most cases. A simplified query will be performed next,
|
|
||||||
// to demonstrate the usage of both.
|
|
||||||
ComponentInterfaceList iLst;
|
|
||||||
|
|
||||||
// This query requests all interfaces, on all components, regardless of name
|
|
||||||
// or owner.
|
|
||||||
sc->getInterfaces( &iLst,
|
|
||||||
// This is the type field. I am passing NULL here to signify that the query
|
|
||||||
// should match all values of 'type' in the list.
|
|
||||||
NULL,
|
|
||||||
|
|
||||||
// The name field, let's pass NULL again just so when you trace execution
|
|
||||||
// you can see how queries work in the simple case, first.
|
|
||||||
NULL );
|
|
||||||
|
|
||||||
// Lets process the list that we've gotten back, and find the interface that
|
|
||||||
// we want.
|
|
||||||
SimpleComponentInterface *scQueriedInterface = NULL;
|
|
||||||
|
|
||||||
for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ )
|
|
||||||
{
|
|
||||||
scQueriedInterface = dynamic_cast<SimpleComponentInterface *>( *i );
|
|
||||||
|
|
||||||
if( scQueriedInterface != NULL )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
|
|
||||||
|
|
||||||
// Lets do it again, only we will execute the query on the parent instead,
|
|
||||||
// in a simplified way. Remember the parent component doesn't expose any
|
|
||||||
// interfaces at all, so the success of this behavior is entirely dependent
|
|
||||||
// on the recursive registration that occurs in registerInterfaces()
|
|
||||||
SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface<SimpleComponentInterface>();
|
|
||||||
|
|
||||||
AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
|
|
||||||
|
|
||||||
// We should now have two pointers to the same interface obtained by querying
|
|
||||||
// different components.
|
|
||||||
EXPECT_EQ( ownerQueriedInterface, scQueriedInterface )
|
|
||||||
<< "This really shouldn't be possible to fail given the setup of the test";
|
|
||||||
|
|
||||||
// Lets call the method that was exposed on the component via the interface.
|
|
||||||
// Trace the execution of this function, if you wish.
|
|
||||||
EXPECT_TRUE( ownerQueriedInterface->isFortyTwo( 42 ) )
|
|
||||||
<< "Don't panic, but it's a bad day in the component system.";
|
|
||||||
EXPECT_TRUE( scQueriedInterface->isFortyTwo( 42 ) )
|
|
||||||
<< "Don't panic, but it's a bad day in the component system.";
|
|
||||||
|
|
||||||
// So there you have it. Writing a simple component that exposes a cached
|
|
||||||
// interface, and testing it. It's time to clean up.
|
|
||||||
testOwner->removeComponent( sc );
|
|
||||||
|
|
||||||
sc->deleteObject();
|
|
||||||
testOwner->deleteObject();
|
|
||||||
|
|
||||||
// Interfaces do not need to be freed. In Juggernaught, these will be ref-counted
|
|
||||||
// for more robust behavior. Right now, however, the values of our two interface
|
|
||||||
// pointers, scQueriedInterface and ownerQueriedInterface, reference invalid
|
|
||||||
// memory.
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -994,6 +994,7 @@ breakContinue:
|
||||||
// This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields().
|
// This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields().
|
||||||
Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName());
|
Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName());
|
||||||
delete currentNewObject;
|
delete currentNewObject;
|
||||||
|
currentNewObject = NULL;
|
||||||
ip = failJump;
|
ip = failJump;
|
||||||
// Prevent stack value corruption
|
// Prevent stack value corruption
|
||||||
CSTK.popFrame();
|
CSTK.popFrame();
|
||||||
|
|
@ -1094,6 +1095,9 @@ breakContinue:
|
||||||
|
|
||||||
case OP_FINISH_OBJECT:
|
case OP_FINISH_OBJECT:
|
||||||
{
|
{
|
||||||
|
if (currentNewObject)
|
||||||
|
currentNewObject->onPostAdd();
|
||||||
|
|
||||||
//Assert( objectCreationStackIndex >= 0 );
|
//Assert( objectCreationStackIndex >= 0 );
|
||||||
// Restore the object info from the stack [7/9/2007 Black]
|
// Restore the object info from the stack [7/9/2007 Black]
|
||||||
currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject;
|
currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject;
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,8 @@ public:
|
||||||
enum FieldFlags
|
enum FieldFlags
|
||||||
{
|
{
|
||||||
FIELD_HideInInspectors = BIT( 0 ), ///< Do not show the field in inspectors.
|
FIELD_HideInInspectors = BIT( 0 ), ///< Do not show the field in inspectors.
|
||||||
|
FIELD_ComponentInspectors = BIT(1), ///< Custom fields used by components. They are likely to be non-standard size/configuration, so
|
||||||
|
///< They are handled specially
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Field
|
struct Field
|
||||||
|
|
|
||||||
|
|
@ -710,6 +710,31 @@ ConsoleSetType( TypeColorI )
|
||||||
Con::printf("Color must be set as { r, g, b [,a] }, { r g b [b] } or { stockColorName }");
|
Con::printf("Color must be set as { r, g, b [,a] }, { r g b [b] } or { stockColorName }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// TypeSimObjectPtr
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
ConsoleType(SimObject, TypeSimObjectPtr, SimObject*, "")
|
||||||
|
|
||||||
|
ConsoleSetType(TypeSimObjectPtr)
|
||||||
|
{
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
SimObject **obj = (SimObject **)dptr;
|
||||||
|
*obj = Sim::findObject(argv[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Con::printf("(TypeSimObjectPtr) Cannot set multiple args to a single S32.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleGetType(TypeSimObjectPtr)
|
||||||
|
{
|
||||||
|
SimObject **obj = (SimObject**)dptr;
|
||||||
|
static const U32 bufSize = 128;
|
||||||
|
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||||
|
dSprintf(returnBuffer, bufSize, "%s", *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : "");
|
||||||
|
return returnBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// TypeSimObjectName
|
// TypeSimObjectName
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ DefineConsoleType( TypeCommand, String )
|
||||||
DefineConsoleType( TypeFilename, const char * )
|
DefineConsoleType( TypeFilename, const char * )
|
||||||
DefineConsoleType( TypeStringFilename, String )
|
DefineConsoleType( TypeStringFilename, String )
|
||||||
|
|
||||||
|
DefineConsoleType(TypeRotationF, RotationF)
|
||||||
|
|
||||||
/// A universally unique identifier.
|
/// A universally unique identifier.
|
||||||
DefineConsoleType( TypeUUID, Torque::UUID )
|
DefineConsoleType( TypeUUID, Torque::UUID )
|
||||||
|
|
||||||
|
|
@ -123,6 +125,8 @@ DefineConsoleType( TypeColorF, ColorF )
|
||||||
DefineConsoleType( TypeSimObjectName, SimObject* )
|
DefineConsoleType( TypeSimObjectName, SimObject* )
|
||||||
DefineConsoleType( TypeShader, GFXShader * )
|
DefineConsoleType( TypeShader, GFXShader * )
|
||||||
|
|
||||||
|
DefineConsoleType(TypeSimObjectPtr, SimObject*)
|
||||||
|
|
||||||
/// A persistent reference to an object. This reference indirectly goes
|
/// A persistent reference to an object. This reference indirectly goes
|
||||||
/// through the referenced object's persistent ID.
|
/// through the referenced object's persistent ID.
|
||||||
DefineConsoleType( TypeSimPersistId, SimPersistID* )
|
DefineConsoleType( TypeSimPersistId, SimPersistID* )
|
||||||
|
|
|
||||||
|
|
@ -606,6 +606,10 @@ class SimObject: public ConsoleObject, public TamlCallbacks
|
||||||
/// Called when the object's name is changed.
|
/// Called when the object's name is changed.
|
||||||
virtual void onNameChange(const char *name);
|
virtual void onNameChange(const char *name);
|
||||||
|
|
||||||
|
/// Called when the adding of the object to the sim is complete, all sub-objects have been processed as well
|
||||||
|
// This is a special-case function that only really gets used with Entities/BehaviorObjects.
|
||||||
|
virtual void onPostAdd() {}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Specifically, these are called by setDataField
|
/// Specifically, these are called by setDataField
|
||||||
/// when a static or dynamic field is modified, see
|
/// when a static or dynamic field is modified, see
|
||||||
|
|
|
||||||
|
|
@ -1015,7 +1015,7 @@ void GFXDrawUtil::_drawSolidPolyhedron( const GFXStateBlockDesc &desc, const Any
|
||||||
|
|
||||||
// Allocate a temp buffer for the face indices.
|
// Allocate a temp buffer for the face indices.
|
||||||
|
|
||||||
const U32 numIndices = poly.getNumEdges() * 2;
|
const U32 numIndices = poly.getNumEdges() * 3;
|
||||||
const U32 numPlanes = poly.getNumPlanes();
|
const U32 numPlanes = poly.getNumPlanes();
|
||||||
|
|
||||||
GFXPrimitiveBufferHandle prims( mDevice, numIndices, 0, GFXBufferTypeVolatile );
|
GFXPrimitiveBufferHandle prims( mDevice, numIndices, 0, GFXBufferTypeVolatile );
|
||||||
|
|
|
||||||
|
|
@ -57,53 +57,31 @@ void GFXGLCardProfiler::setupCardCapabilities()
|
||||||
GLint maxTexSize;
|
GLint maxTexSize;
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
|
||||||
|
|
||||||
const char* versionString = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
|
||||||
F32 glVersion = dAtof(versionString);
|
|
||||||
|
|
||||||
// OpenGL doesn't have separate maximum width/height.
|
// OpenGL doesn't have separate maximum width/height.
|
||||||
setCapability("maxTextureWidth", maxTexSize);
|
setCapability("maxTextureWidth", maxTexSize);
|
||||||
setCapability("maxTextureHeight", maxTexSize);
|
setCapability("maxTextureHeight", maxTexSize);
|
||||||
setCapability("maxTextureSize", maxTexSize);
|
setCapability("maxTextureSize", maxTexSize);
|
||||||
|
|
||||||
// If extensions haven't been inited, we're in trouble here.
|
|
||||||
bool suppVBO = (gglHasExtension(ARB_vertex_buffer_object) || glVersion >= 1.499f);
|
|
||||||
setCapability("GL::suppVertexBufferObject", suppVBO);
|
|
||||||
|
|
||||||
// check if render to texture supported is available
|
|
||||||
bool suppRTT = gglHasExtension(EXT_framebuffer_object);
|
|
||||||
setCapability("GL::suppRenderTexture", suppRTT);
|
|
||||||
|
|
||||||
bool suppBlit = gglHasExtension(EXT_framebuffer_blit);
|
|
||||||
setCapability("GL::suppRTBlit", suppBlit);
|
|
||||||
|
|
||||||
bool suppFloatTex = gglHasExtension(ARB_texture_float);
|
|
||||||
setCapability("GL::suppFloatTexture", suppFloatTex);
|
|
||||||
|
|
||||||
// Check for anisotropic filtering support.
|
// Check for anisotropic filtering support.
|
||||||
bool suppAnisotropic = gglHasExtension( EXT_texture_filter_anisotropic );
|
setCapability("GL_EXT_texture_filter_anisotropic", gglHasExtension(EXT_texture_filter_anisotropic));
|
||||||
setCapability( "GL::suppAnisotropic", suppAnisotropic );
|
|
||||||
|
|
||||||
// check to see if we have the fragment shader extension or the gl version is high enough for glsl to be core
|
// Check for buffer storage
|
||||||
// also check to see if the language version is high enough
|
setCapability("GL_ARB_buffer_storage", gglHasExtension(ARB_buffer_storage));
|
||||||
F32 glslVersion = dAtof(reinterpret_cast<const char*>(glGetString( GL_SHADING_LANGUAGE_VERSION)));
|
|
||||||
bool suppSPU = (gglHasExtension(ARB_fragment_shader) || glVersion >= 1.999f) && glslVersion >= 1.0999;
|
|
||||||
setCapability("GL::suppFragmentShader", suppSPU);
|
|
||||||
|
|
||||||
bool suppAppleFence = gglHasExtension(APPLE_fence);
|
// Check for shader model 5.0
|
||||||
setCapability("GL::APPLE::suppFence", suppAppleFence);
|
setCapability("GL_ARB_gpu_shader5", gglHasExtension(ARB_gpu_shader5));
|
||||||
|
|
||||||
// When enabled, call glGenerateMipmapEXT() to generate mipmaps instead of relying on GL_GENERATE_MIPMAP
|
// Check for texture storage
|
||||||
setCapability("GL::Workaround::needsExplicitGenerateMipmap", false);
|
setCapability("GL_ARB_texture_storage", gglHasExtension(ARB_texture_storage));
|
||||||
// When enabled, binds and unbinds a texture target before doing the depth buffer copy. Failure to do
|
|
||||||
// so will cause a hard freeze on Mac OS 10.4 with a Radeon X1600
|
|
||||||
setCapability("GL::Workaround::X1600DepthBufferCopy", false);
|
|
||||||
// When enabled, does not copy the last column and row of the depth buffer in a depth buffer copy. Failure
|
|
||||||
// to do so will cause a kernel panic on Mac OS 10.5(.1) with a Radeon HD 2600 (fixed in 10.5.2)
|
|
||||||
setCapability("GL::Workaround::HD2600DepthBufferCopy", false);
|
|
||||||
|
|
||||||
// Certain Intel drivers have a divide by 0 crash if mipmaps are specified with
|
// Check for sampler objects
|
||||||
// glTexSubImage2D.
|
setCapability("GL_ARB_sampler_objects", gglHasExtension(ARB_sampler_objects));
|
||||||
setCapability("GL::Workaround::noManualMips", false);
|
|
||||||
|
// Check for copy image support
|
||||||
|
setCapability("GL_ARB_copy_image", gglHasExtension(ARB_copy_image));
|
||||||
|
|
||||||
|
// Check for vertex attrib binding
|
||||||
|
setCapability("GL_ARB_vertex_attrib_binding", gglHasExtension(ARB_vertex_attrib_binding));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GFXGLCardProfiler::_queryCardCap(const String& query, U32& foundResult)
|
bool GFXGLCardProfiler::_queryCardCap(const String& query, U32& foundResult)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public:
|
||||||
|
|
||||||
void init(U32 start, U32 end)
|
void init(U32 start, U32 end)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLQueryFence_issue);
|
||||||
mStart = start;
|
mStart = start;
|
||||||
mEnd = end;
|
mEnd = end;
|
||||||
mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
|
@ -36,6 +37,7 @@ public:
|
||||||
|
|
||||||
void wait()
|
void wait()
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLQueryFence_block);
|
||||||
GLbitfield waitFlags = 0;
|
GLbitfield waitFlags = 0;
|
||||||
GLuint64 waitDuration = 0;
|
GLuint64 waitDuration = 0;
|
||||||
while( 1 )
|
while( 1 )
|
||||||
|
|
@ -158,7 +160,7 @@ public:
|
||||||
const U32 cSizeInMB = 10;
|
const U32 cSizeInMB = 10;
|
||||||
mBufferSize = (cSizeInMB << 20);
|
mBufferSize = (cSizeInMB << 20);
|
||||||
|
|
||||||
if( gglHasExtension(ARB_buffer_storage) )
|
if( GFXGL->mCapabilities.bufferStorage )
|
||||||
{
|
{
|
||||||
const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
|
const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
|
||||||
glBufferStorage(mBinding, mBufferSize, NULL, flags);
|
glBufferStorage(mBinding, mBufferSize, NULL, flags);
|
||||||
|
|
@ -198,7 +200,7 @@ public:
|
||||||
|
|
||||||
outOffset = mBufferFreePos;
|
outOffset = mBufferFreePos;
|
||||||
|
|
||||||
if( gglHasExtension(ARB_buffer_storage) )
|
if( GFXGL->mCapabilities.bufferStorage )
|
||||||
{
|
{
|
||||||
outPtr = (U8*)(mBufferPtr) + mBufferFreePos;
|
outPtr = (U8*)(mBufferPtr) + mBufferFreePos;
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +229,7 @@ public:
|
||||||
|
|
||||||
void unlock()
|
void unlock()
|
||||||
{
|
{
|
||||||
if( gglHasExtension(ARB_buffer_storage) )
|
if( GFXGL->mCapabilities.bufferStorage )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,10 +140,18 @@ void GFXGLDevice::initGLState()
|
||||||
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
// [JTH 5/6/2016] GLSL 1.50 is really SM 4.0
|
||||||
// Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting to run.
|
// Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting to run.
|
||||||
mPixelShaderVersion = 3.0;
|
mPixelShaderVersion = 3.0;
|
||||||
|
|
||||||
mSupportsAnisotropic = mCardProfiler->queryProfile( "GL::suppAnisotropic" );
|
// Set capability extensions.
|
||||||
|
mCapabilities.anisotropicFiltering = mCardProfiler->queryProfile("GL_EXT_texture_filter_anisotropic");
|
||||||
|
mCapabilities.bufferStorage = mCardProfiler->queryProfile("GL_ARB_buffer_storage");
|
||||||
|
mCapabilities.shaderModel5 = mCardProfiler->queryProfile("GL_ARB_gpu_shader5");
|
||||||
|
mCapabilities.textureStorage = mCardProfiler->queryProfile("GL_ARB_texture_storage");
|
||||||
|
mCapabilities.samplerObjects = mCardProfiler->queryProfile("GL_ARB_sampler_objects");
|
||||||
|
mCapabilities.copyImage = mCardProfiler->queryProfile("GL_ARB_copy_image");
|
||||||
|
mCapabilities.vertexAttributeBinding = mCardProfiler->queryProfile("GL_ARB_vertex_attrib_binding");
|
||||||
|
|
||||||
String vendorStr = (const char*)glGetString( GL_VENDOR );
|
String vendorStr = (const char*)glGetString( GL_VENDOR );
|
||||||
if( vendorStr.find("NVIDIA", 0, String::NoCase | String::Left) != String::NPos)
|
if( vendorStr.find("NVIDIA", 0, String::NoCase | String::Left) != String::NPos)
|
||||||
|
|
@ -216,6 +224,9 @@ GFXGLDevice::GFXGLDevice(U32 adapterIndex) :
|
||||||
mCurrentVB_Divisor[i] = 0;
|
mCurrentVB_Divisor[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initiailize capabilities to false.
|
||||||
|
memset(&mCapabilities, 0, sizeof(GLCapabilities));
|
||||||
|
|
||||||
loadGLCore();
|
loadGLCore();
|
||||||
|
|
||||||
GFXGLEnumTranslate::init();
|
GFXGLEnumTranslate::init();
|
||||||
|
|
@ -325,6 +336,7 @@ void GFXGLDevice::resurrect()
|
||||||
|
|
||||||
GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize)
|
GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLDevice_findVBPool);
|
||||||
for(U32 i = 0; i < mVolatileVBs.size(); i++)
|
for(U32 i = 0; i < mVolatileVBs.size(); i++)
|
||||||
if ( mVolatileVBs[i]->mNumVerts >= numVerts &&
|
if ( mVolatileVBs[i]->mNumVerts >= numVerts &&
|
||||||
mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) &&
|
mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) &&
|
||||||
|
|
@ -333,6 +345,7 @@ GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexForma
|
||||||
return mVolatileVBs[i];
|
return mVolatileVBs[i];
|
||||||
|
|
||||||
// No existing VB, so create one
|
// No existing VB, so create one
|
||||||
|
PROFILE_SCOPE(GFXGLDevice_createVBPool);
|
||||||
StrongRefPtr<GFXGLVertexBuffer> buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile));
|
StrongRefPtr<GFXGLVertexBuffer> buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile));
|
||||||
buf->registerResourceWithDevice(this);
|
buf->registerResourceWithDevice(this);
|
||||||
mVolatileVBs.push_back(buf);
|
mVolatileVBs.push_back(buf);
|
||||||
|
|
@ -358,6 +371,7 @@ GFXVertexBuffer *GFXGLDevice::allocVertexBuffer( U32 numVerts,
|
||||||
GFXBufferType bufferType,
|
GFXBufferType bufferType,
|
||||||
void* data )
|
void* data )
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLDevice_allocVertexBuffer);
|
||||||
if(bufferType == GFXBufferTypeVolatile)
|
if(bufferType == GFXBufferTypeVolatile)
|
||||||
return findVolatileVBO(numVerts, vertexFormat, vertSize);
|
return findVolatileVBO(numVerts, vertexFormat, vertSize);
|
||||||
|
|
||||||
|
|
@ -523,6 +537,7 @@ inline GLsizei GFXGLDevice::primCountToIndexCount(GFXPrimitiveType primType, U32
|
||||||
|
|
||||||
GFXVertexDecl* GFXGLDevice::allocVertexDecl( const GFXVertexFormat *vertexFormat )
|
GFXVertexDecl* GFXGLDevice::allocVertexDecl( const GFXVertexFormat *vertexFormat )
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLDevice_allocVertexDecl);
|
||||||
typedef Map<void*, GFXGLVertexDecl> GFXGLVertexDeclMap;
|
typedef Map<void*, GFXGLVertexDecl> GFXGLVertexDeclMap;
|
||||||
static GFXGLVertexDeclMap declMap;
|
static GFXGLVertexDeclMap declMap;
|
||||||
GFXGLVertexDeclMap::Iterator itr = declMap.find( (void*)vertexFormat->getDescription().c_str() ); // description string are interned, safe to use c_str()
|
GFXGLVertexDeclMap::Iterator itr = declMap.find( (void*)vertexFormat->getDescription().c_str() ); // description string are interned, safe to use c_str()
|
||||||
|
|
@ -855,6 +870,7 @@ void GFXGLDevice::setShader(GFXShader *shader, bool force)
|
||||||
|
|
||||||
void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
|
void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLDevice_setShaderConstBufferInternal);
|
||||||
static_cast<GFXGLShaderConstBuffer*>(buffer)->activate();
|
static_cast<GFXGLShaderConstBuffer*>(buffer)->activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,18 @@ class GFXGLVertexDecl;
|
||||||
class GFXGLDevice : public GFXDevice
|
class GFXGLDevice : public GFXDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct GLCapabilities
|
||||||
|
{
|
||||||
|
bool anisotropicFiltering;
|
||||||
|
bool bufferStorage;
|
||||||
|
bool shaderModel5;
|
||||||
|
bool textureStorage;
|
||||||
|
bool samplerObjects;
|
||||||
|
bool copyImage;
|
||||||
|
bool vertexAttributeBinding;
|
||||||
|
};
|
||||||
|
GLCapabilities mCapabilities;
|
||||||
|
|
||||||
void zombify();
|
void zombify();
|
||||||
void resurrect();
|
void resurrect();
|
||||||
GFXGLDevice(U32 adapterIndex);
|
GFXGLDevice(U32 adapterIndex);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
#include "gfx/gl/gfxGLShader.h"
|
#include "gfx/gl/gfxGLShader.h"
|
||||||
#include "gfx/gl/gfxGLVertexAttribLocation.h"
|
#include "gfx/gl/gfxGLVertexAttribLocation.h"
|
||||||
|
#include "gfx/gl/gfxGLDevice.h"
|
||||||
|
|
||||||
#include "core/frameAllocator.h"
|
#include "core/frameAllocator.h"
|
||||||
#include "core/stream/fileStream.h"
|
#include "core/stream/fileStream.h"
|
||||||
|
|
@ -344,6 +345,7 @@ void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* ma
|
||||||
|
|
||||||
void GFXGLShaderConstBuffer::activate()
|
void GFXGLShaderConstBuffer::activate()
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLShaderConstBuffer_activate);
|
||||||
mShader->setConstantsFromBuffer(this);
|
mShader->setConstantsFromBuffer(this);
|
||||||
mWasLost = false;
|
mWasLost = false;
|
||||||
}
|
}
|
||||||
|
|
@ -394,6 +396,7 @@ void GFXGLShader::clearShaders()
|
||||||
|
|
||||||
bool GFXGLShader::_init()
|
bool GFXGLShader::_init()
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLShader_Init);
|
||||||
// Don't initialize empty shaders.
|
// Don't initialize empty shaders.
|
||||||
if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() )
|
if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() )
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -956,7 +959,7 @@ bool GFXGLShader::_loadShaderFromStream( GLuint shader,
|
||||||
buffers.push_back( dStrdup( versionDecl ) );
|
buffers.push_back( dStrdup( versionDecl ) );
|
||||||
lengths.push_back( dStrlen( versionDecl ) );
|
lengths.push_back( dStrlen( versionDecl ) );
|
||||||
|
|
||||||
if(gglHasExtension(ARB_gpu_shader5))
|
if(GFXGL->mCapabilities.shaderModel5)
|
||||||
{
|
{
|
||||||
const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n";
|
const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n";
|
||||||
buffers.push_back( dStrdup( extension ) );
|
buffers.push_back( dStrdup( extension ) );
|
||||||
|
|
@ -1013,6 +1016,7 @@ bool GFXGLShader::initShader( const Torque::Path &file,
|
||||||
bool isVertex,
|
bool isVertex,
|
||||||
const Vector<GFXShaderMacro> ¯os )
|
const Vector<GFXShaderMacro> ¯os )
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLShader_CompileShader);
|
||||||
GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
|
GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
|
||||||
if(isVertex)
|
if(isVertex)
|
||||||
mVertexShader = activeShader;
|
mVertexShader = activeShader;
|
||||||
|
|
@ -1072,6 +1076,7 @@ bool GFXGLShader::initShader( const Torque::Path &file,
|
||||||
/// Returns our list of shader constants, the material can get this and just set the constants it knows about
|
/// Returns our list of shader constants, the material can get this and just set the constants it knows about
|
||||||
const Vector<GFXShaderConstDesc>& GFXGLShader::getShaderConstDesc() const
|
const Vector<GFXShaderConstDesc>& GFXGLShader::getShaderConstDesc() const
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLShader_GetShaderConstants);
|
||||||
return mConstants;
|
return mConstants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ GFXGLStateBlock::GFXGLStateBlock(const GFXStateBlockDesc& desc) :
|
||||||
mDesc(desc),
|
mDesc(desc),
|
||||||
mCachedHashValue(desc.getHashValue())
|
mCachedHashValue(desc.getHashValue())
|
||||||
{
|
{
|
||||||
if( !gglHasExtension(ARB_sampler_objects) )
|
if( !GFXGL->mCapabilities.samplerObjects )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static Map<GFXSamplerStateDesc, U32> mSamplersMap;
|
static Map<GFXSamplerStateDesc, U32> mSamplersMap;
|
||||||
|
|
@ -88,6 +88,7 @@ const GFXStateBlockDesc& GFXGLStateBlock::getDesc() const
|
||||||
/// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states.
|
/// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states.
|
||||||
void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
|
void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLStateBlock_Activate);
|
||||||
// Big scary warning copied from Apple docs
|
// Big scary warning copied from Apple docs
|
||||||
// http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_performance/chapter_13_section_2.html#//apple_ref/doc/uid/TP40001987-CH213-SW12
|
// http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_performance/chapter_13_section_2.html#//apple_ref/doc/uid/TP40001987-CH213-SW12
|
||||||
// Don't set a state that's already set. Once a feature is enabled, it does not need to be enabled again.
|
// Don't set a state that's already set. Once a feature is enabled, it does not need to be enabled again.
|
||||||
|
|
@ -165,7 +166,7 @@ void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
|
||||||
#undef CHECK_TOGGLE_STATE
|
#undef CHECK_TOGGLE_STATE
|
||||||
|
|
||||||
//sampler objects
|
//sampler objects
|
||||||
if( gglHasExtension(ARB_sampler_objects) )
|
if( GFXGL->mCapabilities.samplerObjects )
|
||||||
{
|
{
|
||||||
for (U32 i = 0; i < getMin(getOwningDevice()->getNumSamplers(), (U32) TEXTURE_STAGE_COUNT); i++)
|
for (U32 i = 0; i < getMin(getOwningDevice()->getNumSamplers(), (U32) TEXTURE_STAGE_COUNT); i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
|
||||||
|
|
||||||
glTexParameteri(binding, GL_TEXTURE_MAX_LEVEL, retTex->mMipLevels-1 );
|
glTexParameteri(binding, GL_TEXTURE_MAX_LEVEL, retTex->mMipLevels-1 );
|
||||||
|
|
||||||
if( gglHasExtension(ARB_texture_storage) )
|
if( GFXGL->mCapabilities.textureStorage )
|
||||||
{
|
{
|
||||||
if(binding == GL_TEXTURE_2D)
|
if(binding == GL_TEXTURE_2D)
|
||||||
glTexStorage2D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height );
|
glTexStorage2D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height );
|
||||||
|
|
@ -234,6 +234,7 @@ static void _fastTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
|
||||||
|
|
||||||
if(pDL->getFormat() == GFXFormatR8G8B8A8 || pDL->getFormat() == GFXFormatR8G8B8X8)
|
if(pDL->getFormat() == GFXFormatR8G8B8A8 || pDL->getFormat() == GFXFormatR8G8B8X8)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(Swizzle32_Upload);
|
||||||
FrameAllocatorMarker mem;
|
FrameAllocatorMarker mem;
|
||||||
U8* pboMemory = (U8*)mem.alloc(bufSize);
|
U8* pboMemory = (U8*)mem.alloc(bufSize);
|
||||||
GFX->getDeviceSwizzle32()->ToBuffer(pboMemory, pDL->getBits(0), bufSize);
|
GFX->getDeviceSwizzle32()->ToBuffer(pboMemory, pDL->getBits(0), bufSize);
|
||||||
|
|
@ -241,6 +242,7 @@ static void _fastTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(SwizzleNull_Upload);
|
||||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, pDL->getBits(0) );
|
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, pDL->getBits(0) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,6 +264,7 @@ static void _slowTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
|
||||||
|
|
||||||
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLTextureManager_loadTexture);
|
||||||
GFXGLTextureObject *texture = static_cast<GFXGLTextureObject*>(aTexture);
|
GFXGLTextureObject *texture = static_cast<GFXGLTextureObject*>(aTexture);
|
||||||
|
|
||||||
AssertFatal(texture->getBinding() == GL_TEXTURE_1D || texture->getBinding() == GL_TEXTURE_2D,
|
AssertFatal(texture->getBinding() == GL_TEXTURE_1D || texture->getBinding() == GL_TEXTURE_2D,
|
||||||
|
|
@ -291,6 +294,8 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
|
||||||
|
|
||||||
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLTextureManager_loadTextureDDS);
|
||||||
|
|
||||||
AssertFatal(!(dds->mFormat == GFXFormatDXT2 || dds->mFormat == GFXFormatDXT4), "GFXGLTextureManager::_loadTexture - OpenGL does not support DXT2 or DXT4 compressed textures");
|
AssertFatal(!(dds->mFormat == GFXFormatDXT2 || dds->mFormat == GFXFormatDXT4), "GFXGLTextureManager::_loadTexture - OpenGL does not support DXT2 or DXT4 compressed textures");
|
||||||
GFXGLTextureObject* texture = static_cast<GFXGLTextureObject*>(aTexture);
|
GFXGLTextureObject* texture = static_cast<GFXGLTextureObject*>(aTexture);
|
||||||
|
|
||||||
|
|
@ -304,10 +309,11 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
||||||
glBindTexture(texture->getBinding(), texture->getHandle());
|
glBindTexture(texture->getBinding(), texture->getHandle());
|
||||||
texture->mFormat = dds->mFormat;
|
texture->mFormat = dds->mFormat;
|
||||||
U32 numMips = dds->mSurfaces[0]->mMips.size();
|
U32 numMips = dds->mSurfaces[0]->mMips.size();
|
||||||
if(GFX->getCardProfiler()->queryProfile("GL::Workaround::noManualMips"))
|
|
||||||
numMips = 1;
|
|
||||||
for(U32 i = 0; i < numMips; i++)
|
for(U32 i = 0; i < numMips; i++)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLTexMan_loadSurface);
|
||||||
|
|
||||||
if(isCompressedFormat(dds->mFormat))
|
if(isCompressedFormat(dds->mFormat))
|
||||||
{
|
{
|
||||||
if((!isPow2(dds->getWidth()) || !isPow2(dds->getHeight())) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
|
if((!isPow2(dds->getWidth()) || !isPow2(dds->getHeight())) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
|
||||||
|
|
@ -344,6 +350,7 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
||||||
|
|
||||||
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, void *raw)
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, void *raw)
|
||||||
{
|
{
|
||||||
|
PROFILE_SCOPE(GFXGLTextureManager_loadTextureRaw);
|
||||||
if(aTexture->getDepth() < 1)
|
if(aTexture->getDepth() < 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ void GFXGLTextureObject::unlock(U32 mipLevel)
|
||||||
if(!mLockedRect.bits)
|
if(!mLockedRect.bits)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// I know this is in unlock, but in GL we actually do our submission in unlock.
|
||||||
|
PROFILE_SCOPE(GFXGLTextureObject_lockRT);
|
||||||
|
|
||||||
PRESERVE_TEXTURE(mBinding);
|
PRESERVE_TEXTURE(mBinding);
|
||||||
glBindTexture(mBinding, mHandle);
|
glBindTexture(mBinding, mHandle);
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBuffer);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBuffer);
|
||||||
|
|
@ -175,6 +178,7 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
|
||||||
|
|
||||||
glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig);
|
glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig);
|
||||||
|
|
||||||
|
PROFILE_START(GFXGLTextureObject_copyToBmp_pixCopy);
|
||||||
for(int i = 0; i < srcPixelCount; ++i)
|
for(int i = 0; i < srcPixelCount; ++i)
|
||||||
{
|
{
|
||||||
dest[0] = orig[0];
|
dest[0] = orig[0];
|
||||||
|
|
@ -186,6 +190,7 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
|
||||||
orig += srcBytesPerPixel;
|
orig += srcBytesPerPixel;
|
||||||
dest += dstBytesPerPixel;
|
dest += dstBytesPerPixel;
|
||||||
}
|
}
|
||||||
|
PROFILE_END();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +216,7 @@ void GFXGLTextureObject::bind(U32 textureUnit)
|
||||||
glBindTexture(mBinding, mHandle);
|
glBindTexture(mBinding, mHandle);
|
||||||
GFXGL->getOpenglCache()->setCacheBindedTex(textureUnit, mBinding, mHandle);
|
GFXGL->getOpenglCache()->setCacheBindedTex(textureUnit, mBinding, mHandle);
|
||||||
|
|
||||||
if( gglHasExtension(ARB_sampler_objects) )
|
if(GFXGL->mCapabilities.samplerObjects)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GFXGLStateBlockRef sb = mGLDevice->getCurrentStateBlock();
|
GFXGLStateBlockRef sb = mGLDevice->getCurrentStateBlock();
|
||||||
|
|
@ -298,8 +303,8 @@ void GFXGLTextureObject::reloadFromCache()
|
||||||
else if(mBinding == GL_TEXTURE_1D)
|
else if(mBinding == GL_TEXTURE_1D)
|
||||||
glTexSubImage1D(mBinding, 0, 0, (mTextureSize.x > 1 ? mTextureSize.x : mTextureSize.y), GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], mZombieCache);
|
glTexSubImage1D(mBinding, 0, 0, (mTextureSize.x > 1 ? mTextureSize.x : mTextureSize.y), GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], mZombieCache);
|
||||||
|
|
||||||
if(GFX->getCardProfiler()->queryProfile("GL::Workaround::needsExplicitGenerateMipmap") && mMipLevels != 1)
|
if(mMipLevels != 1)
|
||||||
glGenerateMipmapEXT(mBinding);
|
glGenerateMipmap(mBinding);
|
||||||
|
|
||||||
delete[] mZombieCache;
|
delete[] mZombieCache;
|
||||||
mZombieCache = NULL;
|
mZombieCache = NULL;
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,7 @@ void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj)
|
||||||
AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
|
AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
|
||||||
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
|
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
|
||||||
|
|
||||||
if( gglHasExtension(ARB_copy_image) && mTargets[Color0]->isCompatible(glTexture) )
|
if( GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture) )
|
||||||
{
|
{
|
||||||
GLenum binding = mTargets[Color0]->getBinding();
|
GLenum binding = mTargets[Color0]->getBinding();
|
||||||
binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
|
binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ void GFXGLVertexBuffer::lock( U32 vertexStart, U32 vertexEnd, void **vertexPtr )
|
||||||
if( mBufferType == GFXBufferTypeVolatile )
|
if( mBufferType == GFXBufferTypeVolatile )
|
||||||
{
|
{
|
||||||
AssertFatal(vertexStart == 0, "");
|
AssertFatal(vertexStart == 0, "");
|
||||||
if( gglHasExtension(ARB_vertex_attrib_binding) )
|
if( GFXGL->mCapabilities.vertexAttributeBinding )
|
||||||
{
|
{
|
||||||
getCircularVolatileVertexBuffer()->lock( mNumVerts * mVertexSize, 0, mBufferOffset, *vertexPtr );
|
getCircularVolatileVertexBuffer()->lock( mNumVerts * mVertexSize, 0, mBufferOffset, *vertexPtr );
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ void GFXGLVertexBuffer::prepare()
|
||||||
|
|
||||||
void GFXGLVertexBuffer::prepare(U32 stream, U32 divisor)
|
void GFXGLVertexBuffer::prepare(U32 stream, U32 divisor)
|
||||||
{
|
{
|
||||||
if( gglHasExtension(ARB_vertex_attrib_binding) )
|
if( GFXGL->mCapabilities.vertexAttributeBinding )
|
||||||
{
|
{
|
||||||
glBindVertexBuffer( stream, mBuffer, mBufferOffset, mVertexSize );
|
glBindVertexBuffer( stream, mBuffer, mBufferOffset, mVertexSize );
|
||||||
glVertexBindingDivisor( stream, divisor );
|
glVertexBindingDivisor( stream, divisor );
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ void GFXGLVertexDecl::init(const GFXVertexFormat *format)
|
||||||
void GFXGLVertexDecl::prepareVertexFormat() const
|
void GFXGLVertexDecl::prepareVertexFormat() const
|
||||||
{
|
{
|
||||||
AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
|
AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
|
||||||
if( gglHasExtension(ARB_vertex_attrib_binding) )
|
if( GFXGL->mCapabilities.vertexAttributeBinding )
|
||||||
{
|
{
|
||||||
for ( U32 i=0; i < glVerticesFormat.size(); i++ )
|
for ( U32 i=0; i < glVerticesFormat.size(); i++ )
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +36,7 @@ void GFXGLVertexDecl::prepareBuffer_old(U32 stream, GLint mBuffer, GLint mDiviso
|
||||||
PROFILE_SCOPE(GFXGLVertexDecl_prepare);
|
PROFILE_SCOPE(GFXGLVertexDecl_prepare);
|
||||||
AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
|
AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
|
||||||
|
|
||||||
if( gglHasExtension(ARB_vertex_attrib_binding) )
|
if( GFXGL->mCapabilities.vertexAttributeBinding )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Bind the buffer...
|
// Bind the buffer...
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ void GFXGLWindowTarget::resolveTo(GFXTextureObject* obj)
|
||||||
AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
|
AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
|
||||||
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
|
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
|
||||||
|
|
||||||
if( gglHasExtension(ARB_copy_image) )
|
if( GFXGL->mCapabilities.copyImage )
|
||||||
{
|
{
|
||||||
if(mBackBufferColorTex.getWidth() == glTexture->getWidth()
|
if(mBackBufferColorTex.getWidth() == glTexture->getWidth()
|
||||||
&& mBackBufferColorTex.getHeight() == glTexture->getHeight()
|
&& mBackBufferColorTex.getHeight() == glTexture->getHeight()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
#include "gui/editor/editorFunctions.h"
|
#include "gui/editor/editorFunctions.h"
|
||||||
#endif
|
#endif
|
||||||
#include "console/engineAPI.h"
|
#include "console/engineAPI.h"
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
#include "T3D/entity.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
|
IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
|
||||||
|
|
||||||
|
|
@ -486,6 +488,14 @@ void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf)
|
||||||
{
|
{
|
||||||
FrameAllocatorMarker txtAlloc;
|
FrameAllocatorMarker txtAlloc;
|
||||||
|
|
||||||
|
//if we're doing the special case of forcing the item text, just skip the rest of this junk
|
||||||
|
if (mState.test(ForceItemName))
|
||||||
|
{
|
||||||
|
StringTableEntry text = (mScriptInfo.mText) ? mScriptInfo.mText : StringTable->EmptyString();
|
||||||
|
dStrncpy(buf, text, bufLen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( mState.test( InspectorData ) )
|
if( mState.test( InspectorData ) )
|
||||||
{
|
{
|
||||||
SimObject *pObject = getObject();
|
SimObject *pObject = getObject();
|
||||||
|
|
@ -637,6 +647,20 @@ void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf)
|
||||||
|
|
||||||
bool GuiTreeViewCtrl::Item::isParent() const
|
bool GuiTreeViewCtrl::Item::isParent() const
|
||||||
{
|
{
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
//We might have a special case with entities
|
||||||
|
//So if our entity either has children, or has some component with the EditorInspect interface, we return true
|
||||||
|
if (mInspectorInfo.mObject)
|
||||||
|
{
|
||||||
|
Entity* e = dynamic_cast<Entity*>(mInspectorInfo.mObject.getObject());
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
if (e->size() > 0 || e->getComponentCount() != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if(mState.test(VirtualParent))
|
if(mState.test(VirtualParent))
|
||||||
{
|
{
|
||||||
if( !isInspectorData() )
|
if( !isInspectorData() )
|
||||||
|
|
@ -1518,6 +1542,11 @@ bool GuiTreeViewCtrl::isValidDragTarget( Item* item )
|
||||||
{
|
{
|
||||||
bool isValid = true;
|
bool isValid = true;
|
||||||
|
|
||||||
|
// First, check if we're just going to override this from manually setting the ForceAllowDrag flag
|
||||||
|
// If that's set, we're assuming special circumstances and will just let it go on it's way
|
||||||
|
if (item->isDragTargetAllowed())
|
||||||
|
return true;
|
||||||
|
|
||||||
// If this is inspector data, first make sure the item accepts all
|
// If this is inspector data, first make sure the item accepts all
|
||||||
// selected objects as children. This prevents bad surprises when
|
// selected objects as children. This prevents bad surprises when
|
||||||
// certain SimSet subclasses reject children and start shoving them
|
// certain SimSet subclasses reject children and start shoving them
|
||||||
|
|
@ -3462,6 +3491,11 @@ void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event)
|
||||||
if (mSelectedItems.size() == 0)
|
if (mSelectedItems.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//Check through to make sure all attempted dragged items even allow it
|
||||||
|
for (U32 i = 0; i < mSelectedItems.size(); i++)
|
||||||
|
if (!mSelectedItems[i]->isDragAllowed())
|
||||||
|
return;
|
||||||
|
|
||||||
// Give us a little delta before we actually start a mouse drag so that
|
// Give us a little delta before we actually start a mouse drag so that
|
||||||
// if the user moves the mouse a little while clicking, he/she does not
|
// if the user moves the mouse a little while clicking, he/she does not
|
||||||
// accidentally trigger a drag.
|
// accidentally trigger a drag.
|
||||||
|
|
@ -3756,6 +3790,28 @@ void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
|
||||||
if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
|
if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
|
||||||
onVirtualParentExpand(item);
|
onVirtualParentExpand(item);
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
//Slightly hacky, but I'm not sure of a better setup until we get major update to the editors
|
||||||
|
//We check if our object is an entity, and if it is, we call a 'onInspect' function.
|
||||||
|
//This function is pretty much a special notifier to the entity so if it has any behaviors that do special
|
||||||
|
//stuff in the editor, it can fire that up
|
||||||
|
if (item->isInspectorData())
|
||||||
|
{
|
||||||
|
Entity* e = dynamic_cast<Entity*>(item->getObject());
|
||||||
|
//if (item->mScriptInfo.mText != StringTable->insert("Components"))
|
||||||
|
{
|
||||||
|
Entity* e = dynamic_cast<Entity*>(item->getObject());
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
if (item->isExpanded())
|
||||||
|
e->onInspect();
|
||||||
|
else
|
||||||
|
e->onEndInspect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mFlags.set( RebuildVisible );
|
mFlags.set( RebuildVisible );
|
||||||
scrollVisible(item);
|
scrollVisible(item);
|
||||||
}
|
}
|
||||||
|
|
@ -4492,6 +4548,13 @@ bool GuiTreeViewCtrl::objectSearch( const SimObject *object, Item **item )
|
||||||
if ( !pItem )
|
if ( !pItem )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
//A bit hackish, but we make a special exception here for items that are named 'Components', as they're merely
|
||||||
|
//virtual parents to act as a container to an Entity's components
|
||||||
|
if (pItem->mScriptInfo.mText == StringTable->insert("Components"))
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
SimObject *pObj = pItem->getObject();
|
SimObject *pObj = pItem->getObject();
|
||||||
|
|
||||||
if ( pObj && pObj == object )
|
if ( pObj && pObj == object )
|
||||||
|
|
@ -4555,6 +4618,11 @@ bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
|
||||||
|
|
||||||
// Go through our items and purge those that have disappeared from
|
// Go through our items and purge those that have disappeared from
|
||||||
// the set.
|
// the set.
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
//Entities will be a special case here, if we're an entity, skip this step
|
||||||
|
if (dynamic_cast<Entity*>(srcObj))
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
for( Item* ptr = item->mChild; ptr != NULL; )
|
for( Item* ptr = item->mChild; ptr != NULL; )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,10 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
|
||||||
ShowClassName = BIT( 11 ),
|
ShowClassName = BIT( 11 ),
|
||||||
ShowObjectName = BIT( 12 ),
|
ShowObjectName = BIT( 12 ),
|
||||||
ShowInternalName = BIT( 13 ),
|
ShowInternalName = BIT( 13 ),
|
||||||
ShowClassNameForUnnamed = BIT( 14 )
|
ShowClassNameForUnnamed = BIT( 14 ),
|
||||||
|
ForceItemName = BIT(15),
|
||||||
|
ForceDragTarget = BIT(16),
|
||||||
|
DenyDrag = BIT(17),
|
||||||
};
|
};
|
||||||
|
|
||||||
GuiTreeViewCtrl* mParentControl;
|
GuiTreeViewCtrl* mParentControl;
|
||||||
|
|
@ -169,6 +172,14 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
|
||||||
/// or false if it's just an item.
|
/// or false if it's just an item.
|
||||||
bool isInspectorData() const { return mState.test(InspectorData); };
|
bool isInspectorData() const { return mState.test(InspectorData); };
|
||||||
|
|
||||||
|
/// Returns true if we've been manually set to allow dragging overrides.
|
||||||
|
/// As it's a manually set flag, by default it is false.
|
||||||
|
bool isDragTargetAllowed() const { return mState.test(ForceDragTarget); };
|
||||||
|
|
||||||
|
/// Returns true if we've been manually set to allow dragging overrides.
|
||||||
|
/// As it's a manually set flag, by default it is false.
|
||||||
|
bool isDragAllowed() const { return !mState.test(DenyDrag); };
|
||||||
|
|
||||||
/// Returns true if we should show the expand art
|
/// Returns true if we should show the expand art
|
||||||
/// and make the item interact with the mouse as if
|
/// and make the item interact with the mouse as if
|
||||||
/// it were a parent.
|
/// it were a parent.
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
#include "core/util/tSignal.h"
|
#include "core/util/tSignal.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "component/interfaces/IProcessInput.h"
|
#include "platform/input/IProcessInput.h"
|
||||||
#include "windowManager/platformWindowMgr.h"
|
#include "windowManager/platformWindowMgr.h"
|
||||||
#include "gfx/gfxFence.h"
|
#include "gfx/gfxFence.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,11 @@
|
||||||
#include "gui/containers/guiScrollCtrl.h"
|
#include "gui/containers/guiScrollCtrl.h"
|
||||||
#include "gui/editor/inspector/customField.h"
|
#include "gui/editor/inspector/customField.h"
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
#include "gui/editor/inspector/entityGroup.h"
|
||||||
|
#include "gui/editor/inspector/mountingGroup.h"
|
||||||
|
#include "gui/editor/inspector/componentGroup.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
IMPLEMENT_CONOBJECT(GuiInspector);
|
IMPLEMENT_CONOBJECT(GuiInspector);
|
||||||
|
|
||||||
|
|
@ -584,6 +589,43 @@ void GuiInspector::refresh()
|
||||||
mGroups.push_back(general);
|
mGroups.push_back(general);
|
||||||
addObject(general);
|
addObject(general);
|
||||||
|
|
||||||
|
#ifdef TORQUE_EXPERIMENTAL_EC
|
||||||
|
//Entity inspector group
|
||||||
|
if (mTargets.first()->getClassRep()->isSubclassOf("Entity"))
|
||||||
|
{
|
||||||
|
GuiInspectorEntityGroup *components = new GuiInspectorEntityGroup("Components", this);
|
||||||
|
if (components != NULL)
|
||||||
|
{
|
||||||
|
components->registerObject();
|
||||||
|
mGroups.push_back(components);
|
||||||
|
addObject(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mounting group override
|
||||||
|
GuiInspectorGroup *mounting = new GuiInspectorMountingGroup("Mounting", this);
|
||||||
|
if (mounting != NULL)
|
||||||
|
{
|
||||||
|
mounting->registerObject();
|
||||||
|
mGroups.push_back(mounting);
|
||||||
|
addObject(mounting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTargets.first()->getClassRep()->isSubclassOf("Component"))
|
||||||
|
{
|
||||||
|
//Build the component field groups as the component describes it
|
||||||
|
Component* comp = dynamic_cast<Component*>(mTargets.first().getPointer());
|
||||||
|
|
||||||
|
if (comp->getComponentFieldCount() > 0)
|
||||||
|
{
|
||||||
|
GuiInspectorComponentGroup *compGroup = new GuiInspectorComponentGroup("Component Fields", this);
|
||||||
|
compGroup->registerObject();
|
||||||
|
mGroups.push_back(compGroup);
|
||||||
|
addObject(compGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Create the inspector groups for static fields.
|
// Create the inspector groups for static fields.
|
||||||
|
|
||||||
for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )
|
for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )
|
||||||
|
|
|
||||||
252
Engine/source/gui/editor/inspector/componentGroup.cpp
Normal file
252
Engine/source/gui/editor/inspector/componentGroup.cpp
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "gui/buttons/guiIconButtonCtrl.h"
|
||||||
|
#include "gui/editor/guiInspector.h"
|
||||||
|
#include "gui/editor/inspector/componentGroup.h"
|
||||||
|
#include "core/strings/stringUnit.h"
|
||||||
|
#include "T3D/components/component.h"
|
||||||
|
#include "gui/editor/inspector/field.h"
|
||||||
|
|
||||||
|
#include "console/engineAPI.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(GuiInspectorComponentGroup);
|
||||||
|
|
||||||
|
ConsoleDocClass(GuiInspectorComponentGroup,
|
||||||
|
"@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
|
||||||
|
"of regular persistent fields.\n\n"
|
||||||
|
"Editor use only.\n\n"
|
||||||
|
"@internal"
|
||||||
|
);
|
||||||
|
|
||||||
|
GuiInspectorComponentGroup::GuiInspectorComponentGroup(StringTableEntry groupName, SimObjectPtr<GuiInspector> parent)
|
||||||
|
: GuiInspectorGroup(groupName, parent)
|
||||||
|
{
|
||||||
|
/*mNeedScroll=false;*/
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GuiInspectorComponentGroup::onAdd()
|
||||||
|
{
|
||||||
|
if (!Parent::onAdd())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// GuiInspectorComponentGroup - add custom controls
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool GuiInspectorComponentGroup::createContent()
|
||||||
|
{
|
||||||
|
if(!Parent::createContent())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Con::evaluatef("%d.stack = %d;", this->getId(), mStack->getId());
|
||||||
|
|
||||||
|
Con::executef(this, "createContent");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// GuiInspectorComponentGroup - inspectGroup override
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool GuiInspectorComponentGroup::inspectGroup()
|
||||||
|
{
|
||||||
|
// We can't inspect a group without a target!
|
||||||
|
if (!mParent || !mParent->getNumInspectObjects())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
clearFields();
|
||||||
|
|
||||||
|
// to prevent crazy resizing, we'll just freeze our stack for a sec..
|
||||||
|
mStack->freeze(true);
|
||||||
|
|
||||||
|
bool bNoGroup = false;
|
||||||
|
|
||||||
|
bool bNewItems = false;
|
||||||
|
bool bMakingArray = false;
|
||||||
|
GuiStackControl *pArrayStack = NULL;
|
||||||
|
GuiRolloutCtrl *pArrayRollout = NULL;
|
||||||
|
bool bGrabItems = false;
|
||||||
|
|
||||||
|
Component* comp = dynamic_cast<Component*>(getInspector()->getInspectObject(0));
|
||||||
|
|
||||||
|
//if this isn't a component, what are we even doing here?
|
||||||
|
if (!comp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (U32 i = 0; i < comp->getComponentFieldCount(); i++)
|
||||||
|
{
|
||||||
|
ComponentField* field = comp->getComponentField(i);
|
||||||
|
|
||||||
|
bNewItems = true;
|
||||||
|
|
||||||
|
GuiInspectorField *fieldGui = constructField(field->mFieldType);
|
||||||
|
if (fieldGui == NULL)
|
||||||
|
fieldGui = new GuiInspectorField();
|
||||||
|
|
||||||
|
fieldGui->init(mParent, this);
|
||||||
|
|
||||||
|
AbstractClassRep::Field *refField;
|
||||||
|
|
||||||
|
//check dynamics
|
||||||
|
SimFieldDictionary* fieldDictionary = comp->getFieldDictionary();
|
||||||
|
SimFieldDictionaryIterator itr(fieldDictionary);
|
||||||
|
|
||||||
|
while (*itr)
|
||||||
|
{
|
||||||
|
SimFieldDictionary::Entry* entry = *itr;
|
||||||
|
if (entry->slotName == field->mFieldName)
|
||||||
|
{
|
||||||
|
AbstractClassRep::Field f;
|
||||||
|
f.pFieldname = StringTable->insert(field->mFieldName);
|
||||||
|
|
||||||
|
if (field->mFieldDescription)
|
||||||
|
f.pFieldDocs = field->mFieldDescription;
|
||||||
|
|
||||||
|
f.type = field->mFieldType;
|
||||||
|
f.offset = -1;
|
||||||
|
f.elementCount = 1;
|
||||||
|
f.validator = NULL;
|
||||||
|
f.flag = 0; //change to be the component type
|
||||||
|
|
||||||
|
f.setDataFn = &defaultProtectedSetFn;
|
||||||
|
f.getDataFn = &defaultProtectedGetFn;
|
||||||
|
f.writeDataFn = &defaultProtectedWriteFn;
|
||||||
|
|
||||||
|
f.pFieldDocs = field->mFieldDescription;
|
||||||
|
|
||||||
|
f.pGroupname = "Component Fields";
|
||||||
|
|
||||||
|
ConsoleBaseType* conType = ConsoleBaseType::getType(field->mFieldType);
|
||||||
|
AssertFatal(conType, "ConsoleObject::addField - invalid console type");
|
||||||
|
f.table = conType->getEnumTable();
|
||||||
|
|
||||||
|
tempFields.push_back(f);
|
||||||
|
|
||||||
|
refField = &f;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!refField)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fieldGui->setInspectorField(&tempFields[tempFields.size() - 1]);
|
||||||
|
|
||||||
|
if (fieldGui->registerObject())
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPEW
|
||||||
|
Platform::outputDebugString("[GuiInspectorGroup] Adding field '%s'",
|
||||||
|
field->pFieldname);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mChildren.push_back(fieldGui);
|
||||||
|
mStack->addObject(fieldGui);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SAFE_DELETE(fieldGui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mStack->freeze(false);
|
||||||
|
mStack->updatePanes();
|
||||||
|
|
||||||
|
// If we've no new items, there's no need to resize anything!
|
||||||
|
if (bNewItems == false && !mChildren.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
sizeToContents();
|
||||||
|
|
||||||
|
setUpdate();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiInspectorComponentGroup::updateAllFields()
|
||||||
|
{
|
||||||
|
// We overload this to just reinspect the group.
|
||||||
|
inspectGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiInspectorComponentGroup::onMouseMove(const GuiEvent &event)
|
||||||
|
{
|
||||||
|
//mParent->mOverDivider = false;
|
||||||
|
}
|
||||||
|
ConsoleMethod(GuiInspectorComponentGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
|
||||||
|
{
|
||||||
|
return object->inspectGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiInspectorComponentGroup::clearFields()
|
||||||
|
{
|
||||||
|
// delete everything else
|
||||||
|
mStack->clear();
|
||||||
|
|
||||||
|
// clear the mChildren list.
|
||||||
|
mChildren.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
SimFieldDictionary::Entry* GuiInspectorComponentGroup::findDynamicFieldInDictionary(StringTableEntry fieldName)
|
||||||
|
{
|
||||||
|
SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
|
||||||
|
|
||||||
|
for (SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
|
||||||
|
{
|
||||||
|
SimFieldDictionary::Entry * entry = (*ditr);
|
||||||
|
|
||||||
|
if (entry->slotName == fieldName)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiInspectorComponentGroup::addDynamicField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractClassRep::Field* GuiInspectorComponentGroup::findObjectBehaviorField(Component* target, String fieldName)
|
||||||
|
{
|
||||||
|
AbstractClassRep::FieldList& fieldList = target->getClassRep()->mFieldList;
|
||||||
|
for (AbstractClassRep::FieldList::iterator itr = fieldList.begin();
|
||||||
|
itr != fieldList.end(); ++itr)
|
||||||
|
{
|
||||||
|
AbstractClassRep::Field* field = &(*itr);
|
||||||
|
String fldNm(field->pFieldname);
|
||||||
|
if (fldNm == fieldName)
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ConsoleMethod(GuiInspectorComponentGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();")
|
||||||
|
{
|
||||||
|
object->addDynamicField();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(GuiInspectorComponentGroup, removeDynamicField, void, 3, 3, "")
|
||||||
|
{
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue