Commit Graph
100 Commits
Author SHA1 Message Date
Leonardo Zide e2cb91f6f4 Fix decorated part classification and cache parent IDs
Detect decorations from filenames and description keywords so obsolete
parts with numeric IDs and descriptions starting with ~ are hidden when
Show Decorated Pieces is disabled. Keep decoration flags separate from
the optional plain parent ID, preserving classification when no parent
exists and leaving standalone stickers outside the current heuristic.

Resolve single-reference aliases across official and unofficial sources,
reject cycles, and use resolved parent IDs for category grouping. Cache
classification with descriptions and rebuild dependent alias metadata when
directory sources change. Keep local classification and cache restoration
in lcPieceData; the library handles source reads and registry resolution.

Validated with a warning-free Qt 6.10 Debug build, 58 lifecycle checks,
and offscreen filter checks for 81 obsolete numeric patterned parts on
cold and cached loads.

Fixes #538
2026-10-20 19:59:29 -07:00
Leonardo Zide 8a0b082f77 Merge branch 'master' of https://github.com/leozide/leocad 2026-10-20 11:59:45 -07:00
Leonardo Zide 0d137bcf47 Bound grid generation for distant pieces
A piece at an extreme coordinate expands the grid to millions of lines, causing excessive allocations or a truncated vertex buffer size followed by writes past its end. Larger coordinates can also exceed the integer range during conversion.

Clamp finite grid bounds before converting to integers, limiting the vertex buffer to less than 1 MB, and use the default grid for non-finite bounds. Calculate texture extents in floating point to avoid integer overflow with large spacing values.

Fixes #1044
2026-10-20 11:59:34 -07:00
Leonardo Zide 9709106aff Bound grid generation for distant pieces
A piece at an extreme coordinate expands the grid to millions of lines, causing excessive allocations or a truncated vertex buffer size followed by writes past its end. Larger coordinates can also exceed the integer range during conversion.

Clamp finite grid bounds before converting to integers, limiting the vertex buffer to less than 1 MB, and use the default grid for non-finite bounds. Calculate texture extents in floating point to avoid integer overflow with large spacing values.
2026-10-20 11:54:19 -07:00
Leonardo Zide e1c2df5767 Centralize ZIP filename lookup
Add FindFileIndex to resolve the first case-insensitive filename match
without extracting it. Reuse the lookup in named extraction and library
include resolution, removing the duplicate archive-entry scan while
preserving archive order and source fallback behavior.

Validated with a warning-free Qt 6.10 Debug build and 51 offscreen
lifecycle groups; the final API rename also builds without warnings.
2026-10-19 22:51:03 -07:00
Leonardo Zide 29c1c142df Simplify model metadata updates and discovery names
Update model filenames and descriptions directly in SetModel, removing
metadata-only forwarding through the library and piece manager. Registry
renames remain synchronized in the manager.

Reuse the bounded piece-name normalizer for archive and directory discovery
to remove duplicated conversion loops and avoid writing a terminator past
the name buffer. Preserve original source filenames and archive offsets.

Validated with a warning-free Qt 6.10 Debug build and all 51 offscreen
lifecycle groups; diff whitespace checks pass.
2026-10-19 22:31:17 -07:00
Leonardo Zide 038e091af4 Return owned include files and consolidate source opening
Callback-based include readers hid open failures and required parser callbacks
to report success through captured variables. Replace them with OpenPieceDataFile
and OpenPrimitiveFile returning lcResult of an owned file positioned at zero.
The mesh loader consumes the file directly and retains its existing strict or
permissive policy for missing includes.

Route include reads, piece builds and primitive builds through the existing
OpenFile service for archive and disk access. Preserve registered-source and
fallback search order, directory retries after parse failure, and primitive
high/low-detail selection. Primitive opening uses its own source metadata,
including stud-style archives with directory libraries, and records opening
errors through the existing synchronized failure path.

Document the owning file contract and retry boundary. Validation: warning-free
macOS Qt 6.10 Debug build, all 51 offscreen lifecycle checks and whitespace checks
pass. Coverage includes independent cursors across unload, missing/invalid sources,
include failure policy, parse retries, official precedence and low-detail/styled
primitive builds. Successful GPU rendering was not tested.
2026-10-19 21:28:49 -07:00
Leonardo Zide edd155dad9 Prefer official parts consistently across library sources
Loading the unofficial archive replaced same-name official part registrations,
while directory loading and include/primitive lookup already preferred official
sources. Keep registered official part sources when scanning unofficial archives;
unofficial-only parts remain available and other source policies stay unchanged.

Bump the library cache version so descriptions and mesh geometry built under the
previous override policy cannot survive the precedence change. Document the
consistent official-first behavior and cache invalidation.

Validation: warning-free macOS Qt 6.10 Debug build, all 47 offscreen lifecycle
checks and diff whitespace checks pass. Added checks cover cold/warm archive and
directory parts, primitives, subparts and includes, plus rejection and rebuilding
of legacy description and geometry caches. GPU rendering was not tested.
2026-10-19 20:52:35 -07:00
Leonardo Zide 137c645ce6 Remove obsolete library cleanup and capability APIs
UnloadUnusedParts selects identities with zero references and calls the release
path, which immediately returns without changing them. Remove the no-op scan
and its model, project and preview callers, including the now-unused LoadLDD
library local. Actual last-reference unload and temporary identity cleanup remain
in their existing lifetime paths.

SupportsStudStyle always returns true. Remove the method and the unreachable
preferences branch that disables the stud-style selector.

Validation: warning-free macOS Qt 6.10 Debug build, all 45 offscreen lifecycle
checks and diff whitespace checks pass. No remaining references to either API.
2026-10-19 20:41:18 -07:00
Leonardo Zide 664cec006f Protect library directory and shared buffer access
Move the library directory and shared vertex/index buffer handles into the
protected source and rendering groups. Expose one GetBuffers accessor returning
const references to both buffers, and use structured binding in BindMesh while
preserving GL binding and uncached-mesh fallback behavior.

Add GetLibraryFilePath for mapping-file lookups and update the LXF and BrickLink
callers. Keep file opening and bundled-resource fallback in those callers so the
library only resolves the path and exposes no mutable directory state.

Validation: warning-free macOS Qt 6.10 Debug build, all 45 offscreen lifecycle
checks and diff whitespace checks pass. No remaining external field accesses
for the protected directory or buffers. GPU rendering was not tested.
2026-10-19 18:44:13 -07:00
Leonardo Zide 9d2ec2197c Fix official library update counts and encapsulate access
Counting the combined registry after unofficial archives load can inflate the
installed official-piece count and suppress legitimate update notifications.
Capture the count immediately after the official archive is discovered, before
unofficial additions or overrides, so overrides still count as official entries.

Pass official-library eligibility explicitly into Load from the startup path
metadata. Custom paths, directory libraries and fallback loading retain a zero
update count, and unload clears it before any subsequent load. Remove the
post-load setter and expose the protected count through GetOfficialPieceCount
for the update dialog.

Validation: warning-free macOS Qt 6.10 Debug build and all 45 offscreen lifecycle
checks pass. Count checks cover unofficial additions and overrides, later missing
identities, custom archives, directory/failed reloads, fallback and unload reset.
Diff whitespace checks are clean.
2026-10-19 18:35:17 -07:00
Leonardo Zide 959f351a51 Organize library interfaces and centralize buffer invalidation
Group library functions and protected state by responsibility, and document
their ownership, readiness and worker contracts. Restrict teardown, source
snapshot/build/cache services, file opening and deferred repacking to protected
access; their production callers are internal or the existing loader friend.

Remove model-side buffer dirty writes from inventory loading and piece insertion.
Mesh publication already schedules deferred repacking, which includes all
registered meshes; uncached meshes use their own data until repacking finishes.
Keep mBuffersDirty protected with the library rendering state so model changes
no longer bypass that scheduling or cause an extra rebuild.

Validation: warning-free macOS Qt 6.10 Debug build, all 43 offscreen lifecycle
checks and diff whitespace checks pass. Signatures and inline bodies are
unchanged; explicit teardown ordering remains intact. GPU rendering was not
tested. The existing official-piece update-count behavior is unchanged.
2026-10-19 18:26:45 -07:00
Leonardo Zide 59225f9437 Finish piece manager synchronization and error ownership
Move sparse piece load errors and registry locking into lcPieceManager so
registry access, reference bookkeeping and copied worker metadata share one
owner. Release registry and error locks before resource teardown, recursive
project deletion or loader cancellation; remove library lock scopes around
UI source resolution and worker-drained settings changes.

Add HasOtherReferences to the shared asset reference for piece and texture
consumer checks, removing the library/manager consumer-query wrappers. Keep
the post-release cancellation count check and existing lifetime policies.
Rename the sparse error map to mPieceDataLoadErrors and document the completed
manager boundary and thread/teardown contracts.

Validation: warning-free macOS Qt 6.10 Debug build and all 43 offscreen
lifecycle checks pass, including concurrent metadata/error reads, settings
changes with pending requests and shared reference queries. Qt 5, ASan and
successful GPU rendering were not tested.
2026-10-19 18:03:34 -07:00
Leonardo Zide b38a1ce0fa Move piece lifetime bookkeeping into lcPieceManager
Piece registrations were centralized but creation, reference bookkeeping and
cleanup still lived in the library. Move creation/model reuse, normalization,
metadata renaming, reference operations, temporary cleanup, project transfer and
ordered identity teardown into the manager. Keep registration/deletion helpers
private and remove the temporary local-registry enumeration APIs.

The library remains the acquisition facade and coordinates source resolution,
loading, invalidation and cancellation after unlocking. Handles still bind to
the library. Clear sparse errors from lcPieceData::Unload before resource teardown
so manager deletion does not require a library pointer or callback. Sparse error
storage and the existing synchronization remain library-side.

Use one project-aware manager lookup, passing nullptr for shared-only searches.
Remove the maximum-size name reservation so short names can use inline storage.
Replace remaining bounded C-string copy/terminator pairs with lcstrcpy and record
the helper convention in common/AGENTS.md. Update the asset lifetime documentation.

Validation: macOS Qt 6.10 Debug build, whitespace checks and 40 offscreen lifecycle
checks, including already-zero release, last-owner cancellation, model reuse,
project detach/transfer cleanup, sparse errors, archive/directory caches and forced
nested external-project teardown. Successful GPU rendering is not covered by the
offscreen checks.
2026-10-19 11:48:21 -07:00
Leonardo Zide 41e5432395 Extract piece registration storage into lcPieceManager
Library and project name registries were accessed directly by source discovery,
cache processing and UI consumers. Centralize their storage and index operations
in a library-owned lcPieceManager, with ordered borrowed snapshots and copied
file metadata for worker include lookup.

Keep source resolution, reference counts, unload/deletion policy, cache I/O and
existing synchronization in lcPiecesLibrary. Preserve project-index collisions,
name normalization and reentrant teardown ordering while routing registration
changes through the manager.

Return the GetParts snapshot by value, name model rename/reservation operations
RenameModelPieceData, and centralize manager forward declarations in the PCH.
Document the registry boundary and register the new files in qmake.

Validation: macOS Qt 6.10 Debug build, whitespace checks and 35 offscreen lifecycle
checks, including archive/directory cold and warm caches, source precedence,
worker includes, borrowed snapshot lifetimes, rename collisions, project transfer
and nested external-project destruction. Successful GPU rendering is not covered
by the offscreen checks.
2026-10-19 11:07:43 -07:00
Leonardo Zide 98c67f3705 Align texture acquisition and scoped ownership
Texture lookup previously acquired references implicitly, and mesh sections and
loader requests paired raw texture pointers with manual release paths. Separate
borrowed lookup from scoped acquisition and explicit load requests/waits to match
the piece data ownership API.

