mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-14 04:03:46 +00:00
Removes current implement of shadow caching
Also removes EC stuff as it's not ready for prime-time yet
This commit is contained in:
parent
f007700646
commit
66cc6fb9d1
141 changed files with 67 additions and 19491 deletions
|
|
@ -1,168 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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("(TypeComponentAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set asset.
|
||||
pAssetPtr->setAssetId(pFieldValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Warn.
|
||||
Con::warnf("(TypeComponentAssetPtr) - Cannot set multiple args to a single asset.");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ComponentAsset::ComponentAsset()
|
||||
{
|
||||
mComponentName = StringTable->EmptyString();
|
||||
mComponentClass = StringTable->EmptyString();
|
||||
mFriendlyName = StringTable->EmptyString();
|
||||
mComponentType = StringTable->EmptyString();
|
||||
mDescription = StringTable->EmptyString();
|
||||
|
||||
mScriptFile = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ComponentAsset::~ComponentAsset()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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.");
|
||||
|
||||
addProtectedField("scriptFile", TypeAssetLooseFilePath, Offset(mScriptFile, ComponentAsset),
|
||||
&setScriptFile, &getScriptFile, "A script file with additional scripted functionality for this component.");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ComponentAsset::copyTo(SimObject* object)
|
||||
{
|
||||
// Call to parent.
|
||||
Parent::copyTo(object);
|
||||
}
|
||||
|
||||
void ComponentAsset::initializeAsset()
|
||||
{
|
||||
mScriptFile = expandAssetFilePath(mScriptFile);
|
||||
|
||||
if(Platform::isFile(mScriptFile))
|
||||
Con::executeFile(mScriptFile, false, false);
|
||||
}
|
||||
|
||||
void ComponentAsset::onAssetRefresh()
|
||||
{
|
||||
mScriptFile = expandAssetFilePath(mScriptFile);
|
||||
|
||||
if (Platform::isFile(mScriptFile))
|
||||
Con::executeFile(mScriptFile, false, false);
|
||||
}
|
||||
|
||||
void ComponentAsset::setScriptFile(const char* pScriptFile)
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal(pScriptFile != NULL, "Cannot use a NULL script file.");
|
||||
|
||||
// Fetch image file.
|
||||
pScriptFile = StringTable->insert(pScriptFile);
|
||||
|
||||
// Ignore no change,
|
||||
if (pScriptFile == mScriptFile)
|
||||
return;
|
||||
|
||||
// Update.
|
||||
mScriptFile = getOwned() ? expandAssetFilePath(pScriptFile) : StringTable->insert(pScriptFile);
|
||||
|
||||
// Refresh the asset.
|
||||
refreshAsset();
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
#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;
|
||||
|
||||
StringTableEntry mComponentName;
|
||||
StringTableEntry mComponentClass;
|
||||
StringTableEntry mFriendlyName;
|
||||
StringTableEntry mComponentType;
|
||||
StringTableEntry mDescription;
|
||||
|
||||
StringTableEntry mScriptFile;
|
||||
|
||||
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 getComponentType() { return mComponentType; }
|
||||
StringTableEntry getDescription() { return mDescription; }
|
||||
|
||||
void setComponentName(StringTableEntry name) { mComponentName = name; }
|
||||
void setComponentClass(StringTableEntry name) { mComponentClass = name; }
|
||||
void setFriendlyName(StringTableEntry name) { mFriendlyName = name; }
|
||||
void setComponentType(StringTableEntry typeName) { mComponentType = typeName; }
|
||||
void setDescription(StringTableEntry description) { mDescription = description; }
|
||||
|
||||
AssetDefinition* getAssetDefinition() { return mpAssetDefinition; }
|
||||
|
||||
void setScriptFile(const char* pScriptFile);
|
||||
inline StringTableEntry getScriptFile(void) const { return mScriptFile; };
|
||||
|
||||
protected:
|
||||
virtual void initializeAsset(void);
|
||||
virtual void onAssetRefresh(void);
|
||||
|
||||
static bool setScriptFile(void *obj, const char *index, const char *data) { static_cast<ComponentAsset*>(obj)->setScriptFile(data); return false; }
|
||||
static const char* getScriptFile(void* obj, const char* data) { return static_cast<ComponentAsset*>(obj)->getScriptFile(); }
|
||||
};
|
||||
|
||||
DefineConsoleType(TypeComponentAssetPtr, ComponentAsset)
|
||||
|
||||
#endif // _ASSET_BASE_H_
|
||||
|
||||
|
|
@ -40,8 +40,6 @@
|
|||
#include "assets/assetPtr.h"
|
||||
#endif
|
||||
|
||||
#include "T3D/entity.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,706 +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 "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;
|
||||
|
||||
mFriendlyName = "Animation(Component)";
|
||||
mComponentType = "Animation";
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
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(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;
|
||||
|
||||
if (mOwnerShapeInstance == nullptr || !getShape())
|
||||
return;
|
||||
|
||||
for (U32 i = 0; i < MaxScriptThreads; i++)
|
||||
{
|
||||
Thread& st = mAnimationThreads[i];
|
||||
if (st.thread && st.sequence != -1)
|
||||
{
|
||||
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 (!isClientObject())
|
||||
{
|
||||
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 && !isClientObject())
|
||||
{
|
||||
for (U32 stateIDx = 1; stateIDx < 32; stateIDx++)
|
||||
{
|
||||
if (mOwnerShapeInstance->getTriggerState(stateIDx))
|
||||
{
|
||||
const char* animName = st.thread->getSequenceName().c_str();
|
||||
onAnimationTrigger_callback(this, animName, stateIDx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
mOwnerShapeInstance->animate();
|
||||
/*mOwnerShapeInstance->animateGround();
|
||||
MatrixF groundTransform = mOwnerShapeInstance->getGroundTransform();
|
||||
if (groundTransform != MatrixF::Identity)
|
||||
{
|
||||
mOwner->setPosition(groundTransform.getPosition());
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
|
@ -1,138 +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 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();
|
||||
|
||||
virtual 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
|
||||
|
|
@ -1,222 +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 "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);
|
||||
}
|
||||
|
|
@ -1,422 +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 "T3D/components/audio/SoundComponent.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "sim/netConnection.h"
|
||||
|
||||
#include "sfx/sfxSystem.h"
|
||||
#include "sfx/sfxSource.h"
|
||||
#include "sfx/sfxTrack.h"
|
||||
#include "sfx/sfxDescription.h"
|
||||
#include "T3D/sfx/sfx3DWorld.h"
|
||||
|
||||
#include "sfx/sfxTrack.h"
|
||||
#include "sfx/sfxTypes.h"
|
||||
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
|
||||
// Timeout for non-looping sounds on a channel
|
||||
static SimTime sAudioTimeout = 500;
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SoundComponent::SoundComponent() : Component()
|
||||
{
|
||||
//These flags inform that, in this particular component, we network down to the client, which enables the pack/unpackData functions to operate
|
||||
mNetworked = true;
|
||||
|
||||
mFriendlyName = "Sound(Component)";
|
||||
mComponentType = "Sound";
|
||||
mDescription = getDescriptionText("Stores up to 4 sounds for playback.");
|
||||
|
||||
for (U32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++) {
|
||||
mSoundThread[slotNum].play = false;
|
||||
mSoundThread[slotNum].profile = 0;
|
||||
mSoundThread[slotNum].sound = 0;
|
||||
|
||||
mSoundFile[slotNum] = NULL;
|
||||
mPreviewSound[slotNum] = false;
|
||||
mPlay[slotNum] = false;
|
||||
}
|
||||
}
|
||||
|
||||
SoundComponent::~SoundComponent()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SoundComponent);
|
||||
|
||||
//Standard onAdd function, for when the component is created
|
||||
bool SoundComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
for (U32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
mPreviewSound[slotNum] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Standard onRemove function, when the component object is deleted
|
||||
void SoundComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//This is called when the component has been added to an entity
|
||||
void SoundComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
//Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
|
||||
}
|
||||
|
||||
//This is called when the component has been removed from an entity
|
||||
void SoundComponent::onComponentRemove()
|
||||
{
|
||||
//Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
//This is called any time a component is added to an entity. Every component currently owned by the entity is informed of the event.
|
||||
//This allows you to do dependency behavior, like collisions being aware of a mesh component, etc
|
||||
void SoundComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
if (mPlay[slotNum])
|
||||
{
|
||||
playAudio(slotNum, mSoundFile[slotNum]);
|
||||
}
|
||||
}
|
||||
//Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
|
||||
}
|
||||
|
||||
//This is called any time a component is removed from an entity. Every component current owned by the entity is informed of the event.
|
||||
//This allows cleanup and dependency management.
|
||||
void SoundComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
//Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
|
||||
}
|
||||
|
||||
//Regular init persist fields function to set up static fields.
|
||||
void SoundComponent::initPersistFields()
|
||||
{
|
||||
//addArray("Sounds", MaxSoundThreads);
|
||||
addField("mSoundFile", TypeSFXTrackName, Offset(mSoundFile, SoundComponent), MaxSoundThreads, "If the text will not fit in the control, the deniedSound is played.");
|
||||
addProtectedField("mPreviewSound", TypeBool, Offset(mPreviewSound, SoundComponent),
|
||||
&_previewSound, &defaultProtectedGetFn, MaxSoundThreads, "Preview Sound", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
|
||||
addProtectedField("play", TypeBool, Offset(mPlay, SoundComponent),
|
||||
&_autoplay, &defaultProtectedGetFn, MaxSoundThreads, "Whether playback of the emitter's sound should start as soon as the emitter object is added to the level.\n"
|
||||
"If this is true, the emitter will immediately start to play when the level is loaded.");
|
||||
//endArray("Sounds");
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool SoundComponent::_previewSound(void *object, const char *index, const char *data)
|
||||
{
|
||||
U32 slotNum = (index != NULL) ? dAtoui(index) : 0;
|
||||
SoundComponent* component = reinterpret_cast< SoundComponent* >(object);
|
||||
if (!component->mPreviewSound[slotNum])
|
||||
component->playAudio(slotNum, component->mSoundFile[slotNum]);
|
||||
else
|
||||
component->stopAudio(slotNum);
|
||||
component->mPreviewSound[slotNum] = !component->mPreviewSound[slotNum];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundComponent::_autoplay(void *object, const char *index, const char *data)
|
||||
{
|
||||
U32 slotNum = (index != NULL) ? dAtoui(index) : 0;
|
||||
SoundComponent* component = reinterpret_cast< SoundComponent* >(object);
|
||||
component->mPlay[slotNum] = dAtoui(data);
|
||||
if (component->mPlay[slotNum])
|
||||
component->playAudio(slotNum, component->mSoundFile[slotNum]);
|
||||
else
|
||||
component->stopAudio(slotNum);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 SoundComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (mask & InitialUpdateMask)
|
||||
{
|
||||
// mask off sounds that aren't playing
|
||||
S32 slotNum;
|
||||
for (slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
if (!mSoundThread[slotNum].play)
|
||||
mask &= ~(SoundMaskN << slotNum);
|
||||
}
|
||||
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
stream->writeFlag(mPreviewSound[slotNum]);
|
||||
|
||||
if (stream->writeFlag(mask & SoundMask))
|
||||
{
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
|
||||
if (stream->writeFlag(mask & (SoundMaskN << slotNum)))
|
||||
{
|
||||
if (stream->writeFlag(st.play))
|
||||
//stream->writeRangedU32(st.profile->getId(), DataBlockObjectIdFirst,
|
||||
// DataBlockObjectIdLast);
|
||||
stream->writeString(st.profile->getName());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void SoundComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
mPreviewSound[slotNum] = stream->readFlag();
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
if (stream->readFlag())
|
||||
{
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
st.play = stream->readFlag();
|
||||
if (st.play)
|
||||
{
|
||||
//st.profile = (SFXTrack*)stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
// DataBlockObjectIdLast);
|
||||
char profileName[255];
|
||||
stream->readString(profileName);
|
||||
|
||||
if (!Sim::findObject(profileName, st.profile))
|
||||
Con::errorf("Could not find SFXTrack");
|
||||
}
|
||||
|
||||
//if (isProperlyAdded())
|
||||
updateAudioState(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//This allows custom behavior in the event the owner is being edited
|
||||
void SoundComponent::onInspect()
|
||||
{
|
||||
}
|
||||
|
||||
//This allows cleanup of the custom editor behavior if our owner stopped being edited
|
||||
void SoundComponent::onEndInspect()
|
||||
{
|
||||
}
|
||||
|
||||
//Process tick update function, natch
|
||||
void SoundComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
}
|
||||
|
||||
//Client-side advance function
|
||||
void SoundComponent::advanceTime(F32 dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Client-side interpolation function
|
||||
void SoundComponent::interpolateTick(F32 delta)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SoundComponent::prepRenderImage(SceneRenderState *state)
|
||||
{
|
||||
if (!mEnabled || !mOwner || !gEditingMission)
|
||||
return;
|
||||
ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >();
|
||||
|
||||
ri->renderDelegate.bind(this, &SoundComponent::_renderObject);
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
ri->defaultKey = 0;
|
||||
ri->defaultKey2 = 0;
|
||||
|
||||
state->getRenderPass()->addInst(ri);
|
||||
}
|
||||
|
||||
void SoundComponent::_renderObject(ObjectRenderInst *ri,
|
||||
SceneRenderState *state,
|
||||
BaseMatInstance *overrideMat)
|
||||
{
|
||||
if (overrideMat)
|
||||
return;
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setBlend(true);
|
||||
|
||||
MatrixF camera = GFX->getWorldMatrix();
|
||||
camera.inverse();
|
||||
Point3F pos = mOwner->getPosition();
|
||||
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
if (mPreviewSound[slotNum])
|
||||
{
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
if (st.sound && st.sound->getDescription())
|
||||
{
|
||||
F32 minRad = st.sound->getDescription()->mMinDistance;
|
||||
F32 falloffRad = st.sound->getDescription()->mMaxDistance;
|
||||
SphereF sphere(pos, falloffRad);
|
||||
if (sphere.isContained(camera.getPosition()))
|
||||
desc.setCullMode(GFXCullNone);
|
||||
|
||||
GFX->getDrawUtil()->drawSphere(desc, minRad, pos, ColorI(255, 0, 255, 64));
|
||||
GFX->getDrawUtil()->drawSphere(desc, falloffRad, pos, ColorI(128, 0, 128, 64));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundComponent::playAudio(U32 slotNum, SFXTrack* _profile)
|
||||
{
|
||||
AssertFatal(slotNum < MaxSoundThreads, "ShapeBase::playAudio() bad slot index");
|
||||
SFXTrack* profile = (_profile != NULL) ? _profile : mSoundFile[slotNum];
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
if (profile && (!st.play || st.profile != profile))
|
||||
{
|
||||
setMaskBits(SoundMaskN << slotNum);
|
||||
st.play = true;
|
||||
st.profile = profile;
|
||||
updateAudioState(st);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundComponent::stopAudio(U32 slotNum)
|
||||
{
|
||||
AssertFatal(slotNum < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index");
|
||||
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
if (st.play)
|
||||
{
|
||||
st.play = false;
|
||||
setMaskBits(SoundMaskN << slotNum);
|
||||
updateAudioState(st);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundComponent::updateServerAudio()
|
||||
{
|
||||
// Timeout non-looping sounds
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
Sound& st = mSoundThread[slotNum];
|
||||
if (st.play && st.timeout && st.timeout < Sim::getCurrentTime())
|
||||
{
|
||||
//clearMaskBits(SoundMaskN << slotNum);
|
||||
st.play = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundComponent::updateAudioState(Sound& st)
|
||||
{
|
||||
SFX_DELETE(st.sound);
|
||||
|
||||
if (st.play && st.profile)
|
||||
{
|
||||
if (isClientObject())
|
||||
{
|
||||
//if (Sim::findObject(SimObjectId((uintptr_t)st.profile), st.profile))
|
||||
// {
|
||||
MatrixF transform = mOwner->getTransform();
|
||||
st.sound = SFX->createSource(st.profile, &transform);
|
||||
if (st.sound)
|
||||
st.sound->play();
|
||||
//}
|
||||
else
|
||||
st.play = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-looping sounds timeout on the server
|
||||
st.timeout = 0;
|
||||
if (!st.profile->getDescription()->mIsLooping)
|
||||
st.timeout = Sim::getCurrentTime() + sAudioTimeout;
|
||||
}
|
||||
}
|
||||
else
|
||||
st.play = false;
|
||||
}
|
||||
|
||||
void SoundComponent::updateAudioPos()
|
||||
{
|
||||
for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
|
||||
{
|
||||
SFXSource* source = mSoundThread[slotNum].sound;
|
||||
if (source)
|
||||
source->setTransform(mOwner->getTransform());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
DefineEngineMethod(SoundComponent, playAudio, bool, (S32 slot, SFXTrack* track), (0, nullAsType<SFXTrack*>()),
|
||||
"@brief Attach a sound to this shape and start playing it.\n\n"
|
||||
|
||||
"@param slot Audio slot index for the sound (valid range is 0 - 3)\n" // 3 = ShapeBase::MaxSoundThreads-1
|
||||
"@param track SFXTrack to play\n"
|
||||
"@return true if the sound was attached successfully, false if failed\n\n"
|
||||
|
||||
"@see stopAudio()\n")
|
||||
{
|
||||
if (track && slot >= 0 && slot < SoundComponent::MaxSoundThreads) {
|
||||
object->playAudio(slot, track);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DefineEngineMethod(SoundComponent, stopAudio, bool, (S32 slot), ,
|
||||
"@brief Stop a sound started with playAudio.\n\n"
|
||||
|
||||
"@param slot audio slot index (started with playAudio)\n"
|
||||
"@return true if the sound was stopped successfully, false if failed\n\n"
|
||||
|
||||
"@see playAudio()\n")
|
||||
{
|
||||
if (slot >= 0 && slot < SoundComponent::MaxSoundThreads) {
|
||||
object->stopAudio(slot);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,129 +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 EXAMPLE_COMPONENT_H
|
||||
#define EXAMPLE_COMPONENT_H
|
||||
#pragma once
|
||||
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#endif
|
||||
|
||||
class SFXSource;
|
||||
|
||||
//SoundComponent
|
||||
//A basic example of the various functions you can utilize to make your own component!
|
||||
//This example doesn't really DO anything, persay, but you can readily copy it as a base
|
||||
//and use it as a starting point for your own.
|
||||
class SoundComponent : public Component, public RenderComponentInterface, public EditorInspectInterface
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
public:
|
||||
enum PublicConstants
|
||||
{
|
||||
MaxSoundThreads = 4, ///< Should be a power of 2
|
||||
};
|
||||
|
||||
/// @name Network state masks
|
||||
/// @{
|
||||
|
||||
///
|
||||
enum SoundComponentMasks
|
||||
{
|
||||
SoundMaskN = Parent::NextFreeMask << 6, ///< Extends + MaxSoundThreads bits
|
||||
};
|
||||
|
||||
enum BaseMaskConstants
|
||||
{
|
||||
SoundMask = (SoundMaskN << MaxSoundThreads) - SoundMaskN,
|
||||
};
|
||||
/// @name Scripted Sound
|
||||
/// @{
|
||||
struct Sound {
|
||||
bool play; ///< Are we playing this sound?
|
||||
SimTime timeout; ///< Time until we stop playing this sound.
|
||||
SFXTrack* profile; ///< Profile on server
|
||||
SFXSource* sound; ///< Sound on client
|
||||
Sound()
|
||||
{
|
||||
play = false;
|
||||
timeout = 0;
|
||||
profile = NULL;
|
||||
sound = NULL;
|
||||
}
|
||||
};
|
||||
Sound mSoundThread[MaxSoundThreads];
|
||||
SFXTrack* mSoundFile[MaxSoundThreads];
|
||||
bool mPreviewSound[MaxSoundThreads];
|
||||
bool mPlay[MaxSoundThreads];
|
||||
/// @}
|
||||
|
||||
SoundComponent();
|
||||
virtual ~SoundComponent();
|
||||
DECLARE_CONOBJECT(SoundComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
static bool _previewSound(void *object, const char *index, const char *data);
|
||||
static bool _autoplay(void *object, const char *index, const char *data);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void onComponentRemove();
|
||||
virtual void onComponentAdd();
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void onInspect();
|
||||
virtual void onEndInspect();
|
||||
|
||||
virtual void processTick();
|
||||
virtual void advanceTime(F32 dt);
|
||||
virtual void interpolateTick(F32 delta);
|
||||
|
||||
void prepRenderImage(SceneRenderState* state);
|
||||
void _renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
|
||||
|
||||
virtual void playAudio(U32 slotNum, SFXTrack* profile = NULL);
|
||||
virtual void stopAudio(U32 slot);
|
||||
virtual void updateServerAudio();
|
||||
virtual void updateAudioState(Sound& st);
|
||||
virtual void updateAudioPos();
|
||||
|
||||
//why god why
|
||||
virtual TSShape* getShape() { return NULL; };
|
||||
Signal< void(RenderComponentInterface*) > onShapeChanged;
|
||||
virtual TSShapeInstance* getShapeInstance() { return NULL; };
|
||||
Signal< void(RenderComponentInterface*) > onShapeInstanceChanged;
|
||||
virtual MatrixF getNodeTransform(S32 nodeIdx) { return MatrixF::Identity; };
|
||||
virtual Vector<MatrixF> getNodeTransforms() { return NULL; };
|
||||
virtual void setNodeTransforms(Vector<MatrixF> transforms) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,485 +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 "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;
|
||||
mNetworked = 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 = mOwner;//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
|
||||
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;
|
||||
}
|
||||
|
||||
if(stream->writeFlag(mTargetNodeIdx > -1))
|
||||
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())
|
||||
{
|
||||
if (stream->readFlag())
|
||||
mTargetNodeIdx = stream->readInt(32);
|
||||
else
|
||||
mTargetNodeIdx = -1;
|
||||
|
||||
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 aspectRatio = mClientScreen.x / mClientScreen.y;
|
||||
|
||||
visFrustum.set(false, mDegToRad(mCameraFov), aspectRatio, 0.1f, 1000, mOwner->getTransform());
|
||||
|
||||
return visFrustum;
|
||||
}
|
||||
|
|
@ -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 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
|
||||
|
|
@ -1,92 +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 "console/engineAPI.h"
|
||||
#include "T3D/components/camera/cameraComponent.h"
|
||||
|
||||
//Basically, this only exists for backwards compatibility for parts of the editors
|
||||
DefineEngineMethod(CameraComponent, getMode, const char*, (),,
|
||||
"@brief 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";
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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);
|
||||
}
|
||||
|
||||
DefineEngineMethod(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();
|
||||
}
|
||||
|
|
@ -1,172 +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 "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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +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 CAMERA_ORBITER_COMPONENT_H
|
||||
#define CAMERA_ORBITER_COMPONENT_H
|
||||
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
#ifndef CAMERA_COMPONENT_H
|
||||
#include "T3D/components/camera/cameraComponent.h"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CameraOrbiterComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
F32 mMinOrbitDist;
|
||||
F32 mMaxOrbitDist;
|
||||
F32 mCurOrbitDist;
|
||||
Point3F mPosition;
|
||||
|
||||
F32 mMaxPitchAngle;
|
||||
F32 mMinPitchAngle;
|
||||
|
||||
RotationF mRotation;
|
||||
|
||||
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
|
||||
|
|
@ -1,873 +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 "T3D/components/collision/collisionComponent.h"
|
||||
#include "scene/sceneObject.h"
|
||||
#include "T3D/entity.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/trigger.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "collision/extrudedPolyList.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 "collision/clippedPolyList.h"
|
||||
|
||||
static F32 sTractionDistance = 0.04f;
|
||||
|
||||
IMPLEMENT_CONOBJECT(CollisionComponent);
|
||||
|
||||
CollisionComponent::CollisionComponent() : Component()
|
||||
{
|
||||
mFriendlyName = "Collision Component";
|
||||
|
||||
mComponentType = "Collision";
|
||||
|
||||
mDescription = getDescriptionText("A stub component class that collision components should inherit from.");
|
||||
|
||||
mBlockColliding = true;
|
||||
|
||||
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
||||
StaticShapeObjectType | VehicleObjectType |
|
||||
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
||||
|
||||
mPhysicsRep = nullptr;
|
||||
mPhysicsWorld = nullptr;
|
||||
|
||||
mTimeoutList = nullptr;
|
||||
}
|
||||
|
||||
CollisionComponent::~CollisionComponent()
|
||||
{
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
}
|
||||
|
||||
bool CollisionComponent::checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollisionComponent::updateCollisions(F32 time, VectorF vector, VectorF velocity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollisionComponent::updateWorkingCollisionSet(const U32 mask)
|
||||
{
|
||||
}
|
||||
|
||||
void CollisionComponent::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)
|
||||
{
|
||||
CollisionComponent *colObjectInterface = ent->getComponent<CollisionComponent>();
|
||||
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 CollisionComponent::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 CollisionComponent::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<CollisionComponent::CollisionTimeout> sCollisionTimeoutChunker;
|
||||
CollisionComponent::CollisionTimeout* CollisionComponent::sFreeTimeoutList = 0;
|
||||
|
||||
void CollisionComponent::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 CollisionComponent::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* CollisionComponent::getCollision(S32 col)
|
||||
{
|
||||
if(col < mCollisionList.getCount() && col >= 0)
|
||||
return &mCollisionList[col];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Point3F CollisionComponent::getContactNormal()
|
||||
{
|
||||
return mContactInfo.contactNormal;
|
||||
}
|
||||
|
||||
bool CollisionComponent::hasContact()
|
||||
{
|
||||
if (mContactInfo.contactObject)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 CollisionComponent::getCollisionCount()
|
||||
{
|
||||
return mCollisionList.getCount();
|
||||
}
|
||||
|
||||
Point3F CollisionComponent::getCollisionNormal(S32 collisionIndex)
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return Point3F::Zero;
|
||||
|
||||
return mCollisionList[collisionIndex].normal;
|
||||
}
|
||||
|
||||
F32 CollisionComponent::getCollisionAngle(S32 collisionIndex, Point3F upVector)
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return 0.0f;
|
||||
|
||||
return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
|
||||
}
|
||||
|
||||
S32 CollisionComponent::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 CollisionComponent::getBestCollisionAngle(VectorF upVector)
|
||||
{
|
||||
S32 bestCol = getBestCollision(upVector);
|
||||
|
||||
if (bestCol == -1)
|
||||
return 0;
|
||||
|
||||
return getCollisionAngle(bestCol, upVector);
|
||||
}
|
||||
|
||||
//
|
||||
bool CollisionComponent::buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list)
|
||||
{
|
||||
AssertFatal(dl >= 0 && dl < sI->getShape()->details.size(), "TSShapeInstance::buildConvexOpcode");
|
||||
|
||||
TSShape* shape = sI->getShape();
|
||||
|
||||
const MatrixF &objMat = mOwner->getObjToWorld();
|
||||
const Point3F &objScale = mOwner->getScale();
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &shape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// nothing emitted yet...
|
||||
bool emitted = false;
|
||||
|
||||
S32 start = shape->subShapeFirstObject[ss];
|
||||
S32 end = shape->subShapeNumObjects[ss] + start;
|
||||
if (start<end)
|
||||
{
|
||||
MatrixF initialMat = objMat;
|
||||
Point3F initialScale = objScale;
|
||||
|
||||
// set up for first object's node
|
||||
MatrixF mat;
|
||||
MatrixF scaleMat(true);
|
||||
F32* p = scaleMat;
|
||||
p[0] = initialScale.x;
|
||||
p[5] = initialScale.y;
|
||||
p[10] = initialScale.z;
|
||||
const MatrixF * previousMat = &sI->mMeshObjects[start].getTransform();
|
||||
mat.mul(initialMat, scaleMat);
|
||||
mat.mul(*previousMat);
|
||||
|
||||
// Update our bounding box...
|
||||
Box3F localBox = bounds;
|
||||
MatrixF otherMat = mat;
|
||||
otherMat.inverse();
|
||||
otherMat.mul(localBox);
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i = start; i<end; i++)
|
||||
{
|
||||
TSShapeInstance::MeshObjectInstance * meshInstance = &sI->mMeshObjects[i];
|
||||
|
||||
if (od >= meshInstance->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (&meshInstance->getTransform() != previousMat)
|
||||
{
|
||||
// different node from before, set up for this node
|
||||
previousMat = &meshInstance->getTransform();
|
||||
|
||||
if (previousMat != NULL)
|
||||
{
|
||||
mat.mul(initialMat, scaleMat);
|
||||
mat.mul(*previousMat);
|
||||
|
||||
// Update our bounding box...
|
||||
otherMat = mat;
|
||||
otherMat.inverse();
|
||||
localBox = bounds;
|
||||
otherMat.mul(localBox);
|
||||
}
|
||||
}
|
||||
|
||||
// collide... note we pass the original mech transform
|
||||
// here so that the convex data returned is in mesh space.
|
||||
TSMesh * mesh = meshInstance->getMesh(od);
|
||||
if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f && localBox.isOverlapped(mesh->getBounds()))
|
||||
emitted |= buildMeshOpcode(mesh, *previousMat, localBox, c, list);
|
||||
else
|
||||
emitted |= false;
|
||||
}
|
||||
}
|
||||
|
||||
return emitted;
|
||||
}
|
||||
|
||||
bool CollisionComponent::buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat,
|
||||
const Box3F &nodeBox, Convex *convex, Convex *list)
|
||||
{
|
||||
/*PROFILE_SCOPE(MeshCollider_buildConvexOpcode);
|
||||
|
||||
// This is small... there is no win for preallocating it.
|
||||
Opcode::AABBCollider opCollider;
|
||||
opCollider.SetPrimitiveTests(true);
|
||||
|
||||
// This isn't really needed within the AABBCollider as
|
||||
// we don't use temporal coherance... use a static to
|
||||
// remove the allocation overhead.
|
||||
static Opcode::AABBCache opCache;
|
||||
|
||||
IceMaths::AABB opBox;
|
||||
opBox.SetMinMax(Point(nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z),
|
||||
Point(nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z));
|
||||
Opcode::CollisionAABB opCBox(opBox);
|
||||
|
||||
if (!opCollider.Collide(opCache, opCBox, *mesh->mOptTree))
|
||||
return false;
|
||||
|
||||
U32 cnt = opCollider.GetNbTouchedPrimitives();
|
||||
const udword *idx = opCollider.GetTouchedPrimitives();
|
||||
|
||||
Opcode::VertexPointers vp;
|
||||
for (S32 i = 0; i < cnt; i++)
|
||||
{
|
||||
// First, check our active convexes for a potential match (and clean things
|
||||
// up, too.)
|
||||
const U32 curIdx = idx[i];
|
||||
|
||||
// See if the square already exists as part of the working set.
|
||||
bool gotMatch = false;
|
||||
CollisionWorkingList& wl = convex->getWorkingList();
|
||||
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
|
||||
{
|
||||
if (itr->mConvex->getType() != TSPolysoupConvexType)
|
||||
continue;
|
||||
|
||||
const MeshColliderPolysoupConvex *chunkc = static_cast<MeshColliderPolysoupConvex*>(itr->mConvex);
|
||||
|
||||
if (chunkc->getObject() != mOwner)
|
||||
continue;
|
||||
|
||||
if (chunkc->mesh != mesh)
|
||||
continue;
|
||||
|
||||
if (chunkc->idx != curIdx)
|
||||
continue;
|
||||
|
||||
// A match! Don't need to add it.
|
||||
gotMatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (gotMatch)
|
||||
continue;
|
||||
|
||||
// Get the triangle...
|
||||
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, idx[i]);
|
||||
|
||||
Point3F a(vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z);
|
||||
Point3F b(vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z);
|
||||
Point3F c(vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z);
|
||||
|
||||
// Transform the result into object space!
|
||||
meshToObjectMat.mulP(a);
|
||||
meshToObjectMat.mulP(b);
|
||||
meshToObjectMat.mulP(c);
|
||||
|
||||
//If we're not doing debug rendering on the client, then set up our convex list as normal
|
||||
PlaneF p(c, b, a);
|
||||
Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f);
|
||||
|
||||
// Set up the convex...
|
||||
MeshColliderPolysoupConvex *cp = new MeshColliderPolysoupConvex();
|
||||
|
||||
list->registerObject(cp);
|
||||
convex->addToWorkingList(cp);
|
||||
|
||||
cp->mesh = mesh;
|
||||
cp->idx = curIdx;
|
||||
cp->mObject = mOwner;
|
||||
|
||||
cp->normal = p;
|
||||
cp->verts[0] = a;
|
||||
cp->verts[1] = b;
|
||||
cp->verts[2] = c;
|
||||
cp->verts[3] = peak;
|
||||
|
||||
// Update the bounding box.
|
||||
Box3F &bounds = cp->box;
|
||||
bounds.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
|
||||
bounds.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
|
||||
|
||||
bounds.minExtents.setMin(a);
|
||||
bounds.minExtents.setMin(b);
|
||||
bounds.minExtents.setMin(c);
|
||||
bounds.minExtents.setMin(peak);
|
||||
|
||||
bounds.maxExtents.setMax(a);
|
||||
bounds.maxExtents.setMax(b);
|
||||
bounds.maxExtents.setMax(c);
|
||||
bounds.maxExtents.setMax(peak);
|
||||
}
|
||||
|
||||
return true;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollisionComponent::castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
//if (dl == -1 || !getShapeInstance())
|
||||
return false;
|
||||
|
||||
/*TSShape *shape = getShapeInstance()->getShape();
|
||||
|
||||
AssertFatal(dl >= 0 && dl < shape->details.size(), "TSShapeInstance::castRayOpcode");
|
||||
|
||||
info->t = 100.f;
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &shape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
if (ss < 0)
|
||||
return false;
|
||||
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// nothing emitted yet...
|
||||
bool emitted = false;
|
||||
|
||||
const MatrixF* saveMat = NULL;
|
||||
S32 start = shape->subShapeFirstObject[ss];
|
||||
S32 end = shape->subShapeNumObjects[ss] + start;
|
||||
if (start<end)
|
||||
{
|
||||
MatrixF mat;
|
||||
const MatrixF * previousMat = &getShapeInstance()->mMeshObjects[start].getTransform();
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
Point3F localStart, localEnd;
|
||||
mat.mulP(startPos, &localStart);
|
||||
mat.mulP(endPos, &localEnd);
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i = start; i<end; i++)
|
||||
{
|
||||
TSShapeInstance::MeshObjectInstance * meshInstance = &getShapeInstance()->mMeshObjects[i];
|
||||
|
||||
if (od >= meshInstance->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (&meshInstance->getTransform() != previousMat)
|
||||
{
|
||||
// different node from before, set up for this node
|
||||
previousMat = &meshInstance->getTransform();
|
||||
|
||||
if (previousMat != NULL)
|
||||
{
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
mat.mulP(startPos, &localStart);
|
||||
mat.mulP(endPos, &localEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// collide...
|
||||
TSMesh * mesh = meshInstance->getMesh(od);
|
||||
if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f)
|
||||
{
|
||||
if (castRayMeshOpcode(mesh, localStart, localEnd, info, getShapeInstance()->mMaterialList))
|
||||
{
|
||||
saveMat = previousMat;
|
||||
emitted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emitted)
|
||||
{
|
||||
saveMat->mulV(info->normal);
|
||||
info->point = endPos - startPos;
|
||||
info->point *= info->t;
|
||||
info->point += startPos;
|
||||
}
|
||||
|
||||
return emitted;*/
|
||||
}
|
||||
|
||||
static Point3F texGenAxis[18] =
|
||||
{
|
||||
Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0),
|
||||
Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0),
|
||||
Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1),
|
||||
Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1),
|
||||
Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1),
|
||||
Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1)
|
||||
};
|
||||
|
||||
bool CollisionComponent::castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials)
|
||||
{
|
||||
Opcode::RayCollider ray;
|
||||
Opcode::CollisionFaces cfs;
|
||||
|
||||
IceMaths::Point dir(e.x - s.x, e.y - s.y, e.z - s.z);
|
||||
const F32 rayLen = dir.Magnitude();
|
||||
IceMaths::Ray vec(Point(s.x, s.y, s.z), dir.Normalize());
|
||||
|
||||
ray.SetDestination(&cfs);
|
||||
ray.SetFirstContact(false);
|
||||
ray.SetClosestHit(true);
|
||||
ray.SetPrimitiveTests(true);
|
||||
ray.SetCulling(true);
|
||||
ray.SetMaxDist(rayLen);
|
||||
|
||||
AssertFatal(ray.ValidateSettings() == NULL, "invalid ray settings");
|
||||
|
||||
// Do collision.
|
||||
bool safety = ray.Collide(vec, *mesh->mOptTree);
|
||||
AssertFatal(safety, "CollisionComponent::castRayOpcode - no good ray collide!");
|
||||
|
||||
// If no hit, just skip out.
|
||||
if (cfs.GetNbFaces() == 0)
|
||||
return false;
|
||||
|
||||
// Got a hit!
|
||||
AssertFatal(cfs.GetNbFaces() == 1, "bad");
|
||||
const Opcode::CollisionFace &face = cfs.GetFaces()[0];
|
||||
|
||||
// If the cast was successful let's check if the t value is less than what we had
|
||||
// and toggle the collision boolean
|
||||
// Stupid t... i prefer coffee
|
||||
const F32 t = face.mDistance / rayLen;
|
||||
|
||||
if (t < 0.0f || t > 1.0f)
|
||||
return false;
|
||||
|
||||
if (t <= info->t)
|
||||
{
|
||||
info->t = t;
|
||||
|
||||
// Calculate the normal.
|
||||
Opcode::VertexPointers vp;
|
||||
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, face.mFaceID);
|
||||
|
||||
if (materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size())
|
||||
info->material = materials->getMaterialInst(vp.MatIdx);
|
||||
|
||||
// Get the two edges.
|
||||
IceMaths::Point baseVert = *vp.Vertex[0];
|
||||
IceMaths::Point a = *vp.Vertex[1] - baseVert;
|
||||
IceMaths::Point b = *vp.Vertex[2] - baseVert;
|
||||
|
||||
IceMaths::Point n;
|
||||
n.Cross(a, b);
|
||||
n.Normalize();
|
||||
|
||||
info->normal.set(n.x, n.y, n.z);
|
||||
|
||||
// generate UV coordinate across mesh based on
|
||||
// matching normals, this isn't done by default and is
|
||||
// primarily of interest in matching a collision point to
|
||||
// either a GUI control coordinate or finding a hit pixel in texture space
|
||||
if (info->generateTexCoord)
|
||||
{
|
||||
baseVert = *vp.Vertex[0];
|
||||
a = *vp.Vertex[1];
|
||||
b = *vp.Vertex[2];
|
||||
|
||||
Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z)
|
||||
+ face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z);
|
||||
|
||||
U32 faces[1024];
|
||||
U32 numFaces = 0;
|
||||
for (U32 i = 0; i < mesh->mOptTree->GetMeshInterface()->GetNbTriangles(); i++)
|
||||
{
|
||||
if (i == face.mFaceID)
|
||||
{
|
||||
faces[numFaces++] = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
IceMaths::Point n2;
|
||||
|
||||
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, i);
|
||||
|
||||
baseVert = *vp.Vertex[0];
|
||||
a = *vp.Vertex[1] - baseVert;
|
||||
b = *vp.Vertex[2] - baseVert;
|
||||
n2.Cross(a, b);
|
||||
n2.Normalize();
|
||||
|
||||
F32 eps = .01f;
|
||||
if (mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps)
|
||||
{
|
||||
faces[numFaces++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (numFaces == 1024)
|
||||
{
|
||||
// too many faces in this collision mesh for UV generation
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Point3F min(F32_MAX, F32_MAX, F32_MAX);
|
||||
Point3F max(-F32_MAX, -F32_MAX, -F32_MAX);
|
||||
|
||||
for (U32 i = 0; i < numFaces; i++)
|
||||
{
|
||||
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, faces[i]);
|
||||
|
||||
for (U32 j = 0; j < 3; j++)
|
||||
{
|
||||
a = *vp.Vertex[j];
|
||||
|
||||
if (a.x < min.x)
|
||||
min.x = a.x;
|
||||
if (a.y < min.y)
|
||||
min.y = a.y;
|
||||
if (a.z < min.z)
|
||||
min.z = a.z;
|
||||
|
||||
if (a.x > max.x)
|
||||
max.x = a.x;
|
||||
if (a.y > max.y)
|
||||
max.y = a.y;
|
||||
if (a.z > max.z)
|
||||
max.z = a.z;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// slerp
|
||||
Point3F s = ((max - min) - (facePoint - min)) / (max - min);
|
||||
|
||||
// compute axis
|
||||
S32 bestAxis = 0;
|
||||
F32 best = 0.f;
|
||||
|
||||
for (U32 i = 0; i < 6; i++)
|
||||
{
|
||||
F32 dot = mDot(info->normal, texGenAxis[i * 3]);
|
||||
if (dot > best)
|
||||
{
|
||||
best = dot;
|
||||
bestAxis = i;
|
||||
}
|
||||
}
|
||||
|
||||
Point3F xv = texGenAxis[bestAxis * 3 + 1];
|
||||
Point3F yv = texGenAxis[bestAxis * 3 + 2];
|
||||
|
||||
S32 sv, tv;
|
||||
|
||||
if (xv.x)
|
||||
sv = 0;
|
||||
else if (xv.y)
|
||||
sv = 1;
|
||||
else
|
||||
sv = 2;
|
||||
|
||||
if (yv.x)
|
||||
tv = 0;
|
||||
else if (yv.y)
|
||||
tv = 1;
|
||||
else
|
||||
tv = 2;
|
||||
|
||||
// handle coord translation
|
||||
if (bestAxis == 2 || bestAxis == 3)
|
||||
{
|
||||
S32 x = sv;
|
||||
sv = tv;
|
||||
tv = x;
|
||||
|
||||
if (yv.z < 0)
|
||||
s[sv] = 1.f - s[sv];
|
||||
}
|
||||
|
||||
if (bestAxis < 2)
|
||||
{
|
||||
if (yv.y < 0)
|
||||
s[sv] = 1.f - s[sv];
|
||||
}
|
||||
|
||||
if (bestAxis > 3)
|
||||
{
|
||||
s[sv] = 1.f - s[sv];
|
||||
if (yv.z > 0)
|
||||
s[tv] = 1.f - s[tv];
|
||||
|
||||
}
|
||||
|
||||
// done!
|
||||
info->texCoord.set(s[sv], s[tv]);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,206 +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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#ifndef COLLISION_COMPONENT_H
|
||||
#define COLLISION_COMPONENT_H
|
||||
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
|
||||
#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
|
||||
#ifndef PHYSICS_COMPONENT_H
|
||||
#include "T3D/components/physics/physicsComponent.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
#endif
|
||||
|
||||
struct CollisionContactInfo
|
||||
{
|
||||
bool contacted, move;
|
||||
SceneObject *contactObject;
|
||||
VectorF idealContactNormal;
|
||||
VectorF contactNormal;
|
||||
Point3F contactPoint;
|
||||
F32 contactTime;
|
||||
S32 contactTimer;
|
||||
BaseMatInstance *contactMaterial;
|
||||
|
||||
Vector<SceneObject*> overlapObjects;
|
||||
|
||||
void clear()
|
||||
{
|
||||
contacted=move=false;
|
||||
contactObject = NULL;
|
||||
contactNormal.set(0,0,0);
|
||||
contactTime = 0.f;
|
||||
contactTimer = 0;
|
||||
idealContactNormal.set(0, 0, 1);
|
||||
contactMaterial = NULL;
|
||||
overlapObjects.clear();
|
||||
}
|
||||
|
||||
CollisionContactInfo() { clear(); }
|
||||
|
||||
};
|
||||
|
||||
class CollisionComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
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:
|
||||
PhysicsWorld* mPhysicsWorld;
|
||||
PhysicsBody* mPhysicsRep;
|
||||
|
||||
CollisionTimeout* mTimeoutList;
|
||||
static CollisionTimeout* sFreeTimeoutList;
|
||||
|
||||
CollisionList mCollisionList;
|
||||
Vector<CollisionComponent*> mCollisionNotifyList;
|
||||
|
||||
CollisionContactInfo mContactInfo;
|
||||
|
||||
U32 CollisionMoveMask;
|
||||
|
||||
bool mBlockColliding;
|
||||
|
||||
bool mCollisionInited;
|
||||
|
||||
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:
|
||||
CollisionComponent();
|
||||
virtual ~CollisionComponent();
|
||||
|
||||
DECLARE_CONOBJECT(CollisionComponent);
|
||||
|
||||
//Setup
|
||||
virtual void prepCollision() {};
|
||||
|
||||
/// 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);
|
||||
|
||||
CollisionContactInfo* getContactInfo() { return &mContactInfo; }
|
||||
|
||||
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 bool checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start);
|
||||
virtual bool updateCollisions(F32 time, VectorF vector, VectorF velocity);
|
||||
virtual void updateWorkingCollisionSet(const U32 mask);
|
||||
|
||||
//
|
||||
bool buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list);
|
||||
bool buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat, const Box3F &bounds, Convex *convex, Convex *list);
|
||||
|
||||
bool castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info);
|
||||
bool castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials);
|
||||
|
||||
virtual PhysicsCollision* getCollisionData() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual PhysicsBody *getPhysicsRep()
|
||||
{
|
||||
return mPhysicsRep;
|
||||
}
|
||||
|
||||
void buildConvex(const Box3F& box, Convex* convex) {}
|
||||
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) { return false; }
|
||||
|
||||
//
|
||||
Point3F getContactNormal();
|
||||
bool hasContact();
|
||||
S32 getCollisionCount();
|
||||
Point3F getCollisionNormal(S32 collisionIndex);
|
||||
F32 getCollisionAngle(S32 collisionIndex, Point3F upVector);
|
||||
S32 getBestCollision(Point3F upVector);
|
||||
F32 getBestCollisionAngle(VectorF upVector);
|
||||
|
||||
Signal< void(PhysicsCollision* collision) > onCollisionChanged;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,172 +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 "console/engineAPI.h"
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
DefineEngineMethod(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();
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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);
|
||||
}
|
||||
|
|
@ -1,619 +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 "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.mPointList.size() != 0) {
|
||||
mObjBox.minExtents.set(1e10, 1e10, 1e10);
|
||||
mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
|
||||
for (U32 i = 0; i < mCollisionTriggerPolyhedron.mPointList.size(); i++) {
|
||||
mObjBox.minExtents.setMin(mCollisionTriggerPolyhedron.mPointList[i]);
|
||||
mObjBox.maxExtents.setMax(mCollisionTriggerPolyhedron.mPointList[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.mPlaneList;
|
||||
// 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.mPointList.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() && mObjects.size() != 0)
|
||||
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.mPointList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.mPointList.size(); i++)
|
||||
mathWrite(*stream, mCollisionTriggerPolyhedron.mPointList[i]);
|
||||
|
||||
stream->write(mCollisionTriggerPolyhedron.mPlaneList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.mPlaneList.size(); i++)
|
||||
mathWrite(*stream, mCollisionTriggerPolyhedron.mPlaneList[i]);
|
||||
|
||||
stream->write(mCollisionTriggerPolyhedron.mEdgeList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.mEdgeList.size(); i++) {
|
||||
const Polyhedron::Edge& rEdge = mCollisionTriggerPolyhedron.mEdgeList[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.mPointList.setSize(size);
|
||||
for (i = 0; i < tempPH.mPointList.size(); i++)
|
||||
mathRead(*stream, &tempPH.mPointList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.mPlaneList.setSize(size);
|
||||
for (i = 0; i < tempPH.mPlaneList.size(); i++)
|
||||
mathRead(*stream, &tempPH.mPlaneList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.mEdgeList.setSize(size);
|
||||
for (i = 0; i < tempPH.mEdgeList.size(); i++) {
|
||||
Polyhedron::Edge& rEdge = tempPH.mEdgeList[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();
|
||||
}
|
||||
|
|
@ -1,145 +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 _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
|
||||
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#include "T3D/components/collision/raycastColliderComponent.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(RaycastColliderComponent);
|
||||
|
||||
RaycastColliderComponent::RaycastColliderComponent() :
|
||||
mUseVelocity(false),
|
||||
mOwnerPhysicsComponent(nullptr),
|
||||
mRayDirection(VectorF::Zero),
|
||||
mRayLength(1),
|
||||
mPhysicsWorld(nullptr),
|
||||
mOldPosition(Point3F::Zero),
|
||||
mMask(-1)
|
||||
{
|
||||
}
|
||||
|
||||
RaycastColliderComponent::~RaycastColliderComponent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool RaycastColliderComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (PHYSICSMGR)
|
||||
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
|
||||
return true;
|
||||
}
|
||||
void RaycastColliderComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::onComponentAdd()
|
||||
{
|
||||
PhysicsComponent* physComp = mOwner->getComponent<PhysicsComponent>();
|
||||
|
||||
if (physComp)
|
||||
{
|
||||
mOwnerPhysicsComponent = physComp;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::onComponentRemove()
|
||||
{
|
||||
mOwnerPhysicsComponent = nullptr;
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
Parent::componentAddedToOwner(comp);
|
||||
|
||||
PhysicsComponent* physComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physComp)
|
||||
{
|
||||
mOwnerPhysicsComponent = physComp;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
Parent::componentRemovedFromOwner(comp);
|
||||
|
||||
if (mOwnerPhysicsComponent != nullptr && mOwnerPhysicsComponent->getId() == comp->getId())
|
||||
{
|
||||
mOwnerPhysicsComponent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
// Raycast the abstract PhysicsWorld if a PhysicsPlugin exists.
|
||||
bool hit = false;
|
||||
|
||||
Point3F start = mOldPosition;
|
||||
Point3F end;
|
||||
|
||||
if (mUseVelocity)
|
||||
{
|
||||
//our end is the new position
|
||||
end = mOwner->getPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = start + (mRayDirection * mRayLength);
|
||||
}
|
||||
|
||||
RayInfo rInfo;
|
||||
|
||||
if (mPhysicsWorld)
|
||||
hit = mPhysicsWorld->castRay(start, end, &rInfo, Point3F::Zero);
|
||||
else
|
||||
hit = mOwner->getContainer()->castRay(start, end, mMask, &rInfo);
|
||||
|
||||
if (hit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (mUseVelocity)
|
||||
mOldPosition = end;
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#include "T3D/components/physics/physicsComponent.h"
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
|
||||
class RaycastColliderComponent : public CollisionComponent
|
||||
{
|
||||
typedef CollisionComponent Parent;
|
||||
|
||||
//If we're velocity based, we need a physics component on our owner to calculate the vel
|
||||
bool mUseVelocity;
|
||||
PhysicsComponent* mOwnerPhysicsComponent;
|
||||
|
||||
//If we're not using velocity, we'll just have a set direction and length we check against
|
||||
VectorF mRayDirection;
|
||||
F32 mRayLength;
|
||||
|
||||
PhysicsWorld *mPhysicsWorld;
|
||||
|
||||
Point3F mOldPosition;
|
||||
|
||||
U32 mMask;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(RaycastColliderComponent);
|
||||
|
||||
RaycastColliderComponent();
|
||||
~RaycastColliderComponent();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
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 processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void advanceTime(F32 dt);
|
||||
};
|
||||
|
|
@ -1,607 +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 "T3D/components/collision/shapeCollisionComponent.h"
|
||||
#include "T3D/components/collision/shapeCollisionComponent_ScriptBinding.h"
|
||||
#include "T3D/components/physics/physicsComponent.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(ShapeCollisionComponent,
|
||||
"@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 ShapeCollisionComponentInstance()\n"
|
||||
"{\n"
|
||||
" template = ShapeCollisionComponentTemplate;\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")
|
||||
{ ShapeCollisionComponent::None, "None", "No mesh data." },
|
||||
{ ShapeCollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
|
||||
{ ShapeCollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
|
||||
{ ShapeCollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
|
||||
EndImplementEnumType;
|
||||
|
||||
//
|
||||
ShapeCollisionComponent::ShapeCollisionComponent() : CollisionComponent()
|
||||
{
|
||||
mFriendlyName = "Shape Collision Component";
|
||||
|
||||
mFriendlyName = "Shape Collision";
|
||||
mComponentType = "Collision";
|
||||
|
||||
mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
|
||||
|
||||
mOwnerRenderInterface = NULL;
|
||||
mOwnerPhysicsComp = NULL;
|
||||
|
||||
mBlockColliding = true;
|
||||
|
||||
mCollisionType = CollisionMesh;
|
||||
mLOSType = CollisionMesh;
|
||||
mDecalType = CollisionMesh;
|
||||
|
||||
colisionMeshPrefix = StringTable->insert("Collision");
|
||||
|
||||
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
||||
StaticShapeObjectType | VehicleObjectType |
|
||||
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
||||
|
||||
mAnimated = false;
|
||||
|
||||
mCollisionInited = false;
|
||||
}
|
||||
|
||||
ShapeCollisionComponent::~ShapeCollisionComponent()
|
||||
{
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(ShapeCollisionComponent);
|
||||
|
||||
void ShapeCollisionComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||
if (renderInterface)
|
||||
{
|
||||
renderInterface->onShapeInstanceChanged.notify(this, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponent *physicsComp = mOwner->getComponent<PhysicsComponent>();
|
||||
if (physicsComp)
|
||||
{
|
||||
mOwnerPhysicsComp = physicsComp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PHYSICSMGR)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
}
|
||||
}
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::onComponentRemove()
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
mOwnerPhysicsComp = nullptr;
|
||||
|
||||
mCollisionInited = false;
|
||||
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physicsComp)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
mOwnerPhysicsComp = physicsComp;
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = NULL;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physicsComp)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
|
||||
mCollisionInited = false;
|
||||
|
||||
mOwnerPhysicsComp = nullptr;
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::checkDependencies()
|
||||
{
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Collision");
|
||||
|
||||
addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("BlockCollisions", TypeBool, Offset(mBlockColliding, ShapeCollisionComponent), "");
|
||||
|
||||
endGroup("Collision");
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::inspectPostApply()
|
||||
{
|
||||
// Apply any transformations set in the editor
|
||||
Parent::inspectPostApply();
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(ColliderMask);
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
U32 ShapeCollisionComponent::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 ShapeCollisionComponent::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 ShapeCollisionComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
/*bool isSrv = isServerObject();
|
||||
if (mPhysicsRep && mCollisionInited)
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());*/
|
||||
}
|
||||
|
||||
//Setup
|
||||
void ShapeCollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
|
||||
{
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::prepCollision()
|
||||
{
|
||||
if (!mOwner)
|
||||
return;
|
||||
|
||||
// Let the client know that the collision was updated
|
||||
setMaskBits(ColliderMask);
|
||||
|
||||
mOwner->disableCollision();
|
||||
|
||||
if (mCollisionType == None)
|
||||
return;
|
||||
|
||||
//Physics API
|
||||
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());
|
||||
|
||||
mCollisionInited = true;
|
||||
}
|
||||
}
|
||||
|
||||
mOwner->enableCollision();
|
||||
|
||||
onCollisionChanged.trigger(colShape);
|
||||
}
|
||||
|
||||
//Update
|
||||
void ShapeCollisionComponent::processTick()
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
//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 ShapeCollisionComponent::updatePhysics()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PhysicsCollision* ShapeCollisionComponent::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("ShapeCollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
|
||||
}
|
||||
|
||||
return colShape;
|
||||
}
|
||||
|
||||
bool ShapeCollisionComponent::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* ShapeCollisionComponent::buildColShapes()
|
||||
{
|
||||
PROFILE_SCOPE(ShapeCollisionComponent_buildColShapes);
|
||||
|
||||
PhysicsCollision *colShape = NULL;
|
||||
U32 surfaceKey = 0;
|
||||
|
||||
TSShape* shape = mOwnerRenderInterface->getShape();
|
||||
|
||||
if (shape == nullptr)
|
||||
return nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,137 +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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
#ifndef SHAPE_COLLISION_COMPONENT_H
|
||||
#define SHAPE_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_COMPONENT_H
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#endif
|
||||
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#endif
|
||||
|
||||
class TSShapeInstance;
|
||||
class SceneRenderState;
|
||||
class ShapeCollisionComponent;
|
||||
class PhysicsBody;
|
||||
class PhysicsWorld;
|
||||
|
||||
class ShapeCollisionComponent : public CollisionComponent
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
protected:
|
||||
MeshType mCollisionType;
|
||||
MeshType mDecalType;
|
||||
MeshType mLOSType;
|
||||
|
||||
Vector<S32> mCollisionDetails;
|
||||
Vector<S32> mLOSDetails;
|
||||
|
||||
StringTableEntry colisionMeshPrefix;
|
||||
|
||||
RenderComponentInterface* mOwnerRenderInterface;
|
||||
|
||||
PhysicsComponent* mOwnerPhysicsComp;
|
||||
|
||||
//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:
|
||||
ShapeCollisionComponent();
|
||||
virtual ~ShapeCollisionComponent();
|
||||
DECLARE_CONOBJECT(ShapeCollisionComponent);
|
||||
|
||||
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();
|
||||
|
||||
//Setup
|
||||
virtual void prepCollision();
|
||||
|
||||
//Updates
|
||||
virtual void processTick();
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
typedef ShapeCollisionComponent::MeshType CollisionMeshMeshType;
|
||||
DefineEnumType(CollisionMeshMeshType);
|
||||
|
||||
#endif // COLLISION_COMPONENT_H
|
||||
|
|
@ -1,172 +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 "console/engineAPI.h"
|
||||
#include "T3D/components/collision/shapeCollisionComponent.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
DefineEngineMethod(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();
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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);
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
#include "simpleHitboxComponent.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SimpleHitboxComponent);
|
||||
|
||||
SimpleHitboxComponent::SimpleHitboxComponent() :
|
||||
// location of head, torso, legs
|
||||
mBoxHeadPercentage(0.85f),
|
||||
mBoxTorsoPercentage(0.55f),
|
||||
mBoxLeftPercentage(0),
|
||||
mBoxRightPercentage(1),
|
||||
mBoxBackPercentage(0),
|
||||
mBoxFrontPercentage(1),
|
||||
mIsProne(false)
|
||||
{
|
||||
}
|
||||
|
||||
SimpleHitboxComponent::~SimpleHitboxComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::initPersistFields()
|
||||
{
|
||||
addGroup("Hitbox");
|
||||
addField("headPercentage", TypeF32, Offset(mBoxHeadPercentage, SimpleHitboxComponent), "");
|
||||
addField("torsoPercentage", TypeF32, Offset(mBoxTorsoPercentage, SimpleHitboxComponent), "");
|
||||
addField("leftPercentage", TypeF32, Offset(mBoxLeftPercentage, SimpleHitboxComponent), "");
|
||||
addField("rightPercentage", TypeF32, Offset(mBoxRightPercentage, SimpleHitboxComponent), "");
|
||||
addField("backPercentage", TypeF32, Offset(mBoxBackPercentage, SimpleHitboxComponent), "");
|
||||
addField("frontPercentage", TypeF32, Offset(mBoxFrontPercentage, SimpleHitboxComponent), "");
|
||||
|
||||
addField("isProne", TypeF32, Offset(mIsProne, SimpleHitboxComponent), "");
|
||||
endGroup("Hitbox");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
Parent::componentAddedToOwner(comp);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
Parent::componentRemovedFromOwner(comp);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad)
|
||||
{
|
||||
Point3F newPoint;
|
||||
mOwner->getWorldToObj().mulP(in_rPos, &newPoint);
|
||||
|
||||
Point3F boxSize = mOwner->getObjBox().getExtents();
|
||||
|
||||
F32 boxHeight = boxSize.z;
|
||||
F32 pointHeight = newPoint.z;
|
||||
|
||||
if (mIsProne)
|
||||
pointHeight = newPoint.y; //this assumes we're y-forward
|
||||
|
||||
F32 zTorso = mBoxTorsoPercentage;
|
||||
F32 zHead = mBoxHeadPercentage;
|
||||
|
||||
zTorso *= boxHeight;
|
||||
zHead *= boxHeight;
|
||||
|
||||
if (pointHeight <= zTorso)
|
||||
out_rpVert = "legs";
|
||||
else if (pointHeight <= zHead)
|
||||
out_rpVert = "torso";
|
||||
else
|
||||
out_rpVert = "head";
|
||||
|
||||
if (dStrcmp(out_rpVert, "head") != 0)
|
||||
{
|
||||
if (newPoint.y >= 0.0f)
|
||||
{
|
||||
if (newPoint.x <= 0.0f)
|
||||
out_rpQuad = "front_left";
|
||||
else
|
||||
out_rpQuad = "front_right";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newPoint.x <= 0.0f)
|
||||
out_rpQuad = "back_left";
|
||||
else
|
||||
out_rpQuad = "back_right";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 backToFront = boxSize.x;
|
||||
F32 leftToRight = boxSize.y;
|
||||
|
||||
F32 backPoint = backToFront * mBoxBackPercentage;
|
||||
F32 frontPoint = backToFront * mBoxFrontPercentage;
|
||||
F32 leftPoint = leftToRight * mBoxLeftPercentage;
|
||||
F32 rightPoint = leftToRight * mBoxRightPercentage;
|
||||
|
||||
S32 index = 0;
|
||||
if (newPoint.y < backPoint)
|
||||
index += 0;
|
||||
else if (newPoint.y >= frontPoint)
|
||||
index += 3;
|
||||
else
|
||||
index += 6;
|
||||
|
||||
if (newPoint.x < leftPoint)
|
||||
index += 0;
|
||||
else if (newPoint.x >= rightPoint)
|
||||
index += 1;
|
||||
else
|
||||
index += 2;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: out_rpQuad = "left_back"; break;
|
||||
case 1: out_rpQuad = "middle_back"; break;
|
||||
case 2: out_rpQuad = "right_back"; break;
|
||||
case 3: out_rpQuad = "left_middle"; break;
|
||||
case 4: out_rpQuad = "middle_middle"; break;
|
||||
case 5: out_rpQuad = "right_middle"; break;
|
||||
case 6: out_rpQuad = "left_front"; break;
|
||||
case 7: out_rpQuad = "middle_front"; break;
|
||||
case 8: out_rpQuad = "right_front"; break;
|
||||
|
||||
default:
|
||||
AssertFatal(0, "Bad non-tant index");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(SimpleHitboxComponent, getDamageLocation, const char*, (Point3F pos), ,
|
||||
"@brief Get the named damage location and modifier for a given world position.\n\n"
|
||||
|
||||
"the Player object can simulate different hit locations based on a pre-defined set "
|
||||
"of PlayerData defined percentages. These hit percentages divide up the Player's "
|
||||
"bounding box into different regions. The diagram below demonstrates how the various "
|
||||
"PlayerData properties split up the bounding volume:\n\n"
|
||||
|
||||
"<img src=\"images/player_damageloc.png\">\n\n"
|
||||
|
||||
"While you may pass in any world position and getDamageLocation() will provide a best-fit "
|
||||
"location, you should be aware that this can produce some interesting results. For example, "
|
||||
"any position that is above PlayerData::boxHeadPercentage will be considered a 'head' hit, even "
|
||||
"if the world position is high in the sky. Therefore it may be wise to keep the passed in point "
|
||||
"to somewhere on the surface of, or within, the Player's bounding volume.\n\n"
|
||||
|
||||
"@note This method will not return an accurate location when the player is "
|
||||
"prone or swimming.\n\n"
|
||||
|
||||
"@param pos A world position for which to retrieve a body region on this player.\n"
|
||||
|
||||
"@return a string containing two words (space separated strings), where the "
|
||||
"first is a location and the second is a modifier.\n\n"
|
||||
|
||||
"Posible locations:<ul>"
|
||||
"<li>head</li>"
|
||||
"<li>torso</li>"
|
||||
"<li>legs</li></ul>\n"
|
||||
|
||||
"Head modifiers:<ul>"
|
||||
"<li>left_back</li>"
|
||||
"<li>middle_back</li>"
|
||||
"<li>right_back</li>"
|
||||
"<li>left_middle</li>"
|
||||
"<li>middle_middle</li>"
|
||||
"<li>right_middle</li>"
|
||||
"<li>left_front</li>"
|
||||
"<li>middle_front</li>"
|
||||
"<li>right_front</li></ul>\n"
|
||||
|
||||
"Legs/Torso modifiers:<ul>"
|
||||
"<li>front_left</li>"
|
||||
"<li>front_right</li>"
|
||||
"<li>back_left</li>"
|
||||
"<li>back_right</li></ul>\n"
|
||||
|
||||
"@see PlayerData::boxHeadPercentage\n"
|
||||
"@see PlayerData::boxHeadFrontPercentage\n"
|
||||
"@see PlayerData::boxHeadBackPercentage\n"
|
||||
"@see PlayerData::boxHeadLeftPercentage\n"
|
||||
"@see PlayerData::boxHeadRightPercentage\n"
|
||||
"@see PlayerData::boxTorsoPercentage\n"
|
||||
)
|
||||
{
|
||||
const char *buffer1;
|
||||
const char *buffer2;
|
||||
|
||||
object->getDamageLocation(pos, buffer1, buffer2);
|
||||
|
||||
static const U32 bufSize = 128;
|
||||
char *buff = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(buff, bufSize, "%s %s", buffer1, buffer2);
|
||||
return buff;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
class SimpleHitboxComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
// location of head, torso, legs
|
||||
F32 mBoxHeadPercentage;
|
||||
F32 mBoxTorsoPercentage;
|
||||
|
||||
// damage locations
|
||||
F32 mBoxLeftPercentage;
|
||||
F32 mBoxRightPercentage;
|
||||
F32 mBoxBackPercentage;
|
||||
F32 mBoxFrontPercentage;
|
||||
|
||||
// Is our hitbox horizontal, usually due to being prone, swimming, etc
|
||||
bool mIsProne;
|
||||
|
||||
public:
|
||||
SimpleHitboxComponent();
|
||||
~SimpleHitboxComponent();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void advanceTime(F32 dt);
|
||||
|
||||
void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad);
|
||||
|
||||
DECLARE_CONOBJECT(SimpleHitboxComponent);
|
||||
};
|
||||
|
|
@ -1,739 +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/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"
|
||||
#include "T3D/assets/MaterialAsset.h"
|
||||
|
||||
#define DECLARE_NATIVE_COMPONENT( ComponentType ) \
|
||||
Component* staticComponentTemplate = new ComponentType; \
|
||||
Sim::gNativeComponentSet->addObject(staticComponentTemplate);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Component::Component()
|
||||
{
|
||||
mFriendlyName = StringTable->EmptyString();
|
||||
mFromResource = StringTable->EmptyString();
|
||||
mComponentType = StringTable->EmptyString();
|
||||
mComponentGroup = StringTable->EmptyString();
|
||||
mNetworkType = StringTable->EmptyString();
|
||||
mTemplateName = StringTable->EmptyString();
|
||||
//mDependency = StringTable->EmptyString();
|
||||
|
||||
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;
|
||||
|
||||
mOriginatingAssetId = StringTable->EmptyString();
|
||||
|
||||
mIsServerObject = true;
|
||||
|
||||
componentIdx = 0;
|
||||
|
||||
mHidden = false;
|
||||
mEnabled = true;
|
||||
|
||||
mDirtyMaskBits = 0;
|
||||
}
|
||||
|
||||
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, "");
|
||||
|
||||
addField("originatingAsset", TypeComponentAssetPtr, Offset(mOriginatingAsset, Component),
|
||||
"Asset that spawned this component, used for tracking/housekeeping", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
||||
|
||||
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);
|
||||
setMaskBits(NamespaceMask);
|
||||
|
||||
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 = 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->removeComponent(this, false);
|
||||
}
|
||||
|
||||
mOwner = owner;
|
||||
|
||||
if (mOwner != NULL)
|
||||
{
|
||||
mOwner->onComponentAdded.notify(this, &Component::componentAddedToOwner);
|
||||
mOwner->onComponentRemoved.notify(this, &Component::componentRemovedFromOwner);
|
||||
}
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(OwnerMask);
|
||||
|
||||
//if we have any outstanding maskbits, push them along to have the network update happen on the entity
|
||||
if (mDirtyMaskBits != 0 && mOwner)
|
||||
{
|
||||
mOwner->setMaskBits(Entity::ComponentsUpdateMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Component::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void Component::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void Component::setMaskBits(U32 orMask)
|
||||
{
|
||||
AssertFatal(orMask != 0, "Invalid net mask bits set.");
|
||||
|
||||
if (mOwner)
|
||||
mOwner->setComponentNetMask(this, orMask);
|
||||
}
|
||||
|
||||
U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = 0;
|
||||
|
||||
/*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);
|
||||
}
|
||||
|
||||
/*if (stream->writeFlag(mask & NamespaceMask))
|
||||
{
|
||||
const char* name = getName();
|
||||
if (stream->writeFlag(name && name[0]))
|
||||
stream->writeString(String(name));
|
||||
|
||||
if (stream->writeFlag(mSuperClassName && mSuperClassName[0]))
|
||||
stream->writeString(String(mSuperClassName));
|
||||
|
||||
if (stream->writeFlag(mClassName && mClassName[0]))
|
||||
stream->writeString(String(mClassName));
|
||||
}*/
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void Component::unpackUpdate(NetConnection *con, BitStream *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();
|
||||
}
|
||||
|
||||
/*if (stream->readFlag())
|
||||
{
|
||||
if (stream->readFlag())
|
||||
{
|
||||
char name[256];
|
||||
stream->readString(name);
|
||||
assignName(name);
|
||||
}
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
char superClassname[256];
|
||||
stream->readString(superClassname);
|
||||
mSuperClassName = superClassname;
|
||||
}
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
char classname[256];
|
||||
stream->readString(classname);
|
||||
mClassName = classname;
|
||||
}
|
||||
|
||||
linkNamespaces();
|
||||
}*/
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
StringTableEntry Component::getComponentName()
|
||||
{
|
||||
return getNamespace()->getName();
|
||||
}
|
||||
|
||||
//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 */, const char* customLabel /* = ""*/)
|
||||
{
|
||||
StringTableEntry stFieldName = StringTable->insert(fieldName);
|
||||
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
if (mFields[i].mFieldName == stFieldName)
|
||||
return;
|
||||
}
|
||||
|
||||
ComponentField field;
|
||||
|
||||
if (customLabel != "")
|
||||
field.mFieldLabel = customLabel;
|
||||
else
|
||||
field.mFieldLabel = stFieldName;
|
||||
|
||||
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 = TypeMaterialAssetPtr;
|
||||
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 if (fieldType == StringTable->insert("string"))
|
||||
fieldTypeMask = TypeString;
|
||||
else if (fieldType == StringTable->insert("colorI"))
|
||||
fieldTypeMask = TypeColorI;
|
||||
else if (fieldType == StringTable->insert("colorF"))
|
||||
fieldTypeMask = TypeColorF;
|
||||
else if (fieldType == StringTable->insert("ease"))
|
||||
fieldTypeMask = TypeEaseF;
|
||||
else if (fieldType == StringTable->insert("gameObject"))
|
||||
fieldTypeMask = TypeGameObjectAssetPtr;
|
||||
else
|
||||
fieldTypeMask = TypeString;
|
||||
field.mFieldTypeName = fieldType;
|
||||
|
||||
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))
|
||||
{
|
||||
dsize_t newDescLen = dStrlen(desc) + 1;
|
||||
newDesc = new char[newDescLen];
|
||||
dStrcpy(newDesc, desc, newDescLen);
|
||||
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DefineEngineMethod(Component, beginGroup, void, (String groupName),,
|
||||
"@brief 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(groupName);
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, endGroup, void, (),,
|
||||
"@brief 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();
|
||||
}
|
||||
|
||||
DefineEngineMethod(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);
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, getComponentFieldCount, S32, (),,
|
||||
"@brief 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.
|
||||
|
||||
DefineEngineMethod(Component, getComponentField, const char *, (S32 index),,
|
||||
"@brief 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(index);
|
||||
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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, setComponentield, const char *, (S32 index),,
|
||||
"@brief 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(index);
|
||||
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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, getComponentFieldType, const char *, (String fieldName), ,
|
||||
"Get the number of static fields on the object.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
ComponentField *field = object->getComponentField(fieldName);
|
||||
if (field == NULL)
|
||||
return "";
|
||||
|
||||
return field->mFieldTypeName;;
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, getBehaviorFieldUserData, const char *, (S32 index),,
|
||||
"@brief 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(index);
|
||||
if (field == NULL)
|
||||
return "";
|
||||
|
||||
return field->mUserData;
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, getComponentFieldDescription, const char *, (S32 index),,
|
||||
"@brief 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(index);
|
||||
if (field == NULL)
|
||||
return "";
|
||||
|
||||
return field->mFieldDescription ? field->mFieldDescription : "";
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, addDependency, void, (String behaviorName),,
|
||||
"@brief 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(behaviorName);
|
||||
}
|
||||
|
||||
DefineEngineMethod(Component, setDirty, void, (),,
|
||||
"@brief 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);
|
||||
}
|
||||
|
|
@ -1,229 +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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
#ifndef _ASSET_PTR_H_
|
||||
#include "assets/assetPtr.h"
|
||||
#endif
|
||||
#ifndef COMPONENT_ASSET_H
|
||||
#include "T3D/assets/ComponentAsset.h"
|
||||
#endif
|
||||
|
||||
class Entity;
|
||||
class Namespace;
|
||||
|
||||
struct ComponentField
|
||||
{
|
||||
StringTableEntry mFieldLabel;
|
||||
StringTableEntry mFieldName;
|
||||
StringTableEntry mFieldDescription;
|
||||
|
||||
StringTableEntry mFieldTypeName;
|
||||
S32 mFieldType;
|
||||
StringTableEntry mUserData;
|
||||
|
||||
StringTableEntry mDefaultValue;
|
||||
|
||||
StringTableEntry mGroup;
|
||||
|
||||
StringTableEntry mDependency;
|
||||
|
||||
bool mHidden;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class Component : public SimObject, public UpdateInterface
|
||||
{
|
||||
typedef SimObject 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;
|
||||
|
||||
StringTableEntry mOriginatingAssetId;
|
||||
AssetPtr<ComponentAsset> mOriginatingAsset;
|
||||
|
||||
U32 mDirtyMaskBits;
|
||||
|
||||
bool mIsServerObject;
|
||||
|
||||
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);
|
||||
|
||||
//Overridden by components that actually care
|
||||
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, const char* customLabel = "");
|
||||
|
||||
/// 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),
|
||||
NamespaceMask = BIT(4),
|
||||
NextFreeMask = BIT(5)
|
||||
};
|
||||
|
||||
virtual void setMaskBits(U32 orMask);
|
||||
virtual void clearMaskBits() {
|
||||
mDirtyMaskBits = 0;
|
||||
}
|
||||
|
||||
bool isServerObject() { return mIsServerObject; }
|
||||
bool isClientObject() { return !mIsServerObject; }
|
||||
|
||||
void setIsServerObject(bool isServerObj) { mIsServerObject = isServerObj; }
|
||||
|
||||
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(){}
|
||||
|
||||
StringTableEntry getComponentName();
|
||||
};
|
||||
|
||||
#endif // COMPONENT_H
|
||||
|
|
@ -1,101 +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 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
|
||||
|
|
@ -1,149 +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 "T3D/components/game/controlObjectComponent.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ControlObjectComponent::ControlObjectComponent() : Component(),
|
||||
mOwnerConnection(nullptr),
|
||||
mOwnerConnectionId(0)
|
||||
{
|
||||
mFriendlyName = "Control Object";
|
||||
mComponentType = "Game";
|
||||
|
||||
mDescription = getDescriptionText("Allows owner entity to be controlled by a client.");
|
||||
}
|
||||
|
||||
ControlObjectComponent::~ControlObjectComponent()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(ControlObjectComponent);
|
||||
|
||||
bool ControlObjectComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControlObjectComponent::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 ControlObjectComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
if (mOwnerConnection && mOwnerConnection->getControlObject() == nullptr)
|
||||
{
|
||||
mOwnerConnection->setControlObject(mOwner);
|
||||
mOwnerConnectionId = mOwnerConnection->getId();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlObjectComponent::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
|
||||
if (mOwnerConnection)
|
||||
{
|
||||
mOwnerConnection->setControlObject(nullptr);
|
||||
mOwnerConnectionId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ControlObjectComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("clientOwner", TypeS32, Offset(mOwnerConnectionId, ControlObjectComponent), "Client connection ID");
|
||||
}
|
||||
|
||||
void ControlObjectComponent::setConnectionControlObject(GameConnection* conn)
|
||||
{
|
||||
if (conn)
|
||||
{
|
||||
if (conn->getControlObject() == nullptr)
|
||||
{
|
||||
conn->setControlObject(mOwner);
|
||||
mOwnerConnectionId = conn->getId();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Inform the old control object it's no longer in control?
|
||||
conn->setControlObject(mOwner);
|
||||
mOwnerConnectionId = conn->getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlObjectComponent::onClientConnect(GameConnection* conn)
|
||||
{
|
||||
if (conn && conn->getControlObject() == nullptr)
|
||||
{
|
||||
conn->setControlObject(mOwner);
|
||||
mOwnerConnectionId = conn->getId();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlObjectComponent::onClientDisconnect(GameConnection* conn)
|
||||
{
|
||||
if (conn && conn->getControlObject() == mOwner)
|
||||
{
|
||||
conn->setControlObject(nullptr);
|
||||
mOwnerConnectionId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(ControlObjectComponent, onClientConnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
|
||||
"Triggers a signal call to all components for a certain function.")
|
||||
{
|
||||
if (conn == nullptr)
|
||||
return;
|
||||
|
||||
object->onClientConnect(conn);
|
||||
}
|
||||
|
||||
DefineEngineMethod(ControlObjectComponent, onClientDisconnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
|
||||
"Triggers a signal call to all components for a certain function.")
|
||||
{
|
||||
if (conn == nullptr)
|
||||
return;
|
||||
|
||||
object->onClientDisconnect(conn);
|
||||
}
|
||||
|
||||
DefineEngineMethod(ControlObjectComponent, setConnectionControlObject, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
|
||||
"Triggers a signal call to all components for a certain function.")
|
||||
{
|
||||
if (conn == nullptr)
|
||||
return;
|
||||
|
||||
object->setConnectionControlObject(conn);
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
|
||||
class ControlObjectComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
GameConnection* mOwnerConnection;
|
||||
S32 mOwnerConnectionId;
|
||||
|
||||
public:
|
||||
ControlObjectComponent();
|
||||
~ControlObjectComponent();
|
||||
|
||||
DECLARE_CONOBJECT(ControlObjectComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
void onClientConnect(GameConnection* conn);
|
||||
void onClientDisconnect(GameConnection* conn);
|
||||
void setConnectionControlObject(GameConnection* conn);
|
||||
};
|
||||
|
|
@ -1,359 +0,0 @@
|
|||
#include "T3D/components/game/followPathComponent.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(FollowPathComponent);
|
||||
|
||||
IMPLEMENT_CALLBACK(FollowPathComponent, onNode, void, (S32 node), (node),
|
||||
"A script callback that indicates the path camera has arrived at a specific node in its path. Server side only.\n"
|
||||
"@param Node Unique ID assigned to this node.\n");
|
||||
|
||||
FollowPathComponent::FollowPathComponent()
|
||||
{
|
||||
delta.time = 0;
|
||||
delta.timeVec = 0;
|
||||
|
||||
//mDataBlock = 0;
|
||||
mState = Forward;
|
||||
mNodeBase = 0;
|
||||
mNodeCount = 0;
|
||||
mPosition = 0;
|
||||
mTarget = 0;
|
||||
mTargetSet = false;
|
||||
}
|
||||
|
||||
FollowPathComponent::~FollowPathComponent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FollowPathComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
/*// Initialize from the current transform.
|
||||
if (!mNodeCount)
|
||||
{
|
||||
QuatF rot(getTransform());
|
||||
Point3F pos = getPosition();
|
||||
mSpline.removeAll();
|
||||
mSpline.push_back(new CameraSpline::Knot(pos, rot, 1,
|
||||
CameraSpline::Knot::NORMAL, CameraSpline::Knot::SPLINE));
|
||||
mNodeCount = 1;
|
||||
}
|
||||
|
||||
//
|
||||
mObjBox.maxExtents = mObjScale;
|
||||
mObjBox.minExtents = mObjScale;
|
||||
mObjBox.minExtents.neg();
|
||||
resetWorldBox();*/
|
||||
|
||||
return true;
|
||||
}
|
||||
void FollowPathComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void FollowPathComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void FollowPathComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
Parent::componentAddedToOwner(comp);
|
||||
}
|
||||
|
||||
void FollowPathComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
Parent::componentRemovedFromOwner(comp);
|
||||
}
|
||||
|
||||
void FollowPathComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
// Move to new time
|
||||
advancePosition(TickMs);
|
||||
|
||||
// Set new position
|
||||
MatrixF mat;
|
||||
interpolateMat(mPosition, &mat);
|
||||
mOwner->setTransform(mat);
|
||||
|
||||
mOwner->updateContainer();
|
||||
}
|
||||
|
||||
void FollowPathComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
|
||||
MatrixF mat;
|
||||
interpolateMat(delta.time + (delta.timeVec * dt), &mat);
|
||||
mOwner->setRenderTransform(mat);
|
||||
}
|
||||
|
||||
void FollowPathComponent::interpolateMat(F32 pos, MatrixF* mat)
|
||||
{
|
||||
/*CameraSpline::Knot knot;
|
||||
mSpline.value(pos - mNodeBase, &knot);
|
||||
knot.mRotation.setMatrix(mat);
|
||||
mat->setPosition(knot.mPosition);*/
|
||||
}
|
||||
|
||||
void FollowPathComponent::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
}
|
||||
|
||||
void FollowPathComponent::advancePosition(S32 ms)
|
||||
{
|
||||
/*delta.timeVec = mPosition;
|
||||
|
||||
// Advance according to current speed
|
||||
if (mState == Forward) {
|
||||
mPosition = mSpline.advanceTime(mPosition - mNodeBase, ms);
|
||||
if (mPosition > F32(mNodeCount - 1))
|
||||
mPosition = F32(mNodeCount - 1);
|
||||
mPosition += (F32)mNodeBase;
|
||||
if (mTargetSet && mPosition >= mTarget) {
|
||||
mTargetSet = false;
|
||||
mPosition = mTarget;
|
||||
mState = Stop;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mState == Backward) {
|
||||
mPosition = mSpline.advanceTime(mPosition - mNodeBase, -ms);
|
||||
if (mPosition < 0)
|
||||
mPosition = 0;
|
||||
mPosition += mNodeBase;
|
||||
if (mTargetSet && mPosition <= mTarget) {
|
||||
mTargetSet = false;
|
||||
mPosition = mTarget;
|
||||
mState = Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// Script callbacks
|
||||
if (int(mPosition) != int(delta.timeVec))
|
||||
onNode(int(mPosition));
|
||||
|
||||
// Set frame interpolation
|
||||
delta.time = mPosition;
|
||||
delta.timeVec -= mPosition;*/
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/*void FollowPathComponent::getCameraTransform(F32* pos, MatrixF* mat)
|
||||
{
|
||||
// Overide the ShapeBase method to skip all the first/third person support.
|
||||
getRenderEyeTransform(mat);
|
||||
|
||||
// Apply Camera FX.
|
||||
mat->mul(gCamFXMgr.getTrans());
|
||||
}*/
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void FollowPathComponent::setPosition(F32 pos)
|
||||
{
|
||||
mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1));
|
||||
MatrixF mat;
|
||||
interpolateMat(mPosition, &mat);
|
||||
mOwner->setTransform(mat);
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
|
||||
void FollowPathComponent::setTarget(F32 pos)
|
||||
{
|
||||
mTarget = pos;
|
||||
mTargetSet = true;
|
||||
if (mTarget > mPosition)
|
||||
mState = Forward;
|
||||
else
|
||||
if (mTarget < mPosition)
|
||||
mState = Backward;
|
||||
else {
|
||||
mTargetSet = false;
|
||||
mState = Stop;
|
||||
}
|
||||
setMaskBits(TargetMask | StateMask);
|
||||
}
|
||||
|
||||
void FollowPathComponent::setState(State s)
|
||||
{
|
||||
mState = s;
|
||||
setMaskBits(StateMask);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FollowPathComponent::reset(F32 speed)
|
||||
{
|
||||
/*CameraSpline::Knot *knot = new CameraSpline::Knot;
|
||||
mSpline.value(mPosition - mNodeBase, knot);
|
||||
if (speed)
|
||||
knot->mSpeed = speed;
|
||||
mSpline.removeAll();
|
||||
mSpline.push_back(knot);
|
||||
|
||||
mNodeBase = 0;
|
||||
mNodeCount = 1;
|
||||
mPosition = 0;
|
||||
mTargetSet = false;
|
||||
mState = Forward;
|
||||
setMaskBits(StateMask | PositionMask | WindowMask | TargetMask);*/
|
||||
}
|
||||
|
||||
/*void FollowPathComponent::pushBack(CameraSpline::Knot *knot)
|
||||
{
|
||||
// Make room at the end
|
||||
if (mNodeCount == NodeWindow) {
|
||||
delete mSpline.remove(mSpline.getKnot(0));
|
||||
mNodeBase++;
|
||||
}
|
||||
else
|
||||
mNodeCount++;
|
||||
|
||||
// Fill in the new node
|
||||
mSpline.push_back(knot);
|
||||
setMaskBits(WindowMask);
|
||||
|
||||
// Make sure the position doesn't fall off
|
||||
if (mPosition < mNodeBase) {
|
||||
mPosition = (F32)mNodeBase;
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
}
|
||||
|
||||
void FollowPathComponent::pushFront(CameraSpline::Knot *knot)
|
||||
{
|
||||
// Make room at the front
|
||||
if (mNodeCount == NodeWindow)
|
||||
delete mSpline.remove(mSpline.getKnot(mNodeCount));
|
||||
else
|
||||
mNodeCount++;
|
||||
mNodeBase--;
|
||||
|
||||
// Fill in the new node
|
||||
mSpline.push_front(knot);
|
||||
setMaskBits(WindowMask);
|
||||
|
||||
// Make sure the position doesn't fall off
|
||||
if (mPosition > F32(mNodeBase + (NodeWindow - 1)))
|
||||
{
|
||||
mPosition = F32(mNodeBase + (NodeWindow - 1));
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
}*/
|
||||
|
||||
void FollowPathComponent::popFront()
|
||||
{
|
||||
/*if (mNodeCount < 2)
|
||||
return;
|
||||
|
||||
// Remove the first node. Node base and position are unaffected.
|
||||
mNodeCount--;
|
||||
delete mSpline.remove(mSpline.getKnot(0));
|
||||
|
||||
if (mPosition > 0)
|
||||
mPosition--;*/
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void FollowPathComponent::onNode(S32 node)
|
||||
{
|
||||
//if (!isGhost())
|
||||
// onNode_callback(node);
|
||||
|
||||
}
|
||||
|
||||
/*U32 FollowPathComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (stream->writeFlag(mask & StateMask))
|
||||
stream->writeInt(mState, StateBits);
|
||||
|
||||
if (stream->writeFlag(mask & PositionMask))
|
||||
stream->write(mPosition);
|
||||
|
||||
if (stream->writeFlag(mask & TargetMask))
|
||||
if (stream->writeFlag(mTargetSet))
|
||||
stream->write(mTarget);
|
||||
|
||||
if (stream->writeFlag(mask & WindowMask)) {
|
||||
stream->write(mNodeBase);
|
||||
stream->write(mNodeCount);
|
||||
for (S32 i = 0; i < mNodeCount; i++) {
|
||||
CameraSpline::Knot *knot = mSpline.getKnot(i);
|
||||
mathWrite(*stream, knot->mPosition);
|
||||
mathWrite(*stream, knot->mRotation);
|
||||
stream->write(knot->mSpeed);
|
||||
stream->writeInt(knot->mType, CameraSpline::Knot::NUM_TYPE_BITS);
|
||||
stream->writeInt(knot->mPath, CameraSpline::Knot::NUM_PATH_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of the data is part of the control object packet update.
|
||||
// If we're controlled by this client, we don't need to send it.
|
||||
if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FollowPathComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
// StateMask
|
||||
if (stream->readFlag())
|
||||
mState = stream->readInt(StateBits);
|
||||
|
||||
// PositionMask
|
||||
if (stream->readFlag())
|
||||
{
|
||||
stream->read(&mPosition);
|
||||
delta.time = mPosition;
|
||||
delta.timeVec = 0;
|
||||
}
|
||||
|
||||
// TargetMask
|
||||
if (stream->readFlag())
|
||||
{
|
||||
mTargetSet = stream->readFlag();
|
||||
if (mTargetSet)
|
||||
stream->read(&mTarget);
|
||||
}
|
||||
|
||||
// WindowMask
|
||||
if (stream->readFlag())
|
||||
{
|
||||
mSpline.removeAll();
|
||||
stream->read(&mNodeBase);
|
||||
stream->read(&mNodeCount);
|
||||
for (S32 i = 0; i < mNodeCount; i++)
|
||||
{
|
||||
CameraSpline::Knot *knot = new CameraSpline::Knot();
|
||||
mathRead(*stream, &knot->mPosition);
|
||||
mathRead(*stream, &knot->mRotation);
|
||||
stream->read(&knot->mSpeed);
|
||||
knot->mType = (CameraSpline::Knot::Type)stream->readInt(CameraSpline::Knot::NUM_TYPE_BITS);
|
||||
knot->mPath = (CameraSpline::Knot::Path)stream->readInt(CameraSpline::Knot::NUM_PATH_BITS);
|
||||
mSpline.push_back(knot);
|
||||
}
|
||||
}
|
||||
|
||||
// Controlled by the client?
|
||||
if (stream->readFlag())
|
||||
return;
|
||||
|
||||
}*/
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
class FollowPathComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
enum State {
|
||||
Forward,
|
||||
Backward,
|
||||
Stop,
|
||||
StateBits = 3
|
||||
};
|
||||
|
||||
enum MaskBits {
|
||||
WindowMask = Parent::NextFreeMask,
|
||||
PositionMask = Parent::NextFreeMask + 1,
|
||||
TargetMask = Parent::NextFreeMask + 2,
|
||||
StateMask = Parent::NextFreeMask + 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
struct StateDelta {
|
||||
F32 time;
|
||||
F32 timeVec;
|
||||
};
|
||||
StateDelta delta;
|
||||
|
||||
enum Constants {
|
||||
NodeWindow = 128 // Maximum number of active nodes
|
||||
};
|
||||
|
||||
//
|
||||
//PathCameraData* mDataBlock;
|
||||
//CameraSpline mSpline;
|
||||
S32 mNodeBase;
|
||||
S32 mNodeCount;
|
||||
F32 mPosition;
|
||||
S32 mState;
|
||||
F32 mTarget;
|
||||
bool mTargetSet;
|
||||
|
||||
void interpolateMat(F32 pos, MatrixF* mat);
|
||||
void advancePosition(S32 ms);
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(FollowPathComponent);
|
||||
|
||||
FollowPathComponent();
|
||||
~FollowPathComponent();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
//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 processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void advanceTime(F32 dt);
|
||||
|
||||
//
|
||||
//void onEditorEnable();
|
||||
//void onEditorDisable();
|
||||
|
||||
void onNode(S32 node);
|
||||
|
||||
void reset(F32 speed = 1);
|
||||
//void pushFront(CameraSpline::Knot *knot);
|
||||
//void pushBack(CameraSpline::Knot *knot);
|
||||
void popFront();
|
||||
|
||||
void setPosition(F32 pos);
|
||||
void setTarget(F32 pos);
|
||||
void setState(State s);
|
||||
|
||||
DECLARE_CALLBACK(void, onNode, (S32 node));
|
||||
};
|
||||
|
|
@ -1,171 +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 "T3D/components/game/interactComponent.h"
|
||||
#include "scene/sceneContainer.h"
|
||||
#include "T3D/components/game/interactableComponent.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
InteractComponent::InteractComponent() : Component(),
|
||||
mUseRaycastInteract(true),
|
||||
mUseRenderedRaycast(false),
|
||||
mUseRadiusInteract(true),
|
||||
mInteractRadius(1.5),
|
||||
mInteractRayDist(1.5),
|
||||
mUseNaturalReach(false)
|
||||
{
|
||||
mFriendlyName = "Interact";
|
||||
mComponentType = "Game";
|
||||
|
||||
mDescription = getDescriptionText("Allows owner entity interact.");
|
||||
}
|
||||
|
||||
InteractComponent::~InteractComponent()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(InteractComponent);
|
||||
|
||||
bool InteractComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InteractComponent::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 InteractComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
}
|
||||
|
||||
void InteractComponent::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void InteractComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void InteractComponent::processTick()
|
||||
{
|
||||
if (isClientObject())
|
||||
return; //this shouldn't happen
|
||||
|
||||
//First, if we're doing rays, try that as if we get a hit, we can always assume that's the "correct" option
|
||||
if (mUseRaycastInteract)
|
||||
{
|
||||
mOwner->disableCollision();
|
||||
|
||||
Point3F start = mOwner->getPosition();
|
||||
Point3F end = mOwner->getTransform().getForwardVector() * mInteractRayDist + start;
|
||||
|
||||
RayInfo rinfo;
|
||||
S32 ret = 0;
|
||||
|
||||
if (mUseRenderedRaycast)
|
||||
{
|
||||
rinfo.generateTexCoord = true;
|
||||
if (gServerContainer.castRayRendered(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
|
||||
ret = rinfo.object->getId();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gServerContainer.castRay(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
|
||||
ret = rinfo.object->getId();
|
||||
}
|
||||
|
||||
mOwner->enableCollision();
|
||||
|
||||
Entity* hitEntity = static_cast<Entity*>(rinfo.object);
|
||||
|
||||
if (hitEntity)
|
||||
{
|
||||
//call on that badboy!
|
||||
InteractableComponent* iComp = hitEntity->getComponent<InteractableComponent>();
|
||||
|
||||
if (iComp)
|
||||
{
|
||||
iComp->interact(this, rinfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If not using rays or no hit, then do the radius search if we have it
|
||||
if (mUseRadiusInteract)
|
||||
{
|
||||
gServerContainer.initRadiusSearch(mOwner->getPosition(), mInteractRadius, EntityObjectType);
|
||||
|
||||
F32 lastBestDist = 9999;
|
||||
F32 lastBestWeight = 0;
|
||||
Entity* bestFitEntity = nullptr;
|
||||
|
||||
Entity* e = static_cast<Entity*>(gServerContainer.containerSearchNextObject());
|
||||
|
||||
while (e != nullptr)
|
||||
{
|
||||
InteractableComponent* iComp = e->getComponent<InteractableComponent>();
|
||||
|
||||
if (iComp != nullptr)
|
||||
{
|
||||
F32 weight = iComp->getWeight();
|
||||
VectorF distVec = e->getPosition() - mOwner->getPosition();
|
||||
F32 dist = distVec.len();
|
||||
|
||||
//If the weight is better, always pick it
|
||||
if (weight > lastBestWeight)
|
||||
{
|
||||
lastBestDist = dist;
|
||||
lastBestWeight = weight;
|
||||
bestFitEntity = e;
|
||||
}
|
||||
//Otherwise, if the weight is matched and the distance is closer, pick that
|
||||
else if (weight >= lastBestWeight && lastBestDist < dist)
|
||||
{
|
||||
lastBestWeight = weight;
|
||||
bestFitEntity = e;
|
||||
}
|
||||
}
|
||||
|
||||
e = static_cast<Entity*>(gServerContainer.containerSearchNextObject()); //loop 'round
|
||||
}
|
||||
|
||||
if (bestFitEntity)
|
||||
{
|
||||
//call on that badboy!
|
||||
InteractableComponent* iComp = bestFitEntity->getComponent<InteractableComponent>();
|
||||
|
||||
iComp->interact(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
|
||||
class InteractComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
bool mUseRaycastInteract;
|
||||
bool mUseRenderedRaycast;
|
||||
bool mUseRadiusInteract;
|
||||
|
||||
F32 mInteractRadius;
|
||||
F32 mInteractRayDist;
|
||||
|
||||
//Adjusts the length of the ray based on the idea of further reach if you look down because of crouching
|
||||
bool mUseNaturalReach;
|
||||
|
||||
public:
|
||||
InteractComponent();
|
||||
~InteractComponent();
|
||||
|
||||
DECLARE_CONOBJECT(InteractComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
virtual void processTick();
|
||||
};
|
||||
|
|
@ -1,94 +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 "T3D/components/game/interactableComponent.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
InteractableComponent::InteractableComponent() : Component(),
|
||||
mInteractableWeight(1)
|
||||
{
|
||||
mFriendlyName = "Interactable";
|
||||
mComponentType = "Game";
|
||||
|
||||
mDescription = getDescriptionText("Allows owner entity to be interacted with.");
|
||||
}
|
||||
|
||||
InteractableComponent::~InteractableComponent()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(InteractableComponent);
|
||||
|
||||
bool InteractableComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InteractableComponent::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 InteractableComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
}
|
||||
|
||||
void InteractableComponent::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void InteractableComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("interactableWeight", TypeF32, Offset(mInteractableWeight, InteractableComponent), "Controls importance values when using radius mode for interaction");
|
||||
}
|
||||
|
||||
void InteractableComponent::interact(InteractComponent* interactor)
|
||||
{
|
||||
if (interactor != nullptr)
|
||||
{
|
||||
mOwner->notifyComponents("onInteract", interactor->getIdString());
|
||||
|
||||
if(isMethod("onInteract"))
|
||||
Con::executef(this, "onInteract", interactor);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractableComponent::interact(InteractComponent* interactor, RayInfo rayInfo)
|
||||
{
|
||||
if (interactor != nullptr)
|
||||
{
|
||||
mOwner->notifyComponents("onInteract", interactor->getIdString());
|
||||
|
||||
if (isMethod("onInteract"))
|
||||
Con::executef(this, "onInteract", interactor);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
#include "T3D/components/game/interactComponent.h"
|
||||
|
||||
class InteractableComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
//Controls importance values when using radius mode for interaction
|
||||
F32 mInteractableWeight;
|
||||
|
||||
public:
|
||||
InteractableComponent();
|
||||
~InteractableComponent();
|
||||
|
||||
DECLARE_CONOBJECT(InteractableComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
void interact(InteractComponent* interactor);
|
||||
void interact(InteractComponent* interactor, RayInfo rayInfo);
|
||||
|
||||
F32 getWeight() { return mInteractableWeight; }
|
||||
};
|
||||
|
|
@ -1,437 +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 "T3D/components/game/stateMachine.h"
|
||||
|
||||
StateMachine::StateMachine()
|
||||
{
|
||||
mStateStartTime = -1;
|
||||
mStateTime = 0;
|
||||
|
||||
mStartingState = "";
|
||||
|
||||
mCurCreateState = NULL;
|
||||
|
||||
mStateMachineFile = StringTable->EmptyString();
|
||||
|
||||
mCurCreateState = nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,261 +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 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];
|
||||
|
||||
return StateField(); //return a blank one
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -1,214 +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 "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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,81 +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 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
|
||||
|
|
@ -1,357 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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();
|
||||
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
if(colComp)
|
||||
{
|
||||
colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::onComponentRemove()
|
||||
{
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
if(colComp)
|
||||
{
|
||||
colComp->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void TriggerComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId())
|
||||
return;
|
||||
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
if (colComp)
|
||||
{
|
||||
colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId()) //?????????
|
||||
return;
|
||||
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
if (colComp)
|
||||
{
|
||||
colComp->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
|
||||
CollisionComponent *colComp = enterEntity->getComponent<CollisionComponent>();
|
||||
if (colComp)
|
||||
{
|
||||
colComp->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
|
||||
CollisionComponent *myColComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
//wait, how would we NOT have this?
|
||||
if (myColComp)
|
||||
{
|
||||
//anywho, build our list and then we'll check intersections
|
||||
ClippedPolyList myList;
|
||||
|
||||
MatrixF ownerTransform = mOwner->getTransform();
|
||||
myList.setTransform(&ownerTransform, mOwner->getScale());
|
||||
myList.setObject(mOwner);
|
||||
|
||||
myColComp->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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_COMPONENT_H
|
||||
#include "T3D/components/collision/collisionComponent.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_
|
||||
|
|
@ -1,376 +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 "T3D/components/physics/physicsComponent.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"
|
||||
|
||||
#include "T3D/physics/physicsPlugin.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", "");
|
||||
|
||||
mFriendlyName = "Physics Component";
|
||||
mComponentType = "Physics";
|
||||
|
||||
mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
|
||||
|
||||
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_CONOBJECT(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::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void PhysicsComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("gravity", TypePoint3F, Offset(mGravity, PhysicsComponent));
|
||||
addField("velocity", TypePoint3F, Offset(mVelocity, PhysicsComponent));
|
||||
addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
|
||||
}
|
||||
|
||||
//Networking
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//Setup
|
||||
void PhysicsComponent::prepCollision()
|
||||
{
|
||||
if (!mOwner)
|
||||
return;
|
||||
|
||||
if (mConvexList != NULL)
|
||||
mConvexList->nukeList();
|
||||
|
||||
mOwner->enableCollision();
|
||||
_updatePhysics();
|
||||
}
|
||||
|
||||
void PhysicsComponent::_updatePhysics()
|
||||
{
|
||||
SAFE_DELETE( mPhysicsRep );
|
||||
|
||||
if ( !PHYSICSMGR )
|
||||
return;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void PhysicsComponent::buildConvex(const Box3F& box, Convex* convex)
|
||||
{
|
||||
convex = nullptr;
|
||||
}
|
||||
|
||||
//Updates
|
||||
void PhysicsComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
||||
//Point3F rot = mDelta.rot + mDelta.rotVec * dt;
|
||||
|
||||
setRenderPosition(pos,dt);
|
||||
}
|
||||
|
||||
void PhysicsComponent::updatePos(const F32 travelTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void PhysicsComponent::updateForces()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
mLastContainerInfo = ContainerQueryInfo();
|
||||
mLastContainerInfo.box = mOwner->getWorldBox();
|
||||
mLastContainerInfo.mass = mMass;
|
||||
|
||||
mOwner->getContainer()->findObjects(mLastContainerInfo.box, WaterObjectType | PhysicalZoneObjectType, findRouter, &mLastContainerInfo);
|
||||
|
||||
//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 (mLastContainerInfo.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 = (mLastContainerInfo.waterCoverage * mLastContainerInfo.waterViscosity) +
|
||||
(1.0f - mLastContainerInfo.waterCoverage) * mDrag;
|
||||
//mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
|
||||
}
|
||||
|
||||
//mAppliedForce = info.appliedForce;
|
||||
mGravityMod = mLastContainerInfo.gravityScale;
|
||||
}
|
||||
|
||||
//Events
|
||||
void PhysicsComponent::updateVelocity(const F32 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);
|
||||
}
|
||||
|
||||
//Setters
|
||||
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::setVelocity(const VectorF& vel)
|
||||
{
|
||||
mVelocity = vel;
|
||||
|
||||
mAtRest = false;
|
||||
mAtRestCounter = 0;
|
||||
setMaskBits(VelocityMask);
|
||||
}
|
||||
|
||||
//Getters
|
||||
PhysicsBody *PhysicsComponent::getPhysicsRep()
|
||||
{
|
||||
/*if(mOwner)
|
||||
{
|
||||
Entity* ac = dynamic_cast<Entity*>(mOwner);
|
||||
if(ac)
|
||||
return ac->mPhysicsRep;
|
||||
}*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
#ifndef PHYSICS_COMPONENT_H
|
||||
#define PHYSICS_COMPONENT_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
|
||||
|
||||
PhysicsBody* mPhysicsRep;
|
||||
PhysicsWorld* mPhysicsWorld;
|
||||
|
||||
Convex* mConvexList;
|
||||
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
|
||||
|
||||
ContainerQueryInfo mLastContainerInfo;
|
||||
|
||||
public:
|
||||
PhysicsComponent();
|
||||
virtual ~PhysicsComponent();
|
||||
DECLARE_CONOBJECT(PhysicsComponent);
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
//Components
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
//Setup
|
||||
void prepCollision();
|
||||
virtual void _updatePhysics();
|
||||
virtual void buildConvex(const Box3F& box, Convex* convex);
|
||||
|
||||
//Update
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void updatePos(const F32 dt);
|
||||
|
||||
virtual void updateForces();
|
||||
void updateContainer();
|
||||
|
||||
//Physics 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(const Point3F& p, const Point3F &normal) { return false; }
|
||||
|
||||
//Networking
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
//Events
|
||||
virtual void updateVelocity(const F32 dt);
|
||||
|
||||
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);
|
||||
|
||||
//Gets
|
||||
F32 getMass() { return mMass; }
|
||||
virtual PhysicsBody *getPhysicsRep();
|
||||
virtual Point3F getVelocity() { return mVelocity; }
|
||||
virtual void getOriginVector(const Point3F &p, Point3F* r);
|
||||
virtual void getVelocity(const Point3F& r, Point3F* v);
|
||||
virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
|
||||
|
||||
};
|
||||
|
||||
#endif // _COMPONENT_H_
|
||||
|
|
@ -1,49 +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 PHYSICS_COMPONENT_INTERFACE_H
|
||||
#define PHYSICS_COMPONENT_INTERFACE_H
|
||||
|
||||
#ifndef CORE_INTERFACES_H
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
#endif
|
||||
|
||||
class PhysicsComponentInterface : public Interface<PhysicsComponentInterface>
|
||||
{
|
||||
protected:
|
||||
VectorF mVelocity;
|
||||
F32 mMass;
|
||||
|
||||
F32 mGravityMod;
|
||||
|
||||
public:
|
||||
void updateForces();
|
||||
|
||||
VectorF getVelocity() { return mVelocity; }
|
||||
void setVelocity(VectorF vel) { mVelocity = vel; }
|
||||
|
||||
F32 getMass() { return mMass; }
|
||||
|
||||
Signal< void(VectorF normal, Vector<SceneObject*> overlappedObjects) > onPhysicsCollision;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,779 +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 "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/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() : PhysicsComponent()
|
||||
{
|
||||
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 = nullptr;
|
||||
mPhysicsWorld = nullptr;
|
||||
|
||||
mOwnerCollisionComp = nullptr;
|
||||
mIntegrationCount = 0;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
CollisionComponent *collisionComp = mOwner->getComponent<CollisionComponent>();
|
||||
if (collisionComp)
|
||||
{
|
||||
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
|
||||
mOwnerCollisionComp = collisionComp;
|
||||
}
|
||||
|
||||
updatePhysics();
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId())
|
||||
return;
|
||||
|
||||
CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
|
||||
if (collisionComp)
|
||||
{
|
||||
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
|
||||
mOwnerCollisionComp = collisionComp;
|
||||
updatePhysics();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId()) //?????????
|
||||
return;
|
||||
|
||||
CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
|
||||
if (collisionComp)
|
||||
{
|
||||
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
|
||||
mOwnerCollisionComp = nullptr;
|
||||
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", TypeBool, 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
|
||||
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;
|
||||
|
||||
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;
|
||||
xRot.set(EulerF(mOwner->getRotation().asEulerF().x, 0, 0));
|
||||
zRot.set(EulerF(0, 0, mOwner->getRotation().asEulerF().z));//reset prior uses
|
||||
MatrixF rot;
|
||||
rot.mul(zRot, xRot);
|
||||
rot.getColumn(0, &moveVec);
|
||||
|
||||
moveVec *= move->x;
|
||||
rot.getColumn(1, &tv);//reset prior uses
|
||||
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;
|
||||
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
|
||||
CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
|
||||
if (colComp)
|
||||
{
|
||||
colComp->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateContainer();
|
||||
|
||||
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;
|
||||
|
||||
mContactInfo.run = *run;
|
||||
mContactInfo.jump = *jump;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -1,208 +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 PLAYER_CONTORLLER_COMPONENT_H
|
||||
#define PLAYER_CONTORLLER_COMPONENT_H
|
||||
|
||||
#ifndef PHYSICS_COMPONENT_H
|
||||
#include "T3D/components/physics/physicsComponent.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 COLLISION_COMPONENT_H
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#endif
|
||||
|
||||
class SceneRenderState;
|
||||
class PhysicsWorld;
|
||||
class PhysicsPlayer;
|
||||
class SimplePhysicsBehaviorInstance;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class PlayerControllerComponent : public PhysicsComponent
|
||||
{
|
||||
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;
|
||||
|
||||
CollisionComponent* mOwnerCollisionComp;
|
||||
|
||||
struct ContactInfo
|
||||
{
|
||||
bool contacted, jump, run;
|
||||
SceneObject *contactObject;
|
||||
VectorF contactNormal;
|
||||
F32 contactTime;
|
||||
|
||||
void clear()
|
||||
{
|
||||
contacted = jump = run = false;
|
||||
contactObject = nullptr;
|
||||
contactNormal.set(0,0,0);
|
||||
contactTime = 0;
|
||||
}
|
||||
|
||||
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_
|
||||
|
|
@ -1,467 +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 "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() : PhysicsComponent()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,183 +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 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_H
|
||||
#include "T3D/components/physics/physicsComponent.h"
|
||||
#endif
|
||||
|
||||
class PhysicsBody;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class RigidBodyComponent : public PhysicsComponent
|
||||
{
|
||||
typedef PhysicsComponent 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_
|
||||
|
|
@ -1,391 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/components/physics/simplePhysicsComponent.h"
|
||||
#include "T3D/components/collision/collisionComponent.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"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Callbacks
|
||||
IMPLEMENT_CALLBACK( SimplePhysicsComponent, updateMove, void, ( SimplePhysicsComponent* 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
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SimplePhysicsComponent::SimplePhysicsComponent() : PhysicsComponent()
|
||||
{
|
||||
mBuoyancy = 0.f;
|
||||
mFriction = 0.3f;
|
||||
mElasticity = 0.4f;
|
||||
mMaxVelocity = 3000.f;
|
||||
mSticky = false;
|
||||
|
||||
mDrag = 0.5;
|
||||
|
||||
mVelocity = Point3F::Zero;
|
||||
|
||||
moveSpeed = Point3F(1, 1, 1);
|
||||
|
||||
mFriendlyName = "Simple Physics";
|
||||
mComponentType = "Physics";
|
||||
|
||||
mDescription = getDescriptionText("Simple physics Component that allows gravity and impulses.");
|
||||
}
|
||||
|
||||
SimplePhysicsComponent::~SimplePhysicsComponent()
|
||||
{
|
||||
for(S32 i = 0;i < mFields.size();++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(SimplePhysicsComponent);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SimplePhysicsComponent::onAdd()
|
||||
{
|
||||
if(! Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField( "moveSpeed", TypePoint3F, Offset(moveSpeed, SimplePhysicsComponent), "");
|
||||
}
|
||||
|
||||
U32 SimplePhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
}
|
||||
|
||||
//
|
||||
void SimplePhysicsComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
if (!isServerObject() || !isActive())
|
||||
return;
|
||||
|
||||
//
|
||||
//if (mCollisionObject && !--mCollisionTimeout)
|
||||
// mCollisionObject = 0;
|
||||
|
||||
// Warp to catch up to server
|
||||
if (mDelta.warpCount < mDelta.warpTicks)
|
||||
{
|
||||
mDelta.warpCount++;
|
||||
|
||||
// Set new pos.
|
||||
mOwner->getTransform().getColumn(3,&mDelta.pos);
|
||||
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);
|
||||
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();
|
||||
|
||||
updateForces();
|
||||
updatePos(TickSec);
|
||||
|
||||
// Wrap up interpolation info
|
||||
mDelta.pos = mOwner->getPosition();
|
||||
mDelta.posVec -= mOwner->getPosition();
|
||||
//mDelta.rot[1] = mRigid.angPosition;
|
||||
|
||||
// Update container database
|
||||
setTransform(mOwner->getTransform());
|
||||
setMaskBits(UpdateMask);
|
||||
updateContainer();
|
||||
}
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
// Client side interpolation
|
||||
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
||||
MatrixF mat = mOwner->getRenderTransform();
|
||||
mat.setColumn(3,pos);
|
||||
mOwner->setRenderTransform(mat);
|
||||
mDelta.dt = dt;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::updatePos(const F32 travelTime)
|
||||
{
|
||||
mOwner->getTransform().getColumn(3,&mDelta.posVec);
|
||||
|
||||
// When mounted to another object, only Z rotation used.
|
||||
if (mOwner->isMounted()) {
|
||||
mVelocity = mOwner->getObjectMount()->getVelocity();
|
||||
setPosition(Point3F(0.0f, 0.0f, 0.0f));
|
||||
setMaskBits(UpdateMask);
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F newPos;
|
||||
|
||||
if ( mVelocity.isZero() )
|
||||
newPos = mDelta.posVec;
|
||||
else
|
||||
newPos = _move( travelTime );
|
||||
//}
|
||||
|
||||
// Set new position
|
||||
// If on the client, calc delta for backstepping
|
||||
if (isClientObject())
|
||||
{
|
||||
mDelta.pos = newPos;
|
||||
mDelta.posVec = mDelta.posVec - mDelta.pos;
|
||||
mDelta.dt = 1.0f;
|
||||
}
|
||||
|
||||
setPosition( newPos );
|
||||
setMaskBits( UpdateMask );
|
||||
updateContainer();
|
||||
|
||||
/*if (!isGhost())
|
||||
{
|
||||
// Do mission area callbacks on the server as well
|
||||
checkMissionArea();
|
||||
}*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F SimplePhysicsComponent::_move( const F32 travelTime )
|
||||
{
|
||||
// Try and move to new pos
|
||||
F32 totalMotion = 0.0f;
|
||||
|
||||
Point3F start;
|
||||
Point3F initialPosition;
|
||||
mOwner->getTransform().getColumn(3,&start);
|
||||
initialPosition = start;
|
||||
|
||||
VectorF firstNormal(0.0f, 0.0f, 0.0f);
|
||||
//F32 maxStep = mDataBlock->maxStepHeight;
|
||||
F32 time = travelTime;
|
||||
U32 count = 0;
|
||||
S32 sMoveRetryCount = 5;
|
||||
|
||||
CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
|
||||
|
||||
if(!colComp)
|
||||
return start + mVelocity * time;
|
||||
|
||||
colComp->clearCollisionList();
|
||||
|
||||
for (; count < sMoveRetryCount; count++)
|
||||
{
|
||||
F32 speed = mVelocity.len();
|
||||
if (!speed)
|
||||
break;
|
||||
|
||||
Point3F end = start + mVelocity * time;
|
||||
Point3F distance = end - start;
|
||||
|
||||
bool collided = colComp->checkCollisions(time, &mVelocity, start);
|
||||
|
||||
if (colComp->getCollisionList()->getCount() != 0 && colComp->getCollisionList()->getTime() < 1.0f)
|
||||
{
|
||||
// Set to collision point
|
||||
F32 velLen = mVelocity.len();
|
||||
|
||||
F32 dt = time * getMin(colComp->getCollisionList()->getTime(), 1.0f);
|
||||
start += mVelocity * dt;
|
||||
time -= dt;
|
||||
|
||||
totalMotion += velLen * dt;
|
||||
|
||||
// Back off...
|
||||
if ( velLen > 0.f )
|
||||
{
|
||||
F32 newT = getMin(0.01f / velLen, dt);
|
||||
start -= mVelocity * newT;
|
||||
totalMotion -= velLen * newT;
|
||||
}
|
||||
|
||||
// Pick the surface most parallel to the face that was hit.
|
||||
U32 colCount = colComp->getCollisionList()->getCount();
|
||||
|
||||
const Collision *collision = colComp->getCollision(0);
|
||||
const Collision *cp = collision + 1;
|
||||
const Collision *ep = collision + colComp->getCollisionList()->getCount();
|
||||
for (; cp != ep; cp++)
|
||||
{
|
||||
U32 colCountLoop = colComp->getCollisionList()->getCount();
|
||||
|
||||
//TODO: Move this somewhere else
|
||||
if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
|
||||
{
|
||||
if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
|
||||
{
|
||||
if(!collidingEntityColComp->doesBlockColliding())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp->faceDot > collision->faceDot)
|
||||
collision = cp;
|
||||
}
|
||||
|
||||
//check the last/first one just incase
|
||||
if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
|
||||
{
|
||||
if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
|
||||
{
|
||||
if(!collidingEntityColComp->doesBlockColliding())
|
||||
{
|
||||
//if our ideal surface doesn't stop us, just move along
|
||||
return start + mVelocity * time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//F32 bd = _doCollisionImpact( collision, wasFalling );
|
||||
F32 bd = -mDot( mVelocity, collision->normal);
|
||||
|
||||
// Subtract out velocity
|
||||
F32 sNormalElasticity = 0.01f;
|
||||
VectorF dv = collision->normal * (bd + sNormalElasticity);
|
||||
mVelocity += dv;
|
||||
if (count == 0)
|
||||
{
|
||||
firstNormal = collision->normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
// Re-orient velocity along the crease.
|
||||
if (mDot(dv,firstNormal) < 0.0f &&
|
||||
mDot(collision->normal,firstNormal) < 0.0f)
|
||||
{
|
||||
VectorF nv;
|
||||
mCross(collision->normal,firstNormal,&nv);
|
||||
F32 nvl = nv.len();
|
||||
if (nvl)
|
||||
{
|
||||
if (mDot(nv,mVelocity) < 0.0f)
|
||||
nvl = -nvl;
|
||||
nv *= mVelocity.len() / nvl;
|
||||
mVelocity = nv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
totalMotion += (end - start).len();
|
||||
start = end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U32 colCountThree = colComp->getCollisionList()->getCount();
|
||||
|
||||
if (colCountThree != 0)
|
||||
bool derp = true;
|
||||
|
||||
if (count == sMoveRetryCount)
|
||||
{
|
||||
// Failed to move
|
||||
start = initialPosition;
|
||||
mVelocity.set(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::updateForces()
|
||||
{
|
||||
// Acceleration due to gravity
|
||||
mVelocity += (mGravity * mGravityMod) * TickMs;
|
||||
F32 len = mVelocity.len();
|
||||
|
||||
if (mMaxVelocity > 0 && mAbs(len) > mMaxVelocity)
|
||||
{
|
||||
Point3F excess = mVelocity * (1.0 - (mMaxVelocity / len));
|
||||
excess *= 0.1f;
|
||||
mVelocity -= excess;
|
||||
}
|
||||
|
||||
// Container buoyancy & drag
|
||||
if(mOwner->getContainerInfo().waterCoverage > 0.65f)
|
||||
mVelocity -= mBuoyancy * (mGravity * mGravityMod) * TickMs;
|
||||
|
||||
mVelocity -= mVelocity * mDrag * TickMs;
|
||||
|
||||
if( mVelocity.isZero() )
|
||||
mVelocity = Point3F::Zero;
|
||||
else
|
||||
setMaskBits(VelocityMask);
|
||||
}
|
||||
|
||||
//
|
||||
void SimplePhysicsComponent::setVelocity(const VectorF& vel)
|
||||
{
|
||||
Parent::setVelocity(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef SIMPLE_PHYSICS_COMPONENT_H
|
||||
#define SIMPLE_PHYSICS_COMPONENT_H
|
||||
|
||||
#ifndef PHYSICS_COMPONENT_H
|
||||
#include "T3D/components/physics/physicsComponent.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
|
||||
|
||||
class SceneRenderState;
|
||||
class PhysicsBody;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class SimplePhysicsComponent : public PhysicsComponent
|
||||
{
|
||||
typedef PhysicsComponent Parent;
|
||||
|
||||
protected:
|
||||
F32 mBuoyancy;
|
||||
F32 mFriction;
|
||||
F32 mElasticity;
|
||||
F32 mMaxVelocity;
|
||||
bool mSticky;
|
||||
|
||||
U32 mIntegrationCount;
|
||||
|
||||
Point3F moveSpeed;
|
||||
|
||||
Point3F mStickyCollisionPos;
|
||||
Point3F mStickyCollisionNormal;
|
||||
|
||||
public:
|
||||
SimplePhysicsComponent();
|
||||
virtual ~SimplePhysicsComponent();
|
||||
DECLARE_CONOBJECT(SimplePhysicsComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void updatePos(const F32 dt);
|
||||
void updateForces();
|
||||
|
||||
void updateMove(const Move* move);
|
||||
Point3F _move(const F32 travelTime);
|
||||
|
||||
//virtual void onComponentRemove();
|
||||
|
||||
virtual VectorF getVelocity() { return mVelocity; }
|
||||
virtual void setVelocity(const VectorF& vel);
|
||||
|
||||
//
|
||||
DECLARE_CALLBACK(void, updateMove, (SimplePhysicsComponent* obj));
|
||||
};
|
||||
|
||||
#endif // _COMPONENT_H_
|
||||
|
|
@ -1,611 +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/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/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(), mShape(nullptr)
|
||||
{
|
||||
mFriendlyName = "Mesh Component";
|
||||
mComponentType = "Render";
|
||||
|
||||
mDescription = getDescriptionText("Causes the object to render a non-animating 3d shape using the file provided.");
|
||||
|
||||
mNetworked = true;
|
||||
|
||||
mShapeName = StringTable->EmptyString();
|
||||
mShapeAsset = StringTable->EmptyString();
|
||||
|
||||
mMeshAsset = StringTable->EmptyString();
|
||||
mMeshAssetId = StringTable->EmptyString();
|
||||
|
||||
mInterfaceData = new MeshRenderSystemInterface();
|
||||
}
|
||||
|
||||
MeshComponent::~MeshComponent()
|
||||
{
|
||||
if (mInterfaceData)
|
||||
SAFE_DELETE(mInterfaceData);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(MeshComponent);
|
||||
|
||||
//==========================================================================================
|
||||
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();
|
||||
|
||||
if (isClientObject())
|
||||
mInterfaceData->mIsClient = true;
|
||||
|
||||
// if (mInterfaceData != nullptr)
|
||||
// mInterfaceData->mIsClient = isClientObject();
|
||||
|
||||
//get the default shape, if any
|
||||
updateShape();
|
||||
}
|
||||
|
||||
void MeshComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
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", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn, &writeShape,
|
||||
"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.
|
||||
if (mInterfaceData == nullptr)
|
||||
return false;
|
||||
|
||||
mMeshAssetId = StringTable->insert(assetName);
|
||||
|
||||
mMeshAsset = mMeshAssetId;
|
||||
|
||||
if (mMeshAsset.isNull())
|
||||
{
|
||||
Con::errorf("[MeshComponent] Failed to load mesh asset.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mMeshAsset->onShapeChanged.notify(this, &MeshComponent::_shapeAssetUpdated);
|
||||
|
||||
mShapeName = mMeshAssetId;
|
||||
mShapeAsset = mShapeName;
|
||||
updateShape(); //make sure we force the update to resize the owner bounds
|
||||
setMaskBits(ShapeMask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MeshComponent::_shapeAssetUpdated(ShapeAsset* asset)
|
||||
{
|
||||
updateShape();
|
||||
}
|
||||
|
||||
void MeshComponent::updateShape()
|
||||
{
|
||||
if (mInterfaceData == nullptr)
|
||||
return;
|
||||
|
||||
//if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
|
||||
if ((mShapeName && mShapeName[0] != '\0') || (mMeshAssetId && mMeshAssetId[0] != '\0'))
|
||||
{
|
||||
if (mMeshAsset == NULL)
|
||||
return;
|
||||
|
||||
mShape = mMeshAsset->getShape();
|
||||
|
||||
if (!mMeshAsset->getShape())
|
||||
return;
|
||||
|
||||
setupShape();
|
||||
|
||||
//Do this on both the server and client
|
||||
S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size(); //mMeshAsset->getMaterialCount();
|
||||
|
||||
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++)
|
||||
{
|
||||
StringTableEntry materialname = StringTable->insert(mMeshAsset->getShape()->materialList->getMaterialName(i).c_str());
|
||||
|
||||
//Iterate through our assetList to find the compliant entry in our matList
|
||||
for (U32 m = 0; m < mMeshAsset->getMaterialCount(); m++)
|
||||
{
|
||||
AssetPtr<MaterialAsset> matAsset = mMeshAsset->getMaterialAsset(m);
|
||||
|
||||
if (matAsset->getMaterialDefinitionName() == materialname)
|
||||
{
|
||||
dSprintf(matFieldName, 128, "MaterialSlot%d", i);
|
||||
|
||||
addComponentField(matFieldName, "A material used in the shape file", "Material", matAsset->getAssetId(), "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (materialCount > 0)
|
||||
mComponentGroup = "";
|
||||
}
|
||||
|
||||
if (mOwner != NULL)
|
||||
{
|
||||
Point3F min, max, pos;
|
||||
pos = mOwner->getPosition();
|
||||
|
||||
mOwner->getWorldToObj().mulP(pos);
|
||||
|
||||
min = mMeshAsset->getShape()->mBounds.minExtents;
|
||||
max = mMeshAsset->getShape()->mBounds.maxExtents;
|
||||
|
||||
if (mInterfaceData)
|
||||
{
|
||||
mInterfaceData->mBounds.set(min, max);
|
||||
mInterfaceData->mScale = mOwner->getScale();
|
||||
mInterfaceData->mTransform = mOwner->getRenderTransform();
|
||||
}
|
||||
|
||||
mOwner->setObjectBox(Box3F(min, max));
|
||||
|
||||
mOwner->resetWorldBox();
|
||||
|
||||
if (mOwner->getSceneManager() != NULL)
|
||||
mOwner->getSceneManager()->notifyObjectDirty(mOwner);
|
||||
}
|
||||
|
||||
//finally, notify that our shape was changed
|
||||
onShapeInstanceChanged.trigger(this);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshComponent::setupShape()
|
||||
{
|
||||
mInterfaceData->mShapeInstance = new TSShapeInstance(mMeshAsset->getShape(), true);
|
||||
}
|
||||
|
||||
void MeshComponent::_onResourceChanged( const Torque::Path &path )
|
||||
{
|
||||
/*bool srv = isServerObject();
|
||||
|
||||
if (mInterfaceData == nullptr)
|
||||
return;
|
||||
|
||||
String filePath;
|
||||
if (mMeshAsset)
|
||||
filePath = Torque::Path(mMeshAsset->getShapeFilename());
|
||||
|
||||
if (!mMeshAsset || path != Torque::Path(mMeshAsset->getShapeFilename()) )
|
||||
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].assetId.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.assetId = String(con->unpackNetStringHandleU(stream).getString());
|
||||
|
||||
//do the lookup, now
|
||||
newMatMap.matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newMatMap.assetId);
|
||||
|
||||
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();
|
||||
|
||||
if (mOwner->isMounted())
|
||||
{
|
||||
MatrixF wrldPos = mOwner->getWorldTransform();
|
||||
Point3F wrldPosPos = wrldPos.getPosition();
|
||||
|
||||
Point3F mntPs = mat.getPosition();
|
||||
EulerF mntRt = RotationF(mat).asEulerF();
|
||||
|
||||
bool tr = true;
|
||||
}
|
||||
|
||||
mat.scale(objScale);
|
||||
GFX->setWorldMatrix(mat);
|
||||
|
||||
mShapeInstance->render(rdata);*/
|
||||
}
|
||||
|
||||
void MeshComponent::updateMaterials()
|
||||
{
|
||||
if (mChangingMaterials.empty() || !mMeshAsset->getShape())
|
||||
return;
|
||||
|
||||
TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
|
||||
pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
|
||||
|
||||
bool found = false;
|
||||
const Vector<String> &materialNames = pMatList->getMaterialNameList();
|
||||
for ( S32 i = 0; i < materialNames.size(); i++ )
|
||||
{
|
||||
if (found)
|
||||
break;
|
||||
|
||||
for(U32 m=0; m < mChangingMaterials.size(); m++)
|
||||
{
|
||||
if(mChangingMaterials[m].slot == i)
|
||||
{
|
||||
//Fetch the actual material asset
|
||||
pMatList->renameMaterial( i, mChangingMaterials[m].matAsset->getMaterialDefinitionName());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mChangingMaterials.clear();
|
||||
|
||||
// Initialize the material instances
|
||||
mInterfaceData->mShapeInstance->initMaterialList();
|
||||
}
|
||||
|
||||
MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
|
||||
{
|
||||
if (mInterfaceData != nullptr && !mMeshAsset.isNull() && mMeshAsset->isAssetValid() && mMeshAsset->getShape())
|
||||
{
|
||||
S32 nodeCount = getShape()->nodes.size();
|
||||
|
||||
if(nodeIdx >= 0 && nodeIdx < nodeCount)
|
||||
{
|
||||
//animate();
|
||||
MatrixF nodeTransform = mInterfaceData->mShapeInstance->mNodeTransforms[nodeIdx];
|
||||
const Point3F& scale = mOwner->getScale();
|
||||
|
||||
// The position of the node needs to be scaled.
|
||||
Point3F position = nodeTransform.getPosition();
|
||||
position.convolve(scale);
|
||||
nodeTransform.setPosition(position);
|
||||
|
||||
MatrixF finalTransform = MatrixF::Identity;
|
||||
|
||||
finalTransform.mul(mOwner->getRenderTransform(), nodeTransform);
|
||||
|
||||
return finalTransform;
|
||||
}
|
||||
}
|
||||
|
||||
return MatrixF::Identity;
|
||||
}
|
||||
|
||||
S32 MeshComponent::getNodeByName(String nodeName)
|
||||
{
|
||||
if (mMeshAsset->getShape())
|
||||
{
|
||||
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;
|
||||
|
||||
//Safe to assume the inbound value for the material will be a MaterialAsset, so lets do a lookup on the name
|
||||
MaterialAsset* matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newValue);
|
||||
if (!matAsset)
|
||||
return;
|
||||
|
||||
bool found = false;
|
||||
for(U32 i=0; i < mChangingMaterials.size(); i++)
|
||||
{
|
||||
if(mChangingMaterials[i].slot == slot)
|
||||
{
|
||||
mChangingMaterials[i].matAsset = matAsset;
|
||||
mChangingMaterials[i].assetId = newValue;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
matMap newMatMap;
|
||||
newMatMap.slot = slot;
|
||||
newMatMap.matAsset = matAsset;
|
||||
newMatMap.assetId = newValue;
|
||||
|
||||
mChangingMaterials.push_back(newMatMap);
|
||||
}
|
||||
|
||||
setMaskBits(MaterialMask);
|
||||
}
|
||||
|
||||
Parent::onDynamicModified(slotName, newValue);
|
||||
}
|
||||
|
||||
void MeshComponent::changeMaterial(U32 slot, MaterialAsset* newMat)
|
||||
{
|
||||
char fieldName[512];
|
||||
|
||||
//update our respective field
|
||||
dSprintf(fieldName, 512, "materialSlot%d", slot);
|
||||
setDataField(fieldName, NULL, newMat->getAssetId());
|
||||
}
|
||||
|
||||
bool MeshComponent::setMatInstField(U32 slot, const char* field, const char* value)
|
||||
{
|
||||
TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
|
||||
pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
|
||||
|
||||
MaterialParameters* params = pMatList->getMaterialInst(slot)->getMaterialParameters();
|
||||
|
||||
if (pMatList->getMaterialInst(slot)->getFeatures().hasFeature(MFT_DiffuseColor))
|
||||
{
|
||||
MaterialParameterHandle* handle = pMatList->getMaterialInst(slot)->getMaterialParameterHandle("DiffuseColor");
|
||||
|
||||
params->set(handle, LinearColorF(0, 0, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MeshComponent::onInspect()
|
||||
{
|
||||
}
|
||||
|
||||
void MeshComponent::onEndInspect()
|
||||
{
|
||||
}
|
||||
|
||||
void MeshComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
if (mInterfaceData != nullptr)
|
||||
{
|
||||
MatrixF newTransform = *mat;
|
||||
mInterfaceData->mTransform = newTransform;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +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 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
|
||||
|
||||
#include "T3D/systems/render/meshRenderSystem.h"
|
||||
|
||||
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;
|
||||
|
||||
MeshRenderSystemInterface* mInterfaceData;
|
||||
|
||||
struct matMap
|
||||
{
|
||||
MaterialAsset* matAsset;
|
||||
String assetId;
|
||||
U32 slot;
|
||||
};
|
||||
|
||||
Vector<matMap> mChangingMaterials;
|
||||
Vector<matMap> mMaterials;
|
||||
|
||||
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 mInterfaceData->mBounds; }
|
||||
|
||||
virtual MatrixF getNodeTransform(S32 nodeIdx);
|
||||
S32 getNodeByName(String nodeName);
|
||||
|
||||
void setupShape();
|
||||
void updateShape();
|
||||
void updateMaterials();
|
||||
|
||||
void _shapeAssetUpdated(ShapeAsset* asset);
|
||||
|
||||
virtual void onComponentRemove();
|
||||
virtual void onComponentAdd();
|
||||
|
||||
virtual void ownerTransformSet(MatrixF *mat);
|
||||
|
||||
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);
|
||||
|
||||
static bool writeShape(void* obj, StringTableEntry pFieldName) { return static_cast<MeshComponent*>(obj)->mMeshAsset.notNull(); }
|
||||
|
||||
bool setMeshAsset(const char* assetName);
|
||||
|
||||
virtual TSShape* getShape() { if (mMeshAsset) return mMeshAsset->getShape(); else return NULL; }
|
||||
virtual TSShapeInstance* getShapeInstance() { return mInterfaceData->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, MaterialAsset* newMat);
|
||||
bool setMatInstField(U32 slot, const char* field, const char* value);
|
||||
|
||||
virtual void onInspect();
|
||||
virtual void onEndInspect();
|
||||
|
||||
virtual Vector<MatrixF> getNodeTransforms()
|
||||
{
|
||||
Vector<MatrixF> bob;
|
||||
return bob;
|
||||
}
|
||||
|
||||
virtual void setNodeTransforms(Vector<MatrixF> transforms)
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,183 +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 "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, getNodeRotation, EulerF,
|
||||
(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() );
|
||||
RotationF mat = object->getNodeTransform(node);
|
||||
|
||||
return mat.asEulerF(RotationF::Degrees);
|
||||
}
|
||||
|
||||
return EulerF(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, MaterialAsset* newMat), (0, nullAsType<MaterialAsset*>()),
|
||||
"@brief Change one of the materials on the shape.\n\n")
|
||||
{
|
||||
object->changeMaterial(slot, newMat);
|
||||
}
|
||||
|
||||
DefineEngineMethod(MeshComponent, setMatInstField, bool, (U32 slot, const char* field, const char* value), (0, "", ""),
|
||||
"@brief Change one of the materials on the shape.\n\n")
|
||||
{
|
||||
return object->setMatInstField(slot, field, value);
|
||||
}
|
||||
|
|
@ -1,62 +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 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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,328 +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 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
|
||||
#ifndef _ASSET_PTR_H_
|
||||
#include "assets/assetPtr.h"
|
||||
#endif
|
||||
#ifndef GAME_OBJECT_ASSET_H
|
||||
#include "T3D/assets/GameObjectAsset.h"
|
||||
#endif
|
||||
|
||||
class Component;
|
||||
|
||||
//**************************************************************************
|
||||
// Entity
|
||||
//**************************************************************************
|
||||
class Entity : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
friend class Component;
|
||||
|
||||
private:
|
||||
Point3F mPos;
|
||||
RotationF mRot;
|
||||
|
||||
Vector<Component*> mComponents;
|
||||
|
||||
//Bit of helper data to let us track and manage the adding, removal and updating of networked components
|
||||
struct NetworkedComponent
|
||||
{
|
||||
U32 componentIndex;
|
||||
|
||||
enum UpdateState
|
||||
{
|
||||
None,
|
||||
Adding,
|
||||
Removing,
|
||||
Updating
|
||||
};
|
||||
|
||||
UpdateState updateState;
|
||||
|
||||
U32 updateMaskBits;
|
||||
};
|
||||
|
||||
Vector<NetworkedComponent> mNetworkedComponents;
|
||||
|
||||
U32 mComponentNetMask;
|
||||
|
||||
bool mStartComponentUpdate;
|
||||
|
||||
StringTableEntry mGameObjectAssetId;
|
||||
AssetPtr<GameObjectAsset> mGameObjectAsset;
|
||||
|
||||
ContainerQueryInfo containerInfo;
|
||||
|
||||
bool mInitialized;
|
||||
|
||||
String mTags;
|
||||
|
||||
Signal< void(Component*) > onComponentAdded;
|
||||
Signal< void(Component*) > onComponentRemoved;
|
||||
|
||||
S32 mLifetimeMS;
|
||||
|
||||
protected:
|
||||
//Marked if this entity is a GameObject and deliniates from the parent GO asset
|
||||
bool mDirtyGameObject;
|
||||
|
||||
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,
|
||||
ComponentsUpdateMask = Parent::NextFreeMask << 2,
|
||||
AddComponentsMask = Parent::NextFreeMask << 3,
|
||||
RemoveComponentsMask = Parent::NextFreeMask << 4,
|
||||
NoWarpMask = Parent::NextFreeMask << 5,
|
||||
NamespaceMask = Parent::NextFreeMask << 6,
|
||||
NextFreeMask = Parent::NextFreeMask << 7
|
||||
};
|
||||
|
||||
StateDelta mDelta;
|
||||
S32 mPredictionCount; ///< Number of ticks to predict
|
||||
|
||||
Move lastMove;
|
||||
|
||||
S32 mStartTimeMS;
|
||||
|
||||
//
|
||||
Entity();
|
||||
~Entity();
|
||||
|
||||
static void initPersistFields();
|
||||
virtual void onPostAdd();
|
||||
|
||||
virtual void setTransform(const MatrixF &mat);
|
||||
virtual void setRenderTransform(const MatrixF &mat);
|
||||
|
||||
void setTransform(const Point3F& position, const RotationF& rotation);
|
||||
|
||||
void setRenderTransform(const Point3F& position, const RotationF& rotation);
|
||||
|
||||
virtual MatrixF getTransform();
|
||||
virtual Point3F getPosition() const { return mPos; }
|
||||
|
||||
void setRotation(const RotationF& rotation) {
|
||||
mRot = rotation;
|
||||
setMaskBits(TransformMask);
|
||||
};
|
||||
RotationF getRotation() { return mRot; }
|
||||
|
||||
static bool _setGameObject(void *object, const char *index, const char *data);
|
||||
|
||||
void setMountOffset(const Point3F& posOffset);
|
||||
void setMountRotation(const EulerF& rotOffset);
|
||||
|
||||
//static bool _setEulerRotation( void *object, const char *index, const char *data );
|
||||
static bool _setPosition(void *object, const char *index, const char *data);
|
||||
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);
|
||||
|
||||
virtual void mountObject(SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity);
|
||||
void mountObject(SceneObject* objB, const MatrixF& txfm);
|
||||
void onMount(SceneObject *obj, S32 node);
|
||||
void onUnmount(SceneObject *obj, S32 node);
|
||||
|
||||
/// Sets the client controlling this object
|
||||
/// @param client Client that is now controlling this object
|
||||
virtual void setControllingClient(GameConnection *client);
|
||||
|
||||
//
|
||||
//Networking
|
||||
//
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
void setComponentsDirty();
|
||||
void setComponentDirty(Component *comp, bool forceUpdate = false);
|
||||
|
||||
void setComponentNetMask(Component* comp, U32 mask);
|
||||
|
||||
//Components
|
||||
virtual bool deferAddingComponents() const { return true; }
|
||||
|
||||
void notifyComponents(String signalFunction, String argA, String argB="", String argC = "", String argD = "", String argE = "");
|
||||
|
||||
template <class T>
|
||||
T* getComponent();
|
||||
template <class T>
|
||||
Vector<T*> getComponents();
|
||||
|
||||
Component* getComponent(String componentType);
|
||||
|
||||
U32 getComponentCount() const
|
||||
{
|
||||
return mComponents.size();
|
||||
}
|
||||
|
||||
virtual void setObjectBox(const 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; }
|
||||
|
||||
StateDelta getNetworkDelta() { return mDelta; }
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
|
|
@ -44,9 +44,6 @@
|
|||
#include "console/engineAPI.h"
|
||||
#include "math/mTransform.h"
|
||||
|
||||
#include "T3D/entity.h"
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
|
||||
#ifdef TORQUE_HIFI_NET
|
||||
#include "T3D/gameBase/hifi/hifiMoveList.h"
|
||||
#elif defined TORQUE_EXTENDED_MOVE
|
||||
|
|
@ -787,17 +784,7 @@ bool GameConnection::getControlCameraFov(F32 * fov)
|
|||
}
|
||||
if (cObj)
|
||||
{
|
||||
if (Entity* ent = dynamic_cast<Entity*>(cObj))
|
||||
{
|
||||
if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
|
||||
{
|
||||
*fov = camInterface->getCameraFov();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*fov = cObj->getCameraFov();
|
||||
}
|
||||
*fov = cObj->getCameraFov();
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
|
@ -818,17 +805,7 @@ bool GameConnection::isValidControlCameraFov(F32 fov)
|
|||
|
||||
if (cObj)
|
||||
{
|
||||
if (Entity* ent = dynamic_cast<Entity*>(cObj))
|
||||
{
|
||||
if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
|
||||
{
|
||||
return camInterface->isValidCameraFov(fov);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return cObj->isValidCameraFov(fov);
|
||||
}
|
||||
return cObj->isValidCameraFov(fov);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -847,24 +824,10 @@ bool GameConnection::setControlCameraFov(F32 fov)
|
|||
if (cObj)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// allow shapebase to clamp fov to its datablock values
|
||||
cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
|
||||
newFov = cObj->getCameraFov();
|
||||
|
||||
// server fov of client has 1degree resolution
|
||||
if( S32(newFov) != S32(mCameraFov) || newFov != fov )
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@
|
|||
#include "platform/profiler.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
#include "T3D/components/component.h"
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ProcessObject::ProcessObject()
|
||||
|
|
@ -275,11 +273,6 @@ void ProcessList::advanceObjects()
|
|||
onTickObject(pobj);
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < UpdateInterface::all.size(); i++)
|
||||
{
|
||||
UpdateInterface::all[i]->processTick();
|
||||
}
|
||||
|
||||
mTotalTicks++;
|
||||
|
||||
PROFILE_END();
|
||||
|
|
|
|||
|
|
@ -37,9 +37,6 @@
|
|||
#include "T3D/gameBase/std/stdMoveList.h"
|
||||
#include "T3D/fx/cameraFXMgr.h"
|
||||
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
MODULE_BEGIN( ProcessList )
|
||||
|
||||
MODULE_INIT
|
||||
|
|
@ -135,16 +132,6 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
|
|||
obj = obj->mProcessLink.next;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Inform objects of total elapsed delta so they can advance
|
||||
// client side animations.
|
||||
F32 dt = F32(timeDelta) / 1000;
|
||||
|
|
@ -158,19 +145,6 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
|
|||
obj->advanceTime( dt );
|
||||
obj = obj->mProcessLink.next;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
#include "aiPlayerObject.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(AIPlayerObject);
|
||||
|
||||
AIPlayerObject::AIPlayerObject()
|
||||
//: mAIControllerComponent(nullptr)
|
||||
{
|
||||
mSuperClassName = StringTable->insert("Entity");
|
||||
}
|
||||
AIPlayerObject::~AIPlayerObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool AIPlayerObject::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
//If we don't delinate from the template, just spawn as apropos here
|
||||
if (!mDirtyGameObject)
|
||||
{
|
||||
//AI Controller
|
||||
/*mAIControllerComponent = new AIControllerComponent();
|
||||
if (!mAIControllerComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add mAIControllerComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mAIControllerComponent->setInternalName("aiControllerComponent");
|
||||
|
||||
addComponent(mAIControllerComponent);*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIPlayerObject::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#pragma once
|
||||
#include "playerObject.h"
|
||||
|
||||
//#include "T3D/components/ai/aiControllerComponent.h"
|
||||
|
||||
class AIPlayerObject : public PlayerObject
|
||||
{
|
||||
typedef PlayerObject Parent;
|
||||
|
||||
//AIControllerComponent* mAIControllerComponent;
|
||||
|
||||
public:
|
||||
AIPlayerObject();
|
||||
~AIPlayerObject();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
DECLARE_CONOBJECT(AIPlayerObject);
|
||||
};
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
#include "playerObject.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(PlayerObject);
|
||||
|
||||
PlayerObject::PlayerObject()
|
||||
: mMeshComponent(nullptr),
|
||||
mCollisionComponent(nullptr),
|
||||
//mAnimationComponent(nullptr),
|
||||
mPhysicsComponent(nullptr)
|
||||
{
|
||||
mSuperClassName = StringTable->insert("Entity");
|
||||
}
|
||||
PlayerObject::~PlayerObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool PlayerObject::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
//If we don't delinate from the template, just spawn as apropos here
|
||||
if (!mDirtyGameObject)
|
||||
{
|
||||
//Mesh
|
||||
mMeshComponent = new MeshComponent();
|
||||
if (!mMeshComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add MeshComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mMeshComponent->setInternalName("meshComponent");
|
||||
|
||||
addComponent(mMeshComponent);
|
||||
|
||||
//Collision
|
||||
mCollisionComponent = new ShapeCollisionComponent();
|
||||
if (!mCollisionComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add ShapeCollisionComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mCollisionComponent->setInternalName("collisionComponent");
|
||||
|
||||
addComponent(mCollisionComponent);
|
||||
|
||||
//Animation
|
||||
/*mAnimationComponent = new ActionAnimationComponent();
|
||||
if (!mAnimationComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add ActionAnimationComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mAnimationComponent->setInternalName("animationComponent");
|
||||
|
||||
addComponent(mAnimationComponent);
|
||||
|
||||
//Arm Animation
|
||||
mArmAnimationComponent = new ArmAnimationComponent();
|
||||
if (!mArmAnimationComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add ArmAnimationComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mArmAnimationComponent->setInternalName("armAnimationComponent");
|
||||
|
||||
addComponent(mArmAnimationComponent);*/
|
||||
|
||||
//Physics control
|
||||
mPhysicsComponent = new PlayerControllerComponent();
|
||||
if (!mPhysicsComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add PhysicsComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mPhysicsComponent->setInternalName("physicsComponent");
|
||||
|
||||
addComponent(mPhysicsComponent);
|
||||
|
||||
//State Machine
|
||||
mStateMachineComponent = new StateMachineComponent();
|
||||
if (!mStateMachineComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add StateMachineComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mStateMachineComponent->setInternalName("stateMachineComponent");
|
||||
|
||||
addComponent(mStateMachineComponent);
|
||||
|
||||
//Camera
|
||||
mCameraComponent = new CameraComponent();
|
||||
if (!mCameraComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add CameraComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mCameraComponent->setInternalName("cameraComponent");
|
||||
|
||||
addComponent(mCameraComponent);
|
||||
|
||||
//Camera Orbiter
|
||||
mCameraOrbiterComponent = new CameraOrbiterComponent();
|
||||
if (!mCameraOrbiterComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add CameraOrbiterComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mCameraOrbiterComponent->setInternalName("cameraOrbiterComponent");
|
||||
|
||||
addComponent(mCameraOrbiterComponent);
|
||||
|
||||
//Control Object
|
||||
mControlObjectComponent = new ControlObjectComponent();
|
||||
if (!mControlObjectComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add ControlObjectComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mControlObjectComponent->setInternalName("controlObjectComponent");
|
||||
|
||||
addComponent(mControlObjectComponent);
|
||||
|
||||
//Sound
|
||||
mSoundComponent = new SoundComponent();
|
||||
if (!mSoundComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add SoundComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mSoundComponent->setInternalName("soundComponent");
|
||||
|
||||
addComponent(mSoundComponent);
|
||||
|
||||
//Interaction
|
||||
mInteractComponent = new InteractComponent();
|
||||
if (!mInteractComponent->registerObject())
|
||||
{
|
||||
Con::errorf("PlayerObject::onAdd - unable to add InteractComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mInteractComponent->setInternalName("interactComponent");
|
||||
|
||||
addComponent(mInteractComponent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlayerObject::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/entity.h"
|
||||
#include "T3D/components/render/meshComponent.h"
|
||||
#include "T3D/components/collision/shapeCollisionComponent.h"
|
||||
//#include "T3D/components/animation/actionAnimationComponent.h"
|
||||
//#include "T3D/components/animation/armAnimationComponent.h"
|
||||
#include "T3D/components/physics/playerControllerComponent.h"
|
||||
#include "T3D/components/game/stateMachineComponent.h"
|
||||
#include "T3D/components/camera/cameraComponent.h"
|
||||
#include "T3D/components/camera/cameraOrbiterComponent.h"
|
||||
#include "T3D/components/game/controlObjectComponent.h"
|
||||
#include "T3D/components/audio/SoundComponent.h"
|
||||
#include "T3D/components/game/interactComponent.h"
|
||||
|
||||
class PlayerObject : public Entity
|
||||
{
|
||||
typedef Entity Parent;
|
||||
|
||||
MeshComponent* mMeshComponent;
|
||||
ShapeCollisionComponent* mCollisionComponent;
|
||||
//ActionAnimationComponent* mAnimationComponent;
|
||||
//ArmAnimationComponent* mArmAnimationComponent;
|
||||
PlayerControllerComponent* mPhysicsComponent;
|
||||
StateMachineComponent* mStateMachineComponent;
|
||||
CameraComponent* mCameraComponent;
|
||||
CameraOrbiterComponent* mCameraOrbiterComponent;
|
||||
ControlObjectComponent* mControlObjectComponent;
|
||||
SoundComponent* mSoundComponent;
|
||||
InteractComponent* mInteractComponent;
|
||||
|
||||
public:
|
||||
PlayerObject();
|
||||
~PlayerObject();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
DECLARE_CONOBJECT(PlayerObject);
|
||||
};
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#include "soundEmitterObject.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SoundEmitterObject);
|
||||
|
||||
SoundEmitterObject::SoundEmitterObject()
|
||||
: mSoundComponent(nullptr)
|
||||
{
|
||||
mSuperClassName = StringTable->insert("Entity");
|
||||
}
|
||||
SoundEmitterObject::~SoundEmitterObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool SoundEmitterObject::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
//Sound
|
||||
mSoundComponent = new SoundComponent();
|
||||
if (!mSoundComponent->registerObject())
|
||||
{
|
||||
Con::errorf("SoundEmitterObject::onAdd - unable to add soundComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mSoundComponent->setInternalName("soundComponent");
|
||||
|
||||
addComponent(mSoundComponent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundEmitterObject::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/entity.h"
|
||||
#include "T3D/components/audio/SoundComponent.h"
|
||||
|
||||
class SoundEmitterObject : public Entity
|
||||
{
|
||||
typedef Entity Parent;
|
||||
|
||||
SoundComponent* mSoundComponent;
|
||||
|
||||
public:
|
||||
SoundEmitterObject();
|
||||
~SoundEmitterObject();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
DECLARE_CONOBJECT(SoundEmitterObject);
|
||||
};
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#include "staticShapeObject.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(StaticShapeObject);
|
||||
|
||||
StaticShapeObject::StaticShapeObject()
|
||||
: mMeshComponent(nullptr),
|
||||
mCollisionComponent(nullptr),
|
||||
mAnimationComponent(nullptr)
|
||||
{
|
||||
}
|
||||
StaticShapeObject::~StaticShapeObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool StaticShapeObject::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
//If we don't delinate from the template, just spawn as apropos here
|
||||
if (!mDirtyGameObject)
|
||||
{
|
||||
//Mesh
|
||||
mMeshComponent = new MeshComponent();
|
||||
if (!mMeshComponent->registerObject())
|
||||
{
|
||||
Con::errorf("StaticShapeObject::onAdd - unable to add MeshComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mMeshComponent->setInternalName("meshComponent");
|
||||
|
||||
addComponent(mMeshComponent);
|
||||
|
||||
//Collision
|
||||
mCollisionComponent = new ShapeCollisionComponent();
|
||||
if (!mCollisionComponent->registerObject())
|
||||
{
|
||||
Con::errorf("StaticShapeObject::onAdd - unable to add ShapeCollisionComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mCollisionComponent->setInternalName("collisionComponent");
|
||||
|
||||
addComponent(mCollisionComponent);
|
||||
|
||||
//Animation
|
||||
mAnimationComponent = new AnimationComponent();
|
||||
if (!mAnimationComponent->registerObject())
|
||||
{
|
||||
Con::errorf("StaticShapeObject::onAdd - unable to add AnimationComponent!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mAnimationComponent->setInternalName("animationComponent");
|
||||
|
||||
addComponent(mAnimationComponent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StaticShapeObject::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/entity.h"
|
||||
#include "T3D/components/render/meshComponent.h"
|
||||
#include "T3D/components/collision/shapeCollisionComponent.h"
|
||||
#include "T3D/components/animation/animationComponent.h"
|
||||
|
||||
class StaticShapeObject : public Entity
|
||||
{
|
||||
typedef Entity Parent;
|
||||
|
||||
MeshComponent* mMeshComponent;
|
||||
ShapeCollisionComponent* mCollisionComponent;
|
||||
AnimationComponent* mAnimationComponent;
|
||||
|
||||
public:
|
||||
StaticShapeObject();
|
||||
~StaticShapeObject();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
MeshComponent* getMeshComponent() { return mMeshComponent; }
|
||||
ShapeCollisionComponent* getCollisionComponent() { return mCollisionComponent; }
|
||||
AnimationComponent* getAnimationComponent() { return mAnimationComponent; }
|
||||
|
||||
DECLARE_CONOBJECT(StaticShapeObject);
|
||||
};
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
#include "T3D/systems/render/meshRenderSystem.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* state)
|
||||
{
|
||||
if (sceneManager == nullptr || state == nullptr)
|
||||
return;
|
||||
|
||||
Frustum viewFrustum = state->getCullingFrustum();
|
||||
MatrixF camTransform = state->getCameraTransform();
|
||||
|
||||
U32 count = MeshRenderSystemInterface::all.size();
|
||||
for (U32 i = 0; i < count; i++)
|
||||
{
|
||||
//Server side items exist for data, but we don't actually render them
|
||||
if (!MeshRenderSystemInterface::all[i]->mIsClient)
|
||||
continue;
|
||||
|
||||
//First, do frustum culling
|
||||
if (viewFrustum.isCulled(MeshRenderSystemInterface::all[i]->mBounds))
|
||||
continue;
|
||||
|
||||
// Set the query box for the container query. Never
|
||||
// make it larger than the frustum's AABB. In the editor,
|
||||
// always query the full frustum as that gives objects
|
||||
// the opportunity to render editor visualizations even if
|
||||
// they are otherwise not in view.
|
||||
if (!state->getCullingFrustum().getBounds().isOverlapped(state->getRenderArea()))
|
||||
{
|
||||
// This handles fringe cases like flying backwards into a zone where you
|
||||
// end up pretty much standing on a zone border and looking directly into
|
||||
// its "walls". In that case the traversal area will be behind the frustum
|
||||
// (remember that the camera isn't where visibility starts, it's the near
|
||||
// distance).
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//We can then sort our objects by range since we have it already, so we can do occlusion culling be rendering front-to-back
|
||||
|
||||
//if we've made it this far, call down to the render function to actually display our stuff
|
||||
renderInterface(i, state);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* state)
|
||||
{
|
||||
//Fetch
|
||||
MeshRenderSystemInterface* interface = MeshRenderSystemInterface::all[interfaceIndex];
|
||||
|
||||
if (interface->mShapeInstance == nullptr)
|
||||
return;
|
||||
|
||||
Point3F cameraOffset;
|
||||
interface->mTransform.getColumn(3, &cameraOffset);
|
||||
cameraOffset -= state->getDiffuseCameraPosition();
|
||||
F32 dist = cameraOffset.len();
|
||||
if (dist < 0.01f)
|
||||
dist = 0.01f;
|
||||
|
||||
Point3F objScale = interface->mScale;
|
||||
F32 invScale = (1.0f / getMax(getMax(objScale.x, objScale.y), objScale.z));
|
||||
|
||||
interface->mShapeInstance->setDetailFromDistance(state, dist * invScale);
|
||||
|
||||
if (interface->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(interface->mSphere);
|
||||
rdata.setLightQuery(&query);
|
||||
|
||||
MatrixF mat = interface->mTransform;
|
||||
|
||||
mat.scale(objScale);
|
||||
GFX->setWorldMatrix(mat);
|
||||
|
||||
interface->mShapeInstance->render(rdata);
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "core/util/systemInterfaceList.h"
|
||||
#include "ts/tsShape.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "T3D/assets/ShapeAsset.h"
|
||||
#include "T3D/assets/MaterialAsset.h"
|
||||
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _OPTIMIZEDPOLYLIST_H_
|
||||
#include "collision/optimizedPolyList.h"
|
||||
#endif
|
||||
|
||||
class MeshRenderSystemInterface : public SystemInterface<MeshRenderSystemInterface>
|
||||
{
|
||||
public:
|
||||
TSShapeInstance * mShapeInstance;
|
||||
|
||||
MatrixF mTransform;
|
||||
Point3F mScale;
|
||||
Box3F mBounds;
|
||||
SphereF mSphere;
|
||||
|
||||
bool mIsClient;
|
||||
|
||||
struct matMap
|
||||
{
|
||||
//MaterialAsset* matAsset;
|
||||
String assetId;
|
||||
U32 slot;
|
||||
};
|
||||
|
||||
Vector<matMap> mChangingMaterials;
|
||||
Vector<matMap> mMaterials;
|
||||
|
||||
MeshRenderSystemInterface() : SystemInterface(), mShapeInstance(nullptr), mTransform(MatrixF::Identity), mScale(Point3F::One), mIsClient(false)
|
||||
{
|
||||
mBounds = Box3F(1);
|
||||
mSphere = SphereF();
|
||||
}
|
||||
|
||||
~MeshRenderSystemInterface()
|
||||
{
|
||||
//SAFE_DELETE(mShape);
|
||||
SAFE_DELETE(mShapeInstance);
|
||||
}
|
||||
};
|
||||
|
||||
class MeshRenderSystem
|
||||
{
|
||||
public:
|
||||
//Core render function, which does all the real work
|
||||
static void render(SceneManager *sceneManager, SceneRenderState* state);
|
||||
|
||||
//Render our particular interface's data
|
||||
static void renderInterface(U32 interfaceIndex, SceneRenderState* state);
|
||||
};
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#include "T3D/systems/updateSystem.h"
|
||||
|
||||
void UpdateSystem::processTick()
|
||||
{
|
||||
for (U32 i = 0; i < UpdateSystemInterface::all.size(); i++)
|
||||
{
|
||||
if (UpdateSystemInterface::all[i]->mIsEnabled)
|
||||
{
|
||||
//do work
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSystem::advanceTime(U32 _tickMS)
|
||||
{
|
||||
for (U32 i = 0; i < UpdateSystemInterface::all.size(); i++)
|
||||
{
|
||||
if (UpdateSystemInterface::all[i]->mIsEnabled)
|
||||
{
|
||||
//do work
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSystem::interpolateTick(U32 _deltaMS)
|
||||
{
|
||||
for (U32 i = 0; i < UpdateSystemInterface::all.size(); i++)
|
||||
{
|
||||
if (UpdateSystemInterface::all[i]->mIsEnabled)
|
||||
{
|
||||
//do work
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
#include "core/util/systemInterfaceList.h"
|
||||
|
||||
class UpdateSystemInterface : public SystemInterface<UpdateSystemInterface>
|
||||
{
|
||||
public:
|
||||
bool mIsEnabled;
|
||||
};
|
||||
|
||||
class UpdateSystem
|
||||
{
|
||||
public:
|
||||
static void processTick();
|
||||
static void advanceTime(U32 _tickMS);
|
||||
static void interpolateTick(U32 _deltaMS);
|
||||
};
|
||||
|
|
@ -58,9 +58,6 @@
|
|||
#include "assets/autoloadAssets.h"
|
||||
#endif
|
||||
|
||||
#ifndef COMPONENTASSET_H
|
||||
#include "T3D/assets/ComponentAsset.h"
|
||||
#endif
|
||||
#ifndef GUI_ASSET_H
|
||||
#include "T3D/assets/GUIAsset.h"
|
||||
#endif
|
||||
|
|
@ -259,11 +256,7 @@ bool AssetManager::loadModuleAutoLoadAssets(ModuleDefinition* pModuleDefinition)
|
|||
//TODO: this is stupid and ugly, need to properly automagically parse the class for registration
|
||||
AssetBase* assetBase = nullptr;
|
||||
|
||||
if (assetDef->mAssetType == StringTable->insert("ComponentAsset"))
|
||||
{
|
||||
assetBase = mTaml.read<ComponentAsset>(assetDef->mAssetBaseFilePath);
|
||||
}
|
||||
else if (assetDef->mAssetType == StringTable->insert("GUIAsset"))
|
||||
if (assetDef->mAssetType == StringTable->insert("GUIAsset"))
|
||||
{
|
||||
assetBase = mTaml.read<GUIAsset>(assetDef->mAssetBaseFilePath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
#include "gui/editor/editorFunctions.h"
|
||||
#endif
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/entity.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
|
||||
|
||||
|
|
@ -3479,22 +3478,6 @@ void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
|
|||
if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
|
||||
onVirtualParentExpand(item);
|
||||
|
||||
//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 (e)
|
||||
{
|
||||
if (item->isExpanded())
|
||||
e->onInspect();
|
||||
else
|
||||
e->onEndInspect();
|
||||
}
|
||||
}
|
||||
|
||||
mFlags.set( RebuildVisible );
|
||||
scrollVisible(item);
|
||||
}
|
||||
|
|
@ -4309,11 +4292,6 @@ bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
|
|||
|
||||
// Go through our items and purge those that have disappeared from
|
||||
// the set.
|
||||
|
||||
//Entities will be a special case here, if we're an entity, skip this step
|
||||
if (dynamic_cast<Entity*>(srcObj))
|
||||
return true;
|
||||
|
||||
for( Item* ptr = item->mChild; ptr != NULL; )
|
||||
{
|
||||
Item* next = ptr->mNext;
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@
|
|||
#include "gui/containers/guiScrollCtrl.h"
|
||||
#include "gui/editor/inspector/customField.h"
|
||||
|
||||
#include "gui/editor/inspector/entityGroup.h"
|
||||
#include "gui/editor/inspector/mountingGroup.h"
|
||||
#include "gui/editor/inspector/componentGroup.h"
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspector);
|
||||
|
||||
ConsoleDocClass( GuiInspector,
|
||||
|
|
@ -591,60 +586,6 @@ void GuiInspector::refresh()
|
|||
|
||||
mTargets.first()->onInspect(this);
|
||||
|
||||
//Entity inspector group
|
||||
if (mTargets.first()->getClassRep()->isSubclassOf("Entity"))
|
||||
{
|
||||
//Put the GameObject group before everything that'd be gameobject-effecting, for orginazational purposes
|
||||
GuiInspectorGroup *gameObject = new GuiInspectorGroup("GameObject", this);
|
||||
|
||||
gameObject->registerObject();
|
||||
mGroups.push_back(gameObject);
|
||||
addObject(gameObject);
|
||||
|
||||
GuiInspectorEntityGroup *components = new GuiInspectorEntityGroup("Components", this);
|
||||
if (components != NULL)
|
||||
{
|
||||
components->registerObject();
|
||||
mGroups.push_back(components);
|
||||
addObject(components);
|
||||
}
|
||||
|
||||
Entity* selectedEntity = dynamic_cast<Entity*>(mTargets.first().getObject());
|
||||
|
||||
U32 compCount = selectedEntity->getComponentCount();
|
||||
//Now, add the component groups
|
||||
for (U32 c = 0; c < compCount; ++c)
|
||||
{
|
||||
Component* comp = selectedEntity->getComponent(c);
|
||||
|
||||
String compName;
|
||||
if (comp->getFriendlyName() != StringTable->EmptyString())
|
||||
compName = comp->getFriendlyName();
|
||||
else
|
||||
compName = comp->getComponentName();
|
||||
|
||||
StringBuilder captionString;
|
||||
captionString.format("%s [%i]", compName.c_str(), comp->getId());
|
||||
|
||||
GuiInspectorGroup *compGroup = new GuiInspectorComponentGroup(captionString.data(), this, comp);
|
||||
if (compGroup != NULL)
|
||||
{
|
||||
compGroup->registerObject();
|
||||
mGroups.push_back(compGroup);
|
||||
addObject(compGroup);
|
||||
}
|
||||
}
|
||||
|
||||
//Mounting group override
|
||||
GuiInspectorGroup *mounting = new GuiInspectorMountingGroup("Mounting", this);
|
||||
if (mounting != NULL)
|
||||
{
|
||||
mounting->registerObject();
|
||||
mGroups.push_back(mounting);
|
||||
addObject(mounting);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the inspector groups for static fields.
|
||||
|
||||
for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )
|
||||
|
|
|
|||
|
|
@ -1,531 +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 "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, Component* targetComponent)
|
||||
: GuiInspectorGroup(groupName, parent)
|
||||
{
|
||||
/*mNeedScroll=false;*/
|
||||
mTargetComponent = targetComponent;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
//if this isn't a component, what are we even doing here?
|
||||
if (!mTargetComponent)
|
||||
return false;
|
||||
|
||||
mParent->setComponentGroupTargetId(mTargetComponent->getId());
|
||||
|
||||
//first, relevent static fields
|
||||
AbstractClassRep::FieldList& fieldList = mTargetComponent->getClassRep()->mFieldList;
|
||||
for (AbstractClassRep::FieldList::iterator itr = fieldList.begin();
|
||||
itr != fieldList.end(); ++itr)
|
||||
{
|
||||
AbstractClassRep::Field* field = &(*itr);
|
||||
if (field->type == AbstractClassRep::StartGroupFieldType)
|
||||
{
|
||||
// If we're dealing with general fields, always set grabItems to true (to skip them)
|
||||
if (bNoGroup == true)
|
||||
bGrabItems = true;
|
||||
else if (dStricmp(field->pGroupname, mCaption) == 0)
|
||||
bGrabItems = true;
|
||||
continue;
|
||||
}
|
||||
else if (field->type == AbstractClassRep::EndGroupFieldType)
|
||||
{
|
||||
// If we're dealing with general fields, always set grabItems to false (to grab them)
|
||||
if (bNoGroup == true)
|
||||
bGrabItems = false;
|
||||
else if (dStricmp(field->pGroupname, mCaption) == 0)
|
||||
bGrabItems = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip field if it has the HideInInspectors flag set.
|
||||
if (field->flag.test(AbstractClassRep::FIELD_HideInInspectors))
|
||||
continue;
|
||||
|
||||
if (field->pFieldname == StringTable->insert("locked") || field->pFieldname == StringTable->insert("class")
|
||||
|| field->pFieldname == StringTable->insert("internalName"))
|
||||
continue;
|
||||
|
||||
if (/*(bGrabItems == true || (bNoGroup == true && bGrabItems == false)) &&*/ itr->type != AbstractClassRep::DeprecatedFieldType)
|
||||
{
|
||||
if (bNoGroup == true && bGrabItems == true)
|
||||
continue;
|
||||
|
||||
if (field->type == AbstractClassRep::StartArrayFieldType)
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString("[GuiInspectorGroup] Beginning array '%s'",
|
||||
field->pFieldname);
|
||||
#endif
|
||||
|
||||
// Starting an array...
|
||||
// Create a rollout for the Array, give it the array's name.
|
||||
GuiRolloutCtrl *arrayRollout = new GuiRolloutCtrl();
|
||||
GuiControlProfile *arrayRolloutProfile = dynamic_cast<GuiControlProfile*>(Sim::findObject("GuiInspectorRolloutProfile0"));
|
||||
|
||||
arrayRollout->setControlProfile(arrayRolloutProfile);
|
||||
//arrayRollout->mCaption = StringTable->insert( String::ToString( "%s (%i)", field->pGroupname, field->elementCount ) );
|
||||
arrayRollout->setCaption(field->pGroupname);
|
||||
//arrayRollout->setMargin( 14, 0, 0, 0 );
|
||||
arrayRollout->registerObject();
|
||||
|
||||
GuiStackControl *arrayStack = new GuiStackControl();
|
||||
arrayStack->registerObject();
|
||||
arrayStack->freeze(true);
|
||||
arrayRollout->addObject(arrayStack);
|
||||
|
||||
// Allocate a rollout for each element-count in the array
|
||||
// Give it the element count name.
|
||||
for (U32 i = 0; i < field->elementCount; i++)
|
||||
{
|
||||
GuiRolloutCtrl *elementRollout = new GuiRolloutCtrl();
|
||||
GuiControlProfile *elementRolloutProfile = dynamic_cast<GuiControlProfile*>(Sim::findObject("GuiInspectorRolloutProfile0"));
|
||||
|
||||
char buf[256];
|
||||
dSprintf(buf, 256, " [%i]", i);
|
||||
|
||||
elementRollout->setControlProfile(elementRolloutProfile);
|
||||
elementRollout->setCaption(buf);
|
||||
//elementRollout->setMargin( 14, 0, 0, 0 );
|
||||
elementRollout->registerObject();
|
||||
|
||||
GuiStackControl *elementStack = new GuiStackControl();
|
||||
elementStack->registerObject();
|
||||
elementRollout->addObject(elementStack);
|
||||
elementRollout->instantCollapse();
|
||||
|
||||
arrayStack->addObject(elementRollout);
|
||||
}
|
||||
|
||||
pArrayRollout = arrayRollout;
|
||||
pArrayStack = arrayStack;
|
||||
arrayStack->freeze(false);
|
||||
pArrayRollout->instantCollapse();
|
||||
mStack->addObject(arrayRollout);
|
||||
|
||||
bMakingArray = true;
|
||||
continue;
|
||||
}
|
||||
else if (field->type == AbstractClassRep::EndArrayFieldType)
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString("[GuiInspectorGroup] Ending array '%s'",
|
||||
field->pFieldname);
|
||||
#endif
|
||||
|
||||
bMakingArray = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bMakingArray)
|
||||
{
|
||||
// Add a GuiInspectorField for this field,
|
||||
// for every element in the array...
|
||||
for (U32 i = 0; i < pArrayStack->size(); i++)
|
||||
{
|
||||
FrameTemp<char> intToStr(64);
|
||||
dSprintf(intToStr, 64, "%d", i);
|
||||
|
||||
// The array stack should have a rollout for each element
|
||||
// as children...
|
||||
GuiRolloutCtrl *pRollout = dynamic_cast<GuiRolloutCtrl*>(pArrayStack->at(i));
|
||||
// And the each of those rollouts should have a stack for
|
||||
// fields...
|
||||
GuiStackControl *pStack = dynamic_cast<GuiStackControl*>(pRollout->at(0));
|
||||
|
||||
// And we add a new GuiInspectorField to each of those stacks...
|
||||
GuiInspectorField *fieldGui = constructField(field->type);
|
||||
if (fieldGui == NULL)
|
||||
fieldGui = new GuiInspectorField();
|
||||
|
||||
fieldGui->init(mParent, this);
|
||||
StringTableEntry caption = field->pFieldname;
|
||||
fieldGui->setInspectorField(field, caption, intToStr);
|
||||
|
||||
if (fieldGui->registerObject())
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString("[GuiInspectorGroup] Adding array element '%s[%i]'",
|
||||
field->pFieldname, i);
|
||||
#endif
|
||||
|
||||
mChildren.push_back(fieldGui);
|
||||
pStack->addObject(fieldGui);
|
||||
}
|
||||
else
|
||||
delete fieldGui;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is weird, but it should work for now. - JDD
|
||||
// We are going to check to see if this item is an array
|
||||
// if so, we're going to construct a field for each array element
|
||||
if (field->elementCount > 1)
|
||||
{
|
||||
// Make a rollout control for this array
|
||||
//
|
||||
GuiRolloutCtrl *rollout = new GuiRolloutCtrl();
|
||||
rollout->setDataField(StringTable->insert("profile"), NULL, "GuiInspectorRolloutProfile0");
|
||||
rollout->setCaption(String::ToString("%s (%i)", field->pFieldname, field->elementCount));
|
||||
rollout->setMargin(14, 0, 0, 0);
|
||||
rollout->registerObject();
|
||||
mArrayCtrls.push_back(rollout);
|
||||
|
||||
// Put a stack control within the rollout
|
||||
//
|
||||
GuiStackControl *stack = new GuiStackControl();
|
||||
stack->setDataField(StringTable->insert("profile"), NULL, "GuiInspectorStackProfile");
|
||||
stack->registerObject();
|
||||
stack->freeze(true);
|
||||
rollout->addObject(stack);
|
||||
|
||||
mStack->addObject(rollout);
|
||||
|
||||
// Create each field and add it to the stack.
|
||||
//
|
||||
for (S32 nI = 0; nI < field->elementCount; nI++)
|
||||
{
|
||||
FrameTemp<char> intToStr(64);
|
||||
dSprintf(intToStr, 64, "%d", nI);
|
||||
|
||||
// Construct proper ValueName[nI] format which is "ValueName0" for index 0, etc.
|
||||
|
||||
String fieldName = String::ToString("%s%d", field->pFieldname, nI);
|
||||
|
||||
// If the field already exists, just update it
|
||||
GuiInspectorField *fieldGui = findField(fieldName);
|
||||
if (fieldGui != NULL)
|
||||
{
|
||||
fieldGui->updateValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
bNewItems = true;
|
||||
|
||||
fieldGui = constructField(field->type);
|
||||
if (fieldGui == NULL)
|
||||
fieldGui = new GuiInspectorField();
|
||||
|
||||
fieldGui->init(mParent, this);
|
||||
StringTableEntry caption = StringTable->insert(String::ToString(" [%i]", nI));
|
||||
fieldGui->setInspectorField(field, caption, intToStr);
|
||||
|
||||
fieldGui->setTargetObject(mTargetComponent);
|
||||
|
||||
if (fieldGui->registerObject())
|
||||
{
|
||||
mChildren.push_back(fieldGui);
|
||||
stack->addObject(fieldGui);
|
||||
}
|
||||
else
|
||||
delete fieldGui;
|
||||
}
|
||||
|
||||
stack->freeze(false);
|
||||
stack->updatePanes();
|
||||
rollout->instantCollapse();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the field already exists, just update it
|
||||
GuiInspectorField *fieldGui = findField(field->pFieldname);
|
||||
if (fieldGui != NULL)
|
||||
{
|
||||
fieldGui->updateValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
bNewItems = true;
|
||||
|
||||
fieldGui = constructField(field->type);
|
||||
if (fieldGui == NULL)
|
||||
fieldGui = new GuiInspectorField();
|
||||
|
||||
fieldGui->init(mParent, this);
|
||||
fieldGui->setInspectorField(field);
|
||||
|
||||
fieldGui->setTargetObject(mTargetComponent);
|
||||
|
||||
if (fieldGui->registerObject())
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString("[GuiInspectorGroup] Adding field '%s'",
|
||||
field->pFieldname);
|
||||
#endif
|
||||
fieldGui->setValue(mTargetComponent->getDataField(field->pFieldname, NULL));
|
||||
|
||||
mChildren.push_back(fieldGui);
|
||||
mStack->addObject(fieldGui);
|
||||
}
|
||||
else
|
||||
{
|
||||
SAFE_DELETE(fieldGui);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < mTargetComponent->getComponentFieldCount(); i++)
|
||||
{
|
||||
ComponentField* field = mTargetComponent->getComponentField(i);
|
||||
|
||||
//first and foremost, nab the field type and check if it's a custom field or not.
|
||||
//If it's not a custom field, proceed below, if it is, hand it off to script to be handled by the component
|
||||
if (field->mFieldType == -1)
|
||||
{
|
||||
Con::executef(this, "onConstructComponentField", mTargetComponent, field->mFieldName);
|
||||
continue;
|
||||
}
|
||||
|
||||
bNewItems = true;
|
||||
|
||||
GuiInspectorField *fieldGui = constructField(field->mFieldType);
|
||||
if (fieldGui == NULL)
|
||||
fieldGui = new GuiInspectorField();
|
||||
|
||||
fieldGui->init(mParent, this);
|
||||
|
||||
fieldGui->setTargetObject(mTargetComponent);
|
||||
|
||||
AbstractClassRep::Field *refField = NULL;
|
||||
|
||||
//check dynamics
|
||||
SimFieldDictionary* fieldDictionary = mTargetComponent->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;
|
||||
}
|
||||
|
||||
void GuiInspectorComponentGroup::onRightMouseUp(const GuiEvent &event)
|
||||
{
|
||||
//mParent->mOverDivider = false;
|
||||
if (isMethod("onRightMouseUp"))
|
||||
Con::executef(this, "onRightMouseUp", event.mousePoint);
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorComponentGroup, inspectGroup, bool, (),, "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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorComponentGroup, addDynamicField, void, (), , "obj.addDynamicField();")
|
||||
{
|
||||
object->addDynamicField();
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorComponentGroup, removeDynamicField, void, (), , "")
|
||||
{
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorComponentGroup, getComponent, S32, (), , "")
|
||||
{
|
||||
return object->getComponent()->getId();
|
||||
}
|
||||
|
|
@ -1,76 +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 GUI_INSPECTOR_COMPONENT_GROUP_H
|
||||
#define GUI_INSPECTOR_COMPONENT_GROUP_H
|
||||
|
||||
#include "gui/editor/inspector/group.h"
|
||||
#include "console/simFieldDictionary.h"
|
||||
#include "T3D/components/component.h"
|
||||
#include "gui/controls/guiPopUpCtrlEx.h"
|
||||
|
||||
class GuiInspectorComponentGroup : public GuiInspectorGroup
|
||||
{
|
||||
private:
|
||||
typedef GuiInspectorGroup Parent;
|
||||
GuiControl* mAddCtrl;
|
||||
|
||||
Component* mTargetComponent;
|
||||
|
||||
Vector<AbstractClassRep::Field> tempFields;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(GuiInspectorComponentGroup);
|
||||
GuiInspectorComponentGroup() { /*mNeedScroll=false;*/ };
|
||||
GuiInspectorComponentGroup(StringTableEntry groupName, SimObjectPtr<GuiInspector> parent, Component* targetComponent);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// inspectGroup is overridden in GuiInspectorComponentGroup to inspect an
|
||||
// objects FieldDictionary (dynamic fields) instead of regular persistent
|
||||
// fields.
|
||||
virtual bool onAdd();
|
||||
bool inspectGroup();
|
||||
virtual void updateAllFields();
|
||||
|
||||
void onMouseMove(const GuiEvent &event);
|
||||
virtual void onRightMouseUp(const GuiEvent &event);
|
||||
|
||||
// For scriptable dynamic field additions
|
||||
void addDynamicField();
|
||||
|
||||
// Clear our fields (delete them)
|
||||
void clearFields();
|
||||
|
||||
// Find an already existent field by name in the dictionary
|
||||
virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary(StringTableEntry fieldName);
|
||||
|
||||
AbstractClassRep::Field* findObjectBehaviorField(Component* target, String fieldName);
|
||||
|
||||
Component* getComponent() { return mTargetComponent; }
|
||||
|
||||
protected:
|
||||
// create our inner controls when we add
|
||||
virtual bool createContent();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -26,8 +26,6 @@
|
|||
#include "gui/editor/inspector/dynamicField.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorDynamicGroup);
|
||||
|
||||
ConsoleDocClass( GuiInspectorDynamicGroup,
|
||||
|
|
@ -124,15 +122,6 @@ bool GuiInspectorDynamicGroup::inspectGroup()
|
|||
SimFieldDictionary * fieldDictionary = target->getFieldDictionary();
|
||||
for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
|
||||
{
|
||||
if (target->getClassRep()->isSubclassOf("Component"))
|
||||
{
|
||||
Component* compTarget = dynamic_cast<Component*>(target);
|
||||
|
||||
ComponentField* compField = compTarget->getComponentField((*ditr)->slotName);
|
||||
if (compField)
|
||||
continue;
|
||||
}
|
||||
|
||||
if( i == 0 )
|
||||
{
|
||||
flist.increment();
|
||||
|
|
|
|||
|
|
@ -1,140 +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 "gui/buttons/guiIconButtonCtrl.h"
|
||||
#include "gui/editor/guiInspector.h"
|
||||
#include "gui/editor/inspector/entityGroup.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorEntityGroup);
|
||||
|
||||
ConsoleDocClass(GuiInspectorEntityGroup,
|
||||
"@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
|
||||
"of regular persistent fields.\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
bool GuiInspectorEntityGroup::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorEntityGroup - add custom controls
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GuiInspectorEntityGroup::createContent()
|
||||
{
|
||||
if(!Parent::createContent())
|
||||
return false;
|
||||
|
||||
Con::evaluatef("%d.stack = %d;", this->getId(), mStack->getId());
|
||||
|
||||
Con::executef(this, "createContent");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorEntityGroup - inspectGroup override
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GuiInspectorEntityGroup::inspectGroup()
|
||||
{
|
||||
const U32 numTargets = mParent->getNumInspectObjects();
|
||||
if (numTargets == 1)
|
||||
{
|
||||
Entity* target = dynamic_cast<Entity*>(mParent->getInspectObject(0));
|
||||
|
||||
if(target)
|
||||
Con::executef(this, "inspectObject", target->getIdString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiInspectorEntityGroup::updateAllFields()
|
||||
{
|
||||
// We overload this to just reinspect the group.
|
||||
inspectGroup();
|
||||
}
|
||||
|
||||
void GuiInspectorEntityGroup::onMouseMove(const GuiEvent &event)
|
||||
{
|
||||
//mParent->mOverDivider = false;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorEntityGroup, inspectGroup, bool, (),, "Refreshes the dynamic fields in the inspector.")
|
||||
{
|
||||
return object->inspectGroup();
|
||||
}
|
||||
|
||||
void GuiInspectorEntityGroup::clearFields()
|
||||
{
|
||||
}
|
||||
|
||||
SimFieldDictionary::Entry* GuiInspectorEntityGroup::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 GuiInspectorEntityGroup::addDynamicField()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractClassRep::Field* GuiInspectorEntityGroup::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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorEntityGroup, addDynamicField, void, (), , "obj.addDynamicField();")
|
||||
{
|
||||
object->addDynamicField();
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorEntityGroup, removeDynamicField, void, (), , "")
|
||||
{
|
||||
}
|
||||
|
|
@ -1,72 +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 GUI_INSPECTOR_ENTITY_GROUP_H
|
||||
#define GUI_INSPECTOR_ENTITY_GROUP_H
|
||||
|
||||
#include "gui/editor/inspector/group.h"
|
||||
#include "console/simFieldDictionary.h"
|
||||
#include "T3D/components/component.h"
|
||||
#include "gui/controls/guiPopUpCtrlEx.h"
|
||||
|
||||
class GuiInspectorEntityGroup : public GuiInspectorGroup
|
||||
{
|
||||
private:
|
||||
typedef GuiInspectorGroup Parent;
|
||||
GuiControl* mAddCtrl;
|
||||
|
||||
GuiPopUpMenuCtrlEx* mAddBhvrList;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(GuiInspectorEntityGroup);
|
||||
GuiInspectorEntityGroup() { /*mNeedScroll=false;*/ };
|
||||
GuiInspectorEntityGroup(StringTableEntry groupName, SimObjectPtr<GuiInspector> parent)
|
||||
: GuiInspectorGroup(groupName, parent) { /*mNeedScroll=false;*/
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// inspectGroup is overridden in GuiInspectorEntityGroup to inspect an
|
||||
// objects FieldDictionary (dynamic fields) instead of regular persistent
|
||||
// fields.
|
||||
virtual bool onAdd();
|
||||
bool inspectGroup();
|
||||
virtual void updateAllFields();
|
||||
|
||||
void onMouseMove(const GuiEvent &event);
|
||||
|
||||
// For scriptable dynamic field additions
|
||||
void addDynamicField();
|
||||
|
||||
// Clear our fields (delete them)
|
||||
void clearFields();
|
||||
|
||||
// Find an already existent field by name in the dictionary
|
||||
virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary(StringTableEntry fieldName);
|
||||
|
||||
AbstractClassRep::Field* findObjectBehaviorField(Component* target, String fieldName);
|
||||
protected:
|
||||
// create our inner controls when we add
|
||||
virtual bool createContent();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,505 +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 "gui/buttons/guiIconButtonCtrl.h"
|
||||
#include "gui/editor/guiInspector.h"
|
||||
#include "gui/editor/inspector/mountingGroup.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "T3D/entity.h"
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
//Need this to get node lists
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorMountingGroup);
|
||||
|
||||
ConsoleDocClass( GuiInspectorMountingGroup,
|
||||
"@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
|
||||
"of regular persistent fields.\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorMountingGroup - add custom controls
|
||||
//-----------------------------------------------------------------------------
|
||||
GuiInspectorMountingGroup::GuiInspectorMountingGroup( StringTableEntry groupName, SimObjectPtr<GuiInspector> parent )
|
||||
: GuiInspectorGroup( groupName, parent)
|
||||
{
|
||||
mParentInspector = parent;
|
||||
|
||||
targetMountCtrl = NULL;
|
||||
mountCtrl = NULL;
|
||||
};
|
||||
|
||||
bool GuiInspectorMountingGroup::createContent()
|
||||
{
|
||||
if(!Parent::createContent())
|
||||
return false;
|
||||
|
||||
//give the necessary padding for the nested controls so it looks nice.
|
||||
setMargin(RectI(4,0,4,4));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GuiControl* GuiInspectorMountingGroup::buildMenuCtrl()
|
||||
{
|
||||
GuiControl* retCtrl = new GuiPopUpMenuCtrl();
|
||||
|
||||
// If we couldn't construct the control, bail!
|
||||
if( retCtrl == NULL )
|
||||
return retCtrl;
|
||||
|
||||
GuiPopUpMenuCtrl *menu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
|
||||
|
||||
// Let's make it look pretty.
|
||||
retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiPopUpMenuProfile" );
|
||||
//GuiInspectorTypeMenuBase::_registerEditControl( retCtrl );
|
||||
|
||||
char szName[512];
|
||||
dSprintf( szName, 512, "IE_%s_%d_%s_Field", retCtrl->getClassName(), mParentInspector->getInspectObject()->getId(), mCaption.c_str());
|
||||
|
||||
// Register the object
|
||||
retCtrl->registerObject( szName );
|
||||
|
||||
// Configure it to update our value when the popup is closed
|
||||
char szBuffer[512];
|
||||
dSprintf( szBuffer, 512, "%d.apply( %d.getText() );", getId(), menu->getId() );
|
||||
menu->setField("Command", szBuffer );
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
bool GuiInspectorMountingGroup::buildList(Entity* ent, GuiPopUpMenuCtrl* menu)
|
||||
{
|
||||
RenderComponentInterface* renderInterface = ent->getComponent<RenderComponentInterface>();
|
||||
|
||||
if (renderInterface)
|
||||
{
|
||||
TSShape* shape = renderInterface->getShape();
|
||||
S32 nodeCount = shape ? shape->nodes.size() : 0;
|
||||
|
||||
for(U32 i=0; i < nodeCount; i++)
|
||||
{
|
||||
menu->addEntry(shape->names[i], i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorMountingGroup - inspectGroup override
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GuiInspectorMountingGroup::inspectGroup()
|
||||
{
|
||||
// We can't inspect a group without a target!
|
||||
if( !mParent->getNumInspectObjects() )
|
||||
return false;
|
||||
|
||||
// to prevent crazy resizing, we'll just freeze our stack for a sec..
|
||||
mStack->freeze(true);
|
||||
|
||||
bool bNoGroup = false;
|
||||
|
||||
// Un-grouped fields are all sorted into the 'general' group
|
||||
if ( dStricmp( mCaption, "General" ) == 0 )
|
||||
bNoGroup = true;
|
||||
|
||||
// Just delete all fields and recreate them (like the dynamicGroup)
|
||||
// because that makes creating controls for array fields a lot easier
|
||||
clearFields();
|
||||
|
||||
bool bNewItems = false;
|
||||
bool bGrabItems = false;
|
||||
|
||||
AbstractClassRep* commonAncestorClass = findCommonAncestorClass();
|
||||
AbstractClassRep::FieldList& fieldList = commonAncestorClass->mFieldList;
|
||||
for( AbstractClassRep::FieldList::iterator itr = fieldList.begin();
|
||||
itr != fieldList.end(); ++ itr )
|
||||
{
|
||||
AbstractClassRep::Field* field = &( *itr );
|
||||
if( field->type == AbstractClassRep::StartGroupFieldType )
|
||||
{
|
||||
// If we're dealing with general fields, always set grabItems to true (to skip them)
|
||||
if( bNoGroup == true )
|
||||
bGrabItems = true;
|
||||
else if( dStricmp( field->pGroupname, mCaption ) == 0 )
|
||||
bGrabItems = true;
|
||||
continue;
|
||||
}
|
||||
else if ( field->type == AbstractClassRep::EndGroupFieldType )
|
||||
{
|
||||
// If we're dealing with general fields, always set grabItems to false (to grab them)
|
||||
if( bNoGroup == true )
|
||||
bGrabItems = false;
|
||||
else if( dStricmp( field->pGroupname, mCaption ) == 0 )
|
||||
bGrabItems = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip field if it has the HideInInspectors flag set.
|
||||
|
||||
if( field->flag.test( AbstractClassRep::FIELD_HideInInspectors ) )
|
||||
continue;
|
||||
|
||||
if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DeprecatedFieldType )
|
||||
{
|
||||
if( bNoGroup == true && bGrabItems == true )
|
||||
continue;
|
||||
|
||||
// If the field already exists, just update it
|
||||
GuiInspectorField *fieldGui = findField( field->pFieldname );
|
||||
if ( fieldGui != NULL )
|
||||
{
|
||||
fieldGui->updateValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
bNewItems = true;
|
||||
|
||||
if(field->pFieldname == StringTable->insert("mountNode"))
|
||||
{
|
||||
fieldGui = new GuiInspectorNodeListField();
|
||||
|
||||
Entity* e = dynamic_cast<Entity*>(mParent->getInspectObject(0));
|
||||
if(e)
|
||||
(dynamic_cast<GuiInspectorNodeListField*>(fieldGui))->setTargetEntity(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldGui = constructField( field->type );
|
||||
if ( fieldGui == NULL )
|
||||
fieldGui = new GuiInspectorField();
|
||||
}
|
||||
|
||||
fieldGui->init( mParent, this );
|
||||
fieldGui->setInspectorField( field );
|
||||
|
||||
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 GuiInspectorMountingGroup::updateAllFields()
|
||||
{
|
||||
// We overload this to just reinspect the group.
|
||||
inspectGroup();
|
||||
}
|
||||
|
||||
void GuiInspectorMountingGroup::onMouseMove(const GuiEvent &event)
|
||||
{
|
||||
//mParent->mOverDivider = false;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorMountingGroup, inspectGroup, bool, (),, "Refreshes the dynamic fields in the inspector.")
|
||||
{
|
||||
return object->inspectGroup();
|
||||
}
|
||||
|
||||
void GuiInspectorMountingGroup::clearFields()
|
||||
{
|
||||
}
|
||||
|
||||
bool GuiInspectorMountingGroup::resize( const Point2I &newPosition, const Point2I &newExtent )
|
||||
{
|
||||
if ( !Parent::resize( newPosition, newExtent ) )
|
||||
return false;
|
||||
|
||||
//check if we're set up yet
|
||||
if(!targetMountCtrl || !mountCtrl)
|
||||
//no? bail
|
||||
return false;
|
||||
|
||||
targetMountCtrl->setExtent(newExtent.x, 18);
|
||||
mountCtrl->setExtent(newExtent.x, 18);
|
||||
|
||||
S32 dividerPos, dividerMargin;
|
||||
mParentInspector->getDivider( dividerPos, dividerMargin );
|
||||
|
||||
Point2I fieldExtent = Point2I(newExtent.x, 18);
|
||||
Point2I fieldPos = Point2I(newExtent.x, 18);
|
||||
|
||||
S32 editWidth = dividerPos - dividerMargin;
|
||||
|
||||
targetMountText->setPosition(0,0);
|
||||
targetMountText->setExtent(fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
|
||||
|
||||
targetMountNode->setPosition(fieldExtent.x - dividerPos + dividerMargin, 1);
|
||||
targetMountNode->setExtent(editWidth, fieldExtent.y - 1);
|
||||
|
||||
mountText->setPosition(0,0);
|
||||
mountText->setExtent(fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
|
||||
|
||||
mountNode->setPosition(fieldExtent.x - dividerPos + dividerMargin, 1);
|
||||
mountNode->setExtent(editWidth, fieldExtent.y - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SimFieldDictionary::Entry* GuiInspectorMountingGroup::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 GuiInspectorMountingGroup::addDynamicField()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractClassRep::Field* GuiInspectorMountingGroup::findObjectComponentField(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorMountingGroup, addDynamicField, void, (), , "obj.addDynamicField();")
|
||||
{
|
||||
object->addDynamicField();
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorMountingGroup, removeDynamicField, void, (), , "")
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
IMPLEMENT_CONOBJECT( GuiInspectorNodeListField );
|
||||
|
||||
ConsoleDocClass( GuiInspectorNodeListField,
|
||||
"@brief A control that allows to edit the custom properties (text) of one or more SimObjects.\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
GuiInspectorNodeListField::GuiInspectorNodeListField( GuiInspector *inspector,
|
||||
GuiInspectorGroup* parent,
|
||||
SimFieldDictionary::Entry* field,
|
||||
SimObjectPtr<Entity> target )
|
||||
{
|
||||
mInspector = inspector;
|
||||
mParent = parent;
|
||||
setBounds(0,0,100,20);
|
||||
mTargetEntity = target;
|
||||
}
|
||||
|
||||
GuiInspectorNodeListField::GuiInspectorNodeListField()
|
||||
{
|
||||
mInspector = NULL;
|
||||
mParent = NULL;
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setData( const char* data, bool callbacks )
|
||||
{
|
||||
mCustomValue = data;
|
||||
|
||||
//We aren't updating any mounting info if we're not mounted already
|
||||
if(mTargetEntity.getObject())
|
||||
{
|
||||
Entity* target = dynamic_cast<Entity*>(mTargetEntity->getObjectMount());
|
||||
if(target)
|
||||
{
|
||||
RenderComponentInterface* renderInterface = target->getComponent<RenderComponentInterface>();
|
||||
if (renderInterface)
|
||||
{
|
||||
if (renderInterface->getShape())
|
||||
{
|
||||
S32 nodeIdx = renderInterface->getShape()->findNode(data);
|
||||
|
||||
target->mountObject(mTargetEntity, nodeIdx, MatrixF::Identity);
|
||||
mTargetEntity->setMaskBits(Entity::MountedMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force our edit to update
|
||||
updateValue();
|
||||
}
|
||||
|
||||
const char* GuiInspectorNodeListField::getData( U32 inspectObjectIndex )
|
||||
{
|
||||
return mCustomValue;
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::updateValue()
|
||||
{
|
||||
mMenu->clear();
|
||||
//mMenu->addEntry("Origin");
|
||||
|
||||
//if(mCustomValue.isEmpty())
|
||||
if(mTargetEntity.getObject())
|
||||
{
|
||||
Entity* target = dynamic_cast<Entity*>(mTargetEntity->getObjectMount());
|
||||
if(target)
|
||||
{
|
||||
mMenu->addEntry("Origin");
|
||||
mMenu->setActive(true);
|
||||
|
||||
RenderComponentInterface* renderInterface = target->getComponent<RenderComponentInterface>();
|
||||
|
||||
if (renderInterface)
|
||||
{
|
||||
TSShape* shape = renderInterface->getShape();
|
||||
|
||||
S32 nodeCount = shape ? shape->nodes.size() : 0;
|
||||
|
||||
for(U32 i=0; i < nodeCount; i++)
|
||||
{
|
||||
mMenu->addEntry(shape->names[i], i);
|
||||
}
|
||||
|
||||
S32 targetNode = mTargetEntity->getMountNode();
|
||||
if(targetNode != -1)
|
||||
{
|
||||
String name = shape->names[targetNode];
|
||||
mCustomValue = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCustomValue = String("Origin");
|
||||
}
|
||||
|
||||
setValue( mCustomValue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setValue("Not Mounted");
|
||||
mMenu->setActive(false);
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setDoc( const char* doc )
|
||||
{
|
||||
mDoc = StringTable->insert( doc, true );
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setToolTip( StringTableEntry data )
|
||||
{
|
||||
static StringTableEntry sTooltipProfile = StringTable->insert( "tooltipProfile" );
|
||||
static StringTableEntry sHoverTime = StringTable->insert( "hovertime" );
|
||||
static StringTableEntry sTooltip = StringTable->insert( "tooltip" );
|
||||
|
||||
mEdit->setDataField( sTooltipProfile, NULL, "GuiToolTipProfile" );
|
||||
mEdit->setDataField( sHoverTime, NULL, "1000" );
|
||||
mEdit->setDataField( sTooltip, NULL, data );
|
||||
}
|
||||
|
||||
bool GuiInspectorNodeListField::onAdd()
|
||||
{
|
||||
if( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setInspectorField( AbstractClassRep::Field *field,
|
||||
StringTableEntry caption,
|
||||
const char*arrayIndex )
|
||||
{
|
||||
// Override the base just to be sure it doesn't get called.
|
||||
// We don't use an AbstractClassRep::Field...
|
||||
|
||||
mField = field;
|
||||
mCaption = field->pFieldname;
|
||||
mDoc = field->pFieldDocs;
|
||||
}
|
||||
|
||||
GuiControl* GuiInspectorNodeListField::constructEditControl()
|
||||
{
|
||||
GuiControl* retCtrl = new GuiPopUpMenuCtrl();
|
||||
|
||||
mMenu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
|
||||
|
||||
static StringTableEntry sProfile = StringTable->insert( "profile" );
|
||||
retCtrl->setDataField( sProfile, NULL, "ToolsGuiPopUpMenuEditProfile" );
|
||||
|
||||
// Register the object
|
||||
retCtrl->registerObject();
|
||||
|
||||
char szBuffer[512];
|
||||
dSprintf( szBuffer, 512, "%d.apply( %d.getText() );", getId(), mMenu->getId() );
|
||||
mMenu->setField("Command", szBuffer );
|
||||
|
||||
return retCtrl;
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setValue( const char* newValue )
|
||||
{
|
||||
GuiPopUpMenuCtrl *ctrl = dynamic_cast<GuiPopUpMenuCtrl*>( mEdit );
|
||||
if( ctrl != NULL )
|
||||
ctrl->setText( newValue );
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::_executeSelectedCallback()
|
||||
{
|
||||
}
|
||||
|
||||
void GuiInspectorNodeListField::setTargetEntity(SimObjectPtr<Entity> target)
|
||||
{
|
||||
mTargetEntity = target;
|
||||
}
|
||||
|
|
@ -1,152 +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 GUI_INSPECTOR_MOUNTINGGROUP_H
|
||||
#define GUI_INSPECTOR_MOUNTINGGROUP_H
|
||||
|
||||
#include "gui/editor/inspector/group.h"
|
||||
#include "console/simFieldDictionary.h"
|
||||
#include "T3D/components/component.h"
|
||||
#include "gui/controls/guiPopUpCtrlEx.h"
|
||||
|
||||
#ifndef _GUI_INSPECTOR_TYPES_H_
|
||||
#include "gui/editor/guiInspectorTypes.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ENTITY_H_
|
||||
#include "T3D/entity.h"
|
||||
#endif
|
||||
|
||||
class GuiInspectorMountingGroup;
|
||||
|
||||
class GuiInspectorNodeListField : public GuiInspectorField
|
||||
{
|
||||
typedef GuiInspectorField Parent;
|
||||
friend class GuiInspectorMountingGroup;
|
||||
|
||||
public:
|
||||
|
||||
GuiInspectorNodeListField( GuiInspector *inspector, GuiInspectorGroup* parent, SimFieldDictionary::Entry* field,
|
||||
SimObjectPtr<Entity> target );
|
||||
GuiInspectorNodeListField();
|
||||
~GuiInspectorNodeListField() {};
|
||||
|
||||
DECLARE_CONOBJECT( GuiInspectorNodeListField );
|
||||
|
||||
virtual void setData( const char* data, bool callbacks = true );
|
||||
virtual const char* getData( U32 inspectObjectIndex = 0 );
|
||||
virtual void updateValue();
|
||||
virtual StringTableEntry getFieldName() { return StringTable->EmptyString(); }
|
||||
|
||||
virtual void setDoc( const char* doc );
|
||||
virtual void setToolTip( StringTableEntry data );
|
||||
|
||||
virtual bool onAdd();
|
||||
|
||||
virtual void setInspectorField( AbstractClassRep::Field *field,
|
||||
StringTableEntry caption = NULL,
|
||||
const char *arrayIndex = NULL );
|
||||
|
||||
virtual GuiControl* constructEditControl();
|
||||
|
||||
virtual void setValue( const char* newValue );
|
||||
|
||||
void setTargetEntity(SimObjectPtr<Entity> target);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void _executeSelectedCallback();
|
||||
|
||||
protected:
|
||||
|
||||
String mCustomValue;
|
||||
StringTableEntry mDoc;
|
||||
|
||||
GuiPopUpMenuCtrl *mMenu;
|
||||
|
||||
SimObjectPtr<Entity> mTargetEntity;
|
||||
};
|
||||
|
||||
class GuiInspectorMountingGroup : public GuiInspectorGroup
|
||||
{
|
||||
private:
|
||||
typedef GuiInspectorGroup Parent;
|
||||
GuiControl* mAddCtrl;
|
||||
|
||||
GuiPopUpMenuCtrlEx* mAddBhvrList;
|
||||
|
||||
GuiTextCtrl *persistText;
|
||||
GuiButtonCtrl *reloadFile;
|
||||
GuiButtonCtrl *saveFile;
|
||||
GuiButtonCtrl *overwriteFile;
|
||||
GuiButtonCtrl *mBrowseButton;
|
||||
GuiControl *filePath;
|
||||
|
||||
GuiControl *targetMountCtrl;
|
||||
GuiTextCtrl *targetMountText;
|
||||
GuiPopUpMenuCtrl *targetMountNode;
|
||||
|
||||
GuiControl *mountCtrl;
|
||||
GuiTextCtrl *mountText;
|
||||
GuiPopUpMenuCtrl *mountNode;
|
||||
|
||||
GuiInspectorNodeListField* mountNodeList;
|
||||
GuiInspectorNodeListField* targetMountNodeList;
|
||||
|
||||
SimObjectPtr<GuiInspector> mParentInspector;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(GuiInspectorMountingGroup);
|
||||
GuiInspectorMountingGroup() { /*mNeedScroll=false;*/ };
|
||||
GuiInspectorMountingGroup( StringTableEntry groupName, SimObjectPtr<GuiInspector> parent );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// inspectGroup is overridden in GuiInspectorMountingGroup to inspect an
|
||||
// objects FieldDictionary (dynamic fields) instead of regular persistent
|
||||
// fields.
|
||||
bool inspectGroup();
|
||||
virtual void updateAllFields();
|
||||
|
||||
void onMouseMove(const GuiEvent &event);
|
||||
|
||||
// For scriptable dynamic field additions
|
||||
void addDynamicField();
|
||||
|
||||
// Clear our fields (delete them)
|
||||
void clearFields();
|
||||
|
||||
virtual bool resize( const Point2I &newPosition, const Point2I &newExtent );
|
||||
|
||||
// Find an already existent field by name in the dictionary
|
||||
virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary( StringTableEntry fieldName );
|
||||
|
||||
AbstractClassRep::Field* findObjectComponentField(Component* target, String fieldName);
|
||||
protected:
|
||||
// create our inner controls when we add
|
||||
virtual bool createContent();
|
||||
|
||||
GuiControl* buildMenuCtrl();
|
||||
|
||||
bool buildList(Entity* ent, GuiPopUpMenuCtrl* menu);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
#include "gui/worldEditor/worldEditorSelection.h"
|
||||
#include "gui/worldEditor/worldEditor.h"
|
||||
#include "scene/sceneObject.h"
|
||||
#include "T3D/entity.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT( WorldEditorSelection );
|
||||
|
||||
|
|
@ -410,34 +409,26 @@ void WorldEditorSelection::rotate(const EulerF & rot, const Point3F & center)
|
|||
// single selections will rotate around own axis, multiple about world
|
||||
if(size() == 1)
|
||||
{
|
||||
Entity* eO = dynamic_cast< Entity* >(at(0));
|
||||
if (eO)
|
||||
SceneObject* object = dynamic_cast<SceneObject*>(at(0));
|
||||
if (object)
|
||||
{
|
||||
eO->setTransform(eO->getPosition(), eO->getRotation() + RotationF(rot));
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneObject* object = dynamic_cast<SceneObject*>(at(0));
|
||||
if (object)
|
||||
{
|
||||
MatrixF mat = object->getTransform();
|
||||
MatrixF mat = object->getTransform();
|
||||
|
||||
Point3F pos;
|
||||
mat.getColumn(3, &pos);
|
||||
Point3F pos;
|
||||
mat.getColumn(3, &pos);
|
||||
|
||||
// get offset in obj space
|
||||
Point3F offset = pos - center;
|
||||
MatrixF wMat = object->getWorldTransform();
|
||||
wMat.mulV(offset);
|
||||
// get offset in obj space
|
||||
Point3F offset = pos - center;
|
||||
MatrixF wMat = object->getWorldTransform();
|
||||
wMat.mulV(offset);
|
||||
|
||||
//
|
||||
MatrixF transform(EulerF(0, 0, 0), -offset);
|
||||
transform.mul(MatrixF(rot));
|
||||
transform.mul(MatrixF(EulerF(0, 0, 0), offset));
|
||||
mat.mul(transform);
|
||||
//
|
||||
MatrixF transform(EulerF(0, 0, 0), -offset);
|
||||
transform.mul(MatrixF(rot));
|
||||
transform.mul(MatrixF(EulerF(0, 0, 0), offset));
|
||||
mat.mul(transform);
|
||||
|
||||
object->setTransform(mat);
|
||||
}
|
||||
object->setTransform(mat);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -223,7 +223,6 @@ void AdvancedLightBinManager::addLight( LightInfo *light )
|
|||
// Find a shadow map for this light, if it has one
|
||||
ShadowMapParams *lsp = light->getExtended<ShadowMapParams>();
|
||||
LightShadowMap *lsm = lsp->getShadowMap();
|
||||
LightShadowMap *dynamicShadowMap = lsp->getShadowMap(true);
|
||||
|
||||
// Get the right shadow type.
|
||||
ShadowType shadowType = ShadowType_None;
|
||||
|
|
@ -236,7 +235,6 @@ void AdvancedLightBinManager::addLight( LightInfo *light )
|
|||
LightBinEntry lEntry;
|
||||
lEntry.lightInfo = light;
|
||||
lEntry.shadowMap = lsm;
|
||||
lEntry.dynamicShadowMap = dynamicShadowMap;
|
||||
lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex() );
|
||||
|
||||
if( lightType == LightInfo::Spot )
|
||||
|
|
@ -376,7 +374,6 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
|
|||
setupSGData( sgData, state, curLightInfo );
|
||||
curLightMat->setLightParameters( curLightInfo, state );
|
||||
mShadowManager->setLightShadowMap( curEntry.shadowMap );
|
||||
mShadowManager->setLightDynamicShadowMap( curEntry.dynamicShadowMap );
|
||||
|
||||
// Set geometry
|
||||
GFX->setVertexBuffer( curEntry.vertBuffer );
|
||||
|
|
@ -405,7 +402,6 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
|
|||
|
||||
// Set NULL for active shadow map (so nothing gets confused)
|
||||
mShadowManager->setLightShadowMap(NULL);
|
||||
mShadowManager->setLightDynamicShadowMap(NULL);
|
||||
GFX->setVertexBuffer( NULL );
|
||||
GFX->setPrimitiveBuffer( NULL );
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@ protected:
|
|||
{
|
||||
LightInfo* lightInfo;
|
||||
LightShadowMap* shadowMap;
|
||||
LightShadowMap* dynamicShadowMap;
|
||||
LightMaterialInfo* lightMaterial;
|
||||
GFXPrimitiveBuffer* primBuffer;
|
||||
GFXVertexBuffer* vertBuffer;
|
||||
|
|
|
|||
|
|
@ -360,13 +360,10 @@ void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
|
|||
LightingShaderConstants *lsc = getLightingShaderConstants(shaderConsts);
|
||||
|
||||
LightShadowMap *lsm = SHADOWMGR->getCurrentShadowMap();
|
||||
LightShadowMap *dynamicShadowMap = SHADOWMGR->getCurrentDynamicShadowMap();
|
||||
|
||||
LightInfo *light;
|
||||
if (lsm)
|
||||
light = lsm->getLightInfo();
|
||||
else if ( dynamicShadowMap )
|
||||
light = dynamicShadowMap->getLightInfo();
|
||||
else
|
||||
{
|
||||
light = sgData.lights[0];
|
||||
|
|
@ -436,46 +433,6 @@ void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
|
|||
lsc->mViewToLightProjSC->getType() );
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic
|
||||
if ( dynamicShadowMap )
|
||||
{
|
||||
if ( lsc->mDynamicWorldToLightProjSC->isValid() )
|
||||
shaderConsts->set( lsc->mDynamicWorldToLightProjSC,
|
||||
dynamicShadowMap->getWorldToLightProj(),
|
||||
lsc->mDynamicWorldToLightProjSC->getType() );
|
||||
|
||||
if ( lsc->mDynamicViewToLightProjSC->isValid() )
|
||||
{
|
||||
// TODO: Should probably cache these results and
|
||||
// not do this mul here on every material that needs
|
||||
// this transform.
|
||||
|
||||
shaderConsts->set( lsc->mDynamicViewToLightProjSC,
|
||||
dynamicShadowMap->getWorldToLightProj() * state->getCameraTransform(),
|
||||
lsc->mDynamicViewToLightProjSC->getType() );
|
||||
}
|
||||
|
||||
shaderConsts->setSafe( lsc->mShadowMapSizeSC, 1.0f / (F32)dynamicShadowMap->getTexSize() );
|
||||
|
||||
// Do this last so that overrides can properly override parameters previously set
|
||||
dynamicShadowMap->setShaderParameters(shaderConsts, lsc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( lsc->mDynamicViewToLightProjSC->isValid() )
|
||||
{
|
||||
// TODO: Should probably cache these results and
|
||||
// not do this mul here on every material that needs
|
||||
// this transform.
|
||||
MatrixF proj;
|
||||
light->getWorldToLightProj( &proj );
|
||||
|
||||
shaderConsts->set( lsc->mDynamicViewToLightProjSC,
|
||||
proj * state->getCameraTransform(),
|
||||
lsc->mDynamicViewToLightProjSC->getType() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvancedLightManager::registerGlobalLight(LightInfo *light, SimObject *obj)
|
||||
|
|
@ -504,7 +461,6 @@ bool AdvancedLightManager::setTextureStage( const SceneData &sgData,
|
|||
ShaderConstHandles *handles )
|
||||
{
|
||||
LightShadowMap* lsm = SHADOWMGR->getCurrentShadowMap();
|
||||
LightShadowMap* dynamicShadowMap = SHADOWMGR->getCurrentDynamicShadowMap();
|
||||
|
||||
// Assign Shadowmap, if it exists
|
||||
LightingShaderConstants* lsc = getLightingShaderConstants(shaderConsts);
|
||||
|
|
@ -522,18 +478,7 @@ bool AdvancedLightManager::setTextureStage( const SceneData &sgData,
|
|||
GFX->setTexture( reg, GFXTexHandle::ONE );
|
||||
|
||||
return true;
|
||||
} else if ( currTexFlag == Material::DynamicShadowMap )
|
||||
{
|
||||
// Dynamic
|
||||
if ( dynamicShadowMap )
|
||||
return dynamicShadowMap->setTextureStage( currTexFlag, lsc );
|
||||
|
||||
S32 reg = lsc->mDynamicShadowMapSC->getSamplerRegister();
|
||||
if ( reg != -1 )
|
||||
GFX->setTexture( reg, GFXTexHandle::ONE );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ( currTexFlag == Material::DynamicLightMask )
|
||||
{
|
||||
S32 reg = lsc->mCookieMapSC->getSamplerRegister();
|
||||
|
|
|
|||
|
|
@ -85,27 +85,21 @@ LightShadowMap::LightShadowMap( LightInfo *light )
|
|||
: mWorldToLightProj( true ),
|
||||
mTexSize( 0 ),
|
||||
mLight( light ),
|
||||
mLastUpdate( 0 ),
|
||||
mLastShader( NULL ),
|
||||
mIsViewDependent( false ),
|
||||
mLastCull( 0 ),
|
||||
mLastScreenSize( 0.0f ),
|
||||
mLastPriority( 0.0f ),
|
||||
mIsDynamic( false )
|
||||
mLastPriority( 0.0f )
|
||||
{
|
||||
GFXTextureManager::addEventDelegate( this, &LightShadowMap::_onTextureEvent );
|
||||
|
||||
mTarget = GFX->allocRenderToTextureTarget();
|
||||
smShadowMaps.push_back( this );
|
||||
mStaticRefreshTimer = PlatformTimer::create();
|
||||
mDynamicRefreshTimer = PlatformTimer::create();
|
||||
}
|
||||
|
||||
LightShadowMap::~LightShadowMap()
|
||||
{
|
||||
mTarget = NULL;
|
||||
SAFE_DELETE(mStaticRefreshTimer);
|
||||
SAFE_DELETE(mDynamicRefreshTimer);
|
||||
|
||||
releaseTextures();
|
||||
|
||||
|
|
@ -246,7 +240,6 @@ void LightShadowMap::releaseTextures()
|
|||
{
|
||||
mShadowMapTex = NULL;
|
||||
mDebugTarget.setTexture( NULL );
|
||||
mLastUpdate = 0;
|
||||
smUsedShadowMaps.remove( this );
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +261,7 @@ GFXTextureObject* LightShadowMap::_getDepthTarget( U32 width, U32 height )
|
|||
|
||||
bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc )
|
||||
{
|
||||
if ( currTexFlag == Material::DynamicLight && !isDynamic() )
|
||||
if ( currTexFlag == Material::DynamicLight )
|
||||
{
|
||||
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
|
||||
|
||||
|
|
@ -276,16 +269,7 @@ bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants*
|
|||
GFX->setTexture( reg, mShadowMapTex);
|
||||
|
||||
return true;
|
||||
} else if ( currTexFlag == Material::DynamicShadowMap && isDynamic() )
|
||||
{
|
||||
S32 reg = lsc->mDynamicShadowMapSC->getSamplerRegister();
|
||||
|
||||
if ( reg != -1 )
|
||||
GFX->setTexture( reg, mShadowMapTex);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( currTexFlag == Material::DynamicLightMask )
|
||||
} else if ( currTexFlag == Material::DynamicLightMask )
|
||||
{
|
||||
S32 reg = lsc->mCookieMapSC->getSamplerRegister();
|
||||
if ( reg != -1 )
|
||||
|
|
@ -304,35 +288,16 @@ bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants*
|
|||
return false;
|
||||
}
|
||||
|
||||
void LightShadowMap::render(RenderPassManager* renderPass,
|
||||
const SceneRenderState *diffuseState,
|
||||
bool _dynamic, bool _forceUpdate)
|
||||
void LightShadowMap::render(RenderPassManager* renderPass, const SceneRenderState *diffuseState)
|
||||
{
|
||||
if (!_forceUpdate)
|
||||
{
|
||||
// control how often shadow maps are refreshed
|
||||
if (!_dynamic && (mStaticRefreshTimer->getElapsedMs() < getLightInfo()->getStaticRefreshFreq()))
|
||||
return;
|
||||
}
|
||||
mStaticRefreshTimer->reset();
|
||||
|
||||
|
||||
if (_dynamic && (mDynamicRefreshTimer->getElapsedMs() < getLightInfo()->getDynamicRefreshFreq()))
|
||||
return;
|
||||
mDynamicRefreshTimer->reset();
|
||||
|
||||
mDebugTarget.setTexture( NULL );
|
||||
_render( renderPass, diffuseState );
|
||||
mDebugTarget.setTexture( mShadowMapTex );
|
||||
|
||||
// Add it to the used list unless we're been updated.
|
||||
if ( !mLastUpdate )
|
||||
{
|
||||
AssertFatal( !smUsedShadowMaps.contains( this ), "LightShadowMap::render - Used shadow map inserted twice!" );
|
||||
//AssertFatal( !smUsedShadowMaps.contains( this ), "LightShadowMap::render - Used shadow map inserted twice!" );
|
||||
if(!smUsedShadowMaps.contains(this))
|
||||
smUsedShadowMaps.push_back( this );
|
||||
}
|
||||
|
||||
mLastUpdate = Sim::getCurrentTime();
|
||||
}
|
||||
|
||||
BaseMatInstance* LightShadowMap::getShadowMaterial( BaseMatInstance *inMat ) const
|
||||
|
|
@ -395,8 +360,6 @@ void LightShadowMap::updatePriority( const SceneRenderState *state, U32 currTime
|
|||
return;
|
||||
}
|
||||
|
||||
U32 timeSinceLastUpdate = currTimeMs - mLastUpdate;
|
||||
|
||||
const Point3F &camPt = state->getCameraPosition();
|
||||
F32 range = mLight->getRange().x;
|
||||
F32 dist;
|
||||
|
|
@ -422,7 +385,6 @@ void LightShadowMap::updatePriority( const SceneRenderState *state, U32 currTime
|
|||
|
||||
// Update the priority.
|
||||
mLastPriority = mPow( mLastScreenSize * 50.0f, 2.0f );
|
||||
mLastPriority += timeSinceLastUpdate;
|
||||
mLastPriority *= mLight->getPriority();
|
||||
}
|
||||
|
||||
|
|
@ -466,7 +428,6 @@ LightingShaderConstants::LightingShaderConstants()
|
|||
mVectorLightColorSC(NULL),
|
||||
mVectorLightBrightnessSC(NULL),
|
||||
mShadowMapSC(NULL),
|
||||
mDynamicShadowMapSC(NULL),
|
||||
mShadowMapSizeSC(NULL),
|
||||
mCookieMapSC(NULL),
|
||||
mRandomDirsConst(NULL),
|
||||
|
|
@ -484,15 +445,7 @@ LightingShaderConstants::LightingShaderConstants()
|
|||
mScaleYSC(NULL),
|
||||
mOffsetXSC(NULL),
|
||||
mOffsetYSC(NULL),
|
||||
mFarPlaneScalePSSM(NULL),
|
||||
|
||||
mDynamicWorldToLightProjSC(NULL),
|
||||
mDynamicViewToLightProjSC(NULL),
|
||||
mDynamicScaleXSC(NULL),
|
||||
mDynamicScaleYSC(NULL),
|
||||
mDynamicOffsetXSC(NULL),
|
||||
mDynamicOffsetYSC(NULL),
|
||||
mDynamicFarPlaneScalePSSM(NULL)
|
||||
mFarPlaneScalePSSM(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -534,7 +487,6 @@ void LightingShaderConstants::init(GFXShader* shader)
|
|||
mVectorLightBrightnessSC = shader->getShaderConstHandle(ShaderGenVars::vectorLightBrightness);
|
||||
|
||||
mShadowMapSC = shader->getShaderConstHandle("$shadowMap");
|
||||
mDynamicShadowMapSC = shader->getShaderConstHandle("$dynamicShadowMap");
|
||||
mShadowMapSizeSC = shader->getShaderConstHandle("$shadowMapSize");
|
||||
|
||||
mCookieMapSC = shader->getShaderConstHandle("$cookieMap");
|
||||
|
|
@ -556,14 +508,6 @@ void LightingShaderConstants::init(GFXShader* shader)
|
|||
mOffsetYSC = shader->getShaderConstHandle("$offsetY");
|
||||
mFarPlaneScalePSSM = shader->getShaderConstHandle("$farPlaneScalePSSM");
|
||||
|
||||
mDynamicWorldToLightProjSC = shader->getShaderConstHandle("$dynamicWorldToLightProj");
|
||||
mDynamicViewToLightProjSC = shader->getShaderConstHandle("$dynamicViewToLightProj");
|
||||
mDynamicScaleXSC = shader->getShaderConstHandle("$dynamicScaleX");
|
||||
mDynamicScaleYSC = shader->getShaderConstHandle("$dynamicScaleY");
|
||||
mDynamicOffsetXSC = shader->getShaderConstHandle("$dynamicOffsetX");
|
||||
mDynamicOffsetYSC = shader->getShaderConstHandle("$dynamicOffsetY");
|
||||
mDynamicFarPlaneScalePSSM = shader->getShaderConstHandle("$dynamicFarPlaneScalePSSM");
|
||||
|
||||
mInit = true;
|
||||
}
|
||||
|
||||
|
|
@ -585,9 +529,7 @@ LightInfoExType ShadowMapParams::Type( "" );
|
|||
|
||||
ShadowMapParams::ShadowMapParams( LightInfo *light )
|
||||
: mShadowMap( NULL ),
|
||||
mLight( light ),
|
||||
mDynamicShadowMap ( NULL ),
|
||||
isDynamic ( true )
|
||||
mLight( light )
|
||||
{
|
||||
attenuationRatio.set( 0.0f, 1.0f, 1.0f );
|
||||
shadowType = ShadowType_Spot;
|
||||
|
|
@ -608,7 +550,6 @@ ShadowMapParams::~ShadowMapParams()
|
|||
{
|
||||
SAFE_DELETE( mQuery );
|
||||
SAFE_DELETE( mShadowMap );
|
||||
SAFE_DELETE( mDynamicShadowMap );
|
||||
}
|
||||
|
||||
void ShadowMapParams::_validate()
|
||||
|
|
@ -665,12 +606,9 @@ void ShadowMapParams::_validate()
|
|||
texSize = mClamp( texSize, 32, maxTexSize );
|
||||
}
|
||||
|
||||
LightShadowMap* ShadowMapParams::getOrCreateShadowMap(bool _isDynamic)
|
||||
LightShadowMap* ShadowMapParams::getOrCreateShadowMap()
|
||||
{
|
||||
if (_isDynamic && mDynamicShadowMap)
|
||||
return mDynamicShadowMap;
|
||||
|
||||
if (!_isDynamic && mShadowMap)
|
||||
if (mShadowMap)
|
||||
return mShadowMap;
|
||||
|
||||
if ( !mLight->getCastShadows() )
|
||||
|
|
@ -702,18 +640,8 @@ LightShadowMap* ShadowMapParams::getOrCreateShadowMap(bool _isDynamic)
|
|||
break;
|
||||
}
|
||||
|
||||
if ( _isDynamic )
|
||||
{
|
||||
newShadowMap->setDynamic( true );
|
||||
mDynamicShadowMap = newShadowMap;
|
||||
return mDynamicShadowMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
newShadowMap->setDynamic(false);
|
||||
mShadowMap = newShadowMap;
|
||||
return mShadowMap;
|
||||
}
|
||||
mShadowMap = newShadowMap;
|
||||
return mShadowMap;
|
||||
}
|
||||
|
||||
GFXTextureObject* ShadowMapParams::getCookieTex()
|
||||
|
|
@ -788,7 +716,6 @@ void ShadowMapParams::unpackUpdate( BitStream *stream )
|
|||
// map so it can be reallocated on the next render.
|
||||
shadowType = newType;
|
||||
SAFE_DELETE( mShadowMap );
|
||||
SAFE_DELETE( mDynamicShadowMap );
|
||||
}
|
||||
|
||||
mathRead( *stream, &attenuationRatio );
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ struct LightingShaderConstants
|
|||
GFXShaderConstHandle* mVectorLightBrightnessSC;
|
||||
|
||||
GFXShaderConstHandle* mShadowMapSC;
|
||||
GFXShaderConstHandle* mDynamicShadowMapSC;
|
||||
GFXShaderConstHandle* mShadowMapSizeSC;
|
||||
|
||||
GFXShaderConstHandle* mCookieMapSC;
|
||||
|
|
@ -123,15 +122,6 @@ struct LightingShaderConstants
|
|||
GFXShaderConstHandle* mOffsetYSC;
|
||||
GFXShaderConstHandle* mFarPlaneScalePSSM;
|
||||
|
||||
// Dynamic Specific:
|
||||
GFXShaderConstHandle* mDynamicWorldToLightProjSC;
|
||||
GFXShaderConstHandle* mDynamicViewToLightProjSC;
|
||||
GFXShaderConstHandle* mDynamicScaleXSC;
|
||||
GFXShaderConstHandle* mDynamicScaleYSC;
|
||||
GFXShaderConstHandle* mDynamicOffsetXSC;
|
||||
GFXShaderConstHandle* mDynamicOffsetYSC;
|
||||
GFXShaderConstHandle* mDynamicFarPlaneScalePSSM;
|
||||
|
||||
LightingShaderConstants();
|
||||
~LightingShaderConstants();
|
||||
|
||||
|
|
@ -165,10 +155,7 @@ public:
|
|||
virtual ~LightShadowMap();
|
||||
|
||||
void render( RenderPassManager* renderPass,
|
||||
const SceneRenderState *diffuseState,
|
||||
bool _dynamic, bool _forceUpdate);
|
||||
|
||||
U32 getLastUpdate() const { return mLastUpdate; }
|
||||
const SceneRenderState *diffuseState);
|
||||
|
||||
//U32 getLastVisible() const { return mLastVisible; }
|
||||
|
||||
|
|
@ -248,11 +235,6 @@ protected:
|
|||
/// be skipped if visible and within active range.
|
||||
bool mIsViewDependent;
|
||||
|
||||
/// The time this shadow was last updated.
|
||||
U32 mLastUpdate;
|
||||
PlatformTimer *mStaticRefreshTimer;
|
||||
PlatformTimer *mDynamicRefreshTimer;
|
||||
|
||||
/// The time this shadow was last culled and prioritized.
|
||||
U32 mLastCull;
|
||||
|
||||
|
|
@ -280,13 +262,6 @@ protected:
|
|||
/// The callback used to get texture events.
|
||||
/// @see GFXTextureManager::addEventDelegate
|
||||
void _onTextureEvent( GFXTexCallbackCode code );
|
||||
|
||||
bool mIsDynamic;
|
||||
public:
|
||||
|
||||
bool isDynamic() { return mIsDynamic; }
|
||||
void setDynamic(bool value) { mIsDynamic = value; }
|
||||
|
||||
};
|
||||
|
||||
GFX_DeclareTextureProfile( ShadowMapProfile );
|
||||
|
|
@ -309,9 +284,9 @@ public:
|
|||
virtual void packUpdate( BitStream *stream ) const;
|
||||
virtual void unpackUpdate( BitStream *stream );
|
||||
|
||||
LightShadowMap* getShadowMap(bool _isDynamic = false) const { return _isDynamic ? mDynamicShadowMap : mShadowMap; }
|
||||
LightShadowMap* getShadowMap() const { return mShadowMap; }
|
||||
|
||||
LightShadowMap* getOrCreateShadowMap(bool _isDynamic = false);
|
||||
LightShadowMap* getOrCreateShadowMap();
|
||||
|
||||
bool hasCookieTex() const { return cookie.isNotEmpty(); }
|
||||
|
||||
|
|
@ -330,7 +305,6 @@ protected:
|
|||
|
||||
///
|
||||
LightShadowMap *mShadowMap;
|
||||
LightShadowMap *mDynamicShadowMap;
|
||||
GFXOcclusionQuery* mQuery;
|
||||
|
||||
LightInfo *mLight;
|
||||
|
|
@ -393,7 +367,6 @@ public:
|
|||
bool lastSplitTerrainOnly;
|
||||
|
||||
/// @}
|
||||
bool isDynamic;
|
||||
};
|
||||
|
||||
#endif // _LIGHTSHADOWMAP_H_
|
||||
|
|
|
|||
|
|
@ -445,20 +445,11 @@ void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, Light
|
|||
}
|
||||
|
||||
// These values change based on static/dynamic.
|
||||
if ( mIsDynamic )
|
||||
{
|
||||
params->setSafe(lsc->mDynamicScaleXSC, sx);
|
||||
params->setSafe(lsc->mDynamicScaleYSC, sy);
|
||||
params->setSafe(lsc->mDynamicOffsetXSC, ox);
|
||||
params->setSafe(lsc->mDynamicOffsetYSC, oy);
|
||||
params->setSafe( lsc->mDynamicFarPlaneScalePSSM, mFarPlaneScalePSSM);
|
||||
} else {
|
||||
params->setSafe(lsc->mScaleXSC, sx);
|
||||
params->setSafe(lsc->mScaleYSC, sy);
|
||||
params->setSafe(lsc->mOffsetXSC, ox);
|
||||
params->setSafe(lsc->mOffsetYSC, oy);
|
||||
params->setSafe( lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
|
||||
}
|
||||
params->setSafe(lsc->mScaleXSC, sx);
|
||||
params->setSafe(lsc->mScaleYSC, sy);
|
||||
params->setSafe(lsc->mOffsetXSC, ox);
|
||||
params->setSafe(lsc->mOffsetYSC, oy);
|
||||
params->setSafe(lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
|
||||
|
||||
params->setSafe(lsc->mAtlasXOffsetSC, aXOff);
|
||||
params->setSafe(lsc->mAtlasYOffsetSC, aYOff);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ Signal<void(void)> ShadowMapManager::smShadowDeactivateSignal;
|
|||
ShadowMapManager::ShadowMapManager()
|
||||
: mShadowMapPass(NULL),
|
||||
mCurrentShadowMap(NULL),
|
||||
mCurrentDynamicShadowMap(NULL),
|
||||
mIsActive(false)
|
||||
{
|
||||
}
|
||||
|
|
@ -114,12 +113,10 @@ void ShadowMapManager::setLightShadowMapForLight( LightInfo *light )
|
|||
if ( params )
|
||||
{
|
||||
mCurrentShadowMap = params->getShadowMap();
|
||||
mCurrentDynamicShadowMap = params->getShadowMap(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentShadowMap = NULL;
|
||||
mCurrentDynamicShadowMap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,14 +60,12 @@ public:
|
|||
|
||||
/// Sets the current shadowmap (used in setLightInfo/setTextureStage calls)
|
||||
void setLightShadowMap( LightShadowMap *lm ) { mCurrentShadowMap = lm; }
|
||||
void setLightDynamicShadowMap( LightShadowMap *lm ) { mCurrentDynamicShadowMap = lm; }
|
||||
|
||||
/// Looks up the shadow map for the light then sets it.
|
||||
void setLightShadowMapForLight( LightInfo *light );
|
||||
|
||||
/// Return the current shadow map
|
||||
LightShadowMap* getCurrentShadowMap() const { return mCurrentShadowMap; }
|
||||
LightShadowMap* getCurrentDynamicShadowMap() const { return mCurrentDynamicShadowMap; }
|
||||
|
||||
ShadowMapPass* getShadowMapPass() const { return mShadowMapPass; }
|
||||
|
||||
|
|
@ -90,7 +88,6 @@ protected:
|
|||
|
||||
ShadowMapPass *mShadowMapPass;
|
||||
LightShadowMap *mCurrentShadowMap;
|
||||
LightShadowMap *mCurrentDynamicShadowMap;
|
||||
|
||||
///
|
||||
GFXTexHandle mTapRotationTex;
|
||||
|
|
@ -108,4 +105,4 @@ public:
|
|||
|
||||
GFX_DeclareTextureProfile( ShadowMapTexProfile );
|
||||
|
||||
#endif // _SHADOWMAPMANAGER_H_
|
||||
#endif // _SHADOWMAPMANAGER_H_
|
||||
|
|
|
|||
|
|
@ -79,15 +79,6 @@ ShadowMapPass::ShadowMapPass(LightManager* lightManager, ShadowMapManager* shado
|
|||
mShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) );
|
||||
mShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) );
|
||||
|
||||
// Dynamic
|
||||
mDynamicShadowRPM = new DynamicShadowRenderPassManager();
|
||||
mDynamicShadowRPM->assignName( "DynamicShadowRenderPassManager" );
|
||||
mDynamicShadowRPM->registerObject();
|
||||
Sim::getRootGroup()->addObject( mDynamicShadowRPM );
|
||||
mDynamicShadowRPM->addManager( new RenderMeshMgr(RenderPassManager::RIT_Mesh, 0.3f, 0.3f) );
|
||||
mDynamicShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) );
|
||||
mDynamicShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) );
|
||||
|
||||
mActiveLights = 0;
|
||||
mPrevCamPos = Point3F::Zero;
|
||||
mPrevCamRot = Point3F::Zero;
|
||||
|
|
@ -132,9 +123,6 @@ ShadowMapPass::~ShadowMapPass()
|
|||
|
||||
if ( mShadowRPM )
|
||||
mShadowRPM->deleteObject();
|
||||
|
||||
if ( mDynamicShadowRPM )
|
||||
mDynamicShadowRPM->deleteObject();
|
||||
}
|
||||
|
||||
void ShadowMapPass::render( SceneManager *sceneManager,
|
||||
|
|
@ -165,7 +153,7 @@ void ShadowMapPass::render( SceneManager *sceneManager,
|
|||
// First do a loop thru the lights setting up the shadow
|
||||
// info array for this pass.
|
||||
Vector<LightShadowMap*> shadowMaps;
|
||||
shadowMaps.reserve( mActiveLights * 2 );
|
||||
shadowMaps.reserve( mActiveLights );
|
||||
for ( U32 i = 0; i < mActiveLights; i++ )
|
||||
{
|
||||
ShadowMapParams *params = mLights[i]->getExtended<ShadowMapParams>();
|
||||
|
|
@ -176,7 +164,6 @@ void ShadowMapPass::render( SceneManager *sceneManager,
|
|||
|
||||
// --- Static Shadow Map ---
|
||||
LightShadowMap *lsm = params->getOrCreateShadowMap();
|
||||
LightShadowMap *dlsm = params->getOrCreateShadowMap(true);
|
||||
|
||||
// First check the visiblity query... if it wasn't
|
||||
// visible skip it.
|
||||
|
|
@ -191,17 +178,6 @@ void ShadowMapPass::render( SceneManager *sceneManager,
|
|||
lsm->updatePriority(diffuseState, currTime);
|
||||
|
||||
shadowMaps.push_back(lsm);
|
||||
|
||||
// --- Dynamic Shadow Map ---
|
||||
|
||||
// Any shadow that is visible is counted as being
|
||||
// active regardless if we update it or not.
|
||||
++smActiveShadowMaps;
|
||||
|
||||
// Do a priority update for this shadow.
|
||||
dlsm->updatePriority(diffuseState, currTime);
|
||||
|
||||
shadowMaps.push_back( dlsm );
|
||||
}
|
||||
|
||||
// Now sort the shadow info by priority.
|
||||
|
|
@ -238,32 +214,19 @@ void ShadowMapPass::render( SceneManager *sceneManager,
|
|||
mPrevCamFov = control->getCameraFov();
|
||||
|
||||
// 2 Shadow Maps per Light. This may fail.
|
||||
for ( U32 i = 0; i < shadowMaps.size(); i += 2 )
|
||||
for ( U32 i = 0; i < shadowMaps.size(); i++ )
|
||||
{
|
||||
LightShadowMap *lsm = shadowMaps[i];
|
||||
LightShadowMap *dlsm = shadowMaps[i + 1];
|
||||
|
||||
{
|
||||
GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render_Shadow, ColorI::RED );
|
||||
|
||||
mShadowManager->setLightShadowMap(lsm);
|
||||
mShadowManager->setLightDynamicShadowMap( dlsm );
|
||||
mShadowManager->setLightShadowMap(lsm);
|
||||
|
||||
lsm->render(mShadowRPM, diffuseState, false, forceUpdate);
|
||||
dlsm->render(mDynamicShadowRPM, diffuseState, true, forceUpdate);
|
||||
lsm->render(mShadowRPM, diffuseState);
|
||||
|
||||
++smUpdatedShadowMaps;
|
||||
}
|
||||
|
||||
// View dependent shadows or ones that are covering the entire
|
||||
// screen are updated every frame no matter the time left in
|
||||
// our shadow rendering budget.
|
||||
if ( dlsm->isViewDependent() || dlsm->getLastScreenSize() >= 1.0f )
|
||||
{
|
||||
++smNearShadowMaps;
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if we're over our frame budget for shadow
|
||||
// updates... give up completely in that case.
|
||||
if ( mTimer->getElapsedMs() > smRenderBudgetMs )
|
||||
|
|
@ -284,7 +247,6 @@ void ShadowMapPass::render( SceneManager *sceneManager,
|
|||
// The NULL here is importaint as having it around
|
||||
// will cause extra work in AdvancedLightManager::setLightInfo().
|
||||
mShadowManager->setLightShadowMap( NULL );
|
||||
mShadowManager->setLightDynamicShadowMap( NULL );
|
||||
}
|
||||
|
||||
void ShadowRenderPassManager::addInst( RenderInst *inst )
|
||||
|
|
@ -298,29 +260,7 @@ void ShadowRenderPassManager::addInst( RenderInst *inst )
|
|||
return;
|
||||
|
||||
const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial();
|
||||
if ( !mat->castsShadows() || mat->castsDynamicShadows() || mat->isTranslucent() )
|
||||
{
|
||||
// Do not add this instance, return here and avoid the default behavior
|
||||
// of calling up to Parent::addInst()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Parent::addInst(inst);
|
||||
}
|
||||
|
||||
void DynamicShadowRenderPassManager::addInst( RenderInst *inst )
|
||||
{
|
||||
PROFILE_SCOPE(DynamicShadowRenderPassManager_addInst);
|
||||
|
||||
if ( inst->type == RIT_Mesh )
|
||||
{
|
||||
MeshRenderInst *meshRI = static_cast<MeshRenderInst*>( inst );
|
||||
if ( !meshRI->matInst )
|
||||
return;
|
||||
|
||||
const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial();
|
||||
if ( !mat->castsShadows() || !mat->castsDynamicShadows() || mat->isTranslucent() )
|
||||
if ( !mat->castsShadows() || mat->isTranslucent() )
|
||||
{
|
||||
// Do not add this instance, return here and avoid the default behavior
|
||||
// of calling up to Parent::addInst()
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ class RenderObjectMgr;
|
|||
class RenderTerrainMgr;
|
||||
class PlatformTimer;
|
||||
class ShadowRenderPassManager;
|
||||
class DynamicShadowRenderPassManager;
|
||||
|
||||
/// ShadowMapPass, this is plugged into the SceneManager to generate
|
||||
/// ShadowMaps for the scene.
|
||||
|
|
@ -109,7 +108,6 @@ private:
|
|||
LightInfoList mLights;
|
||||
U32 mActiveLights;
|
||||
SimObjectPtr<ShadowRenderPassManager> mShadowRPM;
|
||||
SimObjectPtr<DynamicShadowRenderPassManager> mDynamicShadowRPM;
|
||||
LightManager* mLightManager;
|
||||
ShadowMapManager* mShadowManager;
|
||||
Point3F mPrevCamPos;
|
||||
|
|
@ -127,14 +125,4 @@ public:
|
|||
virtual void addInst( RenderInst *inst );
|
||||
};
|
||||
|
||||
class DynamicShadowRenderPassManager : public RenderPassManager
|
||||
{
|
||||
typedef RenderPassManager Parent;
|
||||
public:
|
||||
DynamicShadowRenderPassManager() : Parent() {}
|
||||
|
||||
/// Add a RenderInstance to the list
|
||||
virtual void addInst(RenderInst *inst);
|
||||
};
|
||||
|
||||
#endif // _SHADOWMAPPASS_H_
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ public:
|
|||
virtual bool isDoubleSided() const = 0;
|
||||
virtual bool isLightmapped() const = 0;
|
||||
virtual bool castsShadows() const = 0;
|
||||
virtual bool castsDynamicShadows() const = 0;
|
||||
};
|
||||
|
||||
#endif // _BASEMATERIALDEFINITION_H_
|
||||
|
|
|
|||
|
|
@ -203,7 +203,6 @@ Material::Material()
|
|||
mAlphaRef = 1;
|
||||
|
||||
mCastShadows = true;
|
||||
mCastDynamicShadows = false;
|
||||
|
||||
mPlanarReflection = false;
|
||||
|
||||
|
|
@ -431,9 +430,6 @@ void Material::initPersistFields()
|
|||
addField( "castShadows", TypeBool, Offset(mCastShadows, Material),
|
||||
"If set to false the lighting system will not cast shadows from this material." );
|
||||
|
||||
addField( "castDynamicShadows", TypeBool, Offset(mCastDynamicShadows, Material),
|
||||
"If set to false the lighting system will not cast dynamic shadows from this material." );
|
||||
|
||||
addField("planarReflection", TypeBool, Offset(mPlanarReflection, Material), "@internal" );
|
||||
|
||||
addField("translucent", TypeBool, Offset(mTranslucent, Material),
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ public:
|
|||
NormalizeCube,
|
||||
TexTarget,
|
||||
AccuMap,
|
||||
DynamicShadowMap,
|
||||
};
|
||||
|
||||
enum BlendOp
|
||||
|
|
@ -345,7 +344,6 @@ public:
|
|||
/// A generic setting which tells the system to skip
|
||||
/// generation of shadows from this material.
|
||||
bool mCastShadows;
|
||||
bool mCastDynamicShadows;
|
||||
|
||||
bool mAlphaTest;
|
||||
U32 mAlphaRef;
|
||||
|
|
@ -404,7 +402,6 @@ public:
|
|||
virtual void setAutoGenerated(bool isAutoGenerated) { mAutoGenerated = isAutoGenerated; }
|
||||
virtual bool isLightmapped() const;
|
||||
virtual bool castsShadows() const { return mCastShadows; }
|
||||
virtual bool castsDynamicShadows() const { return mCastDynamicShadows; }
|
||||
const String &getPath() const { return mPath; }
|
||||
|
||||
void flush();
|
||||
|
|
|
|||
|
|
@ -86,15 +86,7 @@ void ProcessedCustomMaterial::_setStageData()
|
|||
continue;
|
||||
}
|
||||
|
||||
if(filename.equal(String("$dynamicShadowMap"), String::NoCase))
|
||||
{
|
||||
rpd->mTexType[i] = Material::DynamicShadowMap;
|
||||
rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i];
|
||||
mMaxTex = i+1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(filename.equal(String("$dynamiclightmask"), String::NoCase))
|
||||
if(filename.equal(String("$dynamiclightmask"), String::NoCase))
|
||||
{
|
||||
rpd->mTexType[i] = Material::DynamicLightMask;
|
||||
rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i];
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@
|
|||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "math/mathUtils.h"
|
||||
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#include "T3D/systems/render/meshRenderSystem.h"
|
||||
|
||||
// For player object bounds workaround.
|
||||
#include "T3D/player.h"
|
||||
|
||||
|
|
@ -361,8 +358,6 @@ void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZ
|
|||
if( gEditingMission && state->isDiffusePass() )
|
||||
objectMask = EDITOR_RENDER_TYPEMASK;
|
||||
|
||||
MeshRenderSystem::render(this, state);
|
||||
|
||||
// Update the zoning state and traverse zones.
|
||||
|
||||
if( getZoneManager() )
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@
|
|||
#include "renderInstance/renderPassManager.h"
|
||||
#include "math/util/matrixSet.h"
|
||||
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneRenderState::SceneRenderState( SceneManager* sceneManager,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue