Manage piece data ownership with scoped asset references

Replace manual piece-data retain/release bookkeeping with shared copyable
asset-reference handles. Pieces, models, selection, minifigs, thumbnails,
and stored insertion previews retain their dependencies explicitly, while
transient queries continue to borrow pointers.

Separate registered lookup and acquisition from asynchronous load requests
and explicit readiness waits. Keep loader-held references through work and
notification delivery, resetting shared request handles on the UI thread
and guarding callback-triggered library teardown.

Validated with the Qt 6.10 debug build and 26 offscreen lifecycle checks,
including cancellation, stale requests, copy/move operations, model/preview
teardown, undo, project merge/export, and loader shutdown.
This commit is contained in:
Leonardo Zide committed 2026-10-19 10:02:26 -07:00
1 parent 7bc6c018b1
commit 61be531444
30 files changed
+686 -420

No files matched your search

+28 -23
View File
@@ -41,11 +41,9 @@ void lcAssetLoader::RequestPieceLoad(lcPieceData* PieceData, lcLoadPriority Load
return;
}
mLibrary->AddPieceDataReference(PieceData); // The request owns a separate in-flight hold.
std::shared_ptr<PieceRequest> RequestedPiece = std::make_shared<PieceRequest>();
RequestedPiece->PieceData = PieceData;
RequestedPiece->PieceData = lcPieceDataRef(PieceData);
RequestedPiece->Source = mLibrary->SnapshotPieceDataSource(PieceData);
RequestedPiece->LoadPriority = LoadPriority;
RequestedPiece->QueuedAt = QDateTime::currentMSecsSinceEpoch();
@@ -54,7 +52,7 @@ void lcAssetLoader::RequestPieceLoad(lcPieceData* PieceData, lcLoadPriority Load
DispatchJobs();
emit mLibrary->AssetStatusChanged();
mLibrary->NotifyAssetStatusChanged();
}
void lcAssetLoader::PromotePiecePriority(const std::shared_ptr<PieceRequest>& RequestedPiece, lcLoadPriority LoadPriority)
@@ -102,7 +100,7 @@ void lcAssetLoader::RequestSynthMesh(lcPiece* Piece, lcPieceData* PieceData, std
DispatchJobs();
emit mLibrary->AssetStatusChanged();
mLibrary->NotifyAssetStatusChanged();
}
void lcAssetLoader::RequestTextureLoad(lcTexture* Texture, lcFileSource Source, lcLoadPriority LoadPriority)
@@ -330,7 +328,7 @@ void lcAssetLoader::ProcessPieceCompletion(Completion& Completed)
Q_ASSERT(QThread::currentThread() == thread());
const std::shared_ptr<PieceRequest>& RequestedPiece = Completed.RequestedPiece;
lcPieceData* PieceData = RequestedPiece->PieceData;
lcPieceData* PieceData = RequestedPiece->PieceData.Get();
const auto It = mPieceRequests.find(PieceData);
const bool Current = It != mPieceRequests.end() && It->second == RequestedPiece;
const bool Stale = mStopping || !Current;
@@ -348,7 +346,7 @@ void lcAssetLoader::ProcessPieceCompletion(Completion& Completed)
RequestedPiece->State = RequestState::Cancelled;
mLibrary->ReleasePieceDataLoadHold(PieceData);
RequestedPiece->PieceData.Reset();
return;
}
@@ -609,7 +607,7 @@ void lcAssetLoader::CompletePieceRequest(const std::shared_ptr<PieceRequest>& Re
{
Q_ASSERT(QThread::currentThread() == thread());
lcPieceData* PieceData = RequestedPiece->PieceData;
lcPieceData* PieceData = RequestedPiece->PieceData.Get();
QString LoadError = RequestedPiece->Error;
if (LoadError.isEmpty() && !RequestedPiece->StagedMesh && !RequestedPiece->EmptyGeometry)
@@ -651,8 +649,7 @@ void lcAssetLoader::CompleteSynthRequest(const std::shared_ptr<SynthRequest>& Re
RequestedSynth->State = Loaded ? RequestState::Succeeded : RequestState::Failed;
mLibrary->AddPieceDataReference(PieceData);
mSynthNotifications.push_back({ RequestedSynth, PieceData });
mSynthNotifications.push_back({ RequestedSynth, lcPieceDataRef(PieceData) });
QCoreApplication::postEvent(this, new QEvent(lcAssetLoaderCompletionEvent));
}
@@ -670,11 +667,11 @@ void lcAssetLoader::DispatchNotifications()
mPieceNotifications.pop_front();
lcPieceData* PieceData = Notification.RequestedPiece->PieceData;
lcPieceData* PieceData = Notification.RequestedPiece->PieceData.Get();
if (Notification.RequestedPiece->State == RequestState::Cancelled || !mLibrary->HasPieceDataConsumers(PieceData))
{
mLibrary->ReleasePieceDataLoadHold(PieceData);
Notification.RequestedPiece->PieceData.Reset();
continue;
}
@@ -688,9 +685,13 @@ void lcAssetLoader::DispatchNotifications()
emit mLibrary->PartLoadFailed(PieceData, Notification.Error);
if (!LoaderGuard || !LibraryGuard)
{
// Library teardown can delete the definition while delivering this callback.
Notification.RequestedPiece->PieceData.Release();
return;
}
mLibrary->ReleasePieceDataLoadHold(PieceData);
Notification.RequestedPiece->PieceData.Reset();
}
std::unordered_set<lcPieceData*> SettledSynths;
@@ -701,7 +702,7 @@ void lcAssetLoader::DispatchNotifications()
mSynthNotifications.pop_front();
lcPieceData* PieceData = Notification.PieceData;
lcPieceData* PieceData = Notification.PieceData.Get();
if (!Notification.RequestedSynth->Cancelled.load(std::memory_order_relaxed) && mLibrary->HasPieceDataConsumers(PieceData) && SettledSynths.insert(PieceData).second)
{
@@ -711,10 +712,14 @@ void lcAssetLoader::DispatchNotifications()
emit mLibrary->GeneratedMeshSettled(PieceData);
if (!LoaderGuard || !LibraryGuard)
{
// Library teardown can delete the definition while delivering this callback.
Notification.PieceData.Release();
return;
}
}
mLibrary->ReleasePieceDataLoadHold(PieceData);
Notification.PieceData.Reset();
}
if (!mPieceNotifications.empty() || !mSynthNotifications.empty())
@@ -946,7 +951,7 @@ void lcAssetLoader::InvalidatePieceLoad(lcPieceData* PieceData)
if (It != mPieceRequests.end())
DiscardPieceRequest(It->second);
emit mLibrary->AssetStatusChanged();
mLibrary->NotifyAssetStatusChanged();
}
void lcAssetLoader::CancelPieceLoad(lcPieceData* PieceData)
@@ -998,13 +1003,13 @@ void lcAssetLoader::DiscardPieceRequest(std::shared_ptr<PieceRequest> Request)
const bool HasOutstandingWork = Request->State == RequestState::AwaitingCompletion;
Request->State = RequestState::Cancelled;
mPieceRequests.erase(Request->PieceData);
mPieceRequests.erase(Request->PieceData.Get());
// Running builds retain their hold until their stale completion arrives.
if (!HasOutstandingWork)
{
Request->StagedMesh.reset();
mLibrary->ReleasePieceDataLoadHold(Request->PieceData);
Request->PieceData.Reset();
}
CancelUnusedTextureRequests();
@@ -1093,7 +1098,7 @@ void lcAssetLoader::ResumeQueuedWork()
for (const PieceRequestMap::value_type& Entry : mPieceRequests)
if (Entry.second->State == RequestState::Queued)
Entry.second->Source = mLibrary->SnapshotPieceDataSource(Entry.second->PieceData);
Entry.second->Source = mLibrary->SnapshotPieceDataSource(Entry.second->PieceData.Get());
mPaused = false;
DispatchJobs();
@@ -1119,7 +1124,7 @@ void lcAssetLoader::Shutdown()
else
{
It->second->State = RequestState::Cancelled;
mLibrary->PieceDataLoadCancelled(It->second->PieceData);
mLibrary->PieceDataLoadCancelled(It->second->PieceData.Get());
Cancelled.push_back(It->second);
@@ -1155,7 +1160,7 @@ void lcAssetLoader::Shutdown()
}
for (const std::shared_ptr<PieceRequest>& RequestedPiece : Cancelled)
mLibrary->ReleasePieceDataLoadHold(RequestedPiece->PieceData);
RequestedPiece->PieceData.Reset();
for (const std::shared_ptr<TextureRequest>& RequestedTexture : CancelledTextures)
mLibrary->ReleaseTexture(RequestedTexture->Texture);
@@ -1168,7 +1173,7 @@ void lcAssetLoader::Shutdown()
mPieceNotifications.pop_front();
mLibrary->ReleasePieceDataLoadHold(Notification.RequestedPiece->PieceData);
Notification.RequestedPiece->PieceData.Reset();
}
while (!mSynthNotifications.empty())
@@ -1177,7 +1182,7 @@ void lcAssetLoader::Shutdown()
mSynthNotifications.pop_front();
mLibrary->ReleasePieceDataLoadHold(Notification.PieceData);
Notification.PieceData.Reset();
}
mLibrary->mCancelLoading.store(WasCancelled);
+4 -3
View File
@@ -87,11 +87,12 @@ private:
}
};
// Owns one lcPieceData load hold through completion and deferred notification.
// Owns one definition reference through completion and deferred notification.
// Reset on the UI thread before worker-shared request storage can be destroyed.
// One worker job builds and converts geometry; textures settle before publication.
struct PieceRequest : RequestBase
{
lcPieceData* PieceData;
lcPieceDataRef PieceData;
lcPieceSourceSnapshot Source;
std::unique_ptr<lcMesh> StagedMesh;
bool EmptyGeometry = false;
@@ -147,7 +148,7 @@ private:
struct SynthNotification
{
std::shared_ptr<SynthRequest> RequestedSynth;
lcPieceData* PieceData;
lcPieceDataRef PieceData;
};
// Queue and promote requests on the UI thread.
+139
View File
@@ -0,0 +1,139 @@
#include "lc_global.h"
#include "lc_assettypes.h"
#include "lc_library.h"
#include "lc_application.h"
#include "lc_mesh.h"
template<>
void lcAssetRef<lcPieceData>::Retain(lcPieceData* Asset, lcPiecesLibrary* Library)
{
Library->AddPieceDataReference(Asset);
}
template<>
void lcAssetRef<lcPieceData>::Release(lcPieceData* Asset, lcPiecesLibrary* Library)
{
Library->ReleasePieceData(Asset);
}
template<>
void lcAssetRef<lcTexture>::Retain(lcTexture* Asset, lcPiecesLibrary* Library)
{
Library->AddTextureReference(Asset);
}
template<>
void lcAssetRef<lcTexture>::Release(lcTexture* Asset, lcPiecesLibrary* Library)
{
Library->ReleaseTexture(Asset);
}
template<typename T>
lcAssetRef<T>::lcAssetRef(T* Asset)
: lcAssetRef(Asset, Asset ? lcGetPiecesLibrary() : nullptr)
{
}
template<typename T>
lcAssetRef<T>::lcAssetRef(T* Asset, lcPiecesLibrary* Library)
: mAsset(Asset), mLibrary(Library)
{
if (mAsset)
{
Q_ASSERT(mLibrary && QThread::currentThread() == mLibrary->thread());
Retain(mAsset, mLibrary);
}
}
template<typename T>
lcAssetRef<T>::lcAssetRef(const lcAssetRef& OtherRef)
: lcAssetRef(OtherRef.mAsset, OtherRef.mLibrary)
{
}
template<typename T>
lcAssetRef<T>::lcAssetRef(lcAssetRef&& OtherRef) noexcept
: mAsset(std::exchange(OtherRef.mAsset, nullptr)), mLibrary(std::exchange(OtherRef.mLibrary, nullptr))
{
}
template<typename T>
lcAssetRef<T>::~lcAssetRef()
{
if (mAsset)
{
Q_ASSERT(mLibrary && QThread::currentThread() == mLibrary->thread());
Release(mAsset, mLibrary);
}
}
template<typename T>
lcAssetRef<T>& lcAssetRef<T>::operator=(const lcAssetRef& OtherRef)
{
if (this != &OtherRef)
{
lcAssetRef CopyRef(OtherRef);
Swap(CopyRef);
}
return *this;
}
template<typename T>
lcAssetRef<T>& lcAssetRef<T>::operator=(lcAssetRef&& OtherRef) noexcept
{
if (this != &OtherRef)
{
lcAssetRef MovedRef(std::move(OtherRef));
Swap(MovedRef);
}
return *this;
}
template<typename T>
void lcAssetRef<T>::Reset()
{
lcAssetRef EmptyRef;
Swap(EmptyRef);
}
template<typename T>
T* lcAssetRef<T>::Release()
{
mLibrary = nullptr;
return std::exchange(mAsset, nullptr);
}
template<typename T>
void lcAssetRef<T>::Swap(lcAssetRef& OtherRef) noexcept
{
std::swap(mAsset, OtherRef.mAsset);
std::swap(mLibrary, OtherRef.mLibrary);
}
template<typename T>
lcAssetRef<T> lcAssetRef<T>::Adopt(T* Asset, lcPiecesLibrary* Library)
{
Q_ASSERT(!Asset || (Library && QThread::currentThread() == Library->thread()));
lcAssetRef AssetRef;
AssetRef.mAsset = Asset;
AssetRef.mLibrary = Library;
return AssetRef;
}
template class lcAssetRef<lcPieceData>;
template class lcAssetRef<lcTexture>;
lcPieceSourceSnapshot::lcPieceSourceSnapshot()
: ZipFileType(lcZipFileType::Count), StudStyle(lcStudStyle::Plain)
{
}
lcPieceBuildResult::lcPieceBuildResult() = default;
lcPieceBuildResult::~lcPieceBuildResult() = default;
lcPieceBuildResult::lcPieceBuildResult(lcPieceBuildResult&&) noexcept = default;
lcPieceBuildResult& lcPieceBuildResult::operator=(lcPieceBuildResult&&) noexcept = default;
lcTextureBuildResult::~lcTextureBuildResult() = default;
+95 -1
View File
@@ -1,10 +1,104 @@
#pragma once
class lcPieceData;
class lcTexture;
class lcPiecesLibrary;
class lcMesh;
struct lcEmbeddedData;
enum class lcStudStyle;
enum class lcZipFileType;
// Scoped asset reference. Construction from a borrowed pointer and copying retain;
// moving transfers ownership. Use and destroy on the UI thread before library unload.
// Ownership operations are instantiated for piece definitions and textures in the cpp.
template<typename T>
class lcAssetRef
{
public:
lcAssetRef() = default;
explicit lcAssetRef(T* Asset);
lcAssetRef(const lcAssetRef& OtherRef);
lcAssetRef(lcAssetRef&& OtherRef) noexcept;
~lcAssetRef();
lcAssetRef& operator=(const lcAssetRef& OtherRef);
lcAssetRef& operator=(lcAssetRef&& OtherRef) noexcept;
T* Get() const
{
return mAsset;
}
T* operator->() const
{
return mAsset;
}
T& operator*() const
{
return *mAsset;
}
explicit operator bool() const
{
return mAsset != nullptr;
}
friend bool operator==(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept
{
return LeftRef.mAsset == RightRef.mAsset;
}
friend bool operator!=(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept
{
return !(LeftRef == RightRef);
}
friend bool operator<(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept
{
return std::less<T*>()(LeftRef.mAsset, RightRef.mAsset);
}
friend bool operator==(const lcAssetRef& LeftRef, const T* Right) noexcept
{
return LeftRef.mAsset == Right;
}
friend bool operator!=(const lcAssetRef& LeftRef, const T* Right) noexcept
{
return !(LeftRef == Right);
}
friend bool operator==(const T* Left, const lcAssetRef& RightRef) noexcept
{
return Left == RightRef.mAsset;
}
friend bool operator!=(const T* Left, const lcAssetRef& RightRef) noexcept
{
return !(Left == RightRef);
}
void Reset();
T* Release();
void Swap(lcAssetRef& OtherRef) noexcept;
private:
friend class lcPiecesLibrary;
lcAssetRef(T* Asset, lcPiecesLibrary* Library);
// The library uses this only for a reference it has already acquired.
static lcAssetRef Adopt(T* Asset, lcPiecesLibrary* Library);
static void Retain(T* Asset, lcPiecesLibrary* Library);
static void Release(T* Asset, lcPiecesLibrary* Library);
T* mAsset = nullptr;
lcPiecesLibrary* mLibrary = nullptr;
};
using lcPieceDataRef = lcAssetRef<lcPieceData>;
using lcTextureRef = lcAssetRef<lcTexture>;
// Asset readiness; detailed scheduling progress belongs to loader requests.
enum class lcAssetState
{
@@ -14,7 +108,7 @@ enum class lcAssetState
Failed
};
// Scheduling urgency. The library's RequestPieceDataLoad() also waits when Blocking is requested.
// Scheduling urgency only. Use the library's wait APIs for synchronous loading.
// Queued requests only promote and keep their priority until completion or cancellation.
enum class lcLoadPriority
{
+1 -1
View File
@@ -111,7 +111,7 @@ lcFindReplaceWidget::lcFindReplaceWidget(QWidget* Parent, lcModel* Model, bool R
{
lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
Params.FindPieceData = Focus->mPieceData;
Params.FindPieceData = Focus->mPieceData.Get();
Params.FindColorIndex = Focus->GetColorIndex();
FindColorPicker->SetCurrentColor(Params.FindColorIndex);
+60 -62
View File
@@ -37,16 +37,6 @@ enum class lcLibraryCacheFlag : quint32
Directory = 0x0002
};
lcPieceSourceSnapshot::lcPieceSourceSnapshot()
: ZipFileType(lcZipFileType::Count), StudStyle(lcStudStyle::Plain)
{
}
lcPieceBuildResult::lcPieceBuildResult() = default;
lcPieceBuildResult::~lcPieceBuildResult() = default;
lcPieceBuildResult::lcPieceBuildResult(lcPieceBuildResult&&) noexcept = default;
lcPieceBuildResult& lcPieceBuildResult::operator=(lcPieceBuildResult&&) noexcept = default;
lcPiecesLibrary::lcPiecesLibrary(lcStudStyle StudStyle, bool StudCylinderColorEnabled)
: mStudStyle(StudStyle), mStudCylinderColorEnabled(StudCylinderColorEnabled)
{
@@ -348,7 +338,7 @@ std::string lcPiecesLibrary::NormalizePieceDataName(const char* PieceName)
return Name;
}
lcPieceData* lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused)
lcPieceDataRef lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused)
{
const std::string Name = NormalizePieceDataName(PieceName);
lcPieceData* PieceData;
@@ -373,10 +363,12 @@ lcPieceData* lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Proje
PieceData->AddRef();
}
lcPieceDataRef PieceDataRef = lcPieceDataRef::Adopt(PieceData, this);
if (Reused)
InvalidatePieceDataLoad(PieceData);
return PieceData;
return PieceDataRef;
}
lcResult<Project*> lcPiecesLibrary::LoadExternalProject(const QFileInfo& FileInfo, bool Preview, bool DeferModelMeshRequests)
@@ -404,31 +396,37 @@ lcResult<Project*> lcPiecesLibrary::LoadExternalProject(const QFileInfo& FileInf
return ExternalProject;
}
lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* CurrentProject, bool CreateMissing, bool SearchProjectFolder)
lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* CurrentProject)
{
const std::string Name = NormalizePieceDataName(PieceName);
QString ProjectPath;
QString LoadError;
QMutexLocker LoadLock(&mLoadMutex);
if (CurrentProject)
{
QMutexLocker LoadLock(&mLoadMutex);
if (lcPieceData* PieceData = CurrentProject->FindPieceData(Name))
return PieceData;
if (SearchProjectFolder && !CurrentProject->GetFileName().isEmpty())
ProjectPath = QFileInfo(CurrentProject->GetFileName()).absolutePath();
}
const std::map<std::string, lcPieceData*>::const_iterator PieceIt = mPieces.find(Name);
return PieceIt != mPieces.end() ? PieceIt->second : nullptr;
}
lcPieceDataRef lcPiecesLibrary::AcquirePieceData(const char* PieceName, Project* CurrentProject, bool CreateMissing, bool SearchProjectFolder)
{
QString ProjectPath;
QString LoadError;
{
QMutexLocker LoadLock(&mLoadMutex);
const std::map<std::string, lcPieceData*>::const_iterator PieceIt = mPieces.find(Name);
if (PieceIt != mPieces.end())
return PieceIt->second;
if (lcPieceData* PieceData = FindPieceData(PieceName, CurrentProject))
return lcPieceDataRef(PieceData, this);
if (CurrentProject && SearchProjectFolder && !CurrentProject->GetFileName().isEmpty())
ProjectPath = QFileInfo(CurrentProject->GetFileName()).absolutePath();
}
const std::string Name = NormalizePieceDataName(PieceName);
if (!ProjectPath.isEmpty())
{
QFileInfo ProjectFile = QFileInfo(ProjectPath + QDir::separator() + PieceName);
@@ -444,7 +442,7 @@ lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* Curr
PieceData->CreateProject(ExternalProject.value(), PieceName);
RegisterProjectPieceData(CurrentProject, Name, PieceData);
return PieceData;
return lcPieceDataRef(PieceData, this);
}
else
LoadError = ExternalProject.error();
@@ -466,10 +464,10 @@ lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* Curr
mPieces[Name] = PieceData;
}
return PieceData;
return lcPieceDataRef(PieceData, this);
}
return nullptr;
return lcPieceDataRef();
}
bool lcPiecesLibrary::RemapProjectPieceData(lcPieceData* PieceData, const QString& ProjectDirectory, bool IsPreview)
@@ -975,7 +973,7 @@ bool lcPiecesLibrary::OpenArchive(std::unique_ptr<lcFile> File, lcZipFileType Zi
if (memcmp(Name, "S/", 2))
{
lcPieceData* PieceData = FindPieceData(Name, nullptr, false, false);
lcPieceData* PieceData = FindPieceData(Name, nullptr);
if (!PieceData)
{
@@ -1656,18 +1654,7 @@ bool lcPiecesLibrary::SaveArchiveCacheIndex(const QString& FileName)
return WriteArchiveCacheFile(FileName, IndexFile);
}
bool lcPiecesLibrary::RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority)
{
Q_ASSERT(QThread::currentThread() == thread());
AddPieceDataReference(PieceData);
StartPieceLoad(PieceData, LoadPriority);
return LoadPriority != lcLoadPriority::Blocking || WaitForPieceData({ PieceData });
}
void lcPiecesLibrary::StartPieceLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority)
void lcPiecesLibrary::RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority)
{
Q_ASSERT(QThread::currentThread() == thread());
@@ -1711,9 +1698,9 @@ void lcPiecesLibrary::NotifyAssetStatusChanged()
emit AssetStatusChanged();
}
bool lcPiecesLibrary::EnsurePieceDataAssetsReady(lcPieceData* PieceData)
bool lcPiecesLibrary::EnsurePieceDataAssetsReady(const lcPieceDataRef& PieceData)
{
const bool PieceReady = WaitForPieceData({ PieceData });
const bool PieceReady = WaitForPieceData(PieceData);
if (PieceData->IsModel())
return static_cast<bool>(PieceData->GetModel()->EnsureAssetsReady()) && PieceReady;
@@ -1722,25 +1709,44 @@ bool lcPiecesLibrary::EnsurePieceDataAssetsReady(lcPieceData* PieceData)
{
const bool ProjectReady = static_cast<bool>(PieceData->GetProject()->EnsureAssetsReady());
PieceData->GetProject()->UpdatePieceData(PieceData);
PieceData->GetProject()->UpdatePieceData(PieceData.Get());
return ProjectReady && PieceReady;
}
return PieceReady;
}
bool lcPiecesLibrary::WaitForPieceData(const std::vector<lcPieceData*>& Pieces)
bool lcPiecesLibrary::WaitForPieceData(const lcPieceDataRef& PieceDataRef)
{
Q_ASSERT(QThread::currentThread() == thread());
for (lcPieceData* PieceData : Pieces)
if (PieceData->mState == lcAssetState::Unloaded)
StartPieceLoad(PieceData, lcLoadPriority::Blocking);
if (PieceDataRef->mState == lcAssetState::Unloaded)
RequestPieceDataLoad(PieceDataRef.Get(), lcLoadPriority::Blocking);
const bool RequestsSucceeded = mAssetLoader->WaitForPieces({ PieceDataRef.Get() });
return RequestsSucceeded && PieceDataRef->mState == lcAssetState::Loaded;
}
bool lcPiecesLibrary::WaitForPieceData(const std::vector<lcPieceDataRef>& PieceDataRefs)
{
Q_ASSERT(QThread::currentThread() == thread());
std::vector<lcPieceData*> Pieces;
Pieces.reserve(PieceDataRefs.size());
for (const lcPieceDataRef& PieceDataRef : PieceDataRefs)
{
if (PieceDataRef->mState == lcAssetState::Unloaded)
RequestPieceDataLoad(PieceDataRef.Get(), lcLoadPriority::Blocking);
Pieces.push_back(PieceDataRef.Get());
}
const bool RequestsSucceeded = mAssetLoader->WaitForPieces(Pieces);
for (const lcPieceData* PieceData : Pieces)
if (PieceData->mState != lcAssetState::Loaded)
for (const lcPieceDataRef& PieceDataRef : PieceDataRefs)
if (PieceDataRef->mState != lcAssetState::Loaded)
return false;
return RequestsSucceeded;
@@ -1750,7 +1756,7 @@ void lcPiecesLibrary::RequestSynthMesh(lcPiece* Piece)
{
Q_ASSERT(QThread::currentThread() == thread());
lcPieceData* PieceData = Piece->mPieceData;
lcPieceData* PieceData = Piece->mPieceData.Get();
std::shared_ptr<const lcSynthInfo> SynthInfo = PieceData ? PieceData->GetSynthInfoShared() : nullptr;
if (!SynthInfo)
@@ -1854,7 +1860,7 @@ void lcPiecesLibrary::RebuildModelPieceData(lcPieceData* PieceData)
InvalidatePieceDataLoad(PieceData);
PieceData->ReleaseMesh();
StartPieceLoad(PieceData, lcLoadPriority::Normal);
RequestPieceDataLoad(PieceData, lcLoadPriority::Normal);
}
void lcPiecesLibrary::InvalidatePieceDataLoad(lcPieceData* PieceData)
@@ -1902,14 +1908,6 @@ bool lcPiecesLibrary::HasPieceDataConsumers(const lcPieceData* PieceData)
return PieceData->GetRefCount() > 1;
}
void lcPiecesLibrary::ReleasePieceDataLoadHold(lcPieceData* PieceData)
{
QMutexLocker LoadLock(&mLoadMutex);
if (PieceData->Release() == 0)
UnloadPieceData(PieceData);
}
void lcPiecesLibrary::SetPieceDataLoadError(const lcPieceData* PieceData, QString Error)
{
QMutexLocker ErrorLock(&mPieceErrorMutex);
@@ -2452,12 +2450,12 @@ void lcPiecesLibrary::UpdateStudStyleAndColors(lcStudStyle StudStyle, bool StudC
PieceData->ReleaseMesh();
PieceData->mState = lcAssetState::Unloaded;
StartPieceLoad(PieceData, lcLoadPriority::Low);
RequestPieceDataLoad(PieceData, lcLoadPriority::Low);
}
else if (!PieceData->IsProject())
{
PieceData->Unload();
StartPieceLoad(PieceData, lcLoadPriority::Low);
RequestPieceDataLoad(PieceData, lcLoadPriority::Low);
}
mBuffersDirty = true;
@@ -2738,7 +2736,7 @@ void lcPiecesLibrary::GetCategoryEntries(const char* CategoryKeywords, bool Grou
*strchr(ParentName, 'P') = '\0';
lcstrcat(ParentName, ".dat");
Parent = FindPieceData(ParentName, nullptr, false, false);
Parent = FindPieceData(ParentName, nullptr);
if (Parent)
{
+19 -17
View File
@@ -170,23 +170,26 @@ public:
void SetModelPieceDataName(lcPieceData* PieceData, const char* Name);
bool RenamePieceData(lcPieceData* PieceData, const char* NewName);
static std::string NormalizePieceDataName(const char* PieceName);
// Returns one acquired model reference, paired with ReleasePieceData().
lcPieceData* AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused);
// Returns a borrowed identity; lookup does not acquire a reference or ensure readiness.
lcPieceData* FindPieceData(const char* PieceName, Project* Project, bool CreateMissing, bool SearchProjectFolder);
// Returns one scoped model reference, ready to transfer to the model.
lcPieceDataRef AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused);
// Borrowed registered lookup: project names, then the shared catalog. No I/O or creation.
lcPieceData* FindPieceData(const char* PieceName, Project* Project);
// Resolves or creates an identity and returns one scoped reference; does not ensure readiness.
lcPieceDataRef AcquirePieceData(const char* PieceName, Project* Project, bool CreateMissing, bool SearchProjectFolder);
bool RemapProjectPieceData(lcPieceData* PieceData, const QString& ProjectDirectory, bool IsPreview);
// Asset scheduling, waits, invalidation, and consumer releases below run on the UI thread.
// Acquires one reference even on failure; always pair with ReleasePieceData().
// Blocking waits and reports success; Low and Normal return immediately. See docs/asset-loading.md.
bool RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority);
// Start or promote loading asynchronously. Adds no consumer references and never waits.
// Priority controls scheduling only; the caller retains the identity through completion or cancellation.
void RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority);
void NotifyAssetStatusChanged();
// Start or promote loading, then wait. Adds no consumer references.
// Returns false for failed or cancelled pieces.
// Does not traverse container children or wait for per-instance synth geometry.
bool WaitForPieceData(const std::vector<lcPieceData*>& Pieces);
bool WaitForPieceData(const lcPieceDataRef& PieceDataRef);
bool WaitForPieceData(const std::vector<lcPieceDataRef>& PieceDataRefs);
// Also settles container children/synths and updates bounds; caller retains the identity.
bool EnsurePieceDataAssetsReady(lcPieceData* PieceData);
bool EnsurePieceDataAssetsReady(const lcPieceDataRef& PieceData);
// Obsoletes pending work and resets readiness without acquiring a reference.
void InvalidatePieceDataLoad(lcPieceData* PieceData);
@@ -200,14 +203,8 @@ public:
// Last generation error, or an empty string; an empty error does not imply readiness.
QString GetSynthMeshError(const lcPiece* Piece) const;
void RebuildModelPieceData(lcPieceData* PieceData);
// Releases one reference and lets the loader cancel unused queued work.
void ReleasePieceData(lcPieceData* PieceData);
// Lifetime hold only: does not start loading or establish readiness.
void AddPieceDataReference(lcPieceData* PieceData);
// Loader helper: assumes a pending request/notification owns one in-flight hold.
bool HasPieceDataConsumers(const lcPieceData* PieceData);
// Releases that in-flight hold without triggering consumer-release cancellation.
void ReleasePieceDataLoadHold(lcPieceData* PieceData);
void SetPieceDataLoadError(const lcPieceData* PieceData, QString Error);
QString GetPieceDataLoadError(const lcPieceData* PieceData) const;
void ClearPieceDataLoadError(const lcPieceData* PieceData);
@@ -287,9 +284,14 @@ signals:
protected:
friend class lcAssetLoader;
template<typename T> friend class lcAssetRef;
// Internal lifetime primitive for scoped acquisitions and loader holds. Never starts work.
void AddPieceDataReference(lcPieceData* PieceData);
// Releases one reference and lets the loader cancel unused queued work.
void ReleasePieceData(lcPieceData* PieceData);
// Prepare asset readiness and request work without acquiring a consumer reference or waiting.
void StartPieceLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority);
// The loader calls this only when cancelling the current request, before releasing its hold.
void PieceDataLoadCancelled(lcPieceData* PieceData);
+6 -4
View File
@@ -151,15 +151,17 @@ bool lcImportLXFMLFile(const QString& FileData, Project* Project, std::vector<lc
QString LegoID = PartElement.attribute(QLatin1String("designID"));
int Material = PartElement.attribute(QLatin1String("materials")).split(',').first().toInt();
lcPieceData* PieceData = nullptr;
lcPieceDataRef PieceData;
const auto BrickIt = BrickTable.find(LegoID.toInt());
if (BrickIt != BrickTable.end())
PieceData = lcGetPiecesLibrary()->FindPieceData(BrickIt->second.c_str(), Project, true, false);
PieceData = lcGetPiecesLibrary()->AcquirePieceData(BrickIt->second.c_str(), Project, true, false);
else
PieceData = lcGetPiecesLibrary()->FindPieceData(LegoID.toLatin1() + ".dat", Project, true, false);
PieceData = lcGetPiecesLibrary()->AcquirePieceData(LegoID.toLatin1() + ".dat", Project, true, false);
const auto ColorIt = MaterialTable.find(Material);
int ColorCode = 16;
if (ColorIt != MaterialTable.end())
ColorCode = ColorIt->second;
@@ -179,7 +181,7 @@ bool lcImportLXFMLFile(const QString& FileData, Project* Project, std::vector<lc
lcVector4(-WorldMatrix[2][0], WorldMatrix[2][1], WorldMatrix[2][2], 0.0f), lcVector4(WorldMatrix[3][0] * 25.0f, -WorldMatrix[3][1] * 25.0f, -WorldMatrix[3][2] * 25.0f, 1.0f));
lcPiece* Piece = new lcPiece(nullptr);
Piece->SetPieceData(PieceData, QString(), false, true);
Piece->SetPieceData(std::move(PieceData), QString(), false, true);
Piece->Initialize(lcMatrix44LDrawToLeoCAD(WorldMatrix), 1);
Piece->SetColorCode(ColorCode);
Pieces.push_back(Piece);
+10 -17
View File
@@ -64,11 +64,7 @@ lcMainWindow::~lcMainWindow()
lcPiecesLibrary* Library = lcGetPiecesLibrary();
disconnect(Library, nullptr, this, nullptr);
if (mInsertPieceData)
{
Library->ReleasePieceData(mInsertPieceData);
mInsertPieceData = nullptr;
}
mInsertPieceData.Reset();
for (size_t FileIndex = 0; FileIndex < mRecentFiles.size(); FileIndex++)
lcSetProfileString((LC_PROFILE_KEY)(LC_PROFILE_RECENT_FILE1 + FileIndex), mRecentFiles[FileIndex]);
@@ -1023,11 +1019,12 @@ void lcMainWindow::UpdateAssets()
lcView::UpdateProjectViews(Project);
const std::vector<lcPieceData*> Required = Project->GetRequiredPieceData();
const std::vector<lcPieceDataRef> RequiredPieceData = Project->GetRequiredPieceData();
const std::vector<lcPiece*> GeneratedPieces = Project->GetRequiredSynthPieces();
mStatusRequiredAssets.clear();
mStatusRequiredAssets.insert(Required.begin(), Required.end());
for (const lcPieceDataRef& RequiredData : RequiredPieceData)
mStatusRequiredAssets.insert(RequiredData.Get());
int PendingParts = 0;
int PendingSynths = 0;
@@ -1042,8 +1039,9 @@ void lcMainWindow::UpdateAssets()
FailureDetails += Warning;
}
for (const lcPieceData* PieceData : Required)
for (const lcPieceDataRef& RequiredData : RequiredPieceData)
{
const lcPieceData* PieceData = RequiredData.Get();
if (PieceData->mState == lcAssetState::Failed)
{
Failed++;
@@ -1082,7 +1080,7 @@ void lcMainWindow::UpdateAssets()
}
}
const int Total = static_cast<int>(Required.size() + GeneratedPieces.size());
const int Total = static_cast<int>(RequiredPieceData.size() + GeneratedPieces.size());
const int Pending = PendingParts + PendingSynths;
QString LoadingText;
@@ -2014,15 +2012,10 @@ void lcMainWindow::SetTransformType(lcTransformType TransformType)
void lcMainWindow::SetInsertPieceData(lcPieceData* PieceData)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
mInsertPieceData = lcPieceDataRef(PieceData);
if (mInsertPieceData)
Library->ReleasePieceData(mInsertPieceData);
mInsertPieceData = PieceData;
if (mInsertPieceData)
Library->RequestPieceDataLoad(mInsertPieceData, lcLoadPriority::Normal);
lcGetPiecesLibrary()->RequestPieceDataLoad(mInsertPieceData.Get(), lcLoadPriority::Normal);
}
lcVector3 lcMainWindow::GetTransformAmount()
@@ -2286,7 +2279,7 @@ void lcMainWindow::UpdateSelectedObjects(bool SelectionChanged)
int ColorIndex = Piece->GetColorIndex();
quint32 ColorCode = lcGetColorCode(ColorIndex);
PreviewPiece(Piece->mPieceData, ColorCode, false);
PreviewPiece(Piece->mPieceData.Get(), ColorCode, false);
}
QString Message;
+3 -2
View File
@@ -1,6 +1,7 @@
#pragma once
#include "lc_application.h"
#include "lc_assettypes.h"
#include "lc_shortcuts.h"
#include "lc_result.h"
#include "lc_commands.h"
@@ -140,7 +141,7 @@ public:
return mSelectionMode;
}
lcPieceData* GetInsertPieceData() const
const lcPieceDataRef& GetInsertPieceData() const
{
return mInsertPieceData;
}
@@ -347,7 +348,7 @@ protected:
int mAngleSnapIndex;
bool mRelativeTransform = true;
bool mLocalTransform = false;
lcPieceData* mInsertPieceData = nullptr;
lcPieceDataRef mInsertPieceData;
lcSelectionMode mSelectionMode;
int mMousePressTabIndex = -1;
+2 -2
View File
@@ -206,7 +206,7 @@ void lcMinifigDialog::TemplateComboBoxCurrentTextChanged(const QString& Template
{
if (!Template.Parts[PartIdx].isEmpty())
{
lcPieceData* PieceData = mMinifigWizard->mMinifig.Parts[PartIdx];
lcPieceData* PieceData = mMinifigWizard->mMinifig.PieceData[PartIdx].Get();
if (PieceData)
{
@@ -368,7 +368,7 @@ void lcMinifigDialog::PieceButtonClicked()
return;
int ItemIndex = static_cast<int>(std::distance(mPieceButtons.begin(), Search));
lcPieceData* CurrentPieceData = mMinifigWizard->mMinifig.Parts[ItemIndex];
lcPieceData* CurrentPieceData = mMinifigWizard->mMinifig.PieceData[ItemIndex].Get();
QPoint Position = PieceButton->mapToGlobal(PieceButton->rect().bottomLeft());
std::vector<std::pair<lcPieceData*, std::string>> Parts;
+64 -74
View File
@@ -184,7 +184,6 @@ lcModel::lcModel(const QString& FileName, Project* Project, bool Preview)
mActive = false;
mCurrentStep = 1;
mPieceData = nullptr;
}
void lcModel::SetProject(Project* Project)
@@ -257,15 +256,7 @@ std::vector<QString> lcModel::GetAssetSearchDirectories() const
lcModel::~lcModel()
{
if (!mPreviewInsertPieceInfo.empty())
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
Library->ReleasePieceData(PreviewPieceInfoTransform.PieceData);
mPreviewInsertPieceInfo.clear();
}
mPreviewInsertPieceInfo.clear();
if (mPieceData)
{
@@ -275,14 +266,13 @@ lcModel::~lcModel()
gMainWindow->SetInsertPieceData(nullptr);
// Keep the name indexed while placements still reference the deleted model.
lcGetPiecesLibrary()->InvalidatePieceDataLoad(mPieceData);
lcGetPiecesLibrary()->InvalidatePieceDataLoad(mPieceData.Get());
mPieceData->DetachContainer();
mPieceData->SetFailed(tr("Model '%1' was deleted.").arg(QString::fromLatin1(mPieceData->mFileName)));
lcGetPiecesLibrary()->NotifyAssetStatusChanged();
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->ReleasePieceData(mPieceData);
mPieceData.Reset();
}
DeleteModel();
@@ -298,7 +288,7 @@ bool lcModel::GetPieceWorldMatrix(lcPiece* Piece, lcMatrix44& ParentWorldMatrix)
return true;
}
const lcPieceData* PieceData = ModelPiece->mPieceData;
const lcPieceData* PieceData = ModelPiece->mPieceData.Get();
if (PieceData->IsModel())
{
@@ -328,7 +318,7 @@ bool lcModel::IncludesModel(const lcModel* Model) const
for (const std::unique_ptr<lcPiece>& Piece : Current->mPieces)
{
const lcPieceData* PieceData = Piece->mPieceData;
const lcPieceData* PieceData = Piece->mPieceData.Get();
if (PieceData && PieceData->IsModel() && Visit(PieceData->GetModel()))
return true;
@@ -389,12 +379,14 @@ void lcModel::CreatePieceData(Project* Project)
}), mPieces.end());
}
// A model with deferred mesh requests retains its initial AcquireModelPieceData reference
// until its source lines have been parsed and QueueMeshBuild() runs.
// The model owns the reference acquired by AcquireModelPieceData(), including while
// mesh requests are deferred until its source lines have been parsed.
if (!Project->DefersModelMeshRequests())
{
Library->RequestPieceDataLoad(mPieceData, mIsPreview ? lcLoadPriority::Normal : lcLoadPriority::Blocking);
Library->ReleasePieceData(mPieceData);
if (mIsPreview)
Library->RequestPieceDataLoad(mPieceData.Get(), lcLoadPriority::Normal);
else
Library->WaitForPieceData(mPieceData);
}
if (Reused)
@@ -408,10 +400,9 @@ void lcModel::CreatePieceData(Project* Project)
bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
bool Changed = false;
mPieces.erase(std::remove_if(mPieces.begin(), mPieces.end(), [this, Previous, Replacement, Library, &Changed](const std::unique_ptr<lcPiece>& Piece)
mPieces.erase(std::remove_if(mPieces.begin(), mPieces.end(), [this, Previous, Replacement, &Changed](const std::unique_ptr<lcPiece>& Piece)
{
if (Piece->mPieceData != Previous)
return false;
@@ -422,8 +413,7 @@ bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement)
return true;
const QString PieceId = Piece->GetID();
Piece->SetPieceData(Replacement, PieceId, false, false);
Library->ReleasePieceData(Previous);
Piece->SetPieceData(lcPieceDataRef(Replacement), PieceId, false, false);
return false;
}), mPieces.end());
@@ -434,7 +424,7 @@ bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement)
void lcModel::QueueMeshBuild()
{
mPieceData->SetModel(this);
lcGetPiecesLibrary()->RebuildModelPieceData(mPieceData);
lcGetPiecesLibrary()->RebuildModelPieceData(mPieceData.Get());
}
void lcModel::UpdateAllViews() const
@@ -451,38 +441,38 @@ void lcModel::RefreshPreview() const
gMainWindow->GetPreviewWidget()->RefreshModel(this);
}
std::vector<lcPieceData*> lcModel::GetRequiredPieceData() const
std::vector<lcPieceDataRef> lcModel::GetRequiredPieceData() const
{
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
std::unordered_set<const lcModel*> Visited;
const std::function<void(const lcModel*)> Collect = [&Required, &Visited, &Collect](const lcModel* Model)
const std::function<void(const lcModel*)> Collect = [&RequiredPieceData, &Visited, &Collect](const lcModel* Model)
{
if (!Model || !Visited.insert(Model).second)
return;
if (Model->mPieceData)
Required.push_back(Model->mPieceData);
RequiredPieceData.push_back(Model->mPieceData);
for (const std::unique_ptr<lcPiece>& Piece : Model->mPieces)
{
lcPieceData* PieceData = Piece->mPieceData;
const lcPieceDataRef& PieceData = Piece->mPieceData;
if (PieceData->IsModel())
Collect(PieceData->GetModel());
else if (PieceData->IsProject())
Collect(PieceData->GetProject()->GetMainModel());
else
Required.push_back(PieceData);
RequiredPieceData.push_back(PieceData);
}
};
Collect(this);
std::sort(Required.begin(), Required.end());
Required.erase(std::unique(Required.begin(), Required.end()), Required.end());
std::sort(RequiredPieceData.begin(), RequiredPieceData.end());
RequiredPieceData.erase(std::unique(RequiredPieceData.begin(), RequiredPieceData.end()), RequiredPieceData.end());
return Required;
return RequiredPieceData;
}
std::vector<lcPiece*> lcModel::GetRequiredSynthPieces() const
@@ -497,7 +487,7 @@ std::vector<lcPiece*> lcModel::GetRequiredSynthPieces() const
for (const std::unique_ptr<lcPiece>& Piece : Model->mPieces)
{
lcPieceData* PieceData = Piece->mPieceData;
lcPieceData* PieceData = Piece->mPieceData.Get();
if (PieceData->IsModel())
Collect(PieceData->GetModel());
@@ -515,10 +505,10 @@ std::vector<lcPiece*> lcModel::GetRequiredSynthPieces() const
void lcModel::WaitForAssets() const
{
const std::vector<lcPieceData*> Required = GetRequiredPieceData();
const std::vector<lcPieceDataRef> RequiredPieceData = GetRequiredPieceData();
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->WaitForPieceData(Required);
Library->WaitForPieceData(RequiredPieceData);
Library->WaitForSynthMeshes(GetRequiredSynthPieces());
// Temporary minifigure and thumbnail models have no container piece to update.
@@ -533,15 +523,15 @@ lcResult<void> lcModel::EnsureAssetsReady() const
{
WaitForAssets();
const std::vector<lcPieceData*> Required = GetRequiredPieceData();
const std::vector<lcPieceDataRef> RequiredPieceData = GetRequiredPieceData();
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (const lcPieceData* PieceData : Required)
for (const lcPieceDataRef& RequiredData : RequiredPieceData)
{
if (PieceData->mState == lcAssetState::Loaded)
if (RequiredData->mState == lcAssetState::Loaded)
continue;
const QString Error = Library->GetPieceDataLoadError(PieceData);
const QString Error = Library->GetPieceDataLoadError(RequiredData.Get());
if (!Error.isEmpty())
return lcUnexpected(Error);
@@ -1022,14 +1012,14 @@ void lcModel::LoadLDraw(QIODevice& Device, Project* Project)
if (!CurrentGroups.empty())
Piece->SetGroup(CurrentGroups[CurrentGroups.size() - 1]);
lcPieceData* PieceData = Library->FindPieceData(PartId.toLatin1().constData(), Project, true, true);
lcPieceDataRef PieceData = Library->AcquirePieceData(PartId.toLatin1().constData(), Project, true, true);
const float* Matrix = IncludeTransform.GetFloats();
const lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f),
lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
Piece->SetFileLine(mFileLines.size());
Piece->SetPieceData(PieceData, PartId, false, false);
Piece->SetPieceData(std::move(PieceData), PartId, false, false);
Piece->Initialize(Transform, CurrentStep);
Piece->SetColorCode(ColorCode);
Piece->VerifyControlPoints(ControlPoints);
@@ -1145,8 +1135,7 @@ bool lcModel::LoadBinary(lcFile* file)
lcMatrix44 WorldMatrix = lcMul(lcMatrix44RotationZ(rot[2] * LC_DTOR), lcMul(lcMatrix44RotationY(rot[1] * LC_DTOR), lcMatrix44RotationX(rot[0] * LC_DTOR)));
WorldMatrix.SetTranslation(pos);
lcPieceData* PieceData = Library->FindPieceData(name, mProject, true, false);
lcPiece* pPiece = new lcPiece(PieceData);
lcPiece* pPiece = new lcPiece(Library->AcquirePieceData(name, mProject, true, false));
pPiece->Initialize(WorldMatrix, step);
pPiece->SetColorCode(lcGetColorCodeFromOriginalColor(color));
@@ -1346,7 +1335,7 @@ bool lcModel::LoadInventory(const std::vector<lcSetInventoryItem>& SetInventory)
for (const lcSetInventoryItem& SetInventoryItem : SetInventory)
{
lcPieceData* PieceData = Library->FindPieceData(SetInventoryItem.PartID + ".dat", mProject, true, false);
const lcPieceDataRef PieceData = Library->AcquirePieceData(SetInventoryItem.PartID + ".dat", mProject, true, false);
int Quantity = SetInventoryItem.Quantity;
while (Quantity--)
@@ -1362,14 +1351,14 @@ bool lcModel::LoadInventory(const std::vector<lcSetInventoryItem>& SetInventory)
if (mPieces.empty())
return false;
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
Required.reserve(mPieces.size());
RequiredPieceData.reserve(mPieces.size());
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
Required.push_back(Piece->mPieceData);
RequiredPieceData.push_back(Piece->mPieceData);
if (!Library->WaitForPieceData(Required))
if (!Library->WaitForPieceData(RequiredPieceData))
return false;
Library->mBuffersDirty = true;
@@ -1671,7 +1660,7 @@ void lcModel::GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHig
if (Scene->GetDrawInsertPreview())
{
for (const lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
for (const lcPreviewInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
PreviewPieceInfoTransform.PieceData->AddRenderMeshes(Scene, PreviewPieceInfoTransform.Transform, PreviewPieceInfoTransform.ColorIndex, lcRenderMeshState::Preview, false);
}
}
@@ -1691,7 +1680,7 @@ void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMat
if (Scene->GetDrawInsertPreview())
{
for (const lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
for (const lcPreviewInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
PreviewPieceInfoTransform.PieceData->AddRenderMeshes(Scene, lcMul(PreviewPieceInfoTransform.Transform, WorldMatrix), PreviewPieceInfoTransform.ColorIndex, lcRenderMeshState::Preview, false);
}
}
@@ -3102,11 +3091,13 @@ lcMatrix33 lcModel::GetRelativeRotation() const
lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section)
{
if (!PieceData)
PieceData = gMainWindow->GetInsertPieceData();
PieceData = gMainWindow->GetInsertPieceData().Get();
if (!PieceData)
return nullptr;
const lcPieceDataRef PieceDataRef(PieceData);
lcPiece* Last = mPieces.empty() ? nullptr : mPieces.back().get();
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
@@ -3118,7 +3109,7 @@ lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section)
}
}
lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(PieceData);
lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(PieceDataRef);
if (Last)
{
@@ -3201,7 +3192,7 @@ lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section)
void lcModel::AddPiece(std::unique_ptr<lcPiece> Piece, size_t PieceIndex)
{
const lcPieceData* PieceData = Piece->mPieceData;
const lcPieceData* PieceData = Piece->mPieceData.Get();
if (!PieceData->IsModel())
{
@@ -3332,7 +3323,7 @@ void lcModel::RotateFocusedTrainTrack(int Direction)
lcMatrix44 ConnectionTransform = lcMul(TrainTrackInfo->GetConnections()[RotateConnectionIndex].Transform, FocusPiece->mModelWorld);
int NewConnectionIndex = (RotateConnectionIndex + Direction + static_cast<int>(TrainTrackInfo->GetConnections().size())) % TrainTrackInfo->GetConnections().size();
Transform = lcTrainTrackInfo::CalculateTransformToConnection(ConnectionTransform, FocusPiece->mPieceData, NewConnectionIndex);
Transform = lcTrainTrackInfo::CalculateTransformToConnection(ConnectionTransform, FocusPiece->mPieceData.Get(), NewConnectionIndex);
if (!Transform)
return;
@@ -3412,22 +3403,21 @@ void lcModel::UpdateSelectedPiecesTrainTrackConnections()
void lcModel::SetPreviewInsertPieceInfo(std::vector<lcInsertPieceInfo>&& PreviewInsertPieceInfo)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
std::vector<lcPreviewInsertPieceInfo> PreviewPieces;
Required.reserve(PreviewInsertPieceInfo.size());
RequiredPieceData.reserve(PreviewInsertPieceInfo.size());
PreviewPieces.reserve(PreviewInsertPieceInfo.size());
for (lcInsertPieceInfo& InfoTransform : PreviewInsertPieceInfo)
for (const lcInsertPieceInfo& InfoTransform : PreviewInsertPieceInfo)
{
PreviewPieces.emplace_back(InfoTransform);
Library->RequestPieceDataLoad(InfoTransform.PieceData, lcLoadPriority::Normal);
Required.push_back(InfoTransform.PieceData);
RequiredPieceData.push_back(PreviewPieces.back().PieceData);
}
Library->WaitForPieceData(Required);
for (lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
Library->ReleasePieceData(PreviewPieceInfoTransform.PieceData);
mPreviewInsertPieceInfo = std::move(PreviewInsertPieceInfo);
Library->WaitForPieceData(RequiredPieceData);
mPreviewInsertPieceInfo = std::move(PreviewPieces);
UpdateAllViews();
}
@@ -3746,7 +3736,7 @@ void lcModel::RenamePieces(const std::vector<lcModelPieceRename>& Renames)
}
}
auto RenameHistorySequence = [&Renames, ModelPieceData = mPieceData](std::vector<std::unique_ptr<lcModelHistory>>& HistorySequence)
auto RenameHistorySequence = [&Renames, ModelPieceData = mPieceData.Get()](std::vector<std::unique_ptr<lcModelHistory>>& HistorySequence)
{
for (const std::unique_ptr<lcModelHistory>& ModelHistory : HistorySequence)
ModelHistory->RenamePieces(Renames, ModelPieceData);
@@ -4933,7 +4923,7 @@ void lcModel::GetPartsListForStep(lcStep Step, int DefaultColorIndex, lcPartsLis
void lcModel::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector<lcModelPartsEntry>& ModelParts) const
{
if (mHasDirectGeometry && mPieceData->GetMesh())
ModelParts.emplace_back(lcModelPartsEntry{ WorldMatrix, mPieceData, mPieceData->GetMesh(), DefaultColorIndex });
ModelParts.emplace_back(lcModelPartsEntry{ WorldMatrix, mPieceData.Get(), mPieceData->GetMesh(), DefaultColorIndex });
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
Piece->GetModelParts(WorldMatrix, DefaultColorIndex, ModelParts);
@@ -5098,7 +5088,7 @@ void lcModel::RemoveFromSelectionAction(const std::vector<lcObject*>& Objects)
std::vector<lcObject*> lcModel::GetSelectionModePieces(lcSelectionMode SelectionMode, const lcPiece* SelectedPiece) const
{
const lcPieceData* PieceData = SelectedPiece->mPieceData;
const lcPieceData* PieceData = SelectedPiece->mPieceData.Get();
const int ColorIndex = SelectedPiece->GetColorIndex();
std::vector<lcObject*> Pieces;
@@ -5351,7 +5341,7 @@ void lcModel::FindReplacePiece(bool SearchForward, bool FindAll, bool Replace)
Piece->SetColorIndex(Params.ReplaceColorIndex);
if (ReplacePieceData)
Piece->SetPieceData(Params.ReplacePieceData, QString(), true, true);
Piece->SetPieceData(lcPieceDataRef(Params.ReplacePieceData), QString(), true, true);
};
size_t StartIndex = mPieces.size() - 1;
@@ -5635,7 +5625,7 @@ bool lcModel::InsertPieceToolClicked()
lcPiece* Piece = nullptr;
for (const lcInsertPieceInfo& InsertPieceInfo : mPreviewInsertPieceInfo)
for (const lcPreviewInsertPieceInfo& InsertPieceInfo : mPreviewInsertPieceInfo)
{
Piece = new lcPiece(InsertPieceInfo.PieceData);
@@ -6236,10 +6226,10 @@ void lcModel::ShowMinifigDialog()
for (int PartIndex = 0; PartIndex < LC_MFW_NUMITEMS; PartIndex++)
{
if (!Minifig.Parts[PartIndex])
if (!Minifig.PieceData[PartIndex])
continue;
lcPiece* Piece = new lcPiece(Minifig.Parts[PartIndex]);
lcPiece* Piece = new lcPiece(Minifig.PieceData[PartIndex]);
Piece->Initialize(Minifig.Matrices[PartIndex], mCurrentStep);
Piece->SetColorIndex(Minifig.ColorIndices[PartIndex]);
@@ -6268,10 +6258,10 @@ void lcModel::SetMinifig(const lcMinifig& Minifig)
for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
{
if (!Minifig.Parts[PartIdx])
if (!Minifig.PieceData[PartIdx])
continue;
lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
lcPiece* Piece = new lcPiece(Minifig.PieceData[PartIdx]);
Piece->Initialize(Minifig.Matrices[PartIdx], 1);
Piece->SetColorIndex(Minifig.ColorIndices[PartIdx]);
+19 -5
View File
@@ -1,6 +1,7 @@
#pragma once
#include "lc_math.h"
#include "lc_assettypes.h"
#include "lc_commands.h"
#include "lc_objectproperty.h"
#include "lc_result.h"
@@ -74,6 +75,19 @@ struct lcInsertPieceInfo
int ColorIndex;
};
// Stored insertion previews own their definitions; lcInsertPieceInfo remains borrowed.
struct lcPreviewInsertPieceInfo
{
explicit lcPreviewInsertPieceInfo(const lcInsertPieceInfo& Info)
: PieceData(Info.PieceData), Transform(Info.Transform), ColorIndex(Info.ColorIndex)
{
}
lcPieceDataRef PieceData;
lcMatrix44 Transform;
int ColorIndex;
};
struct lcModelPieceRename
{
lcPieceData* PieceData;
@@ -187,14 +201,14 @@ public:
void QueueMeshBuild();
void UpdateAllViews() const;
void RefreshPreview() const;
std::vector<lcPieceData*> GetRequiredPieceData() const;
std::vector<lcPieceDataRef> GetRequiredPieceData() const;
std::vector<lcPiece*> GetRequiredSynthPieces() const;
void WaitForAssets() const;
lcResult<void> EnsureAssetsReady() const;
lcPieceData* GetPieceData() const
{
return mPieceData;
return mPieceData.Get();
}
const std::vector<std::unique_ptr<lcPiece>>& GetPieces() const
@@ -329,7 +343,7 @@ public:
void UpdateSelectedPiecesTrainTrackConnections();
void SetPreviewInsertPieceInfo(std::vector<lcInsertPieceInfo>&& PreviewInsertPieceInfo);
const std::vector<lcInsertPieceInfo>& GetPreviewInsertPieceInfo() const
const std::vector<lcPreviewInsertPieceInfo>& GetPreviewInsertPieceInfo() const
{
return mPreviewInsertPieceInfo;
}
@@ -506,7 +520,7 @@ protected:
// Their order and the current-directory preference are saved with the model.
std::vector<QString> mAssetSourceDirectories;
bool mPreferCurrentAssetDirectory = true;
lcPieceData* mPieceData;
lcPieceDataRef mPieceData;
bool mIsPreview;
bool mActive;
@@ -517,7 +531,7 @@ protected:
bool mMouseToolMovePositionValid = false;
bool mMouseToolMoveUsesFocus = false;
bool mMouseToolFirstMove;
std::vector<lcInsertPieceInfo> mPreviewInsertPieceInfo;
std::vector<lcPreviewInsertPieceInfo> mPreviewInsertPieceInfo;
std::vector<std::unique_ptr<lcPiece>> mPieces;
std::vector<std::unique_ptr<lcCamera>> mCameras;
+17 -23
View File
@@ -94,9 +94,9 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model)
if (mLockAction->isChecked() || mRefreshingModel)
return;
const std::vector<lcPieceData*> Required = mPreview->GetModel()->GetRequiredPieceData();
const std::vector<lcPieceDataRef> RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData();
if (std::find(Required.begin(), Required.end(), Model->GetPieceData()) == Required.end())
if (std::find(RequiredPieceData.begin(), RequiredPieceData.end(), Model->GetPieceData()) == RequiredPieceData.end())
return;
mRefreshingModel = true;
@@ -106,13 +106,12 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model)
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (lcPieceData* PieceData : Required)
for (const lcPieceDataRef& RequiredData : RequiredPieceData)
{
if (PieceData->mState != lcAssetState::Unloaded)
if (RequiredData->mState != lcAssetState::Unloaded)
continue;
Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Normal);
Library->ReleasePieceData(PieceData);
Library->RequestPieceDataLoad(RequiredData.Get(), lcLoadPriority::Normal);
}
std::vector<lcModel*> UpdatedModels;
@@ -128,10 +127,10 @@ void lcPreviewDockWidget::RefreshAfterAssetChange(const std::vector<lcPieceData*
if (mPreview->GetModel()->GetPieces().empty())
return;
const std::vector<lcPieceData*> Required = mPreview->GetModel()->GetRequiredPieceData();
bool Affected = std::any_of(Required.begin(), Required.end(), [&ChangedPieces](lcPieceData* PieceData)
const std::vector<lcPieceDataRef> RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData();
bool Affected = std::any_of(RequiredPieceData.begin(), RequiredPieceData.end(), [&ChangedPieces](const lcPieceDataRef& RequiredData)
{
return std::find(ChangedPieces.begin(), ChangedPieces.end(), PieceData) != ChangedPieces.end();
return std::find(ChangedPieces.begin(), ChangedPieces.end(), RequiredData) != ChangedPieces.end();
});
if (!Affected)
@@ -154,11 +153,11 @@ void lcPreviewDockWidget::RefreshAfterAssetChange(const std::vector<lcPieceData*
void lcPreviewDockWidget::RefreshPendingAssets()
{
const std::vector<lcPieceData*> Required = mPreview->GetModel()->GetRequiredPieceData();
const std::vector<lcPieceDataRef> RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData();
for (const lcPieceData* PieceData : Required)
for (const lcPieceDataRef& RequiredData : RequiredPieceData)
{
if (PieceData->mState == lcAssetState::Failed || PieceData->mState == lcAssetState::Unloaded)
if (RequiredData->mState == lcAssetState::Failed || RequiredData->mState == lcAssetState::Unloaded)
{
mAssetRefreshPending = false;
mViewWidget->hide();
@@ -166,7 +165,7 @@ void lcPreviewDockWidget::RefreshPendingAssets()
return;
}
if (PieceData->mState != lcAssetState::Loaded)
if (RequiredData->mState != lcAssetState::Loaded)
{
mAssetRefreshPending = true;
mViewWidget->hide();
@@ -294,7 +293,9 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode)
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
bool Ready = Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Blocking);
const lcPieceDataRef PieceDataRef(PieceData);
bool Ready = Library->WaitForPieceData(PieceDataRef);
if (Ready && PieceData->IsModel())
Ready = static_cast<bool>(PieceData->GetModel()->EnsureAssetsReady());
@@ -303,7 +304,6 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode)
if (!Ready)
{
Library->ReleasePieceData(PieceData);
ClearPreview();
return false;
@@ -320,7 +320,6 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode)
mModel->UpdatePieceData(UpdatedModels);
Library->ReleasePieceData(PieceData);
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, true);
return true;
@@ -350,24 +349,19 @@ void lcPreview::UpdatePreview()
{
if (ModelPiece->mPieceData)
{
PieceData = ModelPiece->mPieceData;
PieceData = ModelPiece->mPieceData.Get();
ColorCode = ModelPiece->GetColorCode();
break;
}
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
if (PieceData)
Library->AddPieceDataReference(PieceData);
lcPieceDataRef PreviousPieceData(PieceData);
ClearPreview();
if (PieceData && ColorCode > -1)
SetCurrentPiece(PieceData, ColorCode);
if (PieceData)
Library->ReleasePieceData(PieceData);
}
void lcPreview::RefreshDescription()
+10 -10
View File
@@ -173,7 +173,7 @@ void lcSynthInit()
for (const auto& HoseInfo: FlexibleHoses)
{
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoFlexibleHose(HoseInfo.Length, HoseInfo.NumSections, HoseInfo.EdgePart2));
@@ -224,7 +224,7 @@ void lcSynthInit()
for (const auto& HoseInfo: FlexSystemHoses)
{
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoFlexSystemHose(HoseInfo.Length, HoseInfo.NumSections));
@@ -272,7 +272,7 @@ void lcSynthInit()
for (const auto& TubeInfo: PneumaticTubes)
{
lcPieceData* PieceData = Library->FindPieceData(TubeInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(TubeInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoPneumaticTube(TubeInfo.Length, TubeInfo.NumSections, "71533k02.dat"));
@@ -281,7 +281,7 @@ void lcSynthInit()
RegularInfo.PartID[7] = '2';
RegularInfo.Length -= 40.0f;
PieceData = Library->FindPieceData(RegularInfo.PartID, nullptr, false, false);
PieceData = Library->FindPieceData(RegularInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoPneumaticTube(RegularInfo.Length, RegularInfo.NumSections, "71533k01.dat"));
@@ -318,7 +318,7 @@ void lcSynthInit()
for (const auto& HoseInfo: RibbedHoses)
{
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoRibbedHose(HoseInfo.Length, HoseInfo.NumSections));
@@ -343,7 +343,7 @@ void lcSynthInit()
for (const auto& AxleInfo: FlexibleAxles)
{
lcPieceData* PieceData = Library->FindPieceData(AxleInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(AxleInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoFlexibleAxle(AxleInfo.Length, AxleInfo.NumSections));
@@ -364,7 +364,7 @@ void lcSynthInit()
for (const auto& StringInfo: BraidedStrings)
{
lcPieceData* PieceData = Library->FindPieceData(StringInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(StringInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoBraidedString(StringInfo.Length, StringInfo.NumSections));
@@ -385,7 +385,7 @@ void lcSynthInit()
for (const auto& AbsorberInfo: ShockAbsorbers)
{
lcPieceData* PieceData = Library->FindPieceData(AbsorberInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(AbsorberInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoShockAbsorber(AbsorberInfo.SpringPart));
@@ -414,7 +414,7 @@ void lcSynthInit()
for (const auto& ActuatorInfo: Actuators)
{
lcPieceData* PieceData = Library->FindPieceData(ActuatorInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(ActuatorInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoActuator(ActuatorInfo.BodyPart, ActuatorInfo.PistonPart,
@@ -439,7 +439,7 @@ void lcSynthInit()
for (const auto& JointInfo: UniversalJoints)
{
lcPieceData* PieceData = Library->FindPieceData(JointInfo.PartID, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(JointInfo.PartID, nullptr);
if (PieceData)
PieceData->SetSynthInfo(new lcSynthInfoUniversalJoint(JointInfo.Length, JointInfo.EndOffset, JointInfo.EndPart, JointInfo.CenterPart));
-2
View File
@@ -3,8 +3,6 @@
#include "lc_file.h"
#include "lc_string.h"
lcTextureBuildResult::~lcTextureBuildResult() = default;
lcTextureManager::~lcTextureManager()
{
Clear();
+39 -34
View File
@@ -27,9 +27,6 @@ void lcThumbnailManager::Clear()
mModel.reset();
mPendingGeneratedModels.clear();
for (auto &[ThumbnailId, Thumbnail] : mThumbnails)
ReleaseRequiredPieces(Thumbnail);
mThumbnails.clear();
}
@@ -41,9 +38,9 @@ void lcThumbnailManager::RefreshPieces(const std::vector<lcPieceData*>& ChangedP
for (std::map<lcPartThumbnailId, lcPartThumbnail>::value_type& Entry : mThumbnails)
{
const lcPartThumbnail& Thumbnail = Entry.second;
const bool RequiredChanged = std::any_of(Thumbnail.Required.begin(), Thumbnail.Required.end(), [&Changed](lcPieceData* PieceData)
const bool RequiredChanged = std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [&Changed](const lcPieceDataRef& RequiredData)
{
return Changed.find(PieceData) != Changed.end();
return Changed.find(RequiredData.Get()) != Changed.end();
});
if (Changed.find(Thumbnail.PieceData) == Changed.end() && !RequiredChanged)
@@ -102,8 +99,6 @@ void lcThumbnailManager::ReleaseThumbnail(lcPartThumbnailId ThumbnailId)
{
mPendingGeneratedModels.erase(ThumbnailId);
ReleaseRequiredPieces(Thumbnail);
mThumbnails.erase(ThumbnailIt);
}
}
@@ -114,8 +109,9 @@ void lcThumbnailManager::RefreshRequiredPieces(lcPartThumbnail& Thumbnail)
if (Thumbnail.PieceData->IsModel())
{
std::vector<lcPieceData*> ModelRequired = Thumbnail.PieceData->GetModel()->GetRequiredPieceData();
Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end());
std::vector<lcPieceDataRef> ModelPieceData = Thumbnail.PieceData->GetModel()->GetRequiredPieceData();
for (const lcPieceDataRef& ModelData : ModelPieceData)
Required.push_back(ModelData.Get());
}
else if (Thumbnail.PieceData->IsProject())
{
@@ -123,34 +119,37 @@ void lcThumbnailManager::RefreshRequiredPieces(lcPartThumbnail& Thumbnail)
if (Model)
{
std::vector<lcPieceData*> ModelRequired = Model->GetRequiredPieceData();
Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end());
std::vector<lcPieceDataRef> ModelPieceData = Model->GetRequiredPieceData();
for (const lcPieceDataRef& ModelData : ModelPieceData)
Required.push_back(ModelData.Get());
}
}
std::sort(Required.begin(), Required.end());
Required.erase(std::unique(Required.begin(), Required.end()), Required.end());
std::unordered_set<lcPieceData*> Previous(Thumbnail.Required.begin(), Thumbnail.Required.end());
std::unordered_map<lcPieceData*, size_t> Previous;
for (size_t Index = 0; Index < Thumbnail.RequiredPieceData.size(); Index++)
Previous.emplace(Thumbnail.RequiredPieceData[Index].Get(), Index);
std::vector<lcPieceDataRef> RequiredPieceData;
RequiredPieceData.reserve(Required.size());
for (lcPieceData* PieceData : Required)
{
if (Previous.erase(PieceData) == 0)
const auto It = Previous.find(PieceData);
if (It != Previous.end())
RequiredPieceData.push_back(std::move(Thumbnail.RequiredPieceData[It->second]));
else
{
RequiredPieceData.emplace_back(PieceData);
mLibrary->RequestPieceDataLoad(PieceData, lcLoadPriority::Low);
}
}
for (lcPieceData* PieceData : Previous)
mLibrary->ReleasePieceData(PieceData);
Thumbnail.Required = std::move(Required);
}
void lcThumbnailManager::ReleaseRequiredPieces(lcPartThumbnail& Thumbnail)
{
for (lcPieceData* PieceData : Thumbnail.Required)
mLibrary->ReleasePieceData(PieceData);
Thumbnail.Required.clear();
Thumbnail.RequiredPieceData = std::move(RequiredPieceData);
}
void lcThumbnailManager::UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail)
@@ -159,15 +158,15 @@ void lcThumbnailManager::UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartTh
bool Pending = false;
for (const lcPieceData* PieceData : Thumbnail.Required)
for (const lcPieceDataRef& RequiredData : Thumbnail.RequiredPieceData)
{
if (PieceData->mState == lcAssetState::Failed || PieceData->mState == lcAssetState::Unloaded)
if (RequiredData->mState == lcAssetState::Failed || RequiredData->mState == lcAssetState::Unloaded)
{
DrawFailedThumbnail(ThumbnailId, Thumbnail);
return;
}
if (PieceData->mState != lcAssetState::Loaded)
if (RequiredData->mState != lcAssetState::Loaded)
Pending = true;
}
@@ -190,7 +189,10 @@ void lcThumbnailManager::PartLoaded(lcPieceData* PieceData)
std::vector<lcPartThumbnailId> Ready;
for (const auto& [ThumbnailId, Thumbnail] : mThumbnails)
if (Thumbnail.Pixmap.isNull() && std::find(Thumbnail.Required.begin(), Thumbnail.Required.end(), PieceData) != Thumbnail.Required.end())
if (Thumbnail.Pixmap.isNull() && std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [PieceData](const lcPieceDataRef& RequiredData)
{
return RequiredData == PieceData;
}))
Ready.push_back(ThumbnailId);
for (lcPartThumbnailId ThumbnailId : Ready)
@@ -207,7 +209,10 @@ void lcThumbnailManager::PartLoadFailed(lcPieceData* PieceData)
std::vector<lcPartThumbnailId> Failed;
for (const auto& [ThumbnailId, Thumbnail] : mThumbnails)
if (Thumbnail.Pixmap.isNull() && std::find(Thumbnail.Required.begin(), Thumbnail.Required.end(), PieceData) != Thumbnail.Required.end())
if (Thumbnail.Pixmap.isNull() && std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [PieceData](const lcPieceDataRef& RequiredData)
{
return RequiredData == PieceData;
}))
Failed.push_back(ThumbnailId);
for (lcPartThumbnailId ThumbnailId : Failed)
@@ -296,7 +301,7 @@ void lcThumbnailManager::DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPa
Thumbnail.Pixmap.setDevicePixelRatio(Thumbnail.DeviceScale);
ReleaseRequiredPieces(Thumbnail);
Thumbnail.RequiredPieceData.clear();
emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap);
SchedulePendingThumbnails();
@@ -387,8 +392,8 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum
{
RefreshRequiredPieces(Thumbnail);
for (const lcPieceData* Required : Thumbnail.Required)
if (Required->IsLoading())
for (const lcPieceDataRef& RequiredData : Thumbnail.RequiredPieceData)
if (RequiredData->IsLoading())
return;
DrawFailedThumbnail(ThumbnailId, Thumbnail);
@@ -441,7 +446,7 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum
Thumbnail.Pixmap = QPixmap::fromImage(Image).scaled(ScaledSize, ScaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
ReleaseRequiredPieces(Thumbnail);
Thumbnail.RequiredPieceData.clear();
emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap);
SchedulePendingThumbnails();
+3 -2
View File
@@ -1,12 +1,14 @@
#pragma once
#include "lc_assettypes.h"
class lcPiecesLibrary;
struct lcPartThumbnail
{
QPixmap Pixmap;
lcPieceData* PieceData;
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
int ColorIndex;
int Size;
float DeviceScale;
@@ -41,7 +43,6 @@ protected slots:
protected:
void RefreshRequiredPieces(lcPartThumbnail& Thumbnail);
void ReleaseRequiredPieces(lcPartThumbnail& Thumbnail);
void UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
void DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
void DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
+1 -1
View File
@@ -594,7 +594,7 @@ void lcTimelineWidget::PreviewSelection(QTreeWidgetItem* CurrentItem)
if (!Piece)
return;
lcPieceData* PieceData = Piece->mPieceData;
lcPieceData* PieceData = Piece->mPieceData.Get();
if (!PieceData)
return;
+5 -5
View File
@@ -76,7 +76,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library)
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
{
lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr);
if (PieceData)
PieceData->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, true));
@@ -86,7 +86,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library)
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
{
lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr);
if (PieceData)
PieceData->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, false));
@@ -102,7 +102,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library)
size_t Group = qHash(SleepersIt.key());
QString PartId = SleepersIt->toString();
lcPieceData* SleeperPieceData = Library->FindPieceData(PartId.toLatin1(), nullptr, false, false);
lcPieceData* SleeperPieceData = Library->FindPieceData(PartId.toLatin1(), nullptr);
if (SleeperPieceData)
mSleepers[Group] = SleeperPieceData;
@@ -245,7 +245,7 @@ std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur
if (!SleeperPieceData)
return Pieces;
std::optional<lcMatrix44> SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceData, CurrentPiece->mModelWorld, ConnectionIndex, SleeperPieceData, 0);
std::optional<lcMatrix44> SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceData.Get(), CurrentPiece->mModelWorld, ConnectionIndex, SleeperPieceData, 0);
if (!SleeperTransform)
return Pieces;
@@ -260,7 +260,7 @@ std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur
}
else
{
std::optional<lcMatrix44> Transform = GetConnectionTransform(CurrentPiece->mPieceData, CurrentPiece->mModelWorld, ConnectionIndex, PieceData, NewConnectionIndex);
std::optional<lcMatrix44> Transform = GetConnectionTransform(CurrentPiece->mPieceData.Get(), CurrentPiece->mModelWorld, ConnectionIndex, PieceData, NewConnectionIndex);
if (Transform)
Pieces.emplace_back(lcInsertPieceInfo{ PieceData, Transform.value(), ColorIndex });
+10 -10
View File
@@ -474,7 +474,7 @@ lcVector3 lcView::GetMoveDirection(const lcVector3& Direction) const
void lcView::UpdatePiecePreview()
{
lcPieceData* PreviewPieceData = gMainWindow->GetInsertPieceData();
const lcPieceDataRef PreviewPieceData(gMainWindow->GetInsertPieceData());
std::vector<lcInsertPieceInfo> InsertPieceInfo;
bool IsConnection;
@@ -486,7 +486,7 @@ void lcView::UpdatePiecePreview()
GetActiveModel()->SetPreviewInsertPieceInfo(std::move(InsertPieceInfo));
}
std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, lcPieceData* PieceData, lcPiece* MovingPiece) const
std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, const lcPieceDataRef& PieceData, lcPiece* MovingPiece) const
{
lcModel* ActiveModel = GetActiveModel();
@@ -501,10 +501,10 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
while (ObjectRayTest.ObjectSection.Object)
{
lcPiece* MousePiece = reinterpret_cast<lcPiece*>(ObjectRayTest.ObjectSection.Object);
lcPieceData* MousePieceData = MousePiece->mPieceData;
lcPieceData* MousePieceData = MousePiece->mPieceData.Get();
const bool Retest = MousePieceData->mState != lcAssetState::Loaded || MousePieceData->IsModel() || MousePieceData->IsProject() || MousePiece->IsGeneratedMeshPending();
lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(MousePieceData);
lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(MousePiece->mPieceData);
if (MousePieceData->GetSynthInfo())
lcGetPiecesLibrary()->WaitForSynthMeshes({ MousePiece });
@@ -535,7 +535,7 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
lcVector3 ClosestPoint = ObjectRayTest.Start + lcNormalize(ObjectRayTest.End - ObjectRayTest.Start) * ObjectRayTest.Distance;
quint32 FocusSection = MousePiece->GetFocusSection();
std::vector<lcInsertPieceInfo> TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, PieceData, MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint);
std::vector<lcInsertPieceInfo> TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, PieceData.Get(), MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint);
if (!TrainTracks.empty())
return { TrainTracks, true };
@@ -564,7 +564,7 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
lcMatrix44 WorldMatrix = ObjectRayTest.PieceRayTest.Transform;
WorldMatrix.SetTranslation(Position);
return { { lcInsertPieceInfo{ PieceData, WorldMatrix, gMainWindow->mColorIndex } }, false };
return { { lcInsertPieceInfo{ PieceData.Get(), WorldMatrix, gMainWindow->mColorIndex } }, false };
}
std::array<lcVector3, 2> ClickPoints = {{ lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) }};
@@ -583,7 +583,7 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(0, 0, 1, BoundingBox.Min.z)))
{
Intersection = ActiveModel->SnapPosition(Intersection);
return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
}
lcVector3 Position;
@@ -596,10 +596,10 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(FrontVector, -lcDot(FrontVector, Position))))
{
Intersection = ActiveModel->SnapPosition(Intersection);
return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
}
return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false };
return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false };
}
lcVector3 lcView::GetCameraLightInsertPosition() const
@@ -1619,7 +1619,7 @@ void lcView::DrawGrid()
{
const lcModel* PreviewModel = GetActiveModel();
for (const lcInsertPieceInfo& PreviewInsertPieceInfo : PreviewModel->GetPreviewInsertPieceInfo())
for (const lcPreviewInsertPieceInfo& PreviewInsertPieceInfo : PreviewModel->GetPreviewInsertPieceInfo())
{
lcVector3 Points[8];
lcGetBoxCorners(PreviewInsertPieceInfo.PieceData->GetBoundingBox(), Points);
+2 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include "lc_assettypes.h"
#include "lc_context.h"
#include "lc_math.h"
#include "lc_commands.h"
@@ -288,7 +289,7 @@ public:
float GetOverlayScale() const;
lcVector3 GetMoveDirection(const lcVector3& Direction) const;
void UpdatePiecePreview();
std::pair<std::vector<lcInsertPieceInfo>, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, lcPieceData* PieceData, lcPiece* MovingPiece) const;
std::pair<std::vector<lcInsertPieceInfo>, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, const lcPieceDataRef& PieceData, lcPiece* MovingPiece) const;
lcVector3 GetCameraLightInsertPosition() const;
void GetRayUnderPointer(lcVector3& Start, lcVector3& End) const;
lcObjectRayTest RayTest(bool PiecesOnly, bool IgnoreSelected) const;
+32 -49
View File
@@ -48,7 +48,6 @@ MinifigWizard::MinifigWizard()
for (int PartIndex = 0; PartIndex < LC_MFW_NUMITEMS; PartIndex++)
{
mMinifig.Parts[PartIndex] = nullptr;
mMinifig.ColorIndices[PartIndex] = gDefaultColor;
mMinifig.Angles[PartIndex] = 0.0f;
mMinifig.Matrices[PartIndex] = lcMatrix44Identity();
@@ -57,12 +56,6 @@ MinifigWizard::MinifigWizard()
MinifigWizard::~MinifigWizard()
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (int i = 0; i < LC_MFW_NUMITEMS; i++)
if (mMinifig.Parts[i])
Library->ReleasePieceData(mMinifig.Parts[i]); // todo: don't call ReleasePieceData here because it may release textures and they need a GL context current
SaveTemplates();
}
@@ -152,7 +145,7 @@ void MinifigWizard::ParseSettings(lcFile& Settings)
*NameEnd = 0;
NameEnd++;
lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(NameStart, nullptr, false, false);
lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(NameStart, nullptr);
if (!PieceData && *NameStart)
continue;
@@ -207,24 +200,19 @@ void MinifigWizard::LoadTemplate(const QString& TemplateName)
mMinifig.Angles[PartIndex] = Template.Angles[PartIndex];
mMinifig.Matrices[PartIndex] = lcMatrix44Identity();
if (mMinifig.Parts[PartIndex])
Library->ReleasePieceData(mMinifig.Parts[PartIndex]);
mMinifig.PieceData[PartIndex] = Library->AcquirePieceData(Template.Parts[PartIndex].toLatin1(), nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(Template.Parts[PartIndex].toLatin1(), nullptr, false, false);
mMinifig.Parts[PartIndex] = PieceData;
if (PieceData)
Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Normal);
if (mMinifig.PieceData[PartIndex])
Library->RequestPieceDataLoad(mMinifig.PieceData[PartIndex].Get(), lcLoadPriority::Normal);
}
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
for (lcPieceData* PieceData : mMinifig.Parts)
for (const lcPieceDataRef& PieceData : mMinifig.PieceData)
if (PieceData)
Required.push_back(PieceData);
RequiredPieceData.push_back(PieceData);
Library->WaitForPieceData(Required);
Library->WaitForPieceData(RequiredPieceData);
Calculate();
}
@@ -326,26 +314,26 @@ void MinifigWizard::Calculate()
float HeadOffset = 0.0f;
lcMatrix44 Root, Mat, Mat2;
lcPieceData** Parts = mMinifig.Parts;
const lcPieceDataRef* PieceData = mMinifig.PieceData;
const float* Angles = mMinifig.Angles;
lcMatrix44* Matrices = mMinifig.Matrices;
const bool DroidTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "30375.dat");
const bool SkeletonTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "6260.dat");
const bool DroidTorso = PieceData[LC_MFW_BODY] && !qstricmp(PieceData[LC_MFW_BODY]->mFileName, "30375.dat");
const bool SkeletonTorso = PieceData[LC_MFW_BODY] && !qstricmp(PieceData[LC_MFW_BODY]->mFileName, "6260.dat");
if (Parts[LC_MFW_BODY3])
if (PieceData[LC_MFW_BODY3])
Root = lcMatrix44Translation(lcVector3(0, 0, 74.0f));
else
Root = lcMatrix44Translation(lcVector3(0, 0, 72.0f));
Matrices[LC_MFW_BODY] = lcMul(mSettings[LC_MFW_BODY][GetSelectionIndex(LC_MFW_BODY)].Offset, Root);
if (Parts[LC_MFW_NECK])
if (PieceData[LC_MFW_NECK])
{
Matrices[LC_MFW_NECK] = lcMul(mSettings[LC_MFW_NECK][GetSelectionIndex(LC_MFW_NECK)].Offset, Root);
HeadOffset = 0.08f;
}
if (Parts[LC_MFW_HEAD])
if (PieceData[LC_MFW_HEAD])
{
Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HEAD]);
Mat.SetTranslation(lcVector3(0.0f, 0.0f, 24.0f + HeadOffset));
@@ -353,21 +341,21 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_HEAD] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_HATS])
if (PieceData[LC_MFW_HATS])
{
Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HATS]);
Mat = lcMul(mSettings[LC_MFW_HATS][GetSelectionIndex(LC_MFW_HATS)].Offset, Mat);
Matrices[LC_MFW_HATS] = lcMul(Mat, Matrices[LC_MFW_HEAD]);
}
if (Parts[LC_MFW_HATS2])
if (PieceData[LC_MFW_HATS2])
{
Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_HATS2]);
Mat = lcMul(mSettings[LC_MFW_HATS2][GetSelectionIndex(LC_MFW_HATS2)].Offset, Mat);
Matrices[LC_MFW_HATS2] = lcMul(Mat, Matrices[LC_MFW_HATS]);
}
if (Parts[LC_MFW_RARM])
if (PieceData[LC_MFW_RARM])
{
Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RARM]);
@@ -382,7 +370,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_RARM] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_RHAND])
if (PieceData[LC_MFW_RHAND])
{
Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_RHAND]);
Mat2 = lcMatrix44RotationX(LC_DTOR * 45);
@@ -392,7 +380,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_RHAND] = lcMul(Mat, Matrices[LC_MFW_RARM]);
}
if (Parts[LC_MFW_RHANDA])
if (PieceData[LC_MFW_RHANDA])
{
Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RHANDA]);
Mat.SetTranslation(lcVector3(0, -9.25f, 0));
@@ -401,7 +389,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_RHANDA] = lcMul(Mat, Matrices[LC_MFW_RHAND]);
}
if (Parts[LC_MFW_LARM])
if (PieceData[LC_MFW_LARM])
{
Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LARM]);
@@ -416,7 +404,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_LARM] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_LHAND])
if (PieceData[LC_MFW_LHAND])
{
Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_LHAND]);
Mat2 = lcMatrix44RotationX(LC_DTOR * 45);
@@ -426,7 +414,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_LHAND] = lcMul(Mat, Matrices[LC_MFW_LARM]);
}
if (Parts[LC_MFW_LHANDA])
if (PieceData[LC_MFW_LHANDA])
{
Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LHANDA]);
Mat.SetTranslation(lcVector3(0, -9.25f, 0));
@@ -435,7 +423,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_LHANDA] = lcMul(Mat, Matrices[LC_MFW_LHAND]);
}
if (Parts[LC_MFW_BODY2])
if (PieceData[LC_MFW_BODY2])
{
Mat = lcMatrix44Identity();
Mat.SetTranslation(lcVector3(0, 0, -32.0f));
@@ -443,7 +431,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_BODY2] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_BODY3])
if (PieceData[LC_MFW_BODY3])
{
Mat = lcMatrix44Identity();
Mat.SetTranslation(lcVector3(0, 0, -32.0f));
@@ -451,7 +439,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_BODY3] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_RLEG])
if (PieceData[LC_MFW_RLEG])
{
Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RLEG]);
Mat.SetTranslation(lcVector3(0, 0, -44.0f));
@@ -459,7 +447,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_RLEG] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_RLEGA])
if (PieceData[LC_MFW_RLEGA])
{
const lcVector3 Center(-10.0f, -1.0f, -28.0f);
Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RLEGA]);
@@ -470,7 +458,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_RLEGA] = lcMul(Mat, Matrices[LC_MFW_RLEG]);
}
if (Parts[LC_MFW_LLEG])
if (PieceData[LC_MFW_LLEG])
{
Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LLEG]);
Mat.SetTranslation(lcVector3(0, 0, -44.0f));
@@ -478,7 +466,7 @@ void MinifigWizard::Calculate()
Matrices[LC_MFW_LLEG] = lcMul(Mat, Root);
}
if (Parts[LC_MFW_LLEGA])
if (PieceData[LC_MFW_LLEGA])
{
const lcVector3 Center(10.0f, -1.0f, -28.0f);
Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LLEGA]);
@@ -497,7 +485,7 @@ int MinifigWizard::GetSelectionIndex(int Type) const
const std::vector<lcMinifigPieceInfo>& InfoArray = mSettings[Type];
for (size_t Index = 0; Index < InfoArray.size(); Index++)
if (InfoArray[Index].PieceData == mMinifig.Parts[Type])
if (InfoArray[Index].PieceData == mMinifig.PieceData[Type])
return (int)Index;
return 0;
@@ -505,15 +493,10 @@ int MinifigWizard::GetSelectionIndex(int Type) const
void MinifigWizard::SetPieceData(int Type, lcPieceData* PieceData)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
mMinifig.PieceData[Type] = lcPieceDataRef(PieceData);
if (mMinifig.Parts[Type])
Library->ReleasePieceData(mMinifig.Parts[Type]);
mMinifig.Parts[Type] = PieceData;
if (mMinifig.Parts[Type])
Library->RequestPieceDataLoad(mMinifig.Parts[Type], lcLoadPriority::Blocking);
if (mMinifig.PieceData[Type])
lcGetPiecesLibrary()->WaitForPieceData(mMinifig.PieceData[Type]);
Calculate();
}
+2 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "lc_math.h"
#include "lc_assettypes.h"
enum LC_MFW_TYPES
{
@@ -33,7 +34,7 @@ struct lcMinifigPieceInfo
struct lcMinifig
{
lcPieceData* Parts[LC_MFW_NUMITEMS];
lcPieceDataRef PieceData[LC_MFW_NUMITEMS];
int ColorIndices[LC_MFW_NUMITEMS];
float Angles[LC_MFW_NUMITEMS];
lcMatrix44 Matrices[LC_MFW_NUMITEMS];
+30 -23
View File
@@ -26,9 +26,14 @@ lcPiece::lcPiece()
}
lcPiece::lcPiece(lcPieceData* PieceData)
: lcPiece(lcPieceDataRef(PieceData))
{
}
lcPiece::lcPiece(lcPieceDataRef PieceDataRef)
: lcObject(lcObjectType::Piece)
{
SetPieceData(PieceData, QString(), true, true);
SetPieceData(std::move(PieceDataRef), QString(), true, true);
mColorIndex = gDefaultColor;
mColorCode = 16;
mPivotMatrix = lcMatrix44Identity();
@@ -63,30 +68,36 @@ lcPiece::~lcPiece()
if (mPieceData)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->ReleasePieceData(mPieceData);
mPieceData.Reset();
Library->NotifyAssetStatusChanged();
}
delete mMesh;
}
void lcPiece::SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait, bool UpdateSynthInfo)
void lcPiece::SetPieceData(lcPieceDataRef PieceDataRef, const QString& ID, bool Wait, bool UpdateSynthInfo)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
lcPieceData* PreviousPieceData = mPieceData;
const bool DataChanged = mPieceData != PieceDataRef;
if (PreviousPieceData != PieceData || UpdateSynthInfo)
if (DataChanged || UpdateSynthInfo)
Library->CancelSynthMesh(this);
if (PreviousPieceData != PieceData)
if (DataChanged)
{
delete mMesh;
mMesh = nullptr;
}
mPieceData = PieceData;
lcPieceDataRef PreviousData(std::move(mPieceData));
mPieceData = std::move(PieceDataRef);
if (mPieceData)
Library->RequestPieceDataLoad(mPieceData, Wait ? lcLoadPriority::Blocking : lcLoadPriority::Normal);
{
if (Wait)
Library->WaitForPieceData(mPieceData);
else
Library->RequestPieceDataLoad(mPieceData.Get(), lcLoadPriority::Normal);
}
if (!ID.isEmpty())
mID = ID;
@@ -106,9 +117,11 @@ void lcPiece::SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait,
UpdateMesh();
}
else if (PreviousPieceData && PreviousPieceData != PieceData)
else if (PreviousData && DataChanged)
UpdateMesh();
PreviousData.Reset();
Library->NotifyAssetStatusChanged();
}
@@ -117,9 +130,7 @@ bool lcPiece::SetPieceId(lcPieceData* PieceData)
if (mPieceData == PieceData)
return false;
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->ReleasePieceData(mPieceData);
SetPieceData(PieceData, QString(), true, true);
SetPieceData(lcPieceDataRef(PieceData), QString(), true, true);
return true;
}
@@ -333,8 +344,7 @@ bool lcPiece::FileLoad(lcFile& file, Project* Project)
file.ReadBuffer(name, LC_PIECE_NAME_LEN);
lcstrcat(name, ".dat");
lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(name, Project, true, false);
SetPieceData(PieceData, QString(), true, true);
SetPieceData(lcGetPiecesLibrary()->AcquirePieceData(name, Project, true, false), QString(), true, true);
// 11 (0.77)
if (version < 11)
@@ -532,7 +542,7 @@ bool lcPiece::MinIntersectDist(const lcVector3& Start, const lcVector3& End, flo
if (!Mesh->MinIntersectDist(Start, End, MinDistance, PieceRayTest.Plane))
return false;
PieceRayTest.PieceData = mPieceData;
PieceRayTest.PieceData = mPieceData.Get();
PieceRayTest.Transform = lcMatrix44Identity();
return true;
@@ -742,7 +752,7 @@ QVariant lcPiece::GetPropertyValue(lcObjectPropertyId PropertyId) const
switch (PropertyId)
{
case lcObjectPropertyId::PieceId:
return QVariant::fromValue<void*>(mPieceData);
return QVariant::fromValue<void*>(mPieceData.Get());
case lcObjectPropertyId::PieceColor:
return GetColorIndex();
@@ -1033,16 +1043,13 @@ void lcPiece::SetHistoryState(const lcPieceHistoryState& State, const lcModel* M
mPosition = State.Position;
mRotation = State.Rotation;
lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(mID.toLatin1(), Model->GetProject(), true, true);
lcPieceDataRef RestoredData = lcGetPiecesLibrary()->AcquirePieceData(mID.toLatin1(), Model->GetProject(), true, true);
if (mPieceData != PieceData)
if (mPieceData != RestoredData)
{
const bool NewPiece = !mPieceData;
if (mPieceData)
lcGetPiecesLibrary()->ReleasePieceData(mPieceData);
SetPieceData(PieceData, mID, true, false);
SetPieceData(std::move(RestoredData), mID, true, false);
if (NewPiece)
UpdateMesh();
@@ -1511,7 +1518,7 @@ void lcPiece::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex
if (!mMesh || mPieceData->mState != lcAssetState::Loaded)
mPieceData->GetModelParts(lcMul(mModelWorld, WorldMatrix), ColorIndex, ModelParts);
else
ModelParts.emplace_back(lcModelPartsEntry{ lcMul(mModelWorld, WorldMatrix), mPieceData, mMesh, ColorIndex });
ModelParts.emplace_back(lcModelPartsEntry{ lcMul(mModelWorld, WorldMatrix), mPieceData.Get(), mMesh, ColorIndex });
}
const lcBoundingBox& lcPiece::GetBoundingBox() const
+4 -2
View File
@@ -5,6 +5,7 @@ class lcPieceData;
#include "object.h"
#include "lc_colors.h"
#include "lc_math.h"
#include "lc_assettypes.h"
#define LC_MAX_CONTROL_POINTS 1000
@@ -60,6 +61,7 @@ class lcPiece : public lcObject
public:
lcPiece();
lcPiece(lcPieceData* PieceData);
lcPiece(lcPieceDataRef PieceDataRef);
lcPiece(const lcPiece& Other);
virtual ~lcPiece();
@@ -161,7 +163,7 @@ public:
void Initialize(const lcMatrix44& WorldMatrix, lcStep Step);
const lcBoundingBox& GetBoundingBox() const;
void CompareBoundingBox(lcVector3& Min, lcVector3& Max) const;
void SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait, bool UpdateSynthInfo);
void SetPieceData(lcPieceDataRef PieceDataRef, const QString& ID, bool Wait, bool UpdateSynthInfo);
bool SetPieceId(lcPieceData* PieceData);
bool FileLoad(lcFile& file, Project* Project);
@@ -278,7 +280,7 @@ public:
}
public:
lcPieceData* mPieceData = nullptr;
lcPieceDataRef mPieceData;
lcMatrix44 mModelWorld;
lcMatrix44 mPivotMatrix = lcMatrix44Identity();
+20 -24
View File
@@ -1043,7 +1043,7 @@ std::vector<QString> Project::GetLoadWarningDetails() const
for (const std::unique_ptr<lcModel>& Model : Current->mModels)
for (const std::unique_ptr<lcPiece>& Piece : Model->GetPieces())
{
const lcPieceData* PieceData = Piece->mPieceData;
const lcPieceData* PieceData = Piece->mPieceData.Get();
if (!PieceData || !PieceData->IsProject())
continue;
@@ -1259,7 +1259,7 @@ lcResult<void> Project::Save(const QString& FileName)
if (DirectoryChanged)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
std::vector<lcPieceData*> LocalPieces;
std::vector<lcPieceDataRef> LocalPieceData;
std::vector<lcPieceData*> ChangedPieces;
for (const std::map<std::string, lcPieceData*>::value_type& Entry : mPieceIndex)
@@ -1269,13 +1269,12 @@ lcResult<void> Project::Save(const QString& FileName)
if (PieceData->IsModel())
continue;
Library->AddPieceDataReference(PieceData);
LocalPieces.push_back(PieceData);
LocalPieceData.push_back(lcPieceDataRef(PieceData));
}
for (lcPieceData* PieceData : LocalPieces)
if (Library->RemapProjectPieceData(PieceData, DestinationDirectory, mIsPreview))
ChangedPieces.push_back(PieceData);
for (const lcPieceDataRef& LocalData : LocalPieceData)
if (Library->RemapProjectPieceData(LocalData.Get(), DestinationDirectory, mIsPreview))
ChangedPieces.push_back(LocalData.Get());
for (const std::unique_ptr<lcModel>& Model : mModels)
{
@@ -1304,8 +1303,6 @@ lcResult<void> Project::Save(const QString& FileName)
gMainWindow->GetPreviewWidget()->RefreshAfterAssetChange(ChangedPieces);
}
for (lcPieceData* PieceData : LocalPieces)
Library->ReleasePieceData(PieceData);
}
for (const std::unique_ptr<lcModel>& Model : mModels)
@@ -1543,7 +1540,7 @@ lcResult<void> Project::MergeInternal(const std::vector<Project*>& Sources, cons
for (const std::unique_ptr<lcPiece>& Piece : Model->GetPieces())
{
if (Piece->mPieceData && !Piece->mPieceData->IsModel())
NonModelPieces.emplace(lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData()), Piece->mPieceData);
NonModelPieces.emplace(lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData()), Piece->mPieceData.Get());
}
}
@@ -1587,7 +1584,7 @@ lcResult<void> Project::MergeInternal(const std::vector<Project*>& Sources, cons
{
const std::string Name = lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData());
if (!RegisterSourcePiece(Name, Piece->mPieceData))
if (!RegisterSourcePiece(Name, Piece->mPieceData.Get()))
return lcUnexpected(tr("Both projects contain different pieces named '%1'. Rename one before merging.").arg(ConflictName));
}
}
@@ -1608,7 +1605,7 @@ lcResult<void> Project::MergeInternal(const std::vector<Project*>& Sources, cons
const bool ReservedOriginal = FinalName != OriginalName && OriginalModelNames.find(Name) != OriginalModelNames.end();
const bool Duplicate = DestinationModelNames.find(Name) != DestinationModelNames.end() || NonModelPieces.find(Name) != NonModelPieces.end() ||
PlannedModelNames.find(Name) != PlannedModelNames.end() || ReservedOriginal ||
Library->FindPieceData(FinalName.toLatin1().constData(), nullptr, false, false) != nullptr;
Library->FindPieceData(FinalName.toLatin1().constData(), nullptr) != nullptr;
if (!Duplicate)
{
@@ -1657,7 +1654,7 @@ lcResult<void> Project::MergeInternal(const std::vector<Project*>& Sources, cons
{
for (const std::unique_ptr<lcPiece>& Piece : Model->GetPieces())
{
lcPieceData* Incoming = Piece->mPieceData;
lcPieceData* Incoming = Piece->mPieceData.Get();
if (!Incoming || Incoming->IsModel())
continue;
@@ -1669,8 +1666,7 @@ lcResult<void> Project::MergeInternal(const std::vector<Project*>& Sources, cons
continue;
const QString PieceId = Piece->GetID();
Piece->SetPieceData(Existing, PieceId, false, false);
Library->ReleasePieceData(Incoming);
Piece->SetPieceData(lcPieceDataRef(Existing), PieceId, false, false);
}
}
@@ -1810,20 +1806,20 @@ std::vector<lcModelPartsEntry> Project::GetModelParts()
return ModelParts;
}
std::vector<lcPieceData*> Project::GetRequiredPieceData() const
std::vector<lcPieceDataRef> Project::GetRequiredPieceData() const
{
std::vector<lcPieceData*> Required;
std::vector<lcPieceDataRef> RequiredPieceData;
for (const std::unique_ptr<lcModel>& Model : mModels)
{
std::vector<lcPieceData*> ModelRequired = Model->GetRequiredPieceData();
Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end());
std::vector<lcPieceDataRef> ModelPieceData = Model->GetRequiredPieceData();
RequiredPieceData.insert(RequiredPieceData.end(), ModelPieceData.begin(), ModelPieceData.end());
}
std::sort(Required.begin(), Required.end());
Required.erase(std::unique(Required.begin(), Required.end()), Required.end());
std::sort(RequiredPieceData.begin(), RequiredPieceData.end());
RequiredPieceData.erase(std::unique(RequiredPieceData.begin(), RequiredPieceData.end()), RequiredPieceData.end());
return Required;
return RequiredPieceData;
}
std::vector<lcPiece*> Project::GetRequiredSynthPieces() const
@@ -1885,7 +1881,7 @@ lcResult<QByteArray> Project::SerializeModel(const lcModel* Root, bool SelectedO
if ((FilterSelection && !Piece->IsSelected()) || (LastStep && Piece->GetStepShow() > LastStep))
continue;
const lcPieceData* PieceData = Piece->mPieceData;
const lcPieceData* PieceData = Piece->mPieceData.Get();
const lcModel* Child = nullptr;
if (PieceData && PieceData->IsModel())
@@ -3749,7 +3745,7 @@ lcResult<void> Project::ExportPOVRay(const QString& FileName)
lcstrcat(Src, ".dat");
lcPieceData* PieceData = Library->FindPieceData(Src, nullptr, false, false);
lcPieceData* PieceData = Library->FindPieceData(Src, nullptr);
if (!PieceData)
continue;
+2 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "lc_application.h"
#include "lc_assettypes.h"
#include "lc_result.h"
#define LC_HTML_SINGLEPAGE 0x01
@@ -144,7 +145,7 @@ public:
bool ImportInventory(const std::vector<lcSetInventoryItem>& SetInventory, const QString& Name, const QString& Description);
lcResult<void> SaveImage(const lcImageDialogOptions& Options);
std::vector<lcPieceData*> GetRequiredPieceData() const;
std::vector<lcPieceDataRef> GetRequiredPieceData() const;
std::vector<lcPiece*> GetRequiredSynthPieces() const;
lcResult<void> EnsureAssetsReady() const;
lcResult<void> ExportCurrentStep(const QString& FileName);
+58 -21
View File
@@ -153,11 +153,16 @@ clearing stale details when none are supplied. Decoding and upload failures use
the same setter. It retains decoded pixels for bounded upload
retries; terminal request handling explicitly discards them when required.
`FindPieceData()` returns a borrowed identity, without acquiring a reference. Lookup
checks the supplied project's name index, then the shared library catalog, then
optionally an external file beside the project. Creating a missing identity
records `Failed`. `AcquireModelPieceData()` differs: it returns one acquired reference
for the model to release. Library membership is independent of readiness and
`FindPieceData(Name, Project)` returns a borrowed registered identity from
the supplied project's name index or the shared library catalog. It does no file
I/O, creates no identities, and acquires no reference.
`AcquirePieceData(Name, Project, CreateMissing, SearchProjectFolder)` resolves
or creates an identity and returns one scoped `lcPieceDataRef`, without ensuring mesh
readiness. It may load an external project beside the current project; creating a
missing identity records `Failed`. `lcPieceDataRef(Piece)` retains an
existing borrowed identity, and accepts null. `AcquireModelPieceData()` returns
one scoped reference for the model to adopt using `Release()`.
Library membership is independent of readiness and
Part/Model/Project type. Project-local names cannot replace another project's
names. Worker include resolution uses library sources, while model geometry and
texture search directories are snapshotted before dispatch.
@@ -328,8 +333,8 @@ sequenceDiagram
participant Library as Library
participant Loader as Loader / UI thread
participant Worker as Qt worker
Caller->>Library: AcquirePieceData(...) returns scoped consumer reference
Caller->>Library: RequestPieceDataLoad(PieceData, LoadPriority)
Library->>Library: Acquire consumer reference
Library->>Loader: Queue/promote
Loader->>Worker: Immutable source snapshot
Worker->>Worker: Parse/generate and convert, or read cached mesh
@@ -379,13 +384,44 @@ buffer upload is not part of asset readiness.
## References, cancellation, and generated geometry
`RequestPieceDataLoad()` acquires one reference even if loading fails. Always pair it
with `ReleasePieceData()`. Its asynchronous return value means the call did not
wait, not that the asset loaded. `AddPieceDataReference()` retains an identity without
starting work. `WaitForPieceData()` deduplicates its input without acquiring consumer
references; callers retain every required identity and its owner through the wait.
`lcPieceDataRef` and `lcTextureRef` alias the shared `lcAssetRef<T>` template in
`lc_assettypes.h`. Both are copyable scoped references.
Construction from a borrowed pointer and copying acquire one reference; moving
transfers the reference without retaining it. Assignment retains the new identity
before releasing the old one. Destruction or `Reset()` releases one reference
through its owning library; `Get()` borrows and `Release()` transfers ownership
to an owner responsible for explicitly releasing it. The library has a private
adoption path for references already acquired during model creation.
Use and destroy handles on the UI thread before the library unloads. Empty handles
are safe. `==` and `!=` compare pointer identity between handles or with a
borrowed pointer in either order, including null, without changing reference counts.
Use `Get()` when passing a borrowed pointer to an API.
A part request owns an additional in-flight reference. Removing one instance
`lcPiece::SetPieceData()` accepts an `lcPieceDataRef` by value, adopts that
reference, and releases the previous identity. Passing an lvalue copies and retains;
passing a moved handle transfers the reference. Its raw-pointer constructor is a
borrowing convenience that acquires its own reference. Placed pieces and models
store their definition handles directly, as do the current selection, minifig
parts, and thumbnail dependencies. Stored insertion previews use owning
`lcPreviewInsertPieceInfo` entries, while transient `lcInsertPieceInfo` queries
remain borrowed. Project-remapping owners also retain scoped references.
`AddPieceDataReference()` is the internal retain primitive used by the scoped reference.
`RequestPieceDataLoad()` starts or promotes work without acquiring a
consumer reference, and returns immediately for every priority, including Blocking.
Repeated requests add no consumer references. Callers retain the identity while
they need it. Use `WaitForPieceData()` or `EnsurePieceDataAssetsReady()`
for synchronous loading. Both wait APIs take handles by const reference without acquiring consumer
references. Callers retain every required identity and its owner throughout the
wait. `lcModel::GetRequiredPieceData()` and its project counterpart return
owning handle vectors, deduplicated by identity; releasing a result releases its
snapshot references.
A part request owns an additional in-flight `lcPieceDataRef`; synth notifications also
own a scoped definition reference. Request handles are explicitly reset at the
UI-thread completion, cancellation, and notification boundaries, before shared
request storage can be destroyed on a worker thread. Moves preserve the single
loader hold without introducing additional consumer counts. Removing one instance
cannot destroy an asset still used by another instance or pending work. On the
last consumer release, queued work is removed; running ordinary part work is
allowed to finish and its result is discarded if no consumer survives. Successful
@@ -438,10 +474,10 @@ Priorities only promote: `Low < Normal < Blocking`. Ordinary thumbnail
requests start Low; asynchronous model pieces and generated meshes start
Normal, regardless of step. Synchronous waits promote their required requests
and pending texture dependencies to Blocking. The same `lcLoadPriority` enum
is used at the loading entry point and throughout the scheduler. The library's `RequestPieceDataLoad()`
waits only for Blocking; Low and Normal return immediately. The loader's entry point
only queues or promotes requests; the library prepares readiness and starts unloaded
requirements before delegating waits. Internal queueing
is used at the loading entry point and throughout the scheduler. `RequestPieceDataLoad()`
only starts or promotes work and never waits, even with Blocking priority. Call
`WaitForPieceData()` or `EnsurePieceDataAssetsReady()` for synchronous loading.
The library prepares readiness and starts unloaded requirements before delegating waits. Internal queueing
uses priority solely to rank work and does not wait. Shared textures retain the highest
promotion received from any parent or direct wait. There is no consumer identity
map, priority demotion, or step-based scheduling. Priority lasts until that request
@@ -480,11 +516,12 @@ adds strict failure reporting. During shutdown, the loader's private
| Need | API and lifetime contract |
| --- | --- |
| Resolve a name | `FindPieceData()` with the intended project and explicit `CreateMissing` / `SearchProjectFolder` arguments. The returned pointer is borrowed; retain it before keeping it beyond its owner's lifetime. |
| Retain and start ordinary asynchronous work | `RequestPieceDataLoad()` with `lcLoadPriority::Low` for background work or `Normal` for model/view work. Pair every acquisition with `ReleasePieceData()`, including failures. |
| Retain and synchronously load one identity | `RequestPieceDataLoad()` with `lcLoadPriority::Blocking`; check its return value and pair the acquisition with `ReleasePieceData()`, including failures. |
| Retain without starting work | `AddPieceDataReference()`, paired with `ReleasePieceData()`. This does not make geometry ready. |
| Wait for an explicit part set | `WaitForPieceData()` (use `{ PieceData }` for one identity), check the boolean for strict consumers. Retain every identity and its owner throughout the wait; this call acquires no consumer references. |
| Find a registered identity | `FindPieceData(Name, Project)` returns a borrowed identity without I/O or creation; retain it before keeping it beyond its owner's lifetime. |
| Resolve or create a piece identity | `AcquirePieceData(Name, Project, CreateMissing, SearchProjectFolder)` returns a scoped reference without ensuring mesh readiness. |
| Start ordinary asynchronous work | Retain the identity, then call `RequestPieceDataLoad()` with `lcLoadPriority::Low` for background work or `Normal` for model/view work. Existing owners need no extra reference for another request. |
| Retain and synchronously load one identity | Keep an `lcPieceDataRef` from `AcquirePieceData()`, then call `WaitForPieceData(PieceDataRef)` and check its return value. |
| Retain without starting work | `lcPieceDataRef(Piece)` returns a scoped reference. This does not make geometry ready. |
| Wait for an explicit part set | `WaitForPieceData(PieceDataRefs)` (use `WaitForPieceData(PieceDataRef)` for one identity), check the boolean for strict consumers. Retain every identity and its owner throughout the wait; this call acquires no consumer references. |
| Wait for one identity and its container geometry | `EnsurePieceDataAssetsReady()` settles the identity, then delegates recursive children, synths, and bounds to the model/project API. Retain the identity and its owner throughout. |
| Wait for a model, including nested synths | `WaitForAssets()` for permissive output; `EnsureAssetsReady()` for strict preview-style failure reporting. Keep the model and its pieces alive throughout. |
| Replace/wait for generated geometry | `RequestSynthMesh()` / `WaitForSynthMeshes()`. Supply live `lcPiece` instances; a shared part mesh or a retained previous generated mesh is not readiness for the replacement. |
+1
View File
@@ -185,6 +185,7 @@ SOURCES += \
common/lc_application.cpp \
common/lc_arraydialog.cpp \
common/lc_assetloader.cpp \
common/lc_assettypes.cpp \
common/lc_blenderpreferences.cpp \
common/lc_bricklink.cpp \
common/lc_category.cpp \