Store lcTextureRef in mesh sections and loader requests, retain references on
section copies, and reset worker-shared request handles on the UI thread before
completion, cancellation, or shutdown. Keep reference primitives internal and use
scoped acquisitions for merge validation. Remove redundant section initialization
and synth-notification resets, and document the resulting ownership contracts.

Validation: macOS Debug build, diff whitespace checks, and 30 offscreen lifecycle
checks covering reference copies/moves, texture dependencies, cancellation,
shutdown, merge conflicts, and existing piece ownership paths. Successful GPU
upload/rendering is not covered by the offscreen checks.
2026-10-19 10:38:51 -07:00
Leonardo Zide 61be531444 Manage piece data ownership with scoped asset references
Replace manual piece-data retain/release bookkeeping with shared copyable
asset-reference handles. Pieces, models, selection, minifigs, thumbnails,
and stored insertion previews retain their dependencies explicitly, while
transient queries continue to borrow pointers.

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

Validated with the Qt 6.10 debug build and 26 offscreen lifecycle checks,
including cancellation, stale requests, copy/move operations, model/preview
teardown, undo, project merge/export, and loader shutdown.
2026-10-19 10:02:26 -07:00
Leonardo Zide 7bc6c018b1 Rename PieceInfo to lcPieceData
Use PieceData consistently for the shared part/model record, its related
APIs and variables, and its source files. Keep the existing reference
counting and loading behavior so ownership and readiness changes can be
reviewed separately.
2026-10-18 23:22:28 -07:00
Leonardo Zide 74e16b0d13 Enable shadow warnings for non-MSVC builds
Enable -Wshadow to bring GCC and Clang shadowing diagnostics closer to the MSVC warning configuration. Rename the filename extension variable to avoid shadowing the file type extension, and remove the unused synthesized-piece failure counter.

Validated with a macOS debug rebuild with no warnings or errors.
2026-10-17 13:41:41 -07:00
Leonardo Zide 58f60b9423 Separate asset readiness from loader request progress
Keep piece readiness, mesh publication, and settled synth errors in the
library while the loader manages scheduling, request outcomes, cancellation,
and deferred notifications. Capture synth inputs and base identities at the
library boundary so the loader treats pieces as opaque identities.

Use a shared asset readiness enum for pieces and textures. Keep texture
queueing, decoding, and retry progress in requests, pass resolved sources to
the loader, and centralize texture failure messages in lcTexture. Preserve
scoped wait failure and cancellation results alongside final readiness checks.

Consolidate shared priorities, sources, snapshots, build results, and texture
error codes in lc_assettypes.h, replacing lc_filesource.h. Forward declarations
remove the loader header's piece, library, and texture-manager dependencies.

Validated with the Qt Debug build, header isolation compilation, asset loader
lifecycle and scheduling regression checks, and 14 first/repeat export checks.
2026-10-16 17:42:16 -07:00
Leonardo Zide c7de6bfecc Keep asset refresh orchestration in the library
Keep the persistent registry of live synth instances in the library, where
settings changes can refresh completed and failed generations through the
normal request lifecycle. The loader retains only pending work and errors.

After draining assigned jobs to change shared settings safely, resume
replacement work asynchronously instead of waiting for unrelated queued
assets. Rebuild model geometry through the normal loader entry point,
invalidating stale requests without adding a consumer reference.

Reduce the loader's public API, group its declarations by responsibility,
and document the ownership and asynchronous refresh behavior.

Validated with the Qt Debug build, loader lifecycle and scheduling regression
checks, model and synth refresh checks, and 14 first/repeat export checks.
2026-10-16 15:17:40 -07:00
Leonardo Zide 6795887758 Schedule asset builds on the UI thread
Move request selection and lifecycle updates to the UI thread so worker jobs
only build their assigned asset and hand back a completion. This removes
request-map locking and keeps priority promotion, cancellation, and publication
under one thread's ownership.

Limit outstanding jobs to half the reported hardware threads, with a minimum
of one and no fixed cap. Release job slots before texture dependency settlement
and retain completion wakeups for synchronous waits, pause, and shutdown.

Share piece-request cleanup while preserving in-flight load holds. Use cancelled
request states to suppress invalidated notifications instead of an obsolete flag.
Clarify function names, document the lifecycle, and group header declarations.

Validation: Qt Debug build, scheduler and lifecycle harness, one-slot texture
success/retry checks, notification suppression and reference checks, 14 asset
exports, and git diff --check passed.
2026-10-16 13:13:02 -07:00
Leonardo Zide 793815efe8 Simplify asset loader scheduling with request states
The loader kept separate queues alongside its request maps and represented
request progress with overlapping booleans. Keep pending work in the maps
and select queued requests by priority and age using a shared lifecycle
state and scheduling fields.

Preserve cancellation ownership through outstanding worker completions,
restart staged meshes after settings changes, and protect wait-state reads
with the request mutex. Skip request scans when workers cannot start.
Pass the existing piece source snapshot to texture resolution instead of
separate lookup containers, and document the updated request lifecycle.

Validation: Qt Debug build, loader scheduling/cancellation/cache/lifecycle
checks, 14 first/repeat exports, and git diff --check passed.
2026-10-16 12:30:34 -07:00
Leonardo Zide 4129788f95 Build piece and synth meshes in one worker job
Color finalization no longer requires a UI-thread handoff between parsing and mesh conversion. Have the library and synth build services return converted private meshes, removing intermediate request/result mesh data and conversion requeue paths.

