This commit is contained in:
Brian Roberts 2026-06-28 14:57:02 +01:00 committed by GitHub
commit fd730b057d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 3895 additions and 445 deletions

View file

@ -33,8 +33,6 @@
/// @file
/// Definitions for the core engine structured types.
template< typename T > class Vector;
namespace Torque {
class UUID;
}

View file

@ -47,7 +47,6 @@ class String;
class FileName;
class SimObject;
class SimObjectType;
template<class T> class Vector;
//-----------------------------------------------------------------------------
// String scan/print methods

View file

@ -30,9 +30,8 @@
#endif
#include <string.h>
template< class T > class Vector;
template<typename T, U32 COUNT> struct CustomAllocator;
template<typename T, U32 COUNT, typename Allocator> class Vector;
typedef UTF8 StringChar;
@ -147,7 +146,7 @@ public:
String collapseEscapes() const;
/// Split the string into its components separated by the given delimiter.
void split( const char* delimiter, Vector< String >& outElements ) const;
void split( const char* delimiter, Vector<String, 0, CustomAllocator<String, 0> >& outElements ) const;
/// Return true if the string starts with the given text.
bool startsWith( const char* text ) const;

View file

@ -24,54 +24,16 @@
#include "core/util/tVector.h"
#include "platform/profiler.h"
#include "console/engineAPI.h"
#ifdef TORQUE_DEBUG_GUARD
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize,
const char* fileName,
const U32 lineNum)
bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize,
const char* fileName,
const U32 lineNum)
{
PROFILE_SCOPE( VectorResize );
if (newCount > 0)
{
U32 blocks = newCount / VectorBlockSize;
if (newCount % VectorBlockSize)
blocks++;
S32 mem_size = blocks * VectorBlockSize * elemSize;
const char* pUseFileName = fileName != NULL ? fileName : __FILE__;
U32 useLineNum = fileName != NULL ? lineNum : __LINE__;
if (*arrayPtr != NULL)
*arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum);
else
*arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum);
AssertFatal( *arrayPtr, "VectorResize - Allocation failed." );
*aCount = newCount;
*aSize = blocks * VectorBlockSize;
return true;
}
if (*arrayPtr)
{
dFree(*arrayPtr);
*arrayPtr = 0;
}
*aSize = 0;
*aCount = 0;
return true;
}
#else
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize)
{
PROFILE_SCOPE( VectorResize );
PROFILE_SCOPE(VectorResize);
if (newCount > 0)
{
@ -79,7 +41,46 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el
if (newCount % VectorBlockSize)
blocks++;
S32 mem_size = blocks * VectorBlockSize * elemSize;
*arrayPtr = *arrayPtr ? dRealloc(*arrayPtr,mem_size) :
const char* pUseFileName = fileName != NULL ? fileName : __FILE__;
U32 useLineNum = fileName != NULL ? lineNum : __LINE__;
if (*arrayPtr != NULL)
*arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum);
else
*arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum);
AssertFatal(*arrayPtr, "VectorResize - Allocation failed.");
*aCount = newCount;
*aSize = blocks * VectorBlockSize;
return true;
}
if (*arrayPtr)
{
dFree(*arrayPtr);
*arrayPtr = 0;
}
*aSize = 0;
*aCount = 0;
return true;
}
#else
bool VectorResize(U32* aSize, U32* aCount, void** arrayPtr, U32 newCount, U32 elemSize)
{
PROFILE_SCOPE(VectorResize);
if (newCount > 0)
{
U32 blocks = newCount / VectorBlockSize;
if (newCount % VectorBlockSize)
blocks++;
S32 mem_size = blocks * VectorBlockSize * elemSize;
*arrayPtr = *arrayPtr ? dRealloc(*arrayPtr, mem_size) :
dMalloc(mem_size);
*aCount = newCount;
@ -87,7 +88,7 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el
return true;
}
if (*arrayPtr)
if (*arrayPtr)
{
dFree(*arrayPtr);
*arrayPtr = 0;
@ -99,3 +100,172 @@ bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 el
}
#endif
void test_Vector_dynamic() {
Vector<U8> v;
AssertFatal(v.size() == 0, "Initial size should be 0");
AssertFatal(v.empty(), "Vector should be empty");
// push_back, push_front, front, back
v.push_back(42);
AssertFatal(v.size() == 1, "Size after push_back should be 1");
AssertFatal(v[0] == 42, "push_back value incorrect");
v.push_front(7);
AssertFatal(v.size() == 2, "Size after push_front should be 2");
AssertFatal(v[0] == 7 && v[1] == 42, "push_front value incorrect");
AssertFatal(v.front() == 7, "front() incorrect");
AssertFatal(v.back() == 42, "back() incorrect");
// insert, erase, erase_fast
v.insert(1, 99);
AssertFatal(v.size() == 3, "Size after insert should be 3");
AssertFatal(v[1] == 99, "insert value incorrect");
v.erase(1);
AssertFatal(v.size() == 2, "Size after erase should be 2");
v.push_back(55);
v.erase_fast(static_cast < U32>(0));
AssertFatal(v.size() == 2, "Size after erase_fast should be 2");
// find_next, contains, remove
AssertFatal(v.find_next(42) != -1, "find_next failed");
AssertFatal(v.contains(42), "contains failed");
AssertFatal(v.remove(42), "remove failed");
AssertFatal(!v.contains(42), "remove did not remove value");
// pop_front, pop_back
v.push_back(88);
v.pop_front();
AssertFatal(v.size() == 1, "pop_front failed");
v.pop_back();
AssertFatal(v.size() == 0, "pop_back failed");
// reserve, capacity, setSize, set, clear, compact, fill, copy, merge, reverse
v.reserve(5);
AssertFatal(v.capacity() >= 5, "reserve/capacity failed");
v.setSize(3);
AssertFatal(v.size() == 3, "setSize failed");
U8 arr[3] = { 1,2,3 };
v.set(arr, 3);
for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "set failed");
v.clear();
AssertFatal(v.size() == 0, "clear failed");
v.setSize(3);
v.fill(9);
for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == 9, "fill failed");
U8 arr2[3] = { 4,5,6 };
v.copy(arr2);
for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr2[i], "copy failed");
v.reverse();
AssertFatal(v[0] == 6 && v[2] == 4, "reverse failed");
Vector<U8> v2;
v2.set(arr, 3);
v.merge(v2);
AssertFatal(v.size() == 6, "merge failed");
// increment, decrement
v.increment();
AssertFatal(v.size() == 7, "increment failed");
v.decrement();
AssertFatal(v.size() == 6, "decrement failed");
v.increment(2);
AssertFatal(v.size() == 8, "increment(n) failed");
v.decrement(2);
AssertFatal(v.size() == 6, "decrement(n) failed");
// insert (block), fill(count), fill(count, offset)
U8 block[2] = { 10,11 };
v.insert(block, 2, 2);
AssertFatal(v[2] == 10 && v[3] == 11, "block insert failed");
v.fill(7, 2);
for (U32 i = 0; i < 2; ++i) AssertFatal(v[i] == 7, "fill(count) failed");
v.fill(8, 2, 2);
AssertFatal(v[2] == 8 && v[3] == 8, "fill(count,offset) failed");
// assign, moveFrom, swap, compare, forEach, removeIf, countIf, anyOf, allOf
Vector<U8> v3;
v3.set(arr, 3);
v.assign(&v3, 3, 0, 0);
for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "assign failed");
v3.moveFrom(v);
AssertFatal(v3.size() == 3 && v.size() == 0, "moveFrom failed");
v.set(arr, 3);
v.swap(v3);
for (U32 i = 0; i < 3; ++i) AssertFatal(v[i] == arr[i], "swap failed");
AssertFatal(v.compare(v3), "compare failed");
v.forEach([](U8& x) { x = 1; });
for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 1, "forEach failed");
v.removeIf([](U8 x) { return x == 1; });
AssertFatal(v.size() == 0, "removeIf failed");
v.set(arr, 3);
U32 countifResult = v.countIf([](U8 x) { return x > 2; });
AssertFatal(countifResult, "countIf failed");
U8 anyofResult = v.anyOf([](U8 x) { return x == 2; });
AssertFatal(anyofResult, "anyOf failed");
U8 allofResult = v.allOf([](U8 x) { return x > 0; });
AssertFatal(allofResult, "allOf failed");
}
void test_Vector_fixed() {
Vector<U8, 10> v;
AssertFatal(v.size() == 10, "Fixed vector size should be 10");
// fill, operator[], front, back, first, last
v.fill(5);
for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 5, "fill failed");
v[3] = 99;
AssertFatal(v[3] == 99, "operator[] failed");
AssertFatal(v.front() == 5, "front failed");
AssertFatal(v.back() == 5, "back failed");
AssertFatal(v.first() == 5, "first failed");
AssertFatal(v.last() == 5, "last failed");
// reverse, swap, erase, erase_fast, contains, remove
v.reverse();
AssertFatal(v[6] == 99, "reverse failed");
Vector<U8, 10> v2;
v2.fill(1);
v.swap(v2);
for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 1 && v2[i] == 5, "swap failed");
v.erase(static_cast <U32>(0));
AssertFatal(v.size() == 10, "erase should not change size for fixed vector");
v.erase_fast(1);
AssertFatal(v.size() == 10, "erase_fast should not change size for fixed vector");
AssertFatal(v.contains(1), "contains failed");
v.remove(1);
AssertFatal(v.contains(1), "remove should not change size for fixed vector");
// fill(count), fill(count, offset), copy, merge, assign, compare
v.fill(2, 5);
for (U32 i = 0; i < 5; ++i) AssertFatal(v[i] == 2, "fill(count) failed");
v.fill(3, 2, 5);
AssertFatal(v[5] == 3 && v[6] == 3, "fill(count,offset) failed");
U8 arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
v.copy(arr);
for (U32 i = 0; i < 10; ++i) AssertFatal(v[i] == arr[i], "copy failed");
v2.copy(arr);
v.merge(v2);
AssertFatal(v.size() == 10, "merge should not change size for fixed vector");
v.assign(&v2, 10, 0, 0);
for (U32 i = 0; i < 10; ++i) AssertFatal(v[i] == arr[i], "assign failed");
AssertFatal(v.compare(v2), "compare failed");
// forEach, removeIf, countIf, anyOf, allOf
v.forEach([](U8& x) { x = 4; });
for (U32 i = 0; i < v.size(); ++i) AssertFatal(v[i] == 4, "forEach failed");
v.removeIf([](U8 x) { return x == 4; });
AssertFatal(v.size() == 10, "removeIf should not change size for fixed vector");
v.copy(arr);
U32 countifResult = v.countIf([](U8 x) { return x > 5; });
AssertFatal(countifResult == 4, "countIf failed");
U8 anyofResult = v.anyOf([](U8 x) { return x == 7; });
AssertFatal(anyofResult, "anyOf failed");
U8 allofResult = v.allOf([](U8 x) { return x < 10; });
AssertFatal(allofResult, "allOf failed");
}
DefineEngineFunction(test_Vector, void, (), ,
"Test the Vector class with both dynamic and fixed-size vectors.")
{
test_Vector_dynamic();
test_Vector_fixed();
}

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,8 @@
#include "core/util/tVector.h"
#endif
template<typename T, U32 COUNT> struct CustomAllocator;
template<typename T, U32 COUNT, typename Allocator> class Vector;
// Not exactly a specialization, just a vector to use when speed is a concern
template<class T>
class FastVector : public Vector<T>

