fundamental of Level set method

本文探讨了Level Set Methods在处理动态隐式表面中的应用与技术细节,重点介绍了该方法如何高效地处理复杂的表面演变问题。

Ref: Level Set Methods and Dynamic Implicit Surfaces

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Level Set Methods and Dynamic Implicit Surfaces Contents Preface vii Color Insert (facing page 146) I Implicit Surfaces 1 1 Implicit Functions 3 1.1 Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3 Surfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4 Geometry Toolbox . . . . . . . . . . . . . . . . . . . . . 8 1.5 Calculus Toolbox . . . . . . . . . . . . . . . . . . . . . . 13 2 Signed Distance Functions 17 2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.2 Distance Functions . . . . . . . . . . . . . . . . . . . . . 17 2.3 Signed Distance Functions . . . . . . . . . . . . . . . . . 18 2.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5 Geometry and Calculus Toolboxes . . . . . . . . . . . . . 21 II Level Set Methods 23 3 Motion in an Externally Generated Velocity Field 25 3.1 Convection . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.2 Upwind Dierencing . . . . . . . . . . . . . . . . . . . . 29 3.3 Hamilton-Jacobi ENO . . . . . . . . . . . . . . . . . . . 31 3.4 Hamilton-Jacobi WENO . . . . . . . . . . . . . . . . . . 33 3.5 TVD Runge-Kutta . . . . . . . . . . . . . . . . . . . . . 37 4 Motion Involving Mean Curvature 41 4.1 Equation of Motion . . . . . . . . . . . . . . . . . . . . . 41 4.2 Numerical Discretization . . . . . . . . . . . . . . . . . . 44 4.3 Convection-Diusion Equations . . . . . . . . . . . . . . 45 5 Hamilton-Jacobi Equations 47 5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.2 Connection with Conservation Laws . . . . . . . . . . . . 48 5.3 Numerical Discretization . . . . . . . . . . . . . . . . . . 49 5.3.1 Lax-Friedrichs Schemes . . . . . . . . . . . . . . . 50 5.3.2 The Roe-Fix Scheme . . . . . . . . . . . . . . . . 52 5.3.3 Godunovs Scheme . . . . . . . . . . . . . . . . . 54 6 Motion in the Normal Direction 55 6.1 The Basic Equation . . . . . . . . . . . . . . . . . . . . . 55 6.2 Numerical Discretization . . . . . . . . . . . . . . . . . . 57 6.3 Adding a Curvature-Dependent Term . . . . . . . . . . . 59 6.4 Adding an External Velocity Field . . . . . . . . . . . . . 59 7 Constructing Signed Distance Functions 63 7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 63 7.2 Reinitialization . . . . . . . . . . . . . . . . . . . . . . . 64 7.3 Crossing Times . . . . . . . . . . . . . . . . . . . . . . . 65 7.4 The Reinitialization Equation . . . . . . . . . . . . . . . 65 7.5 The Fast Marching Method . . . . . . . . . . . . . . . . 69 8 Extrapolation in the Normal Direction 75 8.1 One-Way Extrapolation . . . . . . . . . . . . . . . . . . . 75 8.2 Two-Way Extrapolation . . . . . . . . . . . . . . . . . . 76 8.3 Fast Marching Method . . . . . . . . . . . . . . . . . . . 76 9 Particle Level Set Method 79 9.1 Eulerian Versus Lagrangian Representations . . . . . . . 79 9.2 Using Particles to Preserve Characteristics . . . . . . . . 82 10 Codimension-Two Objects 87 10.1 Intersecting Two Level Set Functions . . . . . . . . . . . 87 10.2 Modeling Curves in 3 . . . . . . . . . . . . . . . . . . . 87 10.3 Open Curves and Surfaces . . . . . . . . . . . . . . . . . 90 10.4 Geometric Optics in a Phase-Space-Based Level Set Framework . . . . . . . . . . . . . . . . . . . . . . . . 90 …… ……
class COREUOBJECT_API FUObjectArray //UObject { public: enum ESerialNumberConstants { START_SERIAL_NUMBER = 1000, }; /** * Base class for UObjectBase create class listeners */ class FUObjectCreateListener { public: virtual ~FUObjectCreateListener() {} /** * Provides notification that a UObjectBase has been added to the uobject array * * @param Object object that has been destroyed * @param Index index of object that is being deleted */ virtual void NotifyUObjectCreated(const class UObjectBase *Object, int32 Index)=0; /** * Called when UObject Array is being shut down, this is where all listeners should be removed from it */ virtual void OnUObjectArrayShutdown()=0; }; /** * Base class for UObjectBase delete class listeners */ class FUObjectDeleteListener { public: virtual ~FUObjectDeleteListener() {} /** * Provides notification that a UObjectBase has been removed from the uobject array * * @param Object object that has been destroyed * @param Index index of object that is being deleted */ virtual void NotifyUObjectDeleted(const class UObjectBase *Object, int32 Index)=0; /** * Called when UObject Array is being shut down, this is where all listeners should be removed from it */ virtual void OnUObjectArrayShutdown() = 0; }; /** * Constructor, initializes to no permanent object pool */ FUObjectArray(); /** * Allocates and initializes the permanent object pool * * @param MaxUObjects maximum number of UObjects that can ever exist in the array * @param MaxObjectsNotConsideredByGC number of objects in the permanent object pool */ void AllocateObjectPool(int32 MaxUObjects, int32 MaxObjectsNotConsideredByGC, bool bPreAllocateObjectArray); /** * Disables the disregard for GC optimization. * */ void DisableDisregardForGC(); /** * If there's enough slack in the disregard pool, we can re-open it and keep adding objects to it */ void OpenDisregardForGC(); /** * After the initial load, this closes the disregard pool so that new object are GC-able */ void CloseDisregardForGC(); /** Returns true if the disregard for GC pool is open */ bool IsOpenForDisregardForGC() const { return OpenForDisregardForGC; } /** * indicates if the disregard for GC optimization is active * * @return true if MaxObjectsNotConsideredByGC is greater than zero; this indicates that the disregard for GC optimization is enabled */ bool DisregardForGCEnabled() const { return MaxObjectsNotConsideredByGC > 0; } /** * Adds a uobject to the global array which is used for uobject iteration * * @param Object Object to allocate an index for */ void AllocateUObjectIndex(class UObjectBase* Object, bool bMergingThreads = false); /** * Returns a UObject index top to the global uobject array * * @param Object object to free */ void FreeUObjectIndex(class UObjectBase* Object); /** * Returns the index of a UObject. Be advised this is only for very low level use. * * @param Object object to get the index of * @return index of this object */ FORCEINLINE int32 ObjectToIndex(const class UObjectBase* Object) const { return Object->InternalIndex; } /** * Returns the UObject corresponding to index. Be advised this is only for very low level use. * * @param Index index of object to return * @return Object at this index */ FORCEINLINE FUObjectItem* IndexToObject(int32 Index) { check(Index >= 0); if (Index < ObjObjects.Num()) { return const_cast<FUObjectItem*>(&ObjObjects[Index]); } return nullptr; } FORCEINLINE FUObjectItem* IndexToObjectUnsafeForGC(int32 Index) { return const_cast<FUObjectItem*>(&ObjObjects[Index]); } FORCEINLINE FUObjectItem* IndexToObject(int32 Index, bool bEvenIfPendingKill) { FUObjectItem* ObjectItem = IndexToObject(Index); if (ObjectItem && ObjectItem->Object) { if (!bEvenIfPendingKill && ObjectItem->IsPendingKill()) { ObjectItem = nullptr;; } } return ObjectItem; } FORCEINLINE FUObjectItem* ObjectToObjectItem(UObjectBase* Object) { FUObjectItem* ObjectItem = IndexToObject(Object->InternalIndex); return ObjectItem; } FORCEINLINE bool IsValid(FUObjectItem* ObjectItem, bool bEvenIfPendingKill) { if (ObjectItem) { return bEvenIfPendingKill ? !ObjectItem->IsUnreachable() : !(ObjectItem->IsUnreachable() || ObjectItem->IsPendingKill()); } return false; } FORCEINLINE FUObjectItem* IndexToValidObject(int32 Index, bool bEvenIfPendingKill) { FUObjectItem* ObjectItem = IndexToObject(Index); return IsValid(ObjectItem, bEvenIfPendingKill) ? ObjectItem : nullptr; } FORCEINLINE bool IsValid(int32 Index, bool bEvenIfPendingKill) { // This method assumes Index points to a valid object. FUObjectItem* ObjectItem = IndexToObject(Index); return IsValid(ObjectItem, bEvenIfPendingKill); } FORCEINLINE bool IsStale(FUObjectItem* ObjectItem, bool bEvenIfPendingKill) { // This method assumes ObjectItem is valid. return bEvenIfPendingKill ? (ObjectItem->IsPendingKill() || ObjectItem->IsUnreachable()) : (ObjectItem->IsUnreachable()); } FORCEINLINE bool IsStale(int32 Index, bool bEvenIfPendingKill) { // This method assumes Index points to a valid object. FUObjectItem* ObjectItem = IndexToObject(Index); if (ObjectItem) { return IsStale(ObjectItem, bEvenIfPendingKill); } return true; } /** Returns the index of the first object outside of the disregard for GC pool */ FORCEINLINE int32 GetFirstGCIndex() const { return ObjFirstGCIndex; } /** * Adds a new listener for object creation * * @param Listener listener to notify when an object is deleted */ void AddUObjectCreateListener(FUObjectCreateListener* Listener); /** * Removes a listener for object creation * * @param Listener listener to remove */ void RemoveUObjectCreateListener(FUObjectCreateListener* Listener); /** * Adds a new listener for object deletion * * @param Listener listener to notify when an object is deleted */ void AddUObjectDeleteListener(FUObjectDeleteListener* Listener); /** * Removes a listener for object deletion * * @param Listener listener to remove */ void RemoveUObjectDeleteListener(FUObjectDeleteListener* Listener); /** * Checks if a UObject pointer is valid * * @param Object object to test for validity * @return true if this index is valid */ bool IsValid(const UObjectBase* Object) const; /** Checks if the object index is valid. */ FORCEINLINE bool IsValidIndex(const UObjectBase* Object) const { return ObjObjects.IsValidIndex(Object->InternalIndex); } /** * Returns true if this object is "disregard for GC"...same results as the legacy RF_DisregardForGC flag * * @param Object object to get for disregard for GC * @return true if this object si disregard for GC */ FORCEINLINE bool IsDisregardForGC(const class UObjectBase* Object) { return Object->InternalIndex <= ObjLastNonGCIndex; } /** * Returns the size of the global UObject array, some of these might be unused * * @return the number of UObjects in the global array */ FORCEINLINE int32 GetObjectArrayNum() const { return ObjObjects.Num(); } /** * Returns the size of the global UObject array minus the number of permanent objects * * @return the number of UObjects in the global array */ FORCEINLINE int32 GetObjectArrayNumMinusPermanent() const { return ObjObjects.Num() - (ObjLastNonGCIndex + 1); } /** * Returns the number of permanent objects * * @return the number of permanent objects */ FORCEINLINE int32 GetObjectArrayNumPermanent() const { return ObjLastNonGCIndex + 1; } #if UE_GC_TRACK_OBJ_AVAILABLE /** * Returns the number of actual object indices that are claimed (the total size of the global object array minus * the number of available object array elements * * @return The number of objects claimed */ int32 GetObjectArrayNumMinusAvailable() { return ObjObjects.Num() - ObjAvailableCount.GetValue(); } /** * Returns the estimated number of object indices available for allocation */ int32 GetObjectArrayEstimatedAvailable() { return ObjObjects.Capacity() - GetObjectArrayNumMinusAvailable(); } #endif /** * Clears some internal arrays to get rid of false memory leaks */ void ShutdownUObjectArray(); /** * Given a UObject index return the serial number. If it doesn't have a serial number, give it one. Threadsafe. * @param Index - UObject Index * @return - the serial number for this UObject */ int32 AllocateSerialNumber(int32 Index); /** * Given a UObject index return the serial number. If it doesn't have a serial number, return 0. Threadsafe. * @param Index - UObject Index * @return - the serial number for this UObject */ FORCEINLINE int32 GetSerialNumber(int32 Index) { FUObjectItem* ObjectItem = IndexToObject(Index); checkSlow(ObjectItem); return ObjectItem->GetSerialNumber(); } /** * Low level iterator. */ class TIterator { public: enum EEndTagType { EndTag }; /** * Constructor * * @param InArray the array to iterate on * @param bOnlyGCedObjects if true, skip all of the permanent objects */ TIterator( const FUObjectArray& InArray, bool bOnlyGCedObjects = false ) : Array(InArray), Index(-1), CurrentObject(nullptr) { if (bOnlyGCedObjects) { Index = Array.ObjLastNonGCIndex; } Advance(); } /** * Constructor * * @param InArray the array to iterate on * @param bOnlyGCedObjects if true, skip all of the permanent objects */ TIterator( EEndTagType, const TIterator& InIter ) : Array (InIter.Array), Index(Array.ObjObjects.Num()) { } /** * Iterator advance */ FORCEINLINE void operator++() { Advance(); } friend bool operator==(const TIterator& Lhs, const TIterator& Rhs) { return Lhs.Index == Rhs.Index; } friend bool operator!=(const TIterator& Lhs, const TIterator& Rhs) { return Lhs.Index != Rhs.Index; } /** Conversion to "bool" returning true if the iterator is valid. */ FORCEINLINE explicit operator bool() const { return !!CurrentObject; } /** inverse of the "bool" operator */ FORCEINLINE bool operator !() const { return !(bool)*this; } FORCEINLINE int32 GetIndex() const { return Index; } protected: /** * Dereferences the iterator with an ordinary name for clarity in derived classes * * @return the UObject at the iterator */ FORCEINLINE FUObjectItem* GetObject() const { return CurrentObject; } /** * Iterator advance with ordinary name for clarity in subclasses * @return true if the iterator points to a valid object, false if iteration is complete */ FORCEINLINE bool Advance() { //@todo UE4 check this for LHS on Index on consoles FUObjectItem* NextObject = nullptr; CurrentObject = nullptr; while(++Index < Array.GetObjectArrayNum()) { NextObject = const_cast<FUObjectItem*>(&Array.ObjObjects[Index]); if (NextObject->Object) { CurrentObject = NextObject; return true; } } return false; } private: /** the array that we are iterating on, probably always GUObjectArray */ const FUObjectArray& Array; /** index of the current element in the object array */ int32 Index; /** Current object */ mutable FUObjectItem* CurrentObject; }; private: //typedef TStaticIndirectArrayThreadSafeRead<UObjectBase, 8 * 1024 * 1024 /* Max 8M UObjects */, 16384 /* allocated in 64K/128K chunks */ > TUObjectArray; typedef FChunkedFixedUObjectArray TUObjectArray; // note these variables are left with the Obj prefix so they can be related to the historical GObj versions /** First index into objects array taken into account for GC. */ int32 ObjFirstGCIndex; /** Index pointing to last object created in range disregarded for GC. */ int32 ObjLastNonGCIndex; /** Maximum number of objects in the disregard for GC Pool */ int32 MaxObjectsNotConsideredByGC; /** If true this is the intial load and we should load objects int the disregarded for GC range. */ bool OpenForDisregardForGC; /** Array of all live objects. */ TUObjectArray ObjObjects; //对象指针都存放在这里 /** Synchronization object for all live objects. */ FCriticalSection ObjObjectsCritical; /** Available object indices. */ TLockFreePointerListUnordered<int32, PLATFORM_CACHE_LINE_SIZE> ObjAvailableList; #if UE_GC_TRACK_OBJ_AVAILABLE /** Available object index count. */ FThreadSafeCounter ObjAvailableCount; #endif /** * Array of things to notify when a UObjectBase is created */ TArray<FUObjectCreateListener* > UObjectCreateListeners; /** * Array of things to notify when a UObjectBase is destroyed */ TArray<FUObjectDeleteListener* > UObjectDeleteListeners; #if THREADSAFE_UOBJECTS FCriticalSection UObjectDeleteListenersCritical; #endif /** Current master serial number **/ FThreadSafeCounter MasterSerialNumber; public: /** INTERNAL USE ONLY: gets the internal FUObjectItem array */ TUObjectArray& GetObjectItemArrayUnsafe() { return ObjObjects; } }; class FChunkedFixedUObjectArray { enum { NumElementsPerChunk = 64 * 1024, //所有 chunk 都是这个大小,便于计算偏移。 }; /** Master table to chunks of pointers **/ FUObjectItem** Objects; //指向指针数组的指针,每个元素是一个指向 FUObjectItem 数组(即“chunk”)的指针 /** If requested, a contiguous memory where all objects are allocated **/ FUObjectItem* PreAllocatedObjects;//备用 /** Maximum number of elements **/ int32 MaxElements; //当前支持的最大元素数量 /** Number of elements we currently have **/ int32 NumElements; //当前已使用的元素数量(即有效 UObject 数量)。 /** Maximum number of chunks **/ int32 MaxChunks; //最大 chunk 数量,由 MaxElements 计算得出。 /** Number of chunks we currently have **/ int32 NumChunks; //当前已分配的 chunk 数量。 /** * Allocates new chunk for the array **/ void ExpandChunksToIndex(int32 Index) { check(Index >= 0 && Index < MaxElements); int32 ChunkIndex = Index / NumElementsPerChunk; while (ChunkIndex >= NumChunks) { // add a chunk, and make sure nobody else tries FUObjectItem** Chunk = &Objects[NumChunks]; FUObjectItem* NewChunk = new FUObjectItem[NumElementsPerChunk]; if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)Chunk, NewChunk, nullptr)) { // someone else beat us to the add, we don't support multiple concurrent adds check(0) } else { NumChunks++; check(NumChunks <= MaxChunks); } } check(ChunkIndex < NumChunks && Objects[ChunkIndex]); // should have a valid pointer now } public: /** Constructor : Probably not thread safe **/ FChunkedFixedUObjectArray() TSAN_SAFE : Objects(nullptr) , PreAllocatedObjects(nullptr) , MaxElements(0) , NumElements(0) , MaxChunks(0) , NumChunks(0) { } ~FChunkedFixedUObjectArray() { if (!PreAllocatedObjects) { for (int32 ChunkIndex = 0; ChunkIndex < MaxChunks; ++ChunkIndex) { delete[] Objects[ChunkIndex]; } } else { delete[] PreAllocatedObjects; } delete[] Objects; } /** * Expands the array so that Element[Index] is allocated. New pointers are all zero. * @param Index The Index of an element we want to be sure is allocated **/ void PreAllocate(int32 InMaxElements, bool bPreAllocateChunks) TSAN_SAFE { check(!Objects); MaxChunks = InMaxElements / NumElementsPerChunk + 1; MaxElements = MaxChunks * NumElementsPerChunk; Objects = new FUObjectItem*[MaxChunks]; FMemory::Memzero(Objects, sizeof(FUObjectItem*) * MaxChunks); if (bPreAllocateChunks) { // Fully allocate all chunks as contiguous memory PreAllocatedObjects = new FUObjectItem[MaxElements]; for (int32 ChunkIndex = 0; ChunkIndex < MaxChunks; ++ChunkIndex) { Objects[ChunkIndex] = PreAllocatedObjects + ChunkIndex * NumElementsPerChunk; } NumChunks = MaxChunks; } } /** * Return the number of elements in the array * Thread safe, but you know, someone might have added more elements before this even returns * @return the number of elements in the array **/ FORCEINLINE int32 Num() const { return NumElements; } /** * Return the number max capacity of the array * Thread safe, but you know, someone might have added more elements before this even returns * @return the maximum number of elements in the array **/ FORCEINLINE int32 Capacity() const TSAN_SAFE { return MaxElements; } /** * Return if this index is valid * Thread safe, if it is valid now, it is valid forever. Other threads might be adding during this call. * @param Index Index to test * @return true, if this is a valid **/ FORCEINLINE bool IsValidIndex(int32 Index) const { return Index < Num() && Index >= 0; } /** * Return a pointer to the pointer to a given element * @param Index The Index of an element we want to retrieve the pointer-to-pointer for **/ FORCEINLINE_DEBUGGABLE FUObjectItem const* GetObjectPtr(int32 Index) const TSAN_SAFE { const int32 ChunkIndex = Index / NumElementsPerChunk; const int32 WithinChunkIndex = Index % NumElementsPerChunk; checkf(IsValidIndex(Index), TEXT("IsValidIndex(%d)"), Index); checkf(ChunkIndex < NumChunks, TEXT("ChunkIndex (%d) < NumChunks (%d)"), ChunkIndex, NumChunks); checkf(Index < MaxElements, TEXT("Index (%d) < MaxElements (%d)"), Index, MaxElements); FUObjectItem* Chunk = Objects[ChunkIndex]; check(Chunk); return Chunk + WithinChunkIndex; } FORCEINLINE_DEBUGGABLE FUObjectItem* GetObjectPtr(int32 Index) TSAN_SAFE { const int32 ChunkIndex = Index / NumElementsPerChunk; const int32 WithinChunkIndex = Index % NumElementsPerChunk; checkf(IsValidIndex(Index), TEXT("IsValidIndex(%d)"), Index); checkf(ChunkIndex < NumChunks, TEXT("ChunkIndex (%d) < NumChunks (%d)"), ChunkIndex, NumChunks); checkf(Index < MaxElements, TEXT("Index (%d) < MaxElements (%d)"), Index, MaxElements); FUObjectItem* Chunk = Objects[ChunkIndex]; check(Chunk); return Chunk + WithinChunkIndex; } /** * Return a reference to an element * @param Index Index to return * @return a reference to the pointer to the element * Thread safe, if it is valid now, it is valid forever. This might return nullptr, but by then, some other thread might have made it non-nullptr. **/ FORCEINLINE FUObjectItem const& operator[](int32 Index) const { FUObjectItem const* ItemPtr = GetObjectPtr(Index); check(ItemPtr); return *ItemPtr; } FORCEINLINE FUObjectItem& operator[](int32 Index) { FUObjectItem* ItemPtr = GetObjectPtr(Index); check(ItemPtr); return *ItemPtr; } int32 AddRange(int32 NumToAdd) TSAN_SAFE { int32 Result = NumElements; checkf(Result + NumToAdd <= MaxElements, TEXT("Maximum number of UObjects (%d) exceeded, make sure you update MaxObjectsInGame/MaxObjectsInEditor in project settings."), MaxElements); ExpandChunksToIndex(Result + NumToAdd - 1); NumElements += NumToAdd; return Result; } int32 AddSingle() TSAN_SAFE { return AddRange(1); } /** * Return a naked pointer to the fundamental data structure for debug visualizers. **/ FUObjectItem*** GetRootBlockForDebuggerVisualizers() { return nullptr; } }; struct FUObjectItem { // Pointer to the allocated object class UObjectBase* Object; //这里存放对象指针 // Internal flags int32 Flags; // UObject Owner Cluster Index int32 ClusterRootIndex; // Weak Object Pointer Serial number associated with the object int32 SerialNumber; FUObjectItem() : Object(nullptr) , Flags(0) , ClusterRootIndex(0) , SerialNumber(0) { } FORCEINLINE void SetOwnerIndex(int32 OwnerIndex) { ClusterRootIndex = OwnerIndex; } FORCEINLINE int32 GetOwnerIndex() const { return ClusterRootIndex; } /** Encodes the cluster index in the ClusterRootIndex variable */ FORCEINLINE void SetClusterIndex(int32 ClusterIndex) { ClusterRootIndex = -ClusterIndex - 1; } /** Decodes the cluster index from the ClusterRootIndex variable */ FORCEINLINE int32 GetClusterIndex() const { checkSlow(ClusterRootIndex < 0); return -ClusterRootIndex - 1; } FORCEINLINE int32 GetSerialNumber() const { return SerialNumber; } FORCEINLINE void SetFlags(EInternalObjectFlags FlagsToSet) { check((int32(FlagsToSet) & ~int32(EInternalObjectFlags::AllFlags)) == 0); Flags |= int32(FlagsToSet); } FORCEINLINE EInternalObjectFlags GetFlags() const { return EInternalObjectFlags(Flags); } FORCEINLINE void ClearFlags(EInternalObjectFlags FlagsToClear) { check((int32(FlagsToClear) & ~int32(EInternalObjectFlags::AllFlags)) == 0); Flags &= ~int32(FlagsToClear); } /** * Uses atomics to clear the specified flag(s). * @param FlagsToClear * @return True if this call cleared the flag, false if it has been cleared by another thread. */ FORCEINLINE bool ThisThreadAtomicallyClearedFlag(EInternalObjectFlags FlagToClear) { static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics."); bool bIChangedIt = false; while (1) { int32 StartValue = int32(Flags); if (!(StartValue & int32(FlagToClear))) { break; } int32 NewValue = StartValue & ~int32(FlagToClear); checkSlow(NewValue != StartValue); if ((int32)FPlatformAtomics::InterlockedCompareExchange((int32*)&Flags, NewValue, StartValue) == StartValue) { bIChangedIt = true; break; } } return bIChangedIt; } FORCEINLINE bool ThisThreadAtomicallySetFlag(EInternalObjectFlags FlagToSet) { static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics."); bool bIChangedIt = false; while (1) { int32 StartValue = int32(Flags); if (StartValue & int32(FlagToSet)) { break; } int32 NewValue = StartValue | int32(FlagToSet); checkSlow(NewValue != StartValue); if ((int32)FPlatformAtomics::InterlockedCompareExchange((int32*)&Flags, NewValue, StartValue) == StartValue) { bIChangedIt = true; break; } } return bIChangedIt; } FORCEINLINE bool HasAnyFlags(EInternalObjectFlags InFlags) const { return !!(Flags & int32(InFlags)); } FORCEINLINE void SetUnreachable() { Flags |= int32(EInternalObjectFlags::Unreachable); } FORCEINLINE void ClearUnreachable() { Flags &= ~int32(EInternalObjectFlags::Unreachable); } FORCEINLINE bool IsUnreachable() const { return !!(Flags & int32(EInternalObjectFlags::Unreachable)); } FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable() { return ThisThreadAtomicallyClearedFlag(EInternalObjectFlags::Unreachable); } FORCEINLINE void SetPendingKill() { Flags |= int32(EInternalObjectFlags::PendingKill); } FORCEINLINE void ClearPendingKill() { Flags &= ~int32(EInternalObjectFlags::PendingKill); } FORCEINLINE bool IsPendingKill() const { return !!(Flags & int32(EInternalObjectFlags::PendingKill)); } FORCEINLINE void SetRootSet() { Flags |= int32(EInternalObjectFlags::RootSet); } FORCEINLINE void ClearRootSet() { Flags &= ~int32(EInternalObjectFlags::RootSet); } FORCEINLINE bool IsRootSet() const { return !!(Flags & int32(EInternalObjectFlags::RootSet)); } FORCEINLINE void ResetSerialNumberAndFlags() { Flags = 0; ClusterRootIndex = 0; SerialNumber = 0; } }; class COREUOBJECT_API UObjectBase { friend class UObjectBaseUtility; friend struct Z_Construct_UClass_UObject_Statics; friend class FUObjectArray; // for access to InternalIndex without revealing it to anyone else friend class FUObjectAllocator; // for access to destructor without revealing it to anyone else friend COREUOBJECT_API void UObjectForceRegistration(UObjectBase* Object, bool bCheckForModuleRelease); friend COREUOBJECT_API void InitializePrivateStaticClass( class UClass* TClass_Super_StaticClass, class UClass* TClass_PrivateStaticClass, class UClass* TClass_WithinClass_StaticClass, const TCHAR* PackageName, const TCHAR* Name ); protected: UObjectBase() : NamePrivate(NoInit) // screwy, but the name was already set and we don't want to set it again #if ENABLE_STATNAMEDEVENTS_UOBJECT , StatIDStringStorage(nullptr) #endif { } /** * Constructor used for bootstrapping * @param InFlags RF_Flags to assign */ UObjectBase( EObjectFlags InFlags ); public: /** * Constructor used by StaticAllocateObject * @param InClass non NULL, this gives the class of the new object, if known at this time * @param InFlags RF_Flags to assign * @param InInternalFlags EInternalObjectFlags to assign * @param InOuter outer for this object * @param InName name of the new object */ UObjectBase( UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName ); /** * Final destructor, removes the object from the object array, and indirectly, from any annotations **/ virtual ~UObjectBase(); /** * Emit GC tokens for UObjectBase, this might be UObject::StaticClass or Default__Class **/ static void EmitBaseReferences(UClass *RootClass); protected: /** * Just change the FName and Outer and rehash into name hash tables. For use by higher level rename functions. * * @param NewName new name for this object * @param NewOuter new outer for this object, if NULL, outer will be unchanged */ void LowLevelRename(FName NewName,UObject *NewOuter = NULL); /** Force any base classes to be registered first */ virtual void RegisterDependencies() {} /** Enqueue the registration for this object. */ void Register(const TCHAR* PackageName,const TCHAR* Name); /** * Convert a boot-strap registered class into a real one, add to uobject array, etc * * @param UClassStaticClass Now that it is known, fill in UClass::StaticClass() as the class */ virtual void DeferredRegister(UClass *UClassStaticClass,const TCHAR* PackageName,const TCHAR* Name); private: /** * Add a newly created object to the name hash tables and the object array * * @param Name name to assign to this uobject * @param InSetInternalFlags Internal object flags to be set on the object once it's been added to the array */ void AddObject(FName Name, EInternalObjectFlags InSetInternalFlags); public: /** * Checks to see if the object appears to be valid * @return true if this appears to be a valid object */ bool IsValidLowLevel() const; /** * Faster version of IsValidLowLevel. * Checks to see if the object appears to be valid by checking pointers and their alignment. * Name and InternalIndex checks are less accurate than IsValidLowLevel. * @param bRecursive true if the Class pointer should be checked with IsValidLowLevelFast * @return true if this appears to be a valid object */ bool IsValidLowLevelFast(bool bRecursive = true) const; /** * Returns the unique ID of the object...these are reused so it is only unique while the object is alive. * Useful as a tag. **/ FORCEINLINE uint32 GetUniqueID() const { return (uint32)InternalIndex; } /** Returns the UClass that defines the fields of this object */ FORCEINLINE UClass* GetClass() const { return ClassPrivate; } /** Returns the UObject this object resides in */ FORCEINLINE UObject* GetOuter() const { return OuterPrivate; } /** Returns the logical name of this object */ FORCEINLINE FName GetFName() const { return NamePrivate; } /** * Returns the stat ID of the object, used for profiling. This will create a stat ID if needed. * * @param bForDeferred If true, a stat ID will be created even if a group is disabled */ FORCEINLINE TStatId GetStatID(bool bForDeferredUse = false) const { #if STATS // this is done to avoid even registering stats for a disabled group (unless we plan on using it later) if (bForDeferredUse || FThreadStats::IsCollectingData(GET_STATID(STAT_UObjectsStatGroupTester))) { if (!StatID.IsValidStat()) { CreateStatID(); } return StatID; } #elif ENABLE_STATNAMEDEVENTS_UOBJECT if (!StatID.IsValidStat() && (bForDeferredUse || GCycleStatsShouldEmitNamedEvents)) { CreateStatID(); } return StatID; #endif // STATS return TStatId(); // not doing stats at the moment, or ever } private: #if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT /** Creates a stat ID for this object */ void CreateStatID() const; #endif protected: /** * Set the object flags directly * **/ FORCEINLINE void SetFlagsTo( EObjectFlags NewFlags ) { checkfSlow((NewFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as 0x%x but is trying to set flags to RF_AllFlags"), *GetFName().ToString(), (int)ObjectFlags); ObjectFlags = NewFlags; } public: /** * Retrieve the object flags directly * * @return Flags for this object **/ FORCEINLINE EObjectFlags GetFlags() const { checkfSlow((ObjectFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as RF_AllFlags"), *GetFName().ToString()); return ObjectFlags; } /** * Atomically adds the specified flags. * Do not use unless you know what you are doing. * Designed to be used only by parallel GC and UObject loading thread. */ FORCENOINLINE void AtomicallySetFlags( EObjectFlags FlagsToAdd ) { int32 OldFlags = 0; int32 NewFlags = 0; do { OldFlags = ObjectFlags; NewFlags = OldFlags | FlagsToAdd; } while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags ); } /** * Atomically clears the specified flags. * Do not use unless you know what you are doing. * Designed to be used only by parallel GC and UObject loading thread. */ FORCENOINLINE void AtomicallyClearFlags( EObjectFlags FlagsToClear ) { int32 OldFlags = 0; int32 NewFlags = 0; do { OldFlags = ObjectFlags; NewFlags = OldFlags & ~FlagsToClear; } while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags ); } private: /** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit platforms to reduce memory waste */ EObjectFlags ObjectFlags; /** Index into GObjectArray...very private. */ int32 InternalIndex; /** Class the object belongs to. */ UClass* ClassPrivate; /** Name of this object */ FName NamePrivate; /** Object this object resides in. */ UObject* OuterPrivate; #if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT /** Stat id of this object, 0 if nobody asked for it yet */ mutable TStatId StatID; #if ENABLE_STATNAMEDEVENTS_UOBJECT mutable PROFILER_CHAR* StatIDStringStorage; #endif #endif // STATS || ENABLE_STATNAMEDEVENTS friend class FBlueprintCompileReinstancer; /** This is used by the reinstancer to re-class and re-archetype the current instances of a class before recompiling */ void SetClass(UClass* NewClass); #if HACK_HEADER_GENERATOR // Required by UHT makefiles for internal data serialization. friend struct FObjectBaseArchiveProxy; #endif // HACK_HEADER_GENERATOR }; UObjectBase地址是不是与FUObjectArray地址一样
最新发布
09-30
**不是,`UObjectBase` 的地址与 `FUObjectArray` 的地址完全不同。** --- ### ✅ 正确理解: > ❌ `UObjectBase*` 指针 ≠ `FUObjectArray` 地址 > ✅ `UObjectBase*` 是某个 UObject 对象的地址 > ✅ `FUObjectArray`(即 `GUObjectArray`)是一个全局结构体,管理所有 UObject 的元数据 > ✅ `UObjectBase` 对象本身被存储在 `FUObjectArray::ObjObjects` 数组中对应的 `FUObjectItem.Object` 字段里 --- ## 一、核心结构关系图解 ``` +-----------------------------+ | FUObjectArray | ← 全局单例:GUObjectArray | | | ObjObjects: | | [0] → &Chunk0[0] | → 指向第一个 chunk 的指针数组 | [1] → &Chunk0[65536] | | ... | +-----------------------------+ ↓ +----------------------------+ | Chunk (array of | | FUObjectItem) | +----------------------------+ | [0]: | | Object → APlayerController@0x12345678 ← 这才是 UObjectBase 地址! | Flags: RF_Public | ... | | SerialNumber: 1234 | | | | [1]: | | Object → UGameInstance@0x23456789 ← 另一个 UObjectBase* | ... | +----------------------------+ ``` 所以: - `FUObjectArray` 是容器管理者(类似“目录”) - `FUObjectItem.Object` 才是真正的 `UObjectBase*`(即实际对象地址) --- ## 二、关键字段说明 ```cpp struct FUObjectItem { class UObjectBase* Object; // 真实的对象指针,比如 new 出来的 UObject 实例 int32 Flags; int32 ClusterRootIndex; int32 SerialNumber; }; ``` 和 ```cpp class FChunkedFixedUObjectArray { FUObjectItem** Objects; // 指向多个 chunk 的指针数组 FUObjectItem* PreAllocatedObjects; int32 MaxElements; int32 NumElements; // ... }; ``` 以及: ```cpp extern COREUOBJECT_API FUObjectArray GUObjectArray; // 全局变量,代表整个系统 ``` 当你调用: ```cpp UObject* obj = NewObject<UObject>(); ``` 会发生以下过程: 1. 分配内存:`obj` 是一个 `UObjectBase*`,地址为 `0x12345678` 2. 在 `GUObjectArray.AllocateUObjectIndex(obj)` 中: - 找到一个空闲索引 `i` - 设置 `GUObjectArray.ObjObjects[i].Object = obj;` - 设置 `obj->InternalIndex = i;` 此时: - `obj`(也就是 `UObjectBase*`)→ `0x12345678` - `&GUObjectArray` → `0x0080A000`(假设) - `GUObjectArray.IndexToObject(i)->Object` → `0x12345678` ✅ 所以它们是完全不同的地址空间! --- ## 三、验证方式:代码示例 你可以这样打印地址来确认: ```cpp // 假设你有一个有效的 UObject UObject* MyObject = NewObject<UObject>(); UE_LOG(LogTemp, Log, TEXT("UObjectBase address: %p"), MyObject); UE_LOG(LogTemp, Log, TEXT("GUObjectArray address: %p"), &GUObjectArray); int32 Index = MyObject->GetUniqueID(); // 即 InternalIndex FUObjectItem* Item = GUObjectArray.IndexToObject(Index); UE_LOG(LogTemp, Log, TEXT("Stored Object pointer in FUObjectItem: %p"), Item->Object); ``` 输出可能像: ``` UObjectBase address: 0x12345678 GUObjectArray address: 0x0080A000 Stored Object pointer in FUObjectItem: 0x12345678 ``` ➡️ 可见:`MyObject == Item->Object`,但 `MyObject != &GUObjectArray` --- ## 四、常见误解澄清 | 错误说法 | 正确解释 | |--------|---------| | “`UObjectBase` 就是 `FUObjectArray` 的一项” | ✅ 接近正确,但它只是 `.Object` 成员的值,不是项本身 | | “`InternalIndex` 是偏移地址” | ❌ 它是索引,用于查表 `ObjObjects[Index]`,不是字节偏移 | | “`FUObjectArray` 存放 UObject 内存” | ❌ 它只存放元数据(`FUObjectItem`),真实对象由 `new` 或 `FMemory::Malloc` 分配 | | “`GUObjectArray` 和 `UObject` 在同一块内存” | ❌ 不同分配时机、不同堆区、不同用途 | --- ## 五、为什么会有这种混淆? 因为 Unreal Engine 使用了 **双层结构设计**: | 层级 | 内容 | |------|------| | **第 1 层:对象实例层** | 所有 `UObject` 实例,通过 `new` 创建,地址任意 | | **第 2 层:元数据管理层** | `FUObjectArray` 维护每个对象的状态、标志、GC 信息等 | 这类似于操作系统中的: - 物理内存页(对象实例) - 页表(`FUObjectItem`) - MMU 控制器(`FUObjectArray`) --- ## 总结解释 > ❌ `UObjectBase` 的地址 **不等于** `FUObjectArray` 的地址。 > ✅ `UObjectBase*` 是真实对象的地址,由运行时动态分配。 > ✅ `FUObjectArray` 是全局管理结构,保存所有对象的元数据(包括指向 `UObjectBase` 的指针)。 > ✅ 两者通过 `InternalIndex` 和 `FUObjectItem.Object` 关联,但内存上完全独立。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值