Fall back to source parsing immediately when a cached mesh cannot be decoded, eliminating SkipCache and the UI retry branch. Simplify paused staging cleanup and document the combined worker lifecycle; section finalization and publication remain on the UI thread.
2026-10-15 14:37:07 -07:00
Leonardo Zide 9f7e4d3a41 Unify mesh color resolution and section finalization
Cached meshes retained color-dependent flags and section ordering from the palette used when they were saved. Finalize fresh piece, synth, and cached mesh sections on the UI thread so color indices, flags, and ordering all reflect the current palette before publication.

Keep color codes in private mesh sections until finalization and remove separate color-code vectors, worker palette snapshots, and the early color-resolution path. Geometry conversion now runs without global color access or texture lookup callbacks.
2026-10-15 14:11:05 -07:00
Leonardo Zide f4fe4f8e87 Clarify asset loader request and completion APIs
Use consistent names for piece loading, synth generation, worker completions, and outstanding work. Remove the unused library drain wrapper and call the loader directly during settings updates.

Check for the final consumer release in the library before cancelling queued piece work, and document how running builds retain their load hold until completion. Update callers and asset-loading documentation.
2026-10-15 13:35:34 -07:00
Leonardo Zide d493eee066 Simplify asset loader state and shutdown
Asset requests carried duplicate phase, generation, and notification state.
Remove unused texture dependencies and project-path storage, derive conversion
from owned mesh data, and use request identity, cancellation, and the stopping
flag to reject obsolete results.

Decode cached meshes in the library's worker build service so serialized bytes
stay local and completions retain only decoded data and a cache-origin flag.
Make draining private to the loader destructor and remove duplicate shutdown
calls from library unload and destruction. Document the resulting lifecycle.

Validation: macOS Qt 6.10 Debug build; 14 first/repeat CLI exports; temporary
lifecycle checks covering cold/warm/corrupt caches, notification delivery and
suppression, pause/resume, cancellation, destructor-only draining, and reload.
2026-10-14 21:33:17 -07:00
Leonardo Zide 6a8c05acc5 Initialize stud settings and simplify loader bookkeeping
Pass effective startup stud settings into the library and apply color preferences before loading. Replace the reload toggle with UpdateStudStyleAndColors, preserve configured settings in the fallback library, and save the selected color config before combined preferences updates.

Remove temporary consumer references from synchronous piece waits, the redundant drain mutex, and worker-start queues. Derive loading states from current requests and use explicit piece names for request, queue, and notification storage. Document the updated ownership and settings contracts.

Validated with a Debug build, startup style and color exports, 14 asset export checks, and temporary lifecycle checks covering owned waits, failed and duplicate requirements, regeneration, cancellation, pause/resume, reference cleanup, and shutdown.
2026-10-14 10:57:30 -07:00
Leonardo Zide 3ec8b274b1 Unify asset loading priorities and clarify wait APIs
Replace load flags and the loader's separate priority enum with Low, Normal, and Blocking priorities. Blocking loads wait for completion; asynchronous loads and internal scheduling retain promotion-only behavior.

Acquire consumer references in the library, reuse loader loading for stud-style rebuilds, and remove QueuePiece, single-piece readiness wrappers, and the library texture readiness wrapper. Rename scoped readiness APIs to WaitFor functions and document their scheduling and lifetime contracts.

Validated with a Debug build and a temporary lifecycle harness covering priority promotion, blocking loads, reference cleanup, selective style reloads, cancellation, pause/resume, and shutdown.
2026-10-13 23:27:29 -07:00
Leonardo Zide 2051eeed42 Simplify asset loader lifecycle and paused scheduling
Make the loading lifecycle easier to follow by ordering functions by phase, splitting completion handling by asset type, and sharing color and texture preparation. Use Piece terminology, rename model rebuilding, and document reference and waiting behavior.

Keep pending work in the normal queues while paused, refresh piece snapshots before resuming, and remove duplicate enqueue and cancellation paths. QueuePiece now explicitly schedules background work.

Validated with a Debug build, 14 CLI export checks, and a temporary lifecycle harness covering staging resets, style refresh, cancellation, eight in-flight pause/resume cycles, last-consumer release, and shutdown.
2026-10-13 19:32:12 -07:00
Leonardo Zide 232fc54dee Remove unused asset request IDs
Part request IDs were assigned and copied to build results but never read. Remove the fields, counter, and assignments; stale completion checks continue to use request identity and generation.
2026-10-13 19:07:09 -07:00
Leonardo Zide 85d857c7b0 Clarify texture manager ownership and caller requirements
Document texture acquisition, reference release, CPU image preparation, and
file-position preservation in the public header. Explain that the asset loader
owns scheduling, OpenGL uploads, and retries. Initialize build-result errors
explicitly to lcTextureLoadError::None.

Validation: texture manager compilation and diff checks passed.
2026-10-13 11:36:30 -07:00
Leonardo Zide aa986287a0 Separate texture image assignment from OpenGL upload
SetImage previously uploaded automatically on the UI thread while the asset
loader needed a separate AdoptDecodedImage path to control context switching
and retries. Make SetImage and the renamed SetImages prepare and store pixels
only, remove AdoptDecodedImage and LoadImages, and upload generated atlases
explicitly. CacheStrings now accepts the caller's current rendering context.

Retain cube-map faces and supplied mipmaps through SetImages. Advance the
image index after each face so a supplied mip chain does not leave the next
face reading the previous face's last mip level.

Validation: full application build and diff checks passed. OpenGL readback
checks covered explicit setters, all six cube faces with generated and supplied
mipmaps, narrow and custom-stride image uploads, alignment restoration, failure
cleanup, and all nine grid levels against the previous generator.
2026-10-13 11:13:29 -07:00
Leonardo Zide b69db29428 Replace custom Image buffers with QImage
Texture decoding and generated atlases previously used a custom packed-pixel
container alongside QImage, duplicating storage and image-processing code.
Use QImage throughout texture preparation and upload, remove Image and its
pixel-format enum, and store font and string masks in Alpha8 atlases.