View file

@ -0,0 +1,489 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2025 Ratfish Studios, LLC
// Portions 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 "treeObject.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(TreeObject);
TreeObject::~TreeObject()
{
if (mRoot)
{
delete mRoot;
mRoot = NULL;
}
// Clean up any orphaned nodes (safety measure)
for (Map<S32, Node*>::Iterator i = mKeyMap.begin(); i != mKeyMap.end(); ++i)
{
if (i->value && i->value->parent == NULL && i->value != mRoot)
{
delete i->value;
}
}
mKeyMap.clear();
}
void TreeObject::initPersistFields()
{
addProtectedField("treeType", TypeString, Offset(mTreeType, TreeObject), &_setType, &defaultProtectedGetFn, "Set type by name.");
Parent::initPersistFields();
}
void TreeObject::_internalSetType(const char* typeName)
{
if (!typeName || !typeName[0]) return;
ConsoleBaseType* newType = ConsoleBaseType::getTypeByName(typeName);
if (newType)
mTreeType = newType;
else
Con::errorf("TreeObject: Invalid type '%s'. Use 'TypeS32', 'TypePoint3F', etc.", typeName);
}
bool TreeObject::_setType(void* obj, const char* index, const char* data)
{
TreeObject* tree = static_cast<TreeObject*>(obj);
if (tree != NULL)
tree->_internalSetType(data);
return false;
}
void TreeObject::setType(const char* typeName)
{
if (typeName && typeName[0])
{
ConsoleBaseType* newType = ConsoleBaseType::getTypeByName(typeName);
if (newType)
mTreeType = newType;
else
Con::errorf("TreeObject::setTreeType - Unknown console type '%s'! use listConsoleTypes for options", typeName);
}
}
S32 TreeObject::addNode(S32 parentKey, const char* scriptData, S32 forcedKey)
{
if (mTreeType == NULL)
{
Con::errorf("TreeObject::addNode - treeType not set. Cannot allocate node data.");
return -1;
}
S32 finalKey = (forcedKey == -1) ? mNextFreeKey++ : forcedKey;
if (forcedKey != -1)
{
if (findNode(finalKey))
{
Con::errorf("TreeObject::addNode - Key collision! Key %d already exists.", finalKey);
return -1;
}
if (finalKey >= mNextFreeKey)
mNextFreeKey = finalKey + 1;
}
void* newData = dMalloc(mTreeType->getTypeSize());
dMemset(newData, 0, mTreeType->getTypeSize());
if (scriptData && scriptData[0])
{
const char* scriptArgv[] = { scriptData };
mTreeType->setData(newData, 1, scriptArgv, NULL, 0);
}
Node* newNode = new Node(finalKey, mTreeType);
newNode->data = newData;
Node* parent = findNode(parentKey);
if (parent)
{
parent->push_back(newNode);
newNode->parent = parent;
}
else
{
if (mRoot == NULL)
{
mRoot = newNode;
newNode->parent = NULL;
}
else
{
mRoot->push_back(newNode);
newNode->parent = mRoot;
}
}
mKeyMap.insert(finalKey, newNode);
return finalKey;
}
void TreeObject::deleteNode(S32 key)
{
Node* node = findNode(key);
if (!node)
return;
if (node == mRoot)
{
mRoot = NULL;
}
_deleteNode(node, true);
}
void TreeObject::_deleteNode(Node* node, bool unlinkFromMap)
{
if (!node)
return;
// Collect all keys in subtree for map cleanup
Vector<S32> keysToRemove;
if (unlinkFromMap)
{
node->forEachInSubtree([&keysToRemove](TreeNode<void*>* n) {
Node* scriptNode = static_cast<Node*>(n);
keysToRemove.push_back(scriptNode->key);
});
}
// Remove from parent's children array
node->nullParent();
// If deleting the root node, clear mRoot
if (node == mRoot)
mRoot = NULL;
// Delete the node (destructor handles children recursively)
delete node;
// Clean up map entries
if (unlinkFromMap)
{
for (U32 i = 0; i < keysToRemove.size(); i++)
{
mKeyMap.erase(keysToRemove[i]);
}
}
}
TreeObject::Node* TreeObject::findNode(S32 key)
{
Map<S32, Node*>::Iterator iter = mKeyMap.find(key);
return (iter != mKeyMap.end()) ? iter->value : NULL;
}
S32 TreeObject::getParent(S32 key)
{
Node* n = findNode(key);
if (n == NULL || n->parent == NULL)
return -1;
return static_cast<Node*>(n->getParent())->key;
}
bool TreeObject::toParent(S32 key, S32 newParentKey)
{
Node* targetNode = findNode(key);
Node* newParentNode = findNode(newParentKey);
if (!targetNode || !newParentNode || key == newParentKey)
return false;
// Check for circular reference (prevent making ancestor a child)
Node* checkNode = newParentNode;
while (checkNode != NULL)
{
if (checkNode->key == key)
return false;
checkNode = static_cast<Node*>(checkNode->parent);
}
// Remove from old parent and add to new parent
targetNode->nullParent();
targetNode->parent = newParentNode;
newParentNode->push_back(targetNode);
return true;
}
Vector<S32> TreeObject::getChildren(S32 key)
{
Vector<S32> keys;
Node* n = findNode(key);
if (n)
{
Vector<TreeNode<void*>*> children = n->getChildren();
keys.reserve(children.size());
for (U32 i = 0; i < children.size(); i++)
keys.push_back(static_cast<Node*>(children[i])->key);
}
return keys;
}
Vector<S32> TreeObject::getSiblings(S32 key)
{
Vector<S32> keys;
Node* n = findNode(key);
if (n)
{
Vector<TreeNode<void*>*> siblings = n->getSiblings();
keys.reserve(siblings.size());
for (U32 i = 0; i < siblings.size(); i++)
keys.push_back(static_cast<Node*>(siblings[i])->key);
}
return keys;
}
const char* TreeObject::nodesToString(const Vector<S32>& keys)
{
if (keys.empty())
return "";
static const U32 bufSize = 1024;
char* returnBuffer = Con::getReturnBuffer(bufSize);
returnBuffer[0] = '\0';
bool first = true;
for (S32 i = 0; i < keys.size(); i++)
{
char keyBuf[16];
dSprintf(keyBuf, sizeof(keyBuf), "%d", keys[i]);
if (!first)
dStrcat(returnBuffer, " ", bufSize);
dStrcat(returnBuffer, keyBuf, bufSize);
first = false;
}
return returnBuffer;
}
const char* TreeObject::toString()
{
if (!mRoot || !mTreeType) return "";
const U32 bufSize = 16384;
char* heapBuffer = (char*)dMalloc(bufSize);
dMemset(heapBuffer, 0, bufSize);
dSprintf(heapBuffer, bufSize, "%s\n", mTreeType->getTypeName());
U32 bufferPos = dStrlen(heapBuffer);
_toStringRecursive(mRoot, heapBuffer, bufferPos, bufSize);
heapBuffer[bufferPos] = '\0';
char* returnBuffer = Con::getReturnBuffer(bufSize);
dStrcpy(returnBuffer, heapBuffer, bufSize);
dFree(heapBuffer);
return returnBuffer;
}
void TreeObject::_toStringRecursive(Node* node, char* buffer, U32& bufferPos, U32 bufSize)
{
if (!node || bufferPos >= (bufSize - 1)) return;
char localDataStr[512] = "";
if (node->data && node->type) {
const char* tempStr = node->type->getData(node->data, NULL, 0);
if (tempStr) {
dStrncpy(localDataStr, tempStr, sizeof(localDataStr) - 1);
localDataStr[sizeof(localDataStr) - 1] = '\0';
}
}
S32 pKey = (node->parent) ? static_cast<Node*>(node->parent)->key : -1;
char line[1024];
dSprintf(line, sizeof(line), "%d %d \"%s\"\n", node->key, pKey, localDataStr);
U32 lineLen = dStrlen(line);
if (bufferPos + lineLen < bufSize) {
dStrcpy(buffer + bufferPos, line, bufSize - bufferPos);
bufferPos += lineLen;
}
for (U32 i = 0; i < node->size(); i++) {
Node* child = static_cast<Node*>((*node)[i]);
_toStringRecursive(child, buffer, bufferPos, bufSize);
}
}
void TreeObject::fromString(const char* data)
{
if (!data || !*data)
return;
if (mRoot) {
_deleteNode(mRoot, true);
mRoot = NULL;
}
mKeyMap.clear();
mNextFreeKey = 0;
const char* pipe = dStrchr(data, '\n');
if (pipe)
{
char typeName[256];
U32 typeLen = pipe - data;
if (typeLen > 1)
{
dStrncpy(typeName, data, typeLen);
typeName[typeLen] = '\0';
setType(typeName);
}
_fromStringRecursive(pipe + 1, -1);
}
}
void TreeObject::_fromStringRecursive(const char* str, S32 ignored)
{
if (!str || !*str) return;
const char* p = str;
while (*p) {
while (*p && (*p == '\n' || *p == '\r' || *p == ' ')) p++;
if (!*p) break;
S32 key = 0;
S32 parentID = -1;
char dataBuf[1024] = "";
if (dSscanf(p, "%d %d \"%[^\"]\"", &key, &parentID, dataBuf) < 3) {
const char* nextLine = dStrchr(p, '\n');
if (!nextLine) break;
p = nextLine + 1;
continue;
}
addNode(parentID, dataBuf, key);
const char* nextLine = dStrchr(p, '\n');
if (!nextLine) break;
p = nextLine + 1;
}
}
//==============================================================
DefineEngineMethod(TreeObject, setTreeType, void, (const char* typeName), ,
"@brief Sets the data type for the tree at runtime.\n"
"@param typeName The name of the type (e.g., 'Point3F', 'TransformF').")
{
object->setType(typeName);
}
DefineEngineMethod(TreeObject, addNode, S32, (S32 parentKey, const char* data, S32 forcedKey), (-1),
"@brief Adds a node to the hierarchy.\n"
"@param parentKey The ID of the parent (-1 for root).\n"
"@param data The data string (TransformF/Point3F).\n"
"@param forcedKey Optional S32 ID. If -1, one is generated.\n"
"@return The S32 key assigned to the node.")
{
return object->addNode(parentKey, data, forcedKey);
}
DefineEngineMethod(TreeObject, deleteNode, void, (S32 key), ,
"Removes a node and its entire subtree from the armature using its S32 key.")
{
object->deleteNode(key);
}
DefineEngineMethod(TreeObject, getNode, const char*, (S32 key), ,
"@brief Retrieves the data string stored in a specific node.\n"
"@param key The unique ID of the node.\n"
"@return The data string, or an empty string if the node/data is not found.")
{
TreeObject::Node* node = object->findNode(key);
if (node && node->data && node->type)
{
const char* dataVal = node->type->getData(node->data, NULL, 0);
return dataVal ? dataVal : "";
}
return "";
}
DefineEngineMethod(TreeObject, toParent, bool, (S32 key, S32 newParentKey), ,
"@brief Shifts a node to a new parent in the hierarchy.\n"
"@param key The ID of the node to move.\n"
"@param newParentKey The ID of the new parent, or -1 for root.\n"
"@return True on success.")
{
return object->toParent(key, newParentKey);
}
DefineEngineMethod(TreeObject, getParent, S32, (S32 key), , "Returns parent key.")
{
return object->getParent(key);
}
DefineEngineMethod(TreeObject, getChildren, const char*, (S32 key), , "Returns space-separated child keys.")
{
return object->nodesToString(object->getChildren(key));
}
DefineEngineMethod(TreeObject, getNumChildren, S32, (S32 key), ,
"@brief Returns the number of children attached to the node identified by key.\n"
"@param key The S32 identifier of the node.")
{
return object->getNumChildren(key);
}
DefineEngineMethod(TreeObject, getSiblings, const char*, (S32 key), , "Returns space-separated sibling keys.")
{
return object->nodesToString(object->getSiblings(key));
}
DefineEngineMethod(TreeObject, getNumSiblings, S32, (S32 key), ,
"@brief Returns the number of siblings for the node identified by key.\n"
"@param key The S32 identifier of the node.")
{
return object->getNumSiblings(key);
}
DefineEngineMethod(TreeObject, toString, const char*, (), ,
"Serializes the tree to a single string with a type header.")
{
return object->toString();
}
DefineEngineMethod(TreeObject, fromString, void, (const char* data), ,
"Rebuilds the tree from a serialized string.")
{
object->fromString(data);
}
//==============================================================
DefineEngineFunction(listConsoleTypes, void, (), , "Lists all registered ConsoleBaseTypes.")
{
for (ConsoleBaseType* type = ConsoleBaseType::getListHead(); type != NULL; type = type->getListNext())
{
Con::printf("%s", type->getTypeName());
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,347 @@
//-----------------------------------------------------------------------------
// 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 "vectorObject.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(VectorObject);
void VectorObject::initPersistFields()
{
addProtectedField("varType", TypeString, Offset(mVarType, VectorObject), &_setType, &defaultProtectedGetFn, "Set type by name.");
Parent::initPersistFields();
}
void VectorObject::_internalSetType(const char* typeName)
{
if (!typeName || !typeName[0]) return;
ConsoleBaseType* newType = ConsoleBaseType::getTypeByName(typeName);
if (newType)
mVarType = newType;
else
Con::errorf("VectorObject: Invalid type '%s'. Use 'TypeS32', 'TypePoint3F', etc.", typeName);
}
bool VectorObject::_setType(void* obj, const char* index, const char* data)
{
VectorObject* tree = static_cast<VectorObject*>(obj);
if (tree != NULL)
tree->_internalSetType(data);
return false;
}
void VectorObject::setType(const char* typeName)
{
if (typeName && typeName[0])
{
ConsoleBaseType* newType = ConsoleBaseType::getTypeByName(typeName);
if (newType)
mVarType = newType;
else
Con::errorf("VectorObject::setType - Unknown console type '%s'! use listConsoleTypes for options", typeName);
}
}
void* VectorObject::get(U32 index)
{
if (index >= mData.size())
{
Con::errorf("VectorObject::get - Index %d out of bounds (size: %d)", index, mData.size());
return NULL;
}
return mData[index];
}
void VectorObject::set(U32 index, void* data)
{
if (index >= mData.size())
{
Con::errorf("VectorObject::set - Index %d out of bounds (size: %d)", index, mData.size());
return;
}
mData.set(data, index );
}
void VectorObject::push_back(void* data)
{
mData.push_back(data);
}
void VectorObject::insert(U32 index, void* data)
{
if (index > mData.size())
{
Con::errorf("VectorObject::insert - Index %d out of bounds (size: %d)", index, mData.size());
return;
}
mData.insert(index, data);
}
void VectorObject::erase(U32 index)
{
if (index >= mData.size())
{
Con::errorf("VectorObject::erase - Index %d out of bounds (size: %d)", index, mData.size());
return;
}
mData.erase(index);
}
void VectorObject::clear()
{
mData.clear();
}
void VectorObject::fromString(char* dataString)
{
clear();
if (!mVarType || !dataString || !dataString[0])
return;
String dataStr(dataString);
Vector<String> tokens;
dataStr.split(" \t\n\r", tokens);
const U32 typeSize = 128;
for (U32 i = 0; i < tokens.size(); ++i)
{
const char* tokenStr = tokens[i].c_str();
void* nativeVar = mVarType->getNativeVariable();
Con::setData(mVarType->getTypeID(), nativeVar, 0, 1, &tokenStr);
void* persistentData = dMalloc(typeSize);
dMemcpy(persistentData, nativeVar, typeSize);
mData.push_back(persistentData);
}
}
char* VectorObject::toString()
{
if (!mVarType || mData.empty())
return "";
char* returnBuffer = Con::getReturnBuffer(16384);
returnBuffer[0] = '\0';
for (U32 i = 0; i < mData.size(); ++i)
{
const char* valString = Con::getData(mVarType->getTypeID(), mData[i], 0);
if (!valString) continue;
if (i > 0)
dStrcat(returnBuffer, " ", 16384);
dStrcat(returnBuffer, valString, 16384);
}
return returnBuffer;
}
//--- ENGINE METHODS ---//
DefineEngineMethod(VectorObject, setType, void, (const char* typeName), ,
"@brief Sets the data type for the vector at runtime.\n"
"@param typeName The name of the type (e.g., 'Point3F', 'TransformF').")
{
object->setType(typeName);
}
DefineEngineMethod(VectorObject, getType, const char*, (), ,
"@brief Returns the name of the currently assigned ConsoleBaseType.\n"
"@return The type name as a string.")
{
ConsoleBaseType* type = object->getVarType();
return type ? type->getTypeName() : "";
}
DefineEngineMethod(VectorObject, size, S32, (), ,
"@brief Returns the number of elements in the vector.\n"
"@return The number of elements.")
{
return object->size();
}
DefineEngineMethod(VectorObject, clear, void, (), ,
"@brief Clears all elements from the vector.")
{
object->clear();
}
DefineEngineMethod(VectorObject, push_back, void, (const char* data), ,
"@brief Appends an element to the end of the vector.\n"
"@param data The data string (TransformF/Point3F/etc).")
{
if (!object->getVarType())
{
Con::errorf("VectorObject::push_back - No variable type set!");
return;
}
const char* dataStr = data;
void* nativeVar = object->getVarType()->getNativeVariable();
Con::setData(object->getVarType()->getTypeID(), nativeVar, 0, 1, &dataStr);
const U32 typeSize = 128;
void* persistentData = dMalloc(typeSize);
dMemcpy(persistentData, nativeVar, typeSize);
object->push_back(persistentData);
}
DefineEngineMethod(VectorObject, get, const char*, (S32 index), ,
"@brief Retrieves an element from the vector.\n"
"@param index The index of the element to retrieve.\n"
"@return The data string (TransformF/Point3F/etc).")
{
if (!object->getVarType())
{
Con::errorf("VectorObject::get - No variable type set!");
return "";
}
void* data = object->get(index);
if (!data)
return "";
return Con::getData(object->getVarType()->getTypeID(), data, 0);
}
DefineEngineMethod(VectorObject, set, void, (S32 index, const char* data), ,
"@brief Sets an element in the vector.\n"
"@param index The index of the element to set.\n"
"@param data The data string (TransformF/Point3F/etc).")
{
if (!object->getVarType())
{
Con::errorf("VectorObject::set - No variable type set!");
return;
}
const char* dataStr = data;
void* nativeVar = object->getVarType()->getNativeVariable();
Con::setData(object->getVarType()->getTypeID(), nativeVar, 0, 1, &dataStr);
const U32 typeSize = 128;
void* persistentData = dMalloc(typeSize);
dMemcpy(persistentData, nativeVar, typeSize);
object->set(index, persistentData);
}
DefineEngineMethod(VectorObject, insert, void, (S32 index, const char* data), ,
"@brief Inserts an element into the vector at the specified index.\n"
"@param index The index at which to insert the element.\n"
"@param data The data string (TransformF/Point3F/etc).")
{
if (!object->getVarType())
{
Con::errorf("VectorObject::insert - No variable type set!");
return;
}
const char* dataStr = data;
void* nativeVar = object->getVarType()->getNativeVariable();
Con::setData(object->getVarType()->getTypeID(), nativeVar, 0, 1, &dataStr);
const U32 typeSize = 128;
void* persistentData = dMalloc(typeSize);
dMemcpy(persistentData, nativeVar, typeSize);
object->insert(index, persistentData);
}
DefineEngineMethod(VectorObject, erase, void, (S32 index), ,
"@brief Removes an element from the vector at the specified index.\n"
"@param index The index of the element to remove.")
{
object->erase(index);
}
DefineEngineMethod(VectorObject, fromString, void, (const char* data), ,
"@brief Populates the vector from a space-separated string of data.\n"
"@param data The space-separated data string.")
{
char* dataCopy = dStrdup(data);
object->fromString(dataCopy);
dFree(dataCopy);
}
DefineEngineMethod(VectorObject, toString, const char*, (), ,
"@brief Converts the vector's contents to a space-separated string.\n"
"@return The space-separated data string.")
{
return object->toString();
}
ConsoleType(TypeVector, TypeVector, VectorObject*, NULL)
ImplementConsoleTypeCasters(TypeVector, VectorObject*);
ConsoleGetType(TypeVector)
{
VectorObject* vObj = *((VectorObject**)dptr);
if (!vObj) return "";
// Returns "TypeName Element1 Element2 ..."
const char* typeName = vObj->getVarType() ? vObj->getVarType()->getTypeName() : "TypeVoid";
static const U32 bufSize = 16384;
char* returnBuffer = Con::getReturnBuffer(bufSize);
dSprintf(returnBuffer, bufSize, "%s %s", typeName, vObj->toString());
return returnBuffer;
}
ConsoleSetType(TypeVector)
{
if (argc < 1) return;
VectorObject** vObjPtr = (VectorObject**)dptr;
VectorObject* vObj = *vObjPtr;
if (vObj)
{
// Tokenize the input: The first entry is the TYPE
String dataStr(argv[0]);
Vector<String> tokens;
dataStr.split(" \t\n\r", tokens);
if (tokens.size() > 0)
{
// 1. Set the VarType from the first token (e.g., "TypePoint3F")
vObj->setType(tokens[0].c_str());
// 2. Process the rest as elements
if (tokens.size() > 1)
{
// Re-concatenate the remaining tokens into a data string
String remainingData = "";
for (U32 i = 1; i < tokens.size(); ++i)
{
remainingData += tokens[i];
if (i < tokens.size() - 1) remainingData += " ";
}
vObj->fromString(const_cast<char*>(remainingData.c_str()));
}
}
}
else
{
// If the field is currently null, attempt to resolve argv as an object name
if (!Sim::findObject(argv[0], *vObjPtr))
Con::warnf("TypeVector::setData - Could not resolve VectorObject: %s", argv[0]);
}
}

View file

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// 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 _TVECTOROBJECT_H_
#define _TVECTOROBJECT_H_
#include "console/simObject.h"
class VectorObject : public SimObject {
typedef SimObject Parent;
protected:
ConsoleBaseType* mVarType;
Vector<void*> mData;
public:
VectorObject(): mVarType(NULL) {};
DECLARE_CONOBJECT(VectorObject);
static void initPersistFields();
void _internalSetType(const char* typeName);
static bool _setType(void* obj, const char* index, const char* data);
void setType(const char* typeName);
ConsoleBaseType* getVarType() { return mVarType; };
U32 size() const { return mData.size(); };
void* get(U32 index);
void set(U32 index, void* data);
void push_back(void* data);
void insert(U32 index, void* data);
void erase(U32 index);
void clear();
void fromString(char* data);
char* toString();
};
DefineConsoleType(TypeVector, VectorObject*);
template<typename T>
static inline S32 VecType() {
return TYPEID<VectorObject*>();
}
#endif

View file

@ -34,7 +34,8 @@ class ObjectProxy;
class ObjectProxyList;
class SceneLightingInterface;
template <class T> class Vector;
template<typename T, U32 COUNT> struct CustomAllocator;
template<typename T, U32 COUNT, typename Allocator> class Vector;
typedef Vector<SceneLightingInterface*> SceneLightingInterfaces;
// List of available "systems" that the lighting kit can use

View file

@ -112,7 +112,7 @@ enum DriveType
// Some forward declares for later.
class Point2I;
template<class T> class Vector;
template<typename Signature> class Signal;
struct InputEventInfo;
@ -245,10 +245,10 @@ namespace Platform
bool isFullPath(const char *path);
StringTableEntry makeRelativePathName(const char *path, const char *to);
String stripExtension( String fileName, Vector< String >& validExtensions );
String stripExtension( String fileName, Vector<String, 0, CustomAllocator<String, 0> >& validExtensions );
bool dumpPath(const char *in_pBasePath, Vector<FileInfo>& out_rFileVector, S32 recurseDepth = -1);
bool dumpDirectories( const char *path, Vector<StringTableEntry> &directoryVector, S32 depth = 0, bool noBasePath = false );
bool dumpPath(const char *in_pBasePath, Vector<FileInfo, 0, CustomAllocator<FileInfo, 0> >& out_rFileVector, S32 recurseDepth = -1);
bool dumpDirectories( const char *path, Vector<StringTableEntry, 0, CustomAllocator<StringTableEntry, 0> >&directoryVector, S32 depth = 0, bool noBasePath = false );
bool hasSubDirectory( const char *pPath );
bool getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime);
bool isFile(const char *pFilePath);
@ -285,8 +285,8 @@ namespace Platform
extern struct VolumeInformation *PVolumeInformation;
// Volume functions.
void getVolumeNamesList( Vector<const char*>& out_rNameVector, bool bOnlyFixedDrives = false );
void getVolumeInformationList( Vector<VolumeInformation>& out_rVolumeInfoVector, bool bOnlyFixedDrives = false );
void getVolumeNamesList(Vector<StringTableEntry, 0, CustomAllocator<StringTableEntry, 0> >& out_rNameVector, bool bOnlyFixedDrives = false );
void getVolumeInformationList(Vector<VolumeInformation, 0, CustomAllocator<VolumeInformation, 0> >& out_rVolumeInfoVector, bool bOnlyFixedDrives = false );
struct SystemInfo_struct
{

View file

@ -89,7 +89,7 @@ public:
///
/// @todo Rethink this so we don't have a private public.
virtual bool create( const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET ) = 0;
static void enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16* fontFamily = NULL );
static void enumeratePlatformFonts( Vector<StringTableEntry,0, CustomAllocator<StringTableEntry, 0>>& fonts, UTF16* fontFamily = NULL );
};
extern PlatformFont *createPlatformFont(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET);

