Generate synth meshes asynchronously

Queue per-piece synth builds alongside mesh and texture work, with cooperative cancellation for obsolete requests. Keep the previous mesh visible during edits and account for generated meshes in blocking output and loading progress. Defer preview and thumbnail rendering until their required synth meshes settle.
This commit is contained in:
Leonardo Zide committed 2026-09-28 22:49:16 -07:00
1 parent bd6bd50ecc
commit b242cf9ad5
21 files changed
+1092 -131

No files matched your search

+426 -9
View File
@@ -5,7 +5,10 @@
#include "lc_library.h"
#include "lc_mesh.h"
#include "lc_model.h"
#include "lc_view.h"
#include "lc_texture.h"
#include "lc_synth.h"
#include "piece.h"
#include "pieceinf.h"
constexpr QEvent::Type lcAssetLoaderCompletionEvent = static_cast<QEvent::Type>(QEvent::User + 1);
@@ -52,11 +55,172 @@ void lcAssetLoader::QueuePiece(PieceInfo* Info, bool PriorityHint)
QueuePieceLocked(Info, PriorityHint ? Priority::Visible : Priority::Background);
}
void lcAssetLoader::CancelSynthMesh(lcPiece* Piece)
{
Q_ASSERT(QThread::currentThread() == thread());
mSynthPieces.erase(Piece);
mFailedSynthErrors.erase(Piece);
std::shared_ptr<SynthRequest> Removed;
{
QMutexLocker QueueLock(&mQueueMutex);
const SynthRequestMap::iterator It = mSynthRequests.find(Piece);
if (It == mSynthRequests.end())
return;
Removed = It->second;
Removed->Cancelled.store(true, std::memory_order_relaxed);
Removed->Terminal = true;
mSynthRequests.erase(It);
const auto EraseRequest = [&Removed](auto& Queue)
{
Queue.erase(std::remove(Queue.begin(), Queue.end(), Removed), Queue.end());
};
EraseRequest(mSynthQueue);
EraseRequest(mPausedSynthQueue);
mResultReady.wakeAll();
}
if (!Removed->Running)
{
Removed->StagedMesh.reset();
CancelUnusedTextureRequests();
}
}
void lcAssetLoader::QueueSynthMesh(lcPiece* Piece)
{
Q_ASSERT(QThread::currentThread() == thread());
CancelSynthMesh(Piece);
PieceInfo* Info = Piece->mPieceInfo;
const std::shared_ptr<const lcSynthInfo> SynthInfo = Info ? Info->GetSynthInfoShared() : nullptr;
if (!SynthInfo)
return;
std::shared_ptr<SynthRequest> RequestedSynth = std::make_shared<SynthRequest>();
RequestedSynth->Piece = Piece;
RequestedSynth->SynthInfo = SynthInfo;
RequestedSynth->ControlPoints = Piece->GetControlPoints();
RequestedSynth->LoadPriority = Priority::Visible;
RequestedSynth->EnqueuedAt = QDateTime::currentMSecsSinceEpoch();
RequestedSynth->Generation = mGeneration;
QMutexLocker QueueLock(&mQueueMutex);
if (mStopping)
return;
mSynthPieces.insert(Piece);
mSynthRequests.emplace(Piece, RequestedSynth);
if (mPaused)
mPausedSynthQueue.push_back(RequestedSynth);
else
{
mSynthQueue.push_back(RequestedSynth);
StartWorkersLocked();
}
QueueLock.unlock();
emit mLibrary->AssetRequestsChanged();
}
bool lcAssetLoader::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
{
Q_ASSERT(QThread::currentThread() == thread());
std::vector<std::shared_ptr<SynthRequest>> Required;
{
QMutexLocker QueueLock(&mQueueMutex);
for (lcPiece* Piece : Pieces)
{
const SynthRequestMap::iterator It = mSynthRequests.find(Piece);
if (It == mSynthRequests.end())
continue;
const std::shared_ptr<SynthRequest>& RequestedSynth = It->second;
RequestedSynth->LoadPriority = Priority::Blocking;
Required.push_back(RequestedSynth);
if (RequestedSynth->StagedMesh)
for (const lcMeshLod& Lod : RequestedSynth->StagedMesh->mLods)
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
{
const TextureRequestMap::iterator TextureIt = mTextureRequests.find(Lod.Sections[SectionIdx].Texture);
if (TextureIt != mTextureRequests.end())
TextureIt->second->LoadPriority = Priority::Blocking;
}
}
StartWorkersLocked();
}
QOpenGLContext* const PreviousContext = QOpenGLContext::currentContext();
QSurface* const PreviousSurface = PreviousContext ? PreviousContext->surface() : nullptr;
for (const std::shared_ptr<SynthRequest>& RequestedSynth : Required)
{
while (!RequestedSynth->Terminal)
{
ProcessCompletions();
if (RequestedSynth->Terminal)
break;
QMutexLocker QueueLock(&mQueueMutex);
if (mStopping || RequestedSynth->Terminal)
break;
WaitForResultsLocked();
}
}
if (PreviousContext && PreviousSurface && (QOpenGLContext::currentContext() != PreviousContext || PreviousContext->surface() != PreviousSurface))
PreviousContext->makeCurrent(PreviousSurface);
for (lcPiece* Piece : Pieces)
if (Piece->mPieceInfo && Piece->mPieceInfo->GetSynthInfo() && !Piece->HasGeneratedMesh())
return false;
return true;
}
QString lcAssetLoader::GetSynthMeshError(const lcPiece* Piece) const
{
const std::unordered_map<const lcPiece*, QString>::const_iterator It = mFailedSynthErrors.find(Piece);
return It != mFailedSynthErrors.end() ? It->second : QString();
}
void lcAssetLoader::ReloadSynthMeshes()
{
Q_ASSERT(QThread::currentThread() == thread());
const std::vector<lcPiece*> Pieces(mSynthPieces.begin(), mSynthPieces.end());
for (lcPiece* Piece : Pieces)
Piece->RefreshGeneratedMesh();
}
void lcAssetLoader::SetPieceRequestsVisible(const std::vector<PieceInfo*>& Parts, bool Visible)
{
Q_ASSERT(QThread::currentThread() == thread());
const Priority LoadPriority = Visible ? Priority::Visible : Priority::Background;
const std::unordered_set<PieceInfo*> Included(Parts.begin(), Parts.end());
mQueueMutex.lock();
@@ -68,6 +232,26 @@ void lcAssetLoader::SetPieceRequestsVisible(const std::vector<PieceInfo*>& Parts
SetRequestPriorityLocked(It->second, LoadPriority);
}
for (const SynthRequestMap::value_type& Entry : mSynthRequests)
{
const std::shared_ptr<SynthRequest>& RequestedSynth = Entry.second;
if (Included.find(RequestedSynth->Piece->mPieceInfo) == Included.end() || RequestedSynth->LoadPriority == Priority::Blocking)
continue;
RequestedSynth->LoadPriority = LoadPriority;
if (RequestedSynth->StagedMesh)
for (const lcMeshLod& Lod : RequestedSynth->StagedMesh->mLods)
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
{
const TextureRequestMap::iterator TextureIt = mTextureRequests.find(Lod.Sections[SectionIdx].Texture);
if (TextureIt != mTextureRequests.end() && TextureIt->second->LoadPriority != Priority::Blocking)
TextureIt->second->LoadPriority = LoadPriority;
}
}
mQueueMutex.unlock();
}
@@ -202,7 +386,7 @@ void lcAssetLoader::StartWorkersLocked()
}), mFutures.end());
const int MaxWorkers = qMax(1, qMin(4, QThread::idealThreadCount()));
const int Queued = static_cast<int>(mQueue.size() + mTextureQueue.size());
const int Queued = static_cast<int>(mQueue.size() + mTextureQueue.size() + mSynthQueue.size());
const int Additional = mPaused || mStopping ? 0 : qMin(MaxWorkers - mActiveWorkers, Queued);
for (int Worker = 0; Worker < Additional; Worker++)
@@ -218,11 +402,12 @@ void lcAssetLoader::LoadQueuedPieces()
{
std::shared_ptr<Request> RequestedPart;
std::shared_ptr<TextureRequest> RequestedTexture;
std::shared_ptr<SynthRequest> RequestedSynth;
{
QMutexLocker QueueLock(&mQueueMutex);
if (mStopping || mPaused || (mQueue.empty() && mTextureQueue.empty()))
if (mStopping || mPaused || (mQueue.empty() && mTextureQueue.empty() && mSynthQueue.empty()))
{
mActiveWorkers--;
mResultReady.wakeAll();
@@ -246,14 +431,22 @@ void lcAssetLoader::LoadQueuedPieces()
auto PartBest = std::max_element(mQueue.begin(), mQueue.end(), Less);
auto TextureBest = std::max_element(mTextureQueue.begin(), mTextureQueue.end(), Less);
auto SynthBest = std::max_element(mSynthQueue.begin(), mSynthQueue.end(), Less);
if (TextureBest != mTextureQueue.end() && (PartBest == mQueue.end() || !Less(*TextureBest, *PartBest)))
if (TextureBest != mTextureQueue.end() && (PartBest == mQueue.end() || !Less(*TextureBest, *PartBest)) &&
(SynthBest == mSynthQueue.end() || !Less(*TextureBest, *SynthBest)))
{
RequestedTexture = *TextureBest;
mTextureQueue.erase(TextureBest);
RequestedTexture->Running = true;
mStartedTextures.push_back(RequestedTexture);
}
else if (SynthBest != mSynthQueue.end() && (PartBest == mQueue.end() || !Less(*SynthBest, *PartBest)))
{
RequestedSynth = *SynthBest;
mSynthQueue.erase(SynthBest);
RequestedSynth->Running = true;
}
else
{
RequestedPart = *PartBest;
@@ -263,7 +456,7 @@ void lcAssetLoader::LoadQueuedPieces()
}
}
if (RequestedPart || RequestedTexture)
if (RequestedPart || RequestedTexture || RequestedSynth)
QCoreApplication::postEvent(this, new QEvent(lcAssetLoaderCompletionEvent));
Completion Completed;
@@ -274,7 +467,7 @@ void lcAssetLoader::LoadQueuedPieces()
if (RequestedPart->ConvertingMesh)
{
Completed.Result.Mesh.reset(RequestedPart->MeshData->CreateMeshResolved(RequestedPart->ColorTranslucency, RequestedPart->DefaultColorIndex));
Completed.Result.Mesh.reset(RequestedPart->MeshData->CreateMeshResolved(RequestedPart->ColorTranslucency, RequestedPart->DefaultColorIndex, nullptr));
RequestedPart->MeshData.reset();
if (!Completed.Result.Mesh)
@@ -296,11 +489,25 @@ void lcAssetLoader::LoadQueuedPieces()
Completed.Result.RequestId = RequestedPart->Id;
Completed.Result.Generation = RequestedPart->Generation;
}
else
else if (RequestedTexture)
{
Completed.RequestedTexture = RequestedTexture;
Completed.TextureResult = mLibrary->BuildTextureData(RequestedTexture->Source);
}
else
{
Completed.RequestedSynth = RequestedSynth;
if (RequestedSynth->Cancelled.load(std::memory_order_relaxed))
RequestedSynth->MeshData.reset();
else if (RequestedSynth->ConvertingMesh)
{
Completed.SynthMesh.reset(RequestedSynth->MeshData->CreateMeshResolved(RequestedSynth->ColorTranslucency, RequestedSynth->DefaultColorIndex, &RequestedSynth->Cancelled));
RequestedSynth->MeshData.reset();
}
else
Completed.SynthMeshData = RequestedSynth->SynthInfo->BuildMeshData(RequestedSynth->ControlPoints, RequestedSynth->Cancelled);
}
{
QMutexLocker QueueLock(&mQueueMutex);
@@ -503,6 +710,87 @@ void lcAssetLoader::CheckWaitingParts()
}
}
void lcAssetLoader::FinishSynth(const std::shared_ptr<SynthRequest>& RequestedSynth)
{
lcPiece* Piece = RequestedSynth->Piece;
PieceInfo* Info = Piece->mPieceInfo;
const bool Loaded = RequestedSynth->Error.isEmpty() && RequestedSynth->StagedMesh;
Piece->SetGeneratedMesh(Loaded ? RequestedSynth->StagedMesh.release() : nullptr);
if (Loaded)
mFailedSynthErrors.erase(Piece);
else
mFailedSynthErrors.insert_or_assign(Piece, RequestedSynth->Error);
if (!Loaded)
{
RequestedSynth->StagedMesh.reset();
CancelUnusedTextureRequests();
}
mQueueMutex.lock();
const SynthRequestMap::iterator It = mSynthRequests.find(Piece);
if (It != mSynthRequests.end() && It->second == RequestedSynth)
mSynthRequests.erase(It);
RequestedSynth->Terminal = true;
mResultReady.wakeAll();
mQueueMutex.unlock();
lcView::UpdateAllViews();
QPointer<lcPiecesLibrary> Library(mLibrary);
Library->NotifyConsumersChanged();
if (Library)
emit Library->GeneratedMeshSettled(Info);
}
void lcAssetLoader::CheckWaitingSynths()
{
std::vector<std::shared_ptr<SynthRequest>> Waiting;
{
QMutexLocker QueueLock(&mQueueMutex);
for (const SynthRequestMap::value_type& Entry : mSynthRequests)
if (Entry.second->StagedMesh)
Waiting.push_back(Entry.second);
}
for (const std::shared_ptr<SynthRequest>& RequestedSynth : Waiting)
{
mQueueMutex.lock();
const SynthRequestMap::iterator It = mSynthRequests.find(RequestedSynth->Piece);
const bool Current = It != mSynthRequests.end() && It->second == RequestedSynth;
mQueueMutex.unlock();
if (!Current)
continue;
bool Ready = true;
for (const lcMeshLod& Lod : RequestedSynth->StagedMesh->mLods)
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
{
lcTexture* Texture = Lod.Sections[SectionIdx].Texture;
if (!Texture)
continue;
if (Texture->mState == lcTextureState::Failed)
RequestedSynth->Error = TextureFailureMessage(Texture);
else if (!Texture->IsReady())
Ready = false;
}
if (!RequestedSynth->Error.isEmpty() || Ready)
FinishSynth(RequestedSynth);
}
}
void lcAssetLoader::FinishTextureRequest(const std::shared_ptr<TextureRequest>& RequestedTexture)
{
mQueueMutex.lock();
@@ -515,6 +803,7 @@ void lcAssetLoader::FinishTextureRequest(const std::shared_ptr<TextureRequest>&
mLibrary->ReleaseTexture(RequestedTexture->Texture);
CheckWaitingParts();
CheckWaitingSynths();
}
void lcAssetLoader::UploadTextureRequest(const std::shared_ptr<TextureRequest>& RequestedTexture)
@@ -643,6 +932,7 @@ bool lcAssetLoader::ProcessCompletions()
if (!mStopping && It != mTextureRequests.end() && It->second == RequestedTexture && RequestedTexture->Texture->mState == lcTextureState::Queued)
RequestedTexture->Texture->mState = lcTextureState::Decoding;
}
}
constexpr int MaxCompletionsPerPump = 16;
@@ -669,7 +959,10 @@ bool lcAssetLoader::ProcessCompletions()
const auto PriorityOf = [](const Completion& Item)
{
return Item.RequestedPart ? Item.RequestedPart->LoadPriority : Item.RequestedTexture->LoadPriority;
if (Item.RequestedPart)
return Item.RequestedPart->LoadPriority;
return Item.RequestedTexture ? Item.RequestedTexture->LoadPriority : Item.RequestedSynth->LoadPriority;
};
const auto Best = std::max_element(mCompletions.begin(), mCompletions.end(), [&PriorityOf](const Completion& Left, const Completion& Right)
@@ -735,6 +1028,93 @@ bool lcAssetLoader::ProcessCompletions()
continue;
}
if (Completed.RequestedSynth)
{
const std::shared_ptr<SynthRequest>& RequestedSynth = Completed.RequestedSynth;
bool Stale;
mQueueMutex.lock();
const SynthRequestMap::iterator It = mSynthRequests.find(RequestedSynth->Piece);
Stale = mStopping || RequestedSynth->Cancelled.load(std::memory_order_relaxed) || RequestedSynth->Generation != mGeneration ||
It == mSynthRequests.end() || It->second != RequestedSynth;
if (Stale && It != mSynthRequests.end() && It->second == RequestedSynth)
mSynthRequests.erase(It);
RequestedSynth->Running = false;
mResultReady.wakeAll();
mQueueMutex.unlock();
if (Stale)
{
RequestedSynth->Terminal = true;
continue;
}
if (Completed.SynthMeshData)
{
Completed.SynthMeshData->ResolveColors();
RequestedSynth->ColorTranslucency.clear();
RequestedSynth->ColorTranslucency.reserve(gColorList.size());
for (const lcColor& Color : gColorList)
RequestedSynth->ColorTranslucency.push_back(Color.Translucent);
RequestedSynth->DefaultColorIndex = gDefaultColor;
RequestedSynth->MeshData = std::move(Completed.SynthMeshData);
RequestedSynth->ConvertingMesh = true;
QMutexLocker QueueLock(&mQueueMutex);
if (mPaused)
mPausedSynthQueue.push_back(RequestedSynth);
else
{
mSynthQueue.push_back(RequestedSynth);
StartWorkersLocked();
}
continue;
}
if (!Completed.SynthMesh)
{
RequestedSynth->Error = tr("Could not generate geometry for piece '%1'.").arg(QString::fromLatin1(RequestedSynth->Piece->mPieceInfo->mFileName));
FinishSynth(RequestedSynth);
continue;
}
RequestedSynth->ConvertingMesh = false;
RequestedSynth->ColorTranslucency.clear();
RequestedSynth->StagedMesh = std::move(Completed.SynthMesh);
bool Ready = true;
for (lcMeshLod& Lod : RequestedSynth->StagedMesh->mLods)
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
{
lcMeshSection& Section = Lod.Sections[SectionIdx];
if (Section.TextureName.isEmpty())
continue;
Section.Texture = mLibrary->FindTextureDeferred(Section.TextureName.toLatin1().constData(), {});
if (!Section.Texture)
RequestedSynth->Error = tr("Missing texture %1.").arg(Section.TextureName);
else if (Section.Texture->mState == lcTextureState::Failed)
RequestedSynth->Error = TextureFailureMessage(Section.Texture);
else if (!Section.Texture->IsReady())
{
Ready = false;
QueueTexture(Section.Texture, RequestedSynth->LoadPriority);
}
}
if (!RequestedSynth->Error.isEmpty() || Ready)
FinishSynth(RequestedSynth);
continue;
}
const std::shared_ptr<Request>& RequestedPart = Completed.RequestedPart;
PieceInfo* Info = RequestedPart->Info;
bool Stale;
@@ -1190,7 +1570,7 @@ void lcAssetLoader::WaitForLoadQueue()
QMutexLocker QueueLock(&mQueueMutex);
if (mRequests.empty() && mTextureRequests.empty() && mActiveWorkers == 0 && mCompletions.empty())
if (mRequests.empty() && mTextureRequests.empty() && mSynthRequests.empty() && mActiveWorkers == 0 && mCompletions.empty())
break;
WaitForResultsLocked();
@@ -1205,7 +1585,7 @@ void lcAssetLoader::WaitForLoadQueue()
bool lcAssetLoader::HasPendingWork()
{
mQueueMutex.lock();
const bool Pending = !mRequests.empty() || !mTextureRequests.empty() || !mCompletions.empty();
const bool Pending = !mRequests.empty() || !mTextureRequests.empty() || !mSynthRequests.empty() || !mCompletions.empty();
mQueueMutex.unlock();
return Pending;
@@ -1227,6 +1607,10 @@ void lcAssetLoader::CancelAndDrain()
mPausedQueue.clear();
mTextureQueue.clear();
mPausedTextureQueue.clear();
mSynthQueue.clear();
mPausedSynthQueue.clear();
mSynthPieces.clear();
mFailedSynthErrors.clear();
for (auto It = mRequests.begin(); It != mRequests.end();)
{
@@ -1259,6 +1643,20 @@ void lcAssetLoader::CancelAndDrain()
}
}
for (SynthRequestMap::iterator It = mSynthRequests.begin(); It != mSynthRequests.end();)
{
if (It->second->Running)
{
It->second->Cancelled.store(true, std::memory_order_relaxed);
++It;
}
else
{
It->second->Terminal = true;
It = mSynthRequests.erase(It);
}
}
mResultReady.wakeAll();
}
@@ -1288,6 +1686,7 @@ void lcAssetLoader::PauseQueuedWork()
mPaused = true;
mPausedQueue.swap(mQueue);
mPausedTextureQueue.swap(mTextureQueue);
mPausedSynthQueue.swap(mSynthQueue);
mQueueMutex.unlock();
for (;;)
@@ -1324,6 +1723,23 @@ void lcAssetLoader::PauseQueuedWork()
RequestedPart->Running = false;
mPausedQueue.push_back(RequestedPart);
}
// Pending generated meshes are restarted after the new stud style is installed.
for (const SynthRequestMap::value_type& Entry : mSynthRequests)
{
const std::shared_ptr<SynthRequest>& RequestedSynth = Entry.second;
if (!RequestedSynth->StagedMesh && !RequestedSynth->ConvertingMesh)
continue;
RequestedSynth->MeshData.reset();
RequestedSynth->StagedMesh.reset();
RequestedSynth->ColorTranslucency.clear();
RequestedSynth->ConvertingMesh = false;
if (std::find(mPausedSynthQueue.begin(), mPausedSynthQueue.end(), RequestedSynth) == mPausedSynthQueue.end())
mPausedSynthQueue.push_back(RequestedSynth);
}
}
void lcAssetLoader::ResumeQueuedWork()
@@ -1344,6 +1760,7 @@ void lcAssetLoader::ResumeQueuedWork()
mQueue.swap(mPausedQueue);
mTextureQueue.swap(mPausedTextureQueue);
mSynthQueue.swap(mPausedSynthQueue);
StartWorkersLocked();
}
+38
View File
@@ -1,9 +1,12 @@
#pragma once
#include "lc_library.h"
#include "piece.h"
class PieceInfo;
class lcPiece;
class lcMesh;
class lcSynthInfo;
class lcMemFile;
class lcLibraryMeshData;
class Image;
@@ -74,6 +77,11 @@ public:
void QueuePiece(PieceInfo* Info, bool Priority);
bool EnsurePieceReady(PieceInfo* Info);
bool EnsurePiecesReady(const std::vector<PieceInfo*>& Parts);
void QueueSynthMesh(lcPiece* Piece);
void CancelSynthMesh(lcPiece* Piece);
bool EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces);
QString GetSynthMeshError(const lcPiece* Piece) const;
void ReloadSynthMeshes();
void QueueModelPiece(PieceInfo* Info);
void InvalidatePiece(PieceInfo* Info);
void SetPieceRequestsVisible(const std::vector<PieceInfo*>& Parts, bool Visible);
@@ -127,8 +135,28 @@ private:
qint64 RetryAt = 0;
};
struct SynthRequest
{
lcPiece* Piece;
std::shared_ptr<const lcSynthInfo> SynthInfo;
std::vector<lcPieceControlPoint> ControlPoints;
Priority LoadPriority;
qint64 EnqueuedAt;
quint64 Generation;
bool Running = false;
std::atomic_bool Cancelled = false;
bool Terminal = false;
bool ConvertingMesh = false;
std::unique_ptr<lcLibraryMeshData> MeshData;
std::vector<bool> ColorTranslucency;
int DefaultColorIndex = 0;
std::unique_ptr<lcMesh> StagedMesh;
QString Error;
};
using PartRequestMap = std::map<PieceInfo*, std::shared_ptr<Request>>;
using TextureRequestMap = std::map<lcTexture*, std::shared_ptr<TextureRequest>>;
using SynthRequestMap = std::map<lcPiece*, std::shared_ptr<SynthRequest>>;
struct Completion
{
@@ -136,6 +164,9 @@ private:
lcPartBuildResult Result;
std::shared_ptr<TextureRequest> RequestedTexture;
lcTextureBuildResult TextureResult;
std::shared_ptr<SynthRequest> RequestedSynth;
std::unique_ptr<lcLibraryMeshData> SynthMeshData;
std::unique_ptr<lcMesh> SynthMesh;
};
struct Notification
@@ -156,6 +187,8 @@ private:
void DispatchNotifications();
void FinishPart(const std::shared_ptr<Request>& RequestedPart);
void CheckWaitingParts();
void CheckWaitingSynths();
void FinishSynth(const std::shared_ptr<SynthRequest>& RequestedSynth);
void UploadTextureRequest(const std::shared_ptr<TextureRequest>& RequestedTexture);
void FinishTextureRequest(const std::shared_ptr<TextureRequest>& RequestedTexture);
void WaitForResultsLocked();
@@ -168,10 +201,15 @@ private:
QMutex mDrainMutex;
PartRequestMap mRequests;
TextureRequestMap mTextureRequests;
SynthRequestMap mSynthRequests;
std::unordered_map<const lcPiece*, QString> mFailedSynthErrors;
std::unordered_set<lcPiece*> mSynthPieces;
std::deque<std::shared_ptr<Request>> mQueue;
std::deque<std::shared_ptr<Request>> mPausedQueue;
std::deque<std::shared_ptr<TextureRequest>> mTextureQueue;
std::deque<std::shared_ptr<TextureRequest>> mPausedTextureQueue;
std::deque<std::shared_ptr<SynthRequest>> mSynthQueue;
std::deque<std::shared_ptr<SynthRequest>> mPausedSynthQueue;
std::deque<Completion> mCompletions;
std::deque<std::shared_ptr<Request>> mStarted;
std::deque<std::shared_ptr<TextureRequest>> mStartedTextures;
+22
View File
@@ -1622,6 +1622,27 @@ bool lcPiecesLibrary::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
return mAssetLoader->EnsurePiecesReady(Parts);
}
void lcPiecesLibrary::QueueSynthMesh(lcPiece* Piece)
{
mAssetLoader->QueueSynthMesh(Piece);
}
void lcPiecesLibrary::CancelSynthMesh(lcPiece* Piece)
{
if (mAssetLoader)
mAssetLoader->CancelSynthMesh(Piece);
}
bool lcPiecesLibrary::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
{
return mAssetLoader->EnsureSynthMeshesReady(Pieces);
}
QString lcPiecesLibrary::GetSynthMeshError(const lcPiece* Piece) const
{
return mAssetLoader ? mAssetLoader->GetSynthMeshError(Piece) : QString();
}
void lcPiecesLibrary::QueueModelPiece(PieceInfo* Info)
{
mAssetLoader->QueueModelPiece(Info);
@@ -2281,6 +2302,7 @@ void lcPiecesLibrary::SetStudStyle(lcStudStyle StudStyle, bool Reload, bool Stud
ReloadPiece(Local.first);
mLoadMutex.unlock();
mAssetLoader->ReloadSynthMeshes();
}
mAssetLoader->ResumeQueuedWork();
+6
View File
@@ -5,6 +5,7 @@
#include "lc_meshloader.h"
class PieceInfo;
class lcPiece;
class lcMesh;
class lcTrainTrackInfo;
struct lcTrainTrackConnectionType;
@@ -182,6 +183,10 @@ public:
void NotifyConsumersChanged();
bool EnsurePieceReady(PieceInfo* Info);
bool EnsurePiecesReady(const std::vector<PieceInfo*>& Parts);
void QueueSynthMesh(lcPiece* Piece);
void CancelSynthMesh(lcPiece* Piece);
bool EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces);
QString GetSynthMeshError(const lcPiece* Piece) const;
void QueueModelPiece(PieceInfo* Info);
void InvalidatePiece(PieceInfo* Info);
void SetPieceRequestsVisible(const std::vector<PieceInfo*>& Parts, bool Visible);
@@ -262,6 +267,7 @@ signals:
void AssetRequestsChanged();
void PartLoaded(PieceInfo* Info);
void PartLoadFailed(PieceInfo* Info, const QString& Error);
void GeneratedMeshSettled(PieceInfo* Info);
void ColorsLoaded();
protected:
+22 -2
View File
@@ -1009,6 +1009,7 @@ void lcMainWindow::UpdateAssets()
lcView::UpdateProjectViews(Project);
const std::vector<PieceInfo*> Required = Project->GetRequiredPieces();
const std::vector<lcPiece*> GeneratedPieces = Project->GetRequiredSynthPieces();
mStatusRequiredAssets.clear();
mStatusRequiredAssets.insert(Required.begin(), Required.end());
@@ -1037,8 +1038,27 @@ void lcMainWindow::UpdateAssets()
Pending++;
}
mStatusLoadProgress->setRange(0, static_cast<int>(Required.size()));
mStatusLoadProgress->setValue(static_cast<int>(Required.size()) - Pending);
for (const lcPiece* Piece : GeneratedPieces)
{
if (Piece->IsGeneratedMeshPending())
Pending++;
else if (!Piece->HasGeneratedMesh())
{
Failed++;
if (FailureDetails.isEmpty())
{
FailureDetails = lcGetPiecesLibrary()->GetSynthMeshError(Piece);
if (FailureDetails.isEmpty())
FailureDetails = tr("Could not generate geometry for piece '%1'.").arg(QString::fromLatin1(Piece->mPieceInfo->mFileName));
}
}
}
const int Total = static_cast<int>(Required.size() + GeneratedPieces.size());
mStatusLoadProgress->setRange(0, Total);
mStatusLoadProgress->setValue(Total - Pending);
if (Failed)
{
+101 -14
View File
@@ -831,7 +831,7 @@ lcMesh* lcLibraryMeshData::CreateMesh(const std::function<lcTexture*(const char*
for (const lcColor& Color : gColorList)
ColorTranslucency.push_back(Color.Translucent);
return CreateMeshInternal(TextureLookup, ColorTranslucency, gDefaultColor);
return CreateMeshInternal(TextureLookup, ColorTranslucency, gDefaultColor, nullptr);
}
void lcLibraryMeshData::ResolveColors()
@@ -840,15 +840,18 @@ void lcLibraryMeshData::ResolveColors()
Material->Color = lcGetColorIndex(Material->Color);
}
lcMesh* lcLibraryMeshData::CreateMeshResolved(const std::vector<bool>& ColorTranslucency, int DefaultColorIndex)
lcMesh* lcLibraryMeshData::CreateMeshResolved(const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled)
{
// Keep project texture lookup on the UI thread after conversion.
const std::function<lcTexture*(const char*)> DeferTexture = [](const char*) -> lcTexture* { return nullptr; };
return CreateMeshInternal(DeferTexture, ColorTranslucency, DefaultColorIndex);
return CreateMeshInternal(DeferTexture, ColorTranslucency, DefaultColorIndex, Cancelled);
}
lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex)
lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return nullptr;
lcMesh* Mesh = new lcMesh();
int BaseVertices[LC_NUM_MESHDATA_TYPES];
@@ -911,6 +914,12 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
});
}
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
Mesh->Create(NumSections, NumVertices, static_cast<int>(mTexturedVertices.size()), ConditionalVertexCount, NumIndices);
if ((Mesh->mVertexDataSize && !Mesh->mVertexData) || (Mesh->mIndexDataSize && !Mesh->mIndexData))
@@ -923,8 +932,22 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
for (const lcMeshLoaderTypeData& Data : mData)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
size_t VertexIndex = 0;
for (const lcMeshLoaderVertex& SrcVertex : Data.mVertices)
{
if ((VertexIndex++ & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
lcVertex& DstVertex = *DstVerts++;
DstVertex.Position = lcVector3LDrawToLeoCAD(SrcVertex.Position);
@@ -936,8 +959,16 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
if (mHasTextures)
{
size_t VertexIndex = 0;
for (const lcMeshLoaderTexturedVertex& SrcVertex : mTexturedVertices)
{
if ((VertexIndex++ & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
lcVertexTextured& DstVertex = *DstTexturedVerts++;
DstVertex.Position = lcVector3LDrawToLeoCAD(SrcVertex.Position);
@@ -949,9 +980,9 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
bool SectionsWritten;
if (Mesh->mIndexType == GL_UNSIGNED_SHORT)
SectionsWritten = WriteSections<quint16>(Mesh, FinalSections, BaseVertices, TextureLookup, ColorTranslucency, DefaultColorIndex);
SectionsWritten = WriteSections<quint16>(Mesh, FinalSections, BaseVertices, TextureLookup, ColorTranslucency, DefaultColorIndex, Cancelled);
else
SectionsWritten = WriteSections<quint32>(Mesh, FinalSections, BaseVertices, TextureLookup, ColorTranslucency, DefaultColorIndex);
SectionsWritten = WriteSections<quint32>(Mesh, FinalSections, BaseVertices, TextureLookup, ColorTranslucency, DefaultColorIndex, Cancelled);
if (!SectionsWritten)
{
@@ -959,6 +990,12 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
return nullptr;
}
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
// Pack each color/material group as high, shared, low. Either LOD then
// occupies one contiguous range without duplicating the shared vertices.
std::vector<lcMeshLoaderFinalSection> ConditionalSections;
@@ -980,6 +1017,11 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
int ConditionalOffset = 0;
for (const lcMeshLoaderFinalSection& FinalSection : ConditionalSections)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
const auto AppendVertices = [this, &FinalSection, &ConditionalOffset, ConditionalVertices](lcMeshDataType DataType)
{
const lcMeshLoaderTypeData& Data = mData[DataType];
@@ -1023,13 +1065,23 @@ lcMesh* lcLibraryMeshData::CreateMeshInternal(const std::function<lcTexture*(con
if (mHasStyleStud)
Mesh->mFlags |= lcMeshFlag::HasStyleStud;
UpdateMeshBoundingBox(Mesh);
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
{
delete Mesh;
return nullptr;
}
if (!UpdateMeshBoundingBox(Mesh, Cancelled))
{
delete Mesh;
return nullptr;
}
return Mesh;
}
template<typename IndexType>
bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoaderFinalSection> (&FinalSections)[LC_NUM_MESH_LODS], int(&BaseVertices)[LC_NUM_MESHDATA_TYPES], const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex)
bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoaderFinalSection> (&FinalSections)[LC_NUM_MESH_LODS], int(&BaseVertices)[LC_NUM_MESHDATA_TYPES], const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled)
{
int NumIndices = 0;
@@ -1037,6 +1089,9 @@ bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoad
{
for (size_t SectionIndex = 0; SectionIndex < FinalSections[LodIdx].size(); SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
const lcMeshLoaderFinalSection& FinalSection = FinalSections[LodIdx][SectionIndex];
lcMeshSection& DstSection = Mesh->mLods[LodIdx].Sections[SectionIndex];
@@ -1061,7 +1116,7 @@ bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoad
IndexType* Index = Mesh->mIndexDataSize ? (IndexType*)Mesh->mIndexData + NumIndices : nullptr;
const auto AddSection = [&DstSection, &Index, &BaseVertices](lcMeshLoaderSection* SrcSection, lcMeshDataType SrcDataType)
const auto AddSection = [&DstSection, &Index, &BaseVertices, Cancelled](lcMeshLoaderSection* SrcSection, lcMeshDataType SrcDataType)
{
if (!Index && DstSection.PrimitiveType != LC_MESH_CONDITIONAL_LINES)
return SrcSection->mIndices.empty();
@@ -1074,7 +1129,12 @@ bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoad
const IndexType BaseVertex = BaseVertices[SrcDataType];
for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx++)
{
if ((IndexIdx & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
*Index++ = BaseVertex + SrcSection->mIndices[IndexIdx];
}
}
break;
@@ -1084,7 +1144,12 @@ bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoad
case LC_MESH_TEXTURED_TRIANGLES:
{
for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx++)
{
if ((IndexIdx & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
*Index++ = SrcSection->mIndices[IndexIdx];
}
}
break;
@@ -1134,7 +1199,7 @@ bool lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoad
return true;
}
void lcLibraryMeshData::UpdateMeshBoundingBox(lcMesh* Mesh)
bool lcLibraryMeshData::UpdateMeshBoundingBox(lcMesh* Mesh, const std::atomic_bool* Cancelled)
{
lcVector3 MeshMin(FLT_MAX, FLT_MAX, FLT_MAX), MeshMax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
bool UpdatedBoundingBox = false;
@@ -1145,13 +1210,22 @@ void lcLibraryMeshData::UpdateMeshBoundingBox(lcMesh* Mesh)
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
lcMeshSection& Section = Lod.Sections[SectionIdx];
lcVector3 SectionMin(FLT_MAX, FLT_MAX, FLT_MAX), SectionMax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
if (Mesh->mIndexType == GL_UNSIGNED_SHORT)
UpdateMeshSectionBoundingBox<quint16>(Mesh, Section, SectionMin, SectionMax);
{
if (!UpdateMeshSectionBoundingBox<quint16>(Mesh, Section, SectionMin, SectionMax, Cancelled))
return false;
}
else
UpdateMeshSectionBoundingBox<quint32>(Mesh, Section, SectionMin, SectionMax);
{
if (!UpdateMeshSectionBoundingBox<quint32>(Mesh, Section, SectionMin, SectionMax, Cancelled))
return false;
}
Section.BoundingBox.Max = SectionMax;
Section.BoundingBox.Min = SectionMin;
@@ -1172,10 +1246,12 @@ void lcLibraryMeshData::UpdateMeshBoundingBox(lcMesh* Mesh)
Mesh->mBoundingBox.Max = MeshMax;
Mesh->mBoundingBox.Min = MeshMin;
Mesh->mRadius = lcLength((MeshMax - MeshMin) / 2.0f);
return true;
}
template<typename IndexType>
void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const lcMeshSection& Section, lcVector3& SectionMin, lcVector3& SectionMax)
bool lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const lcMeshSection& Section, lcVector3& SectionMin, lcVector3& SectionMax, const std::atomic_bool* Cancelled)
{
switch (Section.PrimitiveType)
{
@@ -1187,6 +1263,9 @@ void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const l
for (int Index = 0; Index < Section.DrawCount; Index++)
{
if ((Index & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
const lcVector3& Position = VertexBuffer[IndexBuffer[Index]].Position;
SectionMin = lcMin(SectionMin, Position);
SectionMax = lcMax(SectionMax, Position);
@@ -1200,6 +1279,9 @@ void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const l
for (int Index = 0; Index < Section.DrawCount; Index++)
{
if ((Index & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
const lcVector3& Position = VertexBuffer[Section.DrawOffset + Index].Position1;
SectionMin = lcMin(SectionMin, Position);
SectionMax = lcMax(SectionMax, Position);
@@ -1214,6 +1296,9 @@ void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const l
for (int Index = 0; Index < Section.DrawCount; Index++)
{
if ((Index & 1023) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return false;
const lcVector3& Position = VertexBuffer[IndexBuffer[Index]].Position;
SectionMin = lcMin(SectionMin, Position);
SectionMax = lcMax(SectionMax, Position);
@@ -1224,6 +1309,8 @@ void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const l
case LC_MESH_NUM_PRIMITIVE_TYPES:
break;
}
return true;
}
lcMeshLoader::lcMeshLoader(lcLibraryMeshData& MeshData, Project* CurrentProject, lcMeshLoaderFlags Flags)
@@ -1251,7 +1338,7 @@ bool lcMeshLoader::ReadMeshData(lcFile& File, const lcMatrix44& CurrentTransform
while (File.ReadLine(Buffer, sizeof(Buffer)))
{
if (Library->ShouldCancelLoading())
if (Library->ShouldCancelLoading() || (mCancelled && mCancelled->load(std::memory_order_relaxed)))
return false;
bool LastToken = false;
+7 -5
View File
@@ -167,7 +167,7 @@ public:
lcMesh* CreateMesh(const std::function<lcTexture*(const char*)>& TextureLookup = {});
void ResolveColors();
lcMesh* CreateMeshResolved(const std::vector<bool>& ColorTranslucency, int DefaultColorIndex);
lcMesh* CreateMeshResolved(const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled);
QStringList GetTextureDependencies() const;
void AddVertices(lcMeshDataType MeshDataType, size_t VertexCount, int* BaseVertex, lcMeshLoaderVertex** VertexBuffer);
void AddIndices(lcMeshDataType MeshDataType, lcMeshPrimitiveType PrimitiveType, quint32 ColorCode, size_t IndexCount, quint32** IndexBuffer);
@@ -193,12 +193,12 @@ protected:
quint32 AddTexturedVertex(const lcVector3& Position, const lcVector3& Normal, const lcVector2& TexCoords);
template<typename IndexType>
bool WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoaderFinalSection> (&FinalSections)[LC_NUM_MESH_LODS], int (&BaseVertices)[LC_NUM_MESHDATA_TYPES], const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex);
lcMesh* CreateMeshInternal(const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex);
bool WriteSections(lcMesh* Mesh, const std::vector<lcMeshLoaderFinalSection> (&FinalSections)[LC_NUM_MESH_LODS], int (&BaseVertices)[LC_NUM_MESHDATA_TYPES], const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled);
lcMesh* CreateMeshInternal(const std::function<lcTexture*(const char*)>& TextureLookup, const std::vector<bool>& ColorTranslucency, int DefaultColorIndex, const std::atomic_bool* Cancelled);
static void UpdateMeshBoundingBox(lcMesh* Mesh);
static bool UpdateMeshBoundingBox(lcMesh* Mesh, const std::atomic_bool* Cancelled);
template<typename IndexType>
static void UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const lcMeshSection& Section, lcVector3& SectionMin, lcVector3& SectionMax);
static bool UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const lcMeshSection& Section, lcVector3& SectionMin, lcVector3& SectionMax, const std::atomic_bool* Cancelled);
};
enum class lcMeshLoaderFlag
@@ -218,6 +218,7 @@ public:
lcMeshLoader(lcLibraryMeshData& MeshData, Project* CurrentProject, lcMeshLoaderFlags Flags);
bool LoadMesh(lcFile& File, lcMeshDataType MeshDataType);
void SetCancellationFlag(const std::atomic_bool* Cancelled) { mCancelled = Cancelled; }
bool SearchProjectFolder() const { return mFlags.testFlag(lcMeshLoaderFlag::SearchProjectFolder); }
Project* mCurrentProject;
@@ -229,4 +230,5 @@ protected:
lcLibraryMeshData& mMeshData;
lcMeshLoaderFlags mFlags;
const std::atomic_bool* mCancelled = nullptr;
};
+45 -1
View File
@@ -484,6 +484,34 @@ std::vector<PieceInfo*> lcModel::GetRequiredPieces() const
return Required;
}
std::vector<lcPiece*> lcModel::GetRequiredSynthPieces() const
{
std::vector<lcPiece*> Required;
std::unordered_set<const lcModel*> Visited;
const std::function<void(const lcModel*)> Collect = [&Required, &Visited, &Collect](const lcModel* Model)
{
if (!Model || !Visited.insert(Model).second)
return;
for (const std::unique_ptr<lcPiece>& Piece : Model->mPieces)
{
PieceInfo* Info = Piece->mPieceInfo;
if (Info->IsModel())
Collect(Info->GetModel());
else if (Info->IsProject())
Collect(Info->GetProject()->GetMainModel());
else if (Info->GetSynthInfo())
Required.push_back(Piece.get());
}
};
Collect(this);
return Required;
}
lcResult<void> lcModel::EnsureAssetsReady() const
{
const std::vector<PieceInfo*> Required = GetRequiredPieces();
@@ -505,6 +533,22 @@ lcResult<void> lcModel::EnsureAssetsReady() const
return lcUnexpected(tr("Could not load all required pieces."));
}
const std::vector<lcPiece*> GeneratedPieces = GetRequiredSynthPieces();
if (!Library->EnsureSynthMeshesReady(GeneratedPieces))
{
for (const lcPiece* Piece : GeneratedPieces)
if (!Piece->HasGeneratedMesh())
{
QString Error = Library->GetSynthMeshError(Piece);
if (Error.isEmpty())
Error = tr("Could not generate geometry for piece '%1'.").arg(QString::fromLatin1(Piece->mPieceInfo->mFileName));
return lcUnexpected(Error);
}
}
std::vector<lcModel*> UpdatedModels;
const_cast<lcModel*>(this)->UpdatePieceInfo(UpdatedModels);
@@ -1002,7 +1046,7 @@ void lcModel::LoadLDraw(QIODevice& Device, Project* Project)
lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
Piece->SetFileLine(mFileLines.size());
Piece->SetPieceInfo(Info, PartId, false, true);
Piece->SetPieceInfo(Info, PartId, false, false);
Piece->Initialize(Transform, CurrentStep);
Piece->SetColorCode(ColorCode);
Piece->VerifyControlPoints(ControlPoints);
+1
View File
@@ -182,6 +182,7 @@ public:
void UpdateAllViews() const;
void RefreshPreview() const;
std::vector<PieceInfo*> GetRequiredPieces() const;
std::vector<lcPiece*> GetRequiredSynthPieces() const;
lcResult<void> EnsureAssetsReady() const;
PieceInfo* GetPieceInfo() const
+49 -11
View File
@@ -37,6 +37,11 @@ lcPreviewDockWidget::lcPreviewDockWidget(QMainWindow* Parent)
if (mAssetRefreshPending)
RefreshPendingAssets();
});
connect(Library, &lcPiecesLibrary::GeneratedMeshSettled, this, [this](PieceInfo*)
{
if (mAssetRefreshPending)
RefreshPendingAssets();
});
mToolBar = addToolBar(tr("Toolbar"));
mToolBar->setObjectName("Toolbar");
@@ -55,6 +60,7 @@ bool lcPreviewDockWidget::SetCurrentPiece(PieceInfo* Info, int ColorCode)
return true;
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mLabel->setText(tr("Loading..."));
if (mPreview->SetCurrentPiece(Info, ColorCode))
@@ -72,9 +78,9 @@ bool lcPreviewDockWidget::SetCurrentPiece(PieceInfo* Info, int ColorCode)
void lcPreviewDockWidget::UpdatePreview()
{
mAssetRefreshPending = false;
mViewWidget->show();
mRefitAfterAssetLoad = true;
mPreview->UpdatePreview();
mLabel->setText(mPreview->GetDescription());
RefreshPendingAssets();
}
void lcPreviewDockWidget::RefreshDescription()
@@ -95,18 +101,25 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model)
mRefreshingModel = true;
if (mPreview->GetModel()->EnsureAssetsReady())
if (!mAssetRefreshPending)
mRefitAfterAssetLoad = false;
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (PieceInfo* Info : Required)
{
mAssetRefreshPending = false;
mViewWidget->show();
mPreview->Redraw();
}
else
{
mPreview->ClearPreview();
mLabel->setText(tr("Preview unavailable"));
if (Info->mState != lcPieceInfoState::Unloaded)
continue;
Library->LoadPieceInfo(Info, lcPieceLoadFlag::Visible);
Library->ReleasePieceInfo(Info);
}
std::vector<lcModel*> UpdatedModels;
mPreview->GetModel()->UpdatePieceInfo(UpdatedModels);
RefreshPendingAssets();
mRefreshingModel = false;
}
@@ -162,7 +175,30 @@ void lcPreviewDockWidget::RefreshPendingAssets()
}
}
for (const lcPiece* Piece : mPreview->GetModel()->GetRequiredSynthPieces())
{
if (Piece->IsGeneratedMeshPending())
{
mAssetRefreshPending = true;
mViewWidget->hide();
mLabel->setText(tr("Loading..."));
return;
}
if (!Piece->HasGeneratedMesh())
{
mAssetRefreshPending = false;
mViewWidget->hide();
mLabel->setText(tr("Preview unavailable"));
return;
}
}
if (mAssetRefreshPending && mRefitAfterAssetLoad)
mPreview->ZoomExtents();
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mViewWidget->show();
mPreview->RefreshDescription();
mPreview->Redraw();
@@ -186,6 +222,7 @@ void lcPreviewDockWidget::RebindPieceInfo(PieceInfo* Previous, PieceInfo* Replac
return;
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
if (mPreview->SetCurrentPiece(Replacement, ColorCode))
RefreshPendingAssets();
@@ -199,6 +236,7 @@ void lcPreviewDockWidget::RebindPieceInfo(PieceInfo* Previous, PieceInfo* Replac
void lcPreviewDockWidget::ClearPreview()
{
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mViewWidget->show();
if (mPreview->GetModel()->GetPieces().size())
mPreview->ClearPreview();
+1
View File
@@ -32,6 +32,7 @@ protected:
lcViewWidget* mViewWidget;
bool mRefreshingModel = false;
bool mAssetRefreshPending = false;
bool mRefitAfterAssetLoad = true;
};
class lcPreview : public lcView
+133 -40
View File
@@ -17,8 +17,8 @@ public:
protected:
float GetSectionTwist(const lcMatrix44& StartTransform, const lcMatrix44& EndTransform) const;
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const override;
static void AddTubeParts(lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, float Radius, bool IsInner);
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const override;
static void AddTubeParts(lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, float Radius, bool IsInner, const std::atomic_bool* Cancelled);
struct lcSynthComponent
{
@@ -41,7 +41,7 @@ public:
lcSynthInfoFlexibleHose(float Length, int NumSections, const char* EdgePart2);
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
const char* mEdgePart2;
};
@@ -54,7 +54,7 @@ public:
void GetDefaultControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const override;
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
};
class lcSynthInfoPneumaticTube : public lcSynthInfoCurved
@@ -63,7 +63,7 @@ public:
lcSynthInfoPneumaticTube(float Length, int NumSections, const char* EndPart);
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
const char* mEndPart;
};
@@ -74,7 +74,7 @@ public:
lcSynthInfoRibbedHose(float Length, int NumSections);
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
};
class lcSynthInfoFlexibleAxle : public lcSynthInfoCurved
@@ -83,7 +83,7 @@ public:
lcSynthInfoFlexibleAxle(float Length, int NumSections);
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
};
class lcSynthInfoBraidedString : public lcSynthInfoCurved
@@ -92,8 +92,8 @@ public:
lcSynthInfoBraidedString(float Length, int NumSections);
protected:
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
};
class lcSynthInfoStraight : public lcSynthInfo
@@ -104,7 +104,7 @@ public:
void VerifyControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const override;
protected:
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const override;
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const override;
};
class lcSynthInfoShockAbsorber : public lcSynthInfoStraight
@@ -115,7 +115,7 @@ public:
void GetDefaultControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const override;
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
const char* mSpringPart;
};
@@ -128,7 +128,7 @@ public:
void GetDefaultControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const override;
protected:
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
const char* mBodyPart;
const char* mPistonPart;
@@ -145,8 +145,8 @@ public:
void VerifyControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const override;
protected:
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const override;
void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const override;
void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const override;
float mEndOffset;
const char* mEndPart;
@@ -659,7 +659,7 @@ float lcSynthInfoCurved::GetSectionTwist(const lcMatrix44& StartTransform, const
return 0.0f;
}
void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const
void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const
{
if (ControlPoints.empty())
return;
@@ -668,6 +668,9 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
for (quint32 ControlPointIndex = 0; ControlPointIndex < ControlPoints.size() - 1 && Sections.size() < mSectionCount + 2; ControlPointIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcVector3 SegmentControlPoints[4];
lcMatrix44 StartTransform = lcMatrix44LeoCADToLDraw(ControlPoints[ControlPointIndex].Transform);
@@ -700,6 +703,9 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
for (int PointIdx = 0; PointIdx < NumCurvePoints; PointIdx++)
{
if ((PointIdx & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
float t = (float)PointIdx / (float)(NumCurvePoints - 1);
float it = 1.0f - t;
@@ -711,7 +717,12 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
float TotalSegmentLength = 0.0f;
for (size_t PointIdx = 0; PointIdx < CurvePoints.size() - 1; PointIdx++)
{
if ((PointIdx & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
TotalSegmentLength += lcLength(CurvePoints[PointIdx] - CurvePoints[PointIdx + 1]);
}
lcVector3 StartUp = lcMul30(lcVector3(1.0f, 0.0f, 0.0f), StartTransform);
float Twist = GetSectionTwist(StartTransform, EndTransform);
@@ -719,6 +730,9 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
while (CurrentPointIndex < CurvePoints.size() - 1)
{
if ((CurrentPointIndex & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
float Length = lcLength(CurvePoints[CurrentPointIndex + 1] - CurvePoints[CurrentPointIndex]);
CurrentSegmentLength += Length;
SectionLength -= Length;
@@ -765,6 +779,9 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
while (Sections.size() < mSectionCount + 2)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix44 EndTransform = lcMatrix44LeoCADToLDraw(ControlPoints.back().Transform);
EndTransform = lcMatrix44(lcMul(lcMul(lcMatrix33(mEnd.Transform), lcMatrix33(EndTransform)), lcMatrix33Scale(lcVector3(1.0f, -1.0f, 1.0f))), EndTransform.GetTranslation());
lcVector3 Position = lcMul31(lcVector3(0.0f, SectionLength, 0.0f), EndTransform);
@@ -784,7 +801,7 @@ void lcSynthInfoCurved::CalculateSections(const std::vector<lcPieceControlPoint>
}
}
void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const
void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const
{
if (ControlPoints.empty())
return;
@@ -793,6 +810,9 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
for (quint32 ControlPointIndex = 0; ControlPointIndex < ControlPoints.size() - 1 && Sections.size() < mSectionCount + 2; ControlPointIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcVector3 SegmentControlPoints[4];
lcMatrix44 StartTransform = lcMatrix44LeoCADToLDraw(ControlPoints[ControlPointIndex].Transform);
@@ -825,6 +845,9 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
for (int PointIdx = 0; PointIdx < NumCurvePoints; PointIdx++)
{
if ((PointIdx & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
float t = (float)PointIdx / (float)(NumCurvePoints - 1);
float it = 1.0f - t;
@@ -836,7 +859,12 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
float TotalSegmentLength = 0.0f;
for (size_t PointIdx = 0; PointIdx < CurvePoints.size() - 1; PointIdx++)
{
if ((PointIdx & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
TotalSegmentLength += lcLength(CurvePoints[PointIdx] - CurvePoints[PointIdx + 1]);
}
lcVector3 StartUp = lcMul30(lcVector3(0.0f, 1.0f, 0.0f), StartTransform);
float Twist = GetSectionTwist(StartTransform, EndTransform);
@@ -844,6 +872,9 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
while (CurrentPointIndex < CurvePoints.size() - 1)
{
if ((CurrentPointIndex & 63) == 0 && Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
float Length = lcLength(CurvePoints[CurrentPointIndex + 1] - CurvePoints[CurrentPointIndex]);
CurrentSegmentLength += Length;
SectionLength -= Length;
@@ -890,6 +921,9 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
while (Sections.size() < mSectionCount + 2)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix44 EndTransform = lcMatrix44LeoCADToLDraw(ControlPoints.back().Transform);
EndTransform = lcMatrix44(lcMul(lcMul(lcMatrix33(mEnd.Transform), lcMatrix33(EndTransform)), lcMatrix33Scale(lcVector3(1.0f, -1.0f, 1.0f))), EndTransform.GetTranslation());
lcVector3 Position = lcMul31(lcVector3(SectionLength, 0.0f, 0.0f), EndTransform);
@@ -909,10 +943,13 @@ void lcSynthInfoBraidedString::CalculateSections(const std::vector<lcPieceContro
}
}
void lcSynthInfoStraight::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const
void lcSynthInfoStraight::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const
{
for (quint32 ControlPointIndex = 0; ControlPointIndex < ControlPoints.size(); ControlPointIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix44 Transform = lcMatrix44LeoCADToLDraw(ControlPoints[ControlPointIndex].Transform);
Sections.emplace_back(Transform);
@@ -921,10 +958,13 @@ void lcSynthInfoStraight::CalculateSections(const std::vector<lcPieceControlPoin
}
}
void lcSynthInfoUniversalJoint::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const
void lcSynthInfoUniversalJoint::CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const
{
for (quint32 ControlPointIndex = 0; ControlPointIndex < ControlPoints.size(); ControlPointIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix44 Transform = lcMatrix44LeoCADToLDraw(ControlPoints[ControlPointIndex].Transform);
Sections.emplace_back(Transform);
@@ -933,7 +973,7 @@ void lcSynthInfoUniversalJoint::CalculateSections(const std::vector<lcPieceContr
}
}
void lcSynthInfoFlexibleHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoFlexibleHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const
{
if (Sections.empty())
return;
@@ -966,6 +1006,9 @@ void lcSynthInfoFlexibleHose::AddParts(lcMemFile& File, lcLibraryMeshData&, cons
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix33 Transform = lcMatrix33(Sections[SectionIndex]);
lcVector3 Offset = Sections[SectionIndex].GetTranslation();
@@ -997,7 +1040,7 @@ void lcSynthInfoFlexibleHose::AddParts(lcMemFile& File, lcLibraryMeshData&, cons
}
}
void lcSynthInfoCurved::AddTubeParts(lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, float Radius, bool IsInner)
void lcSynthInfoCurved::AddTubeParts(lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, float Radius, bool IsInner, const std::atomic_bool* Cancelled)
{
// a unit circle
static const lcVector3 Vertices[16] =
@@ -1030,6 +1073,9 @@ void lcSynthInfoCurved::AddTubeParts(lcLibraryMeshData& MeshData, const std::vec
for (size_t SectionIndex = 1; SectionIndex < Sections.size(); SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
for (int VertexIdx = 0; VertexIdx < NumSectionVertices; VertexIdx++)
{
VertexBuffer->Position = lcMul31(Radius * Vertices[VertexIdx], Sections[SectionIndex]);
@@ -1055,6 +1101,9 @@ void lcSynthInfoCurved::AddTubeParts(lcLibraryMeshData& MeshData, const std::vec
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
for (int VertexIdx = 0; VertexIdx < NumSectionVertices; VertexIdx++)
{
int Vertex1 = BaseVertex + (VertexIdx + Offset1) % NumSectionVertices;
@@ -1071,7 +1120,7 @@ void lcSynthInfoCurved::AddTubeParts(lcLibraryMeshData& MeshData, const std::vec
}
}
void lcSynthInfoFlexSystemHose::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoFlexSystemHose::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const
{
if (Sections.empty())
return;
@@ -1100,11 +1149,14 @@ void lcSynthInfoFlexSystemHose::AddParts(lcMemFile& File, lcLibraryMeshData& Mes
File.WriteBuffer(Line, strlen(Line));
}
AddTubeParts(MeshData, Sections, 4.0f, false);
AddTubeParts(MeshData, Sections, 2.0f, true);
AddTubeParts(MeshData, Sections, 4.0f, false, Cancelled);
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
AddTubeParts(MeshData, Sections, 2.0f, true, Cancelled);
}
void lcSynthInfoPneumaticTube::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoPneumaticTube::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const
{
if (Sections.empty())
return;
@@ -1135,11 +1187,14 @@ void lcSynthInfoPneumaticTube::AddParts(lcMemFile& File, lcLibraryMeshData& Mesh
File.WriteBuffer(Line, strlen(Line));
}
AddTubeParts(MeshData, Sections, 5.0f, false);
AddTubeParts(MeshData, Sections, 3.0f, true);
AddTubeParts(MeshData, Sections, 5.0f, false, Cancelled);
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
AddTubeParts(MeshData, Sections, 3.0f, true, Cancelled);
}
void lcSynthInfoRibbedHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoRibbedHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const
{
if (Sections.empty())
return;
@@ -1159,6 +1214,9 @@ void lcSynthInfoRibbedHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
const lcMatrix44& Transform = Sections[SectionIndex];
snprintf(Line, sizeof(Line), "1 16 %f %f %f %f %f %f %f %f %f %f %f %f 80.dat\n", Transform[3][0], Transform[3][1], Transform[3][2], Transform[0][0], Transform[1][0], Transform[2][0],
@@ -1179,7 +1237,7 @@ void lcSynthInfoRibbedHose::AddParts(lcMemFile& File, lcLibraryMeshData&, const
}
}
void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const
{
if (Sections.empty())
return;
@@ -1268,6 +1326,9 @@ void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshD
for (size_t SectionIndex = 1; SectionIndex < Sections.size(); SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
for (int VertexIdx = 0; VertexIdx < NumSectionVertices; VertexIdx++)
{
VertexBuffer->Position = lcMul31(SectionVertices[VertexIdx].Position, Sections[SectionIndex]);
@@ -1281,6 +1342,9 @@ void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshD
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
const int Indices[] = { 1, 3, 5, 8, 10, 12, 15, 17, 19, 22, 24, 26 };
for (int VertexIdx = 0; VertexIdx < 12; VertexIdx++)
@@ -1296,6 +1360,9 @@ void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshD
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
for (int VertexIdx = 0; VertexIdx < NumSectionVertices; VertexIdx++)
{
int Vertex1 = BaseVertex + VertexIdx;
@@ -1312,13 +1379,19 @@ void lcSynthInfoFlexibleAxle::AddParts(lcMemFile& File, lcLibraryMeshData& MeshD
}
}
void lcSynthInfoBraidedString::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& SectionsIn) const
void lcSynthInfoBraidedString::AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& SectionsIn, const std::atomic_bool* Cancelled) const
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
std::vector<lcMatrix44> Sections;
Sections.resize(SectionsIn.size());
for (size_t SectionIndex = 0; SectionIndex < Sections.size(); SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix33 Transform(lcMul(lcMatrix33Scale(lcVector3(1.0f, -1.0f, 1.0f)), lcMatrix33(SectionsIn[SectionIndex])));
lcVector3 Offset = SectionsIn[SectionIndex].GetTranslation();
Sections[SectionIndex] = lcMatrix44(Transform, Offset);
@@ -1357,6 +1430,9 @@ void lcSynthInfoBraidedString::AddParts(lcMemFile& File, lcLibraryMeshData& Mesh
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix33 Transform1 = lcMatrix33(Sections[SectionIndex]);
lcMatrix33 Transform2 = lcMatrix33(Sections[SectionIndex + 1]);
lcVector3 Offset1 = Sections[SectionIndex].GetTranslation();
@@ -1394,6 +1470,9 @@ void lcSynthInfoBraidedString::AddParts(lcMemFile& File, lcLibraryMeshData& Mesh
for (size_t SectionIndex = 1; SectionIndex < Sections.size() - 1; SectionIndex++)
{
if (Cancelled && Cancelled->load(std::memory_order_relaxed))
return;
lcMatrix33 Transform1 = lcMatrix33(Sections[SectionIndex]);
lcMatrix33 Transform2 = lcMatrix33(Sections[SectionIndex + 1]);
lcVector3 Offset1 = Sections[SectionIndex].GetTranslation();
@@ -1445,7 +1524,7 @@ void lcSynthInfoBraidedString::AddParts(lcMemFile& File, lcLibraryMeshData& Mesh
}
}
void lcSynthInfoShockAbsorber::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoShockAbsorber::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections, const std::atomic_bool*) const
{
char Line[256];
lcVector3 Offset;
@@ -1466,7 +1545,7 @@ void lcSynthInfoShockAbsorber::AddParts(lcMemFile& File, lcLibraryMeshData&, con
File.WriteBuffer(Line, strlen(Line));
}
void lcSynthInfoActuator::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoActuator::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections, const std::atomic_bool*) const
{
char Line[256];
lcVector3 Offset;
@@ -1484,7 +1563,7 @@ void lcSynthInfoActuator::AddParts(lcMemFile& File, lcLibraryMeshData&, const st
File.WriteBuffer(Line, strlen(Line));
}
void lcSynthInfoUniversalJoint::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections) const
void lcSynthInfoUniversalJoint::AddParts(lcMemFile& File, lcLibraryMeshData&, const std::vector<lcMatrix44>& Sections, const std::atomic_bool*) const
{
char Line[256];
lcVector3 Offset = Sections[0].GetTranslation();
@@ -1512,23 +1591,37 @@ void lcSynthInfoUniversalJoint::AddParts(lcMemFile& File, lcLibraryMeshData&, co
File.WriteBuffer(Line, strlen(Line));
}
lcMesh* lcSynthInfo::CreateMesh(const std::vector<lcPieceControlPoint>& ControlPoints) const
std::unique_ptr<lcLibraryMeshData> lcSynthInfo::BuildMeshData(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const
{
if (Cancelled.load(std::memory_order_relaxed))
return nullptr;
std::vector<lcMatrix44> Sections;
CalculateSections(ControlPoints, Sections, nullptr);
CalculateSections(ControlPoints, Sections, nullptr, &Cancelled);
lcLibraryMeshData MeshData;
if (Cancelled.load(std::memory_order_relaxed))
return nullptr;
std::unique_ptr<lcLibraryMeshData> MeshData(new lcLibraryMeshData);
lcMemFile File; // todo: rewrite this to pass the parts directly
AddParts(File, MeshData, Sections);
AddParts(File, *MeshData, Sections, &Cancelled);
if (Cancelled.load(std::memory_order_relaxed))
return nullptr;
File.WriteU8(0);
File.Seek(0, SEEK_SET);
lcMeshLoader MeshLoader(MeshData, nullptr, lcMeshLoaderFlag::RequireAllIncludes);
if (MeshLoader.LoadMesh(File, LC_MESHDATA_SHARED) && !MeshData.IsEmpty())
return MeshData.CreateMesh();
lcMeshLoader MeshLoader(*MeshData, nullptr, lcMeshLoaderFlag::RequireAllIncludes);
MeshLoader.SetCancellationFlag(&Cancelled);
if (MeshLoader.LoadMesh(File, LC_MESHDATA_SHARED) && !MeshData->IsEmpty() && !Cancelled.load(std::memory_order_relaxed))
{
MeshData->SetMeshLoader(nullptr);
return MeshData;
}
return nullptr;
}
@@ -1553,7 +1646,7 @@ int lcSynthInfo::InsertControlPoint(std::vector<lcPieceControlPoint>& ControlPoi
BestDistance = Distance;
BestPosition = lcVector3LDrawToLeoCAD(CurvePoint);
}
}
}, nullptr
);
if (BestSegment != UINT32_MAX)
+3 -4
View File
@@ -39,12 +39,12 @@ public:
virtual void GetDefaultControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const = 0;
virtual void VerifyControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const = 0;
int InsertControlPoint(std::vector<lcPieceControlPoint>& ControlPoints, const lcVector3& Start, const lcVector3& End) const;
lcMesh* CreateMesh(const std::vector<lcPieceControlPoint>& ControlPoints) const;
std::unique_ptr<lcLibraryMeshData> BuildMeshData(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const;
protected:
using SectionCallbackFunc = std::function<void(const lcVector3& CurvePoint, quint32 SegmentIndex, float t)>;
virtual void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback) const = 0;
virtual void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections) const = 0;
virtual void CalculateSections(const std::vector<lcPieceControlPoint>& ControlPoints, std::vector<lcMatrix44>& Sections, SectionCallbackFunc SectionCallback, const std::atomic_bool* Cancelled) const = 0;
virtual void AddParts(lcMemFile& File, lcLibraryMeshData& MeshData, const std::vector<lcMatrix44>& Sections, const std::atomic_bool* Cancelled) const = 0;
bool mCurve = false;
bool mUnidirectional = false;
@@ -53,4 +53,3 @@ protected:
};
void lcSynthInit();
+100 -1
View File
@@ -4,6 +4,7 @@
#include "pieceinf.h"
#include "lc_view.h"
#include "lc_model.h"
#include "piece.h"
#include "project.h"
#include "camera.h"
@@ -12,6 +13,7 @@ lcThumbnailManager::lcThumbnailManager(lcPiecesLibrary* Library)
{
connect(mLibrary, &lcPiecesLibrary::PartLoaded, this, &lcThumbnailManager::PartLoaded);
connect(mLibrary, &lcPiecesLibrary::PartLoadFailed, this, &lcThumbnailManager::PartLoadFailed);
connect(mLibrary, &lcPiecesLibrary::GeneratedMeshSettled, this, &lcThumbnailManager::GeneratedMeshSettled);
}
lcThumbnailManager::~lcThumbnailManager()
@@ -23,6 +25,7 @@ void lcThumbnailManager::Clear()
{
mView.reset();
mModel.reset();
mPendingGeneratedModels.clear();
for (auto &[ThumbnailId, Thumbnail] : mThumbnails)
ReleaseRequiredPieces(Thumbnail);
@@ -47,6 +50,7 @@ void lcThumbnailManager::RefreshPieces(const std::vector<PieceInfo*>& ChangedPie
continue;
Entry.second.Pixmap = QPixmap();
mPendingGeneratedModels.erase(Entry.first);
ThumbnailIds.push_back(Entry.first);
}
@@ -92,6 +96,8 @@ void lcThumbnailManager::ReleaseThumbnail(lcPartThumbnailId ThumbnailId)
if (Thumbnail.ReferenceCount == 0)
{
mPendingGeneratedModels.erase(ThumbnailId);
ReleaseRequiredPieces(Thumbnail);
mThumbnails.erase(ThumbnailIt);
@@ -209,8 +215,64 @@ void lcThumbnailManager::PartLoadFailed(PieceInfo* Info)
}
}
void lcThumbnailManager::GeneratedMeshSettled(PieceInfo*)
{
std::vector<lcPartThumbnailId> Ready;
for (const std::map<lcPartThumbnailId, std::unique_ptr<lcModel>>::value_type& Entry : mPendingGeneratedModels)
{
bool Pending = false;
for (const lcPiece* Piece : Entry.second->GetRequiredSynthPieces())
if (Piece->IsGeneratedMeshPending())
{
Pending = true;
break;
}
if (!Pending)
Ready.push_back(Entry.first);
}
for (lcPartThumbnailId ThumbnailId : Ready)
{
const std::map<lcPartThumbnailId, lcPartThumbnail>::iterator It = mThumbnails.find(ThumbnailId);
if (It != mThumbnails.end())
UpdateThumbnail(It->first, It->second);
}
}
void lcThumbnailManager::SchedulePendingThumbnails()
{
if (mRetryScheduled)
return;
mRetryScheduled = true;
QTimer::singleShot(0, this, [this]()
{
mRetryScheduled = false;
std::vector<lcPartThumbnailId> Pending;
for (const std::map<lcPartThumbnailId, lcPartThumbnail>::value_type& Entry : mThumbnails)
if (Entry.second.Pixmap.isNull())
Pending.push_back(Entry.first);
for (lcPartThumbnailId ThumbnailId : Pending)
{
const std::map<lcPartThumbnailId, lcPartThumbnail>::iterator It = mThumbnails.find(ThumbnailId);
if (It != mThumbnails.end() && It->second.Pixmap.isNull())
UpdateThumbnail(It->first, It->second);
}
});
}
void lcThumbnailManager::DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail)
{
mPendingGeneratedModels.erase(ThumbnailId);
const int Size = qMax(1, static_cast<int>(Thumbnail.Size * Thumbnail.DeviceScale));
Thumbnail.Pixmap = QPixmap(Size, Size);
@@ -233,10 +295,25 @@ void lcThumbnailManager::DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPa
ReleaseRequiredPieces(Thumbnail);
emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap);
SchedulePendingThumbnails();
}
void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail)
{
const std::map<lcPartThumbnailId, std::unique_ptr<lcModel>>::iterator PendingIt = mPendingGeneratedModels.find(ThumbnailId);
const bool ReuseGeneratedPiece = PendingIt != mPendingGeneratedModels.end();
if (ReuseGeneratedPiece)
{
for (const lcPiece* Piece : PendingIt->second->GetRequiredSynthPieces())
if (Piece->IsGeneratedMeshPending())
return;
mView.reset();
mModel = std::move(PendingIt->second);
mPendingGeneratedModels.erase(PendingIt);
}
const int Width = Thumbnail.Size * 2 * Thumbnail.DeviceScale;
const int Height = Thumbnail.Size * 2 * Thumbnail.DeviceScale;
@@ -268,7 +345,28 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum
mView->SetBackgroundColorOverride(LC_RGBA(qRed(BackgroundColor), qGreen(BackgroundColor), qBlue(BackgroundColor), 0));
PieceInfo* Info = Thumbnail.Info;
mModel->SetPreviewPieceInfo(Info, Thumbnail.ColorIndex);
if (!ReuseGeneratedPiece)
{
mModel->SetPreviewPieceInfo(Info, Thumbnail.ColorIndex);
bool GeneratedPiecePending = false;
for (const lcPiece* Piece : mModel->GetRequiredSynthPieces())
if (Piece->IsGeneratedMeshPending())
{
GeneratedPiecePending = true;
break;
}
if (GeneratedPiecePending)
{
mView->UnbindRenderFramebuffer();
mView.reset();
mPendingGeneratedModels.emplace(ThumbnailId, std::move(mModel));
return;
}
}
const lcVector3 Center = (Info->GetBoundingBox().Min + Info->GetBoundingBox().Max) / 2.0f;
const lcVector3 Position = Center + lcVector3(100.0f, -100.0f, 75.0f);
@@ -342,4 +440,5 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum
ReleaseRequiredPieces(Thumbnail);
emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap);
SchedulePendingThumbnails();
}
+4
View File
@@ -37,6 +37,7 @@ signals:
protected slots:
void PartLoaded(PieceInfo* Info);
void PartLoadFailed(PieceInfo* Info);
void GeneratedMeshSettled(PieceInfo* Info);
protected:
void RefreshRequiredPieces(lcPartThumbnail& Thumbnail);
@@ -44,6 +45,7 @@ protected:
void UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
void DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
void DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail);
void SchedulePendingThumbnails();
lcPiecesLibrary* mLibrary = nullptr;
std::map<lcPartThumbnailId, lcPartThumbnail> mThumbnails;
@@ -51,4 +53,6 @@ protected:
std::unique_ptr<lcView> mView;
std::unique_ptr<lcModel> mModel;
std::map<lcPartThumbnailId, std::unique_ptr<lcModel>> mPendingGeneratedModels;
bool mRetryScheduled = false;
};
+97 -38
View File
@@ -37,7 +37,7 @@ lcPiece::lcPiece(PieceInfo* Info)
lcPiece::lcPiece(const lcPiece& Other)
: lcObject(lcObjectType::Piece)
{
SetPieceInfo(Other.mPieceInfo, Other.mID, true, true);
SetPieceInfo(Other.mPieceInfo, Other.mID, true, false);
mHidden = Other.mHidden;
mSelected = Other.mSelected;
mColorIndex = Other.mColorIndex;
@@ -58,6 +58,8 @@ lcPiece::lcPiece(const lcPiece& Other)
lcPiece::~lcPiece()
{
lcGetPiecesLibrary()->CancelSynthMesh(this);
if (mPieceInfo)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
@@ -71,6 +73,16 @@ lcPiece::~lcPiece()
void lcPiece::SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait, bool UpdateSynthInfo)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
PieceInfo* PreviousInfo = mPieceInfo;
if (PreviousInfo != Info || UpdateSynthInfo)
Library->CancelSynthMesh(this);
if (PreviousInfo != Info)
{
delete mMesh;
mMesh = nullptr;
}
mPieceInfo = Info;
if (mPieceInfo)
@@ -87,17 +99,15 @@ void lcPiece::SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait, bool U
{
mControlPoints.clear();
delete mMesh;
mMesh = nullptr;
const lcSynthInfo* SynthInfo = mPieceInfo ? mPieceInfo->GetSynthInfo() : nullptr;
if (SynthInfo)
{
SynthInfo->GetDefaultControlPoints(mControlPoints);
UpdateMesh();
}
UpdateMesh();
}
else if (PreviousInfo && PreviousInfo != Info)
UpdateMesh();
Library->NotifyConsumersChanged();
}
@@ -514,10 +524,12 @@ void lcPiece::RayTest(lcObjectRayTest& ObjectRayTest) const
bool lcPiece::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDistance, lcPieceInfoRayTest& PieceInfoRayTest) const
{
if (!mMesh || mPieceInfo->IsLoading())
lcMesh* Mesh = GetDisplayMesh();
if (!Mesh || mPieceInfo->IsLoading())
return mPieceInfo->MinIntersectDist(Start, End, MinDistance, PieceInfoRayTest);
if (!mMesh->MinIntersectDist(Start, End, MinDistance, PieceInfoRayTest.Plane))
if (!Mesh->MinIntersectDist(Start, End, MinDistance, PieceInfoRayTest.Plane))
return false;
PieceInfoRayTest.Info = mPieceInfo;
@@ -534,7 +546,9 @@ void lcPiece::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
bool lcPiece::IntersectsPlanes(const lcMatrix44& WorldMatrix, const lcVector4 Planes[6]) const
{
if (!mMesh || mPieceInfo->IsLoading())
lcMesh* Mesh = GetDisplayMesh();
if (!Mesh || mPieceInfo->IsLoading())
return mPieceInfo->BoxTest(WorldMatrix, Planes);
const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(WorldMatrix);
@@ -547,7 +561,7 @@ bool lcPiece::IntersectsPlanes(const lcMatrix44& WorldMatrix, const lcVector4 Pl
}
lcVector3 Corners[8];
lcGetBoxCorners(mMesh->mBoundingBox, Corners);
lcGetBoxCorners(Mesh->mBoundingBox, Corners);
int OutcodesOR = 0;
int OutcodesAND = 0x3f;
@@ -570,7 +584,7 @@ bool lcPiece::IntersectsPlanes(const lcMatrix44& WorldMatrix, const lcVector4 Pl
if (OutcodesOR == 0)
return true;
return mMesh->IntersectsPlanes(LocalPlanes);
return Mesh->IntersectsPlanes(LocalPlanes);
}
void lcPiece::DrawInterface(lcContext* Context, const lcScene& Scene) const
@@ -1020,13 +1034,18 @@ void lcPiece::SetHistoryState(const lcPieceHistoryState& State, const lcModel* M
if (mPieceInfo != Info)
{
const bool NewPiece = !mPieceInfo;
if (mPieceInfo)
lcGetPiecesLibrary()->ReleasePieceInfo(mPieceInfo);
SetPieceInfo(Info, mID, true, false);
}
UpdateMesh();
if (NewPiece)
UpdateMesh();
}
else
UpdateMesh();
// std::vector<bool> mTrainTrackConnections;
}
@@ -1054,15 +1073,15 @@ void lcPiece::AddMainModelRenderMeshes(lcScene* Scene, bool Highlight, bool Fade
RenderMeshState = lcRenderMeshState::Faded;
}
if (!mMesh || mPieceInfo->IsLoading())
{
if (!mMesh && mPieceInfo->GetSynthInfo() && Scene->GetRequireGeneratedMeshes())
Scene->MarkMissingAssets();
if (mPieceInfo->GetSynthInfo() && !HasGeneratedMesh() && Scene->GetRequireGeneratedMeshes())
Scene->MarkMissingAssets();
mPieceInfo->AddRenderMeshes(Scene, mModelWorld, mColorIndex, RenderMeshState, ParentActive);
}
if (mSynthPending && Scene->GetRequireCompleteAssets())
Scene->MarkMissingAssets();
else if (lcMesh* Mesh = GetDisplayMesh(); Mesh && !mPieceInfo->IsLoading())
Scene->AddMesh(Mesh, mModelWorld, mColorIndex, RenderMeshState);
else
Scene->AddMesh(mMesh, mModelWorld, mColorIndex, RenderMeshState);
mPieceInfo->AddRenderMeshes(Scene, mModelWorld, mColorIndex, RenderMeshState, ParentActive);
if (RenderMeshState == lcRenderMeshState::Focused || RenderMeshState == lcRenderMeshState::Selected)
Scene->AddInterfaceObject(this);
@@ -1082,15 +1101,15 @@ void lcPiece::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMat
else if (ParentActive)
RenderMeshState = IsFocused() ? lcRenderMeshState::Focused : (IsSelected() ? lcRenderMeshState::Selected : lcRenderMeshState::Default);
if (!mMesh || mPieceInfo->IsLoading())
{
if (!mMesh && mPieceInfo->GetSynthInfo() && Scene->GetRequireGeneratedMeshes())
Scene->MarkMissingAssets();
if (mPieceInfo->GetSynthInfo() && !HasGeneratedMesh() && Scene->GetRequireGeneratedMeshes())
Scene->MarkMissingAssets();
mPieceInfo->AddRenderMeshes(Scene, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState, ActiveSubmodelInstance == this);
}
if (mSynthPending && Scene->GetRequireCompleteAssets())
Scene->MarkMissingAssets();
else if (lcMesh* Mesh = GetDisplayMesh(); Mesh && !mPieceInfo->IsLoading())
Scene->AddMesh(Mesh, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState);
else
Scene->AddMesh(mMesh, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState);
mPieceInfo->AddRenderMeshes(Scene, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState, ActiveSubmodelInstance == this);
if (ParentActive && (RenderMeshState == lcRenderMeshState::Focused || RenderMeshState == lcRenderMeshState::Selected))
Scene->AddInterfaceObject(this);
@@ -1099,15 +1118,16 @@ void lcPiece::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMat
void lcPiece::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const
{
const lcMatrix44 ModelWorldMatrix = lcMul(mModelWorld, WorldMatrix);
lcMesh* Mesh = GetDisplayMesh();
if (!mMesh || mPieceInfo->IsLoading())
if (!Mesh || mPieceInfo->IsLoading())
{
mPieceInfo->CompareBoundingBox(ModelWorldMatrix, Min, Max);
return;
}
lcVector3 Points[8];
lcGetBoxCorners(mMesh->mBoundingBox, Points);
lcGetBoxCorners(Mesh->mBoundingBox, Points);
for (const lcVector3& Corner : Points)
{
@@ -1119,14 +1139,16 @@ void lcPiece::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector
void lcPiece::SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector<lcVector3>& Points) const
{
if (!mMesh || mPieceInfo->IsLoading())
lcMesh* Mesh = GetDisplayMesh();
if (!Mesh || mPieceInfo->IsLoading())
mPieceInfo->AddSubModelBoundingBoxPoints(lcMul(mModelWorld, WorldMatrix), Points);
else
{
lcVector3 BoxPoints[8];
const lcMatrix44 ModelWorldMatrix = lcMul(mModelWorld, WorldMatrix);
lcGetBoxCorners(mMesh->mBoundingBox, BoxPoints);
lcGetBoxCorners(Mesh->mBoundingBox, BoxPoints);
for (int i = 0; i < 8; i++)
Points.emplace_back(lcMul31(BoxPoints[i], ModelWorldMatrix));
@@ -1482,21 +1504,25 @@ void lcPiece::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex
const lcBoundingBox& lcPiece::GetBoundingBox() const
{
if (!mMesh || mPieceInfo->IsLoading())
lcMesh* Mesh = GetDisplayMesh();
if (!Mesh || mPieceInfo->IsLoading())
return mPieceInfo->GetBoundingBox();
else
return mMesh->mBoundingBox;
return Mesh->mBoundingBox;
}
void lcPiece::CompareBoundingBox(lcVector3& Min, lcVector3& Max) const
{
if (!mMesh || mPieceInfo->IsLoading())
lcMesh* Mesh = GetDisplayMesh();
if (!Mesh || mPieceInfo->IsLoading())
mPieceInfo->CompareBoundingBox(mModelWorld, Min, Max);
else
{
lcVector3 Points[8];
lcGetBoxCorners(mMesh->mBoundingBox, Points);
lcGetBoxCorners(Mesh->mBoundingBox, Points);
for (int i = 0; i < 8; i++)
{
@@ -1523,7 +1549,40 @@ void lcPiece::UpdatePosition(lcStep Step)
void lcPiece::UpdateMesh()
{
mSynthPending = mPieceInfo && mPieceInfo->GetSynthInfo();
if (mSynthPending)
lcGetPiecesLibrary()->QueueSynthMesh(this);
else
{
delete mMesh;
mMesh = nullptr;
lcGetPiecesLibrary()->CancelSynthMesh(this);
}
}
lcMesh* lcPiece::GetDisplayMesh() const
{
if (mMesh)
return mMesh;
if (mSynthPending && mPieceInfo && mPieceInfo->GetSynthInfo())
return lcGetPiecesLibrary()->GetLoadingMesh();
return nullptr;
}
void lcPiece::SetGeneratedMesh(lcMesh* Mesh)
{
delete mMesh;
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
mMesh = SynthInfo ? SynthInfo->CreateMesh(mControlPoints) : nullptr;
mMesh = Mesh;
mSynthPending = false;
}
void lcPiece::RefreshGeneratedMesh()
{
delete mMesh;
mMesh = nullptr;
UpdateMesh();
}
+7
View File
@@ -177,6 +177,11 @@ public:
bool InsertControlPoint(const lcVector3& WorldStart, const lcVector3& WorldEnd);
bool RemoveFocusedControlPoint();
void VerifyControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const;
bool IsGeneratedMeshPending() const { return mSynthPending; }
// An older mesh may remain visible while its replacement is pending.
bool HasGeneratedMesh() const { return mMesh != nullptr && !mSynthPending; }
void SetGeneratedMesh(lcMesh* Mesh);
void RefreshGeneratedMesh();
lcGroup* GetTopGroup();
@@ -280,6 +285,7 @@ public:
protected:
void UpdateMesh();
lcMesh* GetDisplayMesh() const;
bool IsPivotPointVisible() const
{
@@ -311,4 +317,5 @@ protected:
std::vector<lcPieceControlPoint> mControlPoints;
std::vector<bool> mTrainTrackConnections;
lcMesh* mMesh = nullptr;
bool mSynthPending = false;
};
+5 -1
View File
@@ -34,13 +34,17 @@ void PieceInfo::SetLoadingBoundingBox()
PieceInfo::~PieceInfo()
{
delete mSynthInfo;
delete mTrainTrackInfo;
if (mMesh)
Unload();
}
void PieceInfo::SetSynthInfo(lcSynthInfo* SynthInfo)
{
mSynthInfo.reset(SynthInfo);
}
void PieceInfo::SetMesh(lcMesh* Mesh)
{
mBoundingBox = Mesh->mBoundingBox;
+7 -5
View File
@@ -59,14 +59,16 @@ public:
}
lcSynthInfo* GetSynthInfo() const
{
return mSynthInfo.get();
}
std::shared_ptr<const lcSynthInfo> GetSynthInfoShared() const
{
return mSynthInfo;
}
void SetSynthInfo(lcSynthInfo* SynthInfo)
{
mSynthInfo = SynthInfo;
}
void SetSynthInfo(lcSynthInfo* SynthInfo);
lcTrainTrackInfo* GetTrainTrackInfo() const
{
@@ -219,6 +221,6 @@ protected:
Project* mProject = nullptr;
lcMesh* mMesh = nullptr;
lcBoundingBox mBoundingBox;
lcSynthInfo* mSynthInfo = nullptr;
std::shared_ptr<lcSynthInfo> mSynthInfo;
lcTrainTrackInfo* mTrainTrackInfo = nullptr;
};
+17
View File
@@ -1157,6 +1157,23 @@ std::vector<PieceInfo*> Project::GetRequiredPieces() const
return Required;
}
std::vector<lcPiece*> Project::GetRequiredSynthPieces() const
{
std::vector<lcPiece*> Required;
std::unordered_set<lcPiece*> Seen;
for (const std::unique_ptr<lcModel>& Model : mModels)
{
const std::vector<lcPiece*> ModelRequired = Model->GetRequiredSynthPieces();
for (lcPiece* Piece : ModelRequired)
if (Seen.insert(Piece).second)
Required.push_back(Piece);
}
return Required;
}
lcResult<void> Project::EnsureAssetsReady() const
{
const lcModel* Model = GetMainModel();
+1
View File
@@ -122,6 +122,7 @@ public:
lcResult<void> SaveImage(const lcImageDialogOptions& Options);
std::vector<PieceInfo*> GetRequiredPieces() const;
std::vector<lcPiece*> GetRequiredSynthPieces() const;
lcResult<void> EnsureAssetsReady() const;
lcResult<void> ExportCurrentStep(const QString& FileName);
lcResult<void> ExportModel(const QString& FileName, lcModel* Model) const;