Preserve power-of-two dimensions with smooth scaling and keep the existing
OpenGL formats, shaders, and CPU mipmap filtering. Handle padded scanlines,
restore the caller's unpack alignment on every upload return, and centralize
binding cleanup so failed uploads do not leave stale texture caches.

Validation: full application build and diff checks passed. OpenGL readback
checks covered RGB/RGBA/alpha data, padded and custom strides, generated and
supplied mipmaps, unpack-state restoration, and failure cleanup. All nine
grid mip levels matched the previous generator. Visual rendering was checked
by the user.
2026-10-12 19:24:44 -07:00
Leonardo Zide 05ba97dd88 Simplify texture sources and remove unused loading paths
The direct texture-loading overloads and synchronous library/manager chain
have no callers. Remove them and the filename image-loading overload so
texture requests use the existing loader path.

Replace the texture's separate source fields with a shared lcFileSource
value in its own header. Keep source identity comparisons unchanged,
including content-based embedded texture checks during project merging.
The loader copies the source directly into its request, eliminating the
library and manager snapshot forwarding methods. Preserve the library
header's regrouping and update the asset-loading documentation.

Validated the debug build, file ownership/cursor/error checks, loader
failure and retry behavior, 800 concurrent archive reads, and matching
embedded/disk texture renders.
2026-10-12 12:15:48 -07:00
Leonardo Zide 1098c2de75 Separate texture management from library source access
Texture lookup, references, decoding, and scheduling were mixed into the pieces library. Introduce lcTextureManager to own texture identities, references, and CPU decoding, while keeping source lookup and file indexes in the library and asynchronous scheduling, uploads, retries, and publication in lcAssetLoader.

Add lcFileSourceSnapshot and OpenFile to return owned disk or memory streams. The library opens files on the existing loading thread and passes lcFile references to the manager. Image decoding uses QFile or existing memory buffers directly, preserving file cursors without an extra adapter.

Hide loader-only texture services, centralize failure state and diagnostics in lcTexture::SetFailed, and use each zip archive internal extraction lock instead of a library-wide mutex. Workers are drained before archive replacement or texture-manager teardown.

Validation: debug build, file ownership and cursor checks, texture lookup and decoding checks, bounded upload-retry and reference-release checks, 800 concurrent archive reads, and matching native GPU renders for embedded and disk textures.
2026-10-12 11:45:25 -07:00
Leonardo Zide 3019471e71 Make the library responsible for piece identity destruction
PieceInfo::Unload previously called back into the library to delete itself, making model and external-project teardown difficult to follow. Unregister and delete local identities in the library, and limit PieceInfo cleanup to its mesh, borrowed model pointer, and owned project.

Always clean owned projects in the PieceInfo destructor, including meshless containers. Bulk unload destroys outer external projects before remaining local identities and the shared catalog so nested teardown can safely release references.

Validation: debug build and lifetime checks covering final-reference release, retained deleted models, direct container deletion, 30 nested bulk-unload cycles, and 20 owner deletions with pending loads. AddressSanitizer stalled before main; sanitizer coverage remains unverified.
2026-10-11 22:49:47 -07:00
Leonardo Zide e3b0c24ecd Share library texture decoding between loading paths
The worker and synchronous texture paths duplicated embedded data decoding and disk/archive source handling. Centralize CPU decoding in DecodeTextureData while keeping resizing and upload behavior in each caller.

Report synchronous decode failures consistently with the worker path. Validation: debug build and ten CPU decoding checks covering valid, corrupt, missing, and invalid embedded, disk, and archive sources.
2026-10-11 22:26:17 -07:00
Leonardo Zide 476ebc06bf Separate main panel and popup parts display preferences
Changing parts display options in a popup previously overwrote the settings
used by the main Pieces panel. Store icon size, list mode, part names,
decorated pieces, and aliases separately for the main panel and for all
popups, while retaining shared search preferences.

Initialize popup context before loading preferences. Keep color locking
exclusive to the main panel so popups use the color supplied by their
caller, and hide color locking and palette editing in popup menus.

Popup preferences begin with their own defaults.

Validation: the Qt 6.10 debug build passes.
2026-10-11 22:12:32 -07:00
Leonardo Zide a84c3d7b7e Clean up shader initialization and preserve optional rendering fallbacks
The fixed-function renderer was removed, but shader capability guards,
bitmap text paths, and legacy attribute state still implied a working
shaderless fallback. Remove that obsolete state and check shader support
once when initializing rendering resources.

Return detailed context and shader errors through lcResult and show fatal
startup errors in GUI mode. Keep lighting, conditional lines, SDF text,
view sphere, and outlines optional, with program validity checks selecting
Flat shading, skipping unavailable overlays, or retaining selection tint.
Non-rendering exports initialize only the context needed for asset loading.

Apply icon availability to every parts list without overwriting requested
sizes. Disable unavailable lighting and view-sphere preference controls,
make attribute aliases explicit, and prevent signed-byte glyph indexing.

Validation: macOS debug build, GUI and image export under forced optional
shader compile/link failures, Flat fallback image comparisons, export
initialization checks, preference preservation, and glyph byte-range checks.
2026-10-11 21:00:12 -07:00
Leonardo Zide 75dc3883da Store profile enums as stable strings and harden settings reads
Persist enum preferences through named typed accessors using stable PascalCase tokens. Keep explicit legacy integer mappings so existing settings continue to load when enum members are reordered, and fall back to declared defaults for invalid values.

Validate scalar conversions and reject non-finite floats while preserving unsigned color bits returned by signed native settings backends. Read fixed part colors as unsigned values and remove their override when unlocking so the unlocked state survives restart.

Save both view-sphere enabled flags directly, preserving disabled spheres even when their remembered sizes are nonzero.