View file

@ -336,13 +336,14 @@ void SceneObject::onRemove()
{
smSceneObjectRemove.trigger(this);
if (getParent() != NULL) attachToParent(NULL);
unmount();
plUnlink();
if (mContainer)
mContainer->removeObject(this);
Parent::onRemove();
// PATHSHAPE
if ( getParent() != NULL) attachToParent( NULL);
// PATHSHAPE END
}
//-----------------------------------------------------------------------------
@ -395,7 +396,7 @@ void SceneObject::inspectPostApply()
//-----------------------------------------------------------------------------
void SceneObject::setGlobalBounds()
{
{
mGlobalBounds = true;
mObjBox.minExtents.set( -1e10, -1e10, -1e10 );
mObjBox.maxExtents.set( 1e10, 1e10, 1e10 );
@ -434,6 +435,7 @@ void SceneObject::setTransform( const MatrixF& mat )
mSceneManager->notifyObjectDirty( this );
setRenderTransform( mat );
setMountTransforms();
}
//-----------------------------------------------------------------------------
@ -1351,6 +1353,19 @@ void SceneObject::getRenderMountTransform( F32 delta, S32 index, const MatrixF &
outMat->mul( mRenderObjToWorld, mountTransform );
}
void SceneObject::setMountTransforms()
{
// Update all objects mounted to us
for (SceneObject* mounted = mMount.list; mounted; mounted = mounted->mMount.link)
{
// Compute mounted object's new world transform
MatrixF worldXfm;
getMountTransform(mounted->mMount.node, mounted->mMount.xfm, &worldXfm);
mounted->setTransform(worldXfm);
}
}
//=============================================================================
// Console API.
//=============================================================================
@ -1666,7 +1681,8 @@ void SceneObject::moveRender(const Point3F &delta)
void SceneObject::PerformUpdatesForChildren(MatrixF mat){
for (U32 i=0; i < getNumChildren(); i++) {
SceneObject *o = getChild(i);
o->updateChildTransform(); //update the position of the child object
if (!o->isMounted())
o->updateChildTransform(); //update the position of the child object
}
}

View file

@ -674,6 +674,7 @@ class SceneObject : public NetObject, public ProcessObject
void resolveMountPID();
void setMountTransforms();
/// @}
/// @name Sound