mirror of
https://github.com/leozide/leocad.git
synced 2026-10-27 06:20:55 +00:00
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:
1 parent
bd6bd50ecc
commit
b242cf9ad5
21 files changed
+1092
-131
No files matched your search
+426
-9
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -32,6 +32,7 @@ protected:
|
||||
lcViewWidget* mViewWidget;
|
||||
bool mRefreshingModel = false;
|
||||
bool mAssetRefreshPending = false;
|
||||
bool mRefitAfterAssetLoad = true;
|
||||
};
|
||||
|
||||
class lcPreview : public lcView
|
||||
|
||||
+133
-40
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user