Validation: macOS application build and temporary enum and persistence regression checks passed. Windows and Linux execution was not performed.
2026-10-10 23:32:40 -07:00
Leonardo Zide 78fe37515c Add selection outlines with obscured geometry fill
Render selected pieces with an outline above scene geometry and a faint selection-colored fill only over obscured portions. Preserve line-only selection colors and insertion preview tint, and suppress bounding box corners in outline mode.

Keep the existing tint and edges mode as a preference and fallback when outline rendering is unavailable. Own outline framebuffers in each view, keep OpenGL operations in lcContext, and capture selection visibility before the grid through a callback scoped to each scene draw.

Validation: debug build and desktop OpenGL checks passed for occlusion, transparency, MSAA, HiDPI, line-only selection, framebuffer fallback, grid exclusion, and widget resource cleanup.
2026-10-10 15:54:13 -07:00
Leonardo Zide 6438bf2682 Expand far clipping distance when fitting large models
Large models can extend beyond the default far plane after fitting,
leaving the view blank or clipped. Pass an explicit adjustment flag
through view, model, and camera fitting, and reuse the fitted bounds
to expand clipping with a margin, rounded up to multiples of 10,000.

Adjust restored ordinary cameras after applying their saved state.
Preserve named cameras and explicit command-line clipping planes,
and avoid recalculating clipping during redraws or navigation.

Route output fitting through the view and move the bounds policy
definition out of the precompiled header.
2026-10-09 15:00:18 -07:00
Leonardo Zide ce26574116 Fix part selection highlighting and picker sizing
Highlight the full selected item and preserve thumbnail colors when part names are hidden. Keep cell dimensions stable while thumbnails load and update every entry sharing a completed thumbnail.

Use four columns for the default parts grid and fixed popup layouts for each icon size. Resize open popups when icons, names, or list mode change, account for capped widths and menu margins, and keep complete rows visible with room for at least two when needed. Enable part names by default for fresh preferences.

Validated with a Qt build, 240 sizing cases, and actual popup checks for constrained widths, name toggling, and menu frames across three Qt styles.

Closes #670
2026-10-09 10:09:03 -07:00
Leonardo Zide 50aa42f195 Keep part names visible and highlight selected rows
Freeze the Part column with a width cap and full-name tooltips. Synchronize row selection across views, forward wheel scrolling, and avoid the initial cell highlight.

Closes #565
2026-10-08 23:33:09 -07:00
Leonardo Zide 6d8f778e3d Show malformed model warnings in the status tooltip
Duplicate MPD definitions and circular model references were skipped silently, leaving files such as the examples in issue #533 empty without an explanation.

Record load warnings and display them alongside asset failures in the existing status icon tooltip. Collect warnings from nested external projects, deduplicate repeated references, and preserve source filenames across successful merges. Reset diagnostics when loading another document.

Validated both issue examples, nested and repeated references, merge warning preservation and deduplication, failed merges, and clean reloads. The macOS debug build and diff checks pass.

Fixes #533
2026-10-08 23:01:52 -07:00
Leonardo Zide f39e1ae70a Support embedded LDraw texture data
Parse !DATA blocks as project-scoped assets and resolve TEXMAP textures
from immutable asset snapshots without sharing same-named embedded images
between external projects. Preserve original document blocks when saving,
including blank lines, payload formatting, and unchanged model text.

Carry required submodels and embedded assets through copy, paste, and
exports, validating conflicts before import. Return clipboard errors to
the main window and preserve selected objects when Cut cannot copy them.

Fixes #937
2026-10-08 21:49:14 -07:00
Leonardo Zide 202035d4ea Follow TEXMAP texture search order
Search all texture locations with the textures/ prefix before trying bare filenames, so model-folder images cannot override prefixed library textures. Resolve nested textures across LDraw library roots and retain the selected disk path or archive entry for decoding and reuse.

Index disk-library PNG paths when loading the library to avoid repeated directory scans on the UI thread. Parse quoted filenames and escaped quotes and backslashes in all TEXMAP projection methods, and reject names that exceed texture storage.

Validated with the macOS debug build and focused checks for disk and archive precedence, nested and mixed-case paths, exact archive decoding, source reuse, reloads, symlinks, and filename parsing.
2026-10-08 10:54:38 -07:00
Leonardo Zide 7e9283fec0 Monitor only the active project file
Watching referenced and inactive projects can produce reload prompts outside the current document. Register watches only when a project becomes active, and retain content baselines so unchanged writes and application saves stay quiet.

Debounce file and directory events, retry transient read failures, and restore monitoring after replacement or deletion and recreation. Validate reload ownership around the dialog and scope candidate projects so failed reloads release their resources. Check read errors before clearing existing model data.
2026-10-08 10:12:14 -07:00
Leonardo Zide b3c325dbc3 Suppress file change prompts for unchanged contents
Filesystem watcher events can be triggered by metadata updates or identical rewrites, so forwarding every event directly to the reload dialog produces false notifications.

Track a content hash after loading, saving, and observing external edits. Only prompt for different readable contents, ignore stale and duplicate events, and restore the file watch after atomic replacement even when the contents are unchanged.

Validation: macOS debug build and native Qt watcher checks covering timestamp updates, identical replacements, same-size edits, repeated changes, own-save baselines, stale events, and empty files.
2026-10-07 09:49:25 -07:00
Leonardo Zide 8197a28517 Carry explicit meshes for direct export geometry
Direct geometry in root and nested models was collected with a null mesh override, despite the documented export contract. Exporters recovered the mesh through PieceInfo, which also selected the shared-part path in POV-Ray.

Supply the direct model mesh explicitly and document the borrowed mesh override semantics. This aligns collection with the contract while preserving catalog fallback and generated instance meshes.

Validation: Debug build, 33 targeted collection/export checks covering OBJ, COLLADA, 3DS and POV-Ray/LGEO, and git diff --check passed.
2026-10-05 14:24:34 -07:00
Leonardo Zide cd6cf53c7b Propagate parts-list rendering failures with lcResult
A null framebuffer readback was treated as empty geometry, so a parts
list could retain a blank entry or silently omit it after rendering failed.
The QImage return type also conflated an empty inventory with an error.

Return lcResult<QImage> and abort on failed thumbnail readback after
framebuffer and renderer cleanup. Report framebuffer creation and image
allocation failures explicitly while preserving successful empty inventory
and unavailable-geometry behavior. HTML export propagates the error text;
the instructions UI clears its stale image and displays a warning parented
to the scene's view.

Validation: full debug build and whitespace checks passed. A temporary
OpenGL fault-injection harness covered first and later readback failures
with both missing-parts policies, framebuffer creation failure, successful
empty results, and restoration of caller context, surface, and framebuffer
bindings. The warning dialog was not exercised interactively.
2026-10-05 14:18:57 -07:00
Leonardo Zide a152d3442c Restore caller GL state after output rendering
Step-image and parts-list helpers switched to the shared offscreen context
and left it current after rendering. Framebuffer release and destruction
also replaced a caller's existing binding with the default framebuffer.
This violated the output helpers' state-restoration contract on both
success and failure.

Capture the caller's context, surface, and framebuffer bindings in a scope
guard before asset waits and rendering. Restore them after the temporary
view is destroyed, preserving separate draw/read bindings where supported
and leaving no context current when the caller started without one.

Validation: full debug build and whitespace checks passed. A temporary
OpenGL regression harness verified step-image and parts-list success and
framebuffer creation failure with an alternate caller context, an alternate
surface on the shared context, nondefault framebuffer bindings, and no
initially current context.
2026-10-05 14:01:41 -07:00
Leonardo Zide 62a84dff86 Update AGENTS.md 2026-10-05 13:55:22 -07:00
Leonardo Zide 265b543f64 Detect include cycles and cancel blocked primitive loads
External projects were loaded before their identities were registered,
allowing mutually referencing files to recurse until stack exhaustion.
Track canonical paths during nested loads and reject repeated paths
with a failed-piece error.

Primitive dependencies could wait indefinitely on themselves or form
cycles across workers. Track active dependency edges, reject cycles,
and replace polling with cancellation-aware condition waits. Signal
cancellation during queue drains and restore its previous state afterward.

Validation: full debug build and whitespace checks passed. A temporary
regression harness covered external and primitive cycles, 100 concurrent
cyclic-load attempts, cancellation, completion, cache hits, and queue
shutdown.
2026-10-05 13:54:50 -07:00
Leonardo Zide 45de08aaae Propagate framebuffer tile readback failures before copying pixels 2026-10-05 13:33:16 -07:00
Leonardo Zide 1e9b0df568 Fix reference counting for shared thumbnail cache hits 2026-10-05 13:31:21 -07:00
Leonardo Zide e780f4a30a Settle assets before insertion and dragging 2026-10-05 12:47:30 -07:00
Leonardo Zide 145b3238db Document asset loading contracts and clarify queue helper naming 2026-10-04 19:29:33 -07:00
Leonardo Zide 622774f254 Simplify streaming asset priorities to promotion only
Keep each pending mesh and texture request at its highest requested priority
until it completes or is cancelled. Use one promotion helper for visible
requests and synchronous waits, propagating promotions to pending textures
without lowering priorities required by shared work.

Remove outgoing-project demotion and restoration during file opening and
LDD import. Existing reference counts continue to cancel unused queued work
and preserve assets shared with the incoming project or other views.

Validation: macOS Debug build, git diff --check, 16 priority/cancellation
checks, and 199 bounds, external-project undo, and output regression checks.
2026-10-04 17:19:28 -07:00
Leonardo Zide 669467d305 Restore external project references when undoing deletion 2026-10-04 14:05:54 -07:00
Leonardo Zide 8ceceb95ba Allow outputs with missing geometry and fit available meshes 2026-10-04 13:57:35 -07:00
Leonardo Zide dcd2ef5a3b Defer synth notifications and reject missing mesh includes 2026-10-04 12:37:17 -07:00
Leonardo Zide 586bba300f Include root direct geometry in camera fitting 2026-10-04 12:21:14 -07:00
Leonardo Zide 1753a185c8 Fix OBJ face syntax and COLLADA source counts 2026-10-04 11:37:19 -07:00
Leonardo Zide a861557c38 Replace placeholder piece type with failed asset state 2026-10-04 11:32:47 -07:00
Leonardo Zide 1f012285f1 Fix textured mesh collada export. 2026-10-01 19:19:27 -07:00
Leonardo Zide f772eeaa92 Fix exporting meshes with textures to obj. 2026-10-01 19:09:28 -07:00
Leonardo Zide 7b746acccf Increase arrow hover visibility. 2026-10-01 14:19:30 -07:00
Leonardo Zide 4d82380a41 Fix warning. 2026-10-01 12:15:04 -07:00
Leonardo Zide 941dd38724 Update loading tooltip. 2026-10-01 10:25:59 -07:00
Leonardo Zide 129e56f704 Fix mip generation OOB on odd sized textures. 2026-10-01 10:04:44 -07:00
Leonardo Zide c791447cdb Merge pull request #1037 from schodet/fix-appdata
Fix AppStream metadata
2026-09-30 15:28:27 -07:00
Leonardo Zide a719139d47 Replace asset loading progress bar with animated spinner 2026-09-30 10:15:44 -07:00
Leonardo Zide 6c8184b03c Merge pull request #1038 from schodet/fix-typo
Fix spelling typo
2026-09-30 08:55:37 -07:00
Leonardo Zide f4d00bf41d Fix Fade Previous Step affecting thumbnails. Fixes #1035. 2026-09-29 23:27:10 -07:00
Leonardo Zide 11bbd2a103 Guard asset readiness and buffer cleanup without renderer state
Skip container bounds publication for temporary models without a PieceInfo. Avoid accessing the GL context when releasing an empty shared buffer set while preserving dirty-state tracking.
2026-09-29 23:17:55 -07:00
Leonardo Zide 7e9384ed41 Sanitize export names and fix LGEO slope output
Keep COLLADA and POV-Ray identifiers close to source names while resolving collisions and maintaining consistent references.

Encode HTML filenames safely, escape display text, and use stable model ordinals. Preserve LGEO slope mappings and apply slope materials and bump normals correctly.
2026-09-29 19:00:09 -07:00
Leonardo Zide f87ee484eb Include direct model geometry in 3D exports 2026-09-29 09:22:56 -07:00
Leonardo Zide b242cf9ad5 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.
2026-09-28 22:49:16 -07:00
Leonardo Zide bd6bd50ecc Require complete geometry for image and 3D exports
Report missing generated meshes and failed image writes, keep thumbnails and interactive geometry using their fallback behavior, and frame command-line images at the requested step or full range. Use explicit mesh loader flags and reject incomplete synthesized meshes. Keep selection and ray tests aligned with the rendered mesh.
2026-09-28 11:39:49 -07:00
Leonardo Zide 50d4e15c53 Add missing include. 2026-09-28 10:54:24 -07:00
Leonardo Zide 1d02dde890 Scope project pieces and preserve assets across model changes
Keep project-local piece mappings separate from library pieces and preserve references across rename, merge, delete, and Save As.

Remap local external pieces after Save As, retain texture source order, invalidate stale loads, and refresh previews and thumbnails after asset changes. Save projects atomically and preserve active model selection in the model list.
2026-09-28 09:22:09 -07:00
Leonardo Zide 8e2490c4ba Open LDraw and LDD projects without waiting for meshes
Queue model geometry builds and mesh conversion on workers so project opening can return after parsing. Keep complete-asset waits for previews and output, and show project loading progress in the status bar.

Preserve live pieces when project names overlap, restore mappings after failed loads, and include detached pieces in buffer and stud-style updates.
2026-09-27 10:59:34 -07:00
Leonardo Zide d6359a063a Show loading placeholders and require complete assets for output
- Draw a shared cube for parts whose meshes or textures are still loading.
- Update model bounds, picking, views, and loading status as assets complete.
- Wait for required assets before thumbnails, screenshots, instructions,
  and geometry exports; report failures instead of rendering placeholders.
- Render and pick direct geometry in external projects.
- Defer shared buffer repacking until pending loads finish.
2026-09-26 16:06:20 -07:00
Leonardo Zide 9fd8d50179 Harden asset cache and mesh loading paths
Validate cache I/O and zlib results, reject incomplete mesh allocations, and keep shared buffer offsets safe when rebuilding. Document loader priorities and repository editing conventions.
2026-09-26 11:10:02 -07:00
Leonardo Zide a79d4953e3 Implement staged mesh and texture loading
Build meshes and decode textures in workers, then publish meshes and upload textures on the UI thread. Mark a part loaded only after its textures are ready.

Add request cancellation, priorities, and waits scoped to required assets. Keep previews and thumbnails from rendering incomplete parts, preserve failure diagnostics, and handle corrupt caches and project changes.
2026-09-25 16:58:29 -07:00
Leonardo Zide 29c9c98402 Introduce asset loader boundary for streaming groundwork
Move part scheduling and futures into a library-owned lcAssetLoader while preserving current loading behavior. Hold queued parts alive through completion, deliver completion notifications on the UI thread, and drain work before library teardown.

Pause queued work during stud-style changes, rebuild affected meshes, and use an unambiguous mesh-cache settings key. Guard mesh-buffer rebuilding against allocation failure.

Add the loader to the qmake build and document common build conventions.
2026-09-24 18:56:48 -07:00
Leonardo Zide 2c3a5a5a4e Fix TEXMAP NEXT and included-file state
Consume NEXT after the next geometry line, keep each included file’s TEXMAP entries scoped to that file, and propagate nested load failures. Reset failed primitive state and partial mesh data before retries.
2026-09-23 22:19:31 -07:00
Leonardo Zide 3b11dcc6a2 Resolve project texture filenames without case sensitivity 2026-09-23 12:28:02 -07:00
Leonardo Zide 3e437bcb7d Manage temporary project texture references 2026-09-23 11:57:17 -07:00
Leonardo Zide 3e376ddb84 Release mesh texture references on destruction 2026-09-23 11:09:15 -07:00
Leonardo Zide 197a3d18e6 Validate mesh geometry records before use
Skip incomplete type 1-5 records instead of processing uninitialized coordinates or filenames. Bound type-1 filename parsing to its destination buffer.
2026-09-22 22:58:36 -07:00
Leonardo Zide cec7e8a79f Remove conditional-line index data
Pack conditional vertices by color and material in high/shared/low order so each LOD draws with one non-indexed call per section. Calculate bounds from direct vertex ranges, support meshes without index data, and rename the section draw fields.

Fix conditional vertex remapping in included primitives and bump the mesh cache version to rebuild stale data.

Fixes #644
2026-09-22 22:46:01 -07:00
Leonardo Zide 57332f834e Improve move gizmo feedback 2026-09-21 22:34:18 -07:00
Leonardo Zide 499984b9aa Persist view menu display settings 2026-09-19 20:57:07 -07:00
Leonardo Zide 6228ae4313 Add release checklist 2026-09-19 17:44:46 -07:00
Leonardo Zide 61b30d0a42 Finalize release library links 2026-09-19 17:28:49 -07:00
Leonardo Zide 5196355d4a Build release packages from tags 2026-09-19 16:44:29 -07:00
Leonardo Zide 94f32bd0ed Update appveyor.yml 2026-09-19 11:16:39 -07:00