mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-11 11:07:40 +00:00
Compare commits
16 Commits
hanif/user
...
v2.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d9eb1792f | ||
|
|
691d97867c | ||
|
|
da7e0540a9 | ||
|
|
70fd764e7d | ||
|
|
ed2e7d02d0 | ||
|
|
75cd880336 | ||
|
|
8e4b188473 | ||
|
|
fc9a8aa93f | ||
|
|
21f48a2654 | ||
|
|
548f291a81 | ||
|
|
bc3af3d00f | ||
|
|
269eb81028 | ||
|
|
08d4ae7dbd | ||
|
|
b1411e253f | ||
|
|
42cce5399c | ||
|
|
b17f5e3946 |
19
.github/workflows/build_all.yml
vendored
19
.github/workflows/build_all.yml
vendored
@@ -55,11 +55,23 @@ jobs:
|
|||||||
build_linux:
|
build_linux:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
# Build both arches on every event (PRs included), through the same
|
||||||
|
# build_check_cache -> build_deps -> build_orca chain (the AppImage).
|
||||||
|
# aarch64 always uses the GitHub-hosted arm runner (there is no arm
|
||||||
|
# self-hosted server). amd64's empty arch is load-bearing: it keeps the
|
||||||
|
# historical 'linux-clang' deps cache key and the unsuffixed asset names.
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: ""
|
||||||
|
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||||
|
- arch: "aarch64"
|
||||||
|
os: ubuntu-24.04-arm
|
||||||
# Don't run scheduled builds on forks:
|
# Don't run scheduled builds on forks:
|
||||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||||
uses: ./.github/workflows/build_check_cache.yml
|
uses: ./.github/workflows/build_check_cache.yml
|
||||||
with:
|
with:
|
||||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
os: ${{ matrix.os }}
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
build_windows:
|
build_windows:
|
||||||
@@ -99,7 +111,9 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
unit_tests:
|
unit_tests:
|
||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
# Tests are built on the aarch64 leg by default (faster GitHub arm runner),
|
||||||
|
# so run them there; self-hosted builds them on the amd64 server instead.
|
||||||
|
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
|
||||||
needs: build_linux
|
needs: build_linux
|
||||||
if: ${{ !cancelled() && success() }}
|
if: ${{ !cancelled() && success() }}
|
||||||
steps:
|
steps:
|
||||||
@@ -237,4 +251,3 @@ jobs:
|
|||||||
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
|
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/build_check_cache.yml
vendored
6
.github/workflows/build_check_cache.yml
vendored
@@ -33,8 +33,10 @@ jobs:
|
|||||||
- name: set outputs
|
- name: set outputs
|
||||||
id: set_outputs
|
id: set_outputs
|
||||||
env:
|
env:
|
||||||
# Keep macOS cache keys and paths architecture-specific.
|
# Keep macOS/Linux cache keys architecture-specific. amd64 Linux passes
|
||||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || 'linux-clang') }}
|
# no arch (key stays 'linux-clang', preserving the existing cache);
|
||||||
|
# aarch64 gets its own 'linux-clang-aarch64' key.
|
||||||
|
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||||
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || '/OrcaSlicer_dep' }}
|
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || '/OrcaSlicer_dep' }}
|
||||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
42
.github/workflows/build_orca.yml
vendored
42
.github/workflows/build_orca.yml
vendored
@@ -65,6 +65,11 @@ jobs:
|
|||||||
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
|
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
|
||||||
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
||||||
|
# Per-arch Linux AppImage naming: amd64 keeps the historical unsuffixed
|
||||||
|
# name (arch_suffix empty). Unused on macOS/Windows.
|
||||||
|
if [ '${{ inputs.arch }}' = 'aarch64' ]; then
|
||||||
|
echo "arch_suffix=_aarch64" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Get the version and date on Windows
|
- name: Get the version and date on Windows
|
||||||
@@ -401,16 +406,23 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
./build_linux.sh -istrlL
|
# Build + tar the unit tests (-t) only on the leg that runs them: the
|
||||||
|
# aarch64 leg by default (faster GitHub arm runner), or amd64 when using
|
||||||
|
# self-hosted runners (no arm self-hosted server). unit_tests downloads
|
||||||
|
# this tarball. The profile validator is built with -s, so amd64 keeps it.
|
||||||
|
tests=${{ (!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64') }}
|
||||||
|
if $tests; then flags=-istrlL; else flags=-isrlL; fi
|
||||||
|
./build_linux.sh "$flags"
|
||||||
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
||||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||||
chmod +x ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
|
||||||
tar -cvpf build_tests.tar build/tests
|
chmod +x "$appimage"
|
||||||
|
if $tests; then tar -cvpf build_tests.tar build/tests; fi
|
||||||
|
|
||||||
# Use tar because upload-artifacts won't always preserve directory structure
|
# Use tar because upload-artifacts won't always preserve directory structure
|
||||||
# and doesn't preserve file permissions
|
# and doesn't preserve file permissions
|
||||||
- name: Upload Test Artifact
|
- name: Upload Test Artifact
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux' && ((!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64'))
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: ${{ github.sha }}-tests
|
name: ${{ github.sha }}-tests
|
||||||
@@ -420,7 +432,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Run external slicer regression tests
|
- name: Run external slicer regression tests
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -430,7 +442,7 @@ jobs:
|
|||||||
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
||||||
|
|
||||||
- name: Build orca_custom_preset_tests
|
- name: Build orca_custom_preset_tests
|
||||||
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
|
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64'
|
||||||
working-directory: ${{ github.workspace }}/build/src/Release
|
working-directory: ${{ github.workspace }}/build/src/Release
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -442,11 +454,11 @@ jobs:
|
|||||||
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
|
||||||
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
|
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
|
||||||
|
|
||||||
- name: Upload OrcaSlicer_profile_validator Ubuntu
|
- name: Upload OrcaSlicer_profile_validator Ubuntu
|
||||||
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
@@ -458,12 +470,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||||
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.AppImage
|
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_nightly.AppImage
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||||
- name: Deploy Ubuntu release
|
- name: Deploy Ubuntu release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
uses: rickstaa/action-create-tag@v1
|
uses: rickstaa/action-create-tag@v1
|
||||||
with:
|
with:
|
||||||
tag: "nightly-builds"
|
tag: "nightly-builds"
|
||||||
@@ -472,7 +484,7 @@ jobs:
|
|||||||
message: "nightly-builds"
|
message: "nightly-builds"
|
||||||
|
|
||||||
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
|
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
@@ -483,7 +495,7 @@ jobs:
|
|||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy orca_custom_preset_tests
|
- name: Deploy orca_custom_preset_tests
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
|
|||||||
2
.github/workflows/publish_release.yml
vendored
2
.github/workflows/publish_release.yml
vendored
@@ -17,7 +17,7 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
tag:
|
tag:
|
||||||
description: 'Tag of the draft release to upload to (e.g. v2.4.0-beta)'
|
description: 'Tag of the draft release to upload to (e.g. v2.4.0)'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ It can also be installed through graphical software managers (KDE Discover, GNOM
|
|||||||
|
|
||||||
### AppImage
|
### AppImage
|
||||||
|
|
||||||
|
AppImages are published for both **x86_64** and **aarch64** (ARM64). Pick the file matching your CPU — the ARM64 build has `aarch64` in its name (e.g. `OrcaSlicer_Linux_AppImage_Ubuntu2404_aarch64_*.AppImage`).
|
||||||
|
|
||||||
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||||
2. Double click the downloaded file to run it.
|
2. Double click the downloaded file to run it.
|
||||||
|
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ const float EPSILON = 0.0001;
|
|||||||
|
|
||||||
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
|
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
|
||||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
#define LIGHT_TOP_DIFFUSE (0.85 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
|
#define LIGHT_TOP_SPECULAR (0.35 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_TOP_SHININESS 128.0
|
#define LIGHT_TOP_SHININESS 32.0
|
||||||
|
|
||||||
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
|
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
|
||||||
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
#define LIGHT_FRONT_DIFFUSE (0.35 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
|
#define LIGHT_FRONT_SPECULAR (0.12 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_FRONT_SHININESS 64.0
|
#define LIGHT_FRONT_SHININESS 16.0
|
||||||
|
|
||||||
#define INTENSITY_AMBIENT 0.22
|
#define INTENSITY_AMBIENT 0.25
|
||||||
#define WINDOW_REFLECTION_INTENSITY 0.55
|
#define WINDOW_REFLECTION_INTENSITY 0.30
|
||||||
|
|
||||||
struct PrintVolumeDetection
|
struct PrintVolumeDetection
|
||||||
{
|
{
|
||||||
@@ -162,11 +162,11 @@ vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
|
|||||||
float bars = 1.0;
|
float bars = 1.0;
|
||||||
|
|
||||||
// Fresnel effect for edge glow
|
// Fresnel effect for edge glow
|
||||||
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.0);
|
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.2);
|
||||||
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
|
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
|
||||||
|
|
||||||
float intensity = window_light * bars * (0.25 + 0.25 * fresnel) * facing;
|
float intensity = window_light * bars * (0.15 + 0.15 * fresnel) * facing;
|
||||||
intensity = clamp(intensity, 0.0, 0.45);
|
intensity = clamp(intensity, 0.0, 0.25);
|
||||||
|
|
||||||
return vec3(intensity);
|
return vec3(intensity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ const float EPSILON = 0.0001;
|
|||||||
|
|
||||||
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
|
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
|
||||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
#define LIGHT_TOP_DIFFUSE (0.85 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
|
#define LIGHT_TOP_SPECULAR (0.35 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_TOP_SHININESS 128.0
|
#define LIGHT_TOP_SHININESS 32.0
|
||||||
|
|
||||||
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
|
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
|
||||||
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
#define LIGHT_FRONT_DIFFUSE (0.35 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
|
#define LIGHT_FRONT_SPECULAR (0.12 * INTENSITY_CORRECTION)
|
||||||
#define LIGHT_FRONT_SHININESS 64.0
|
#define LIGHT_FRONT_SHININESS 16.0
|
||||||
|
|
||||||
#define INTENSITY_AMBIENT 0.22
|
#define INTENSITY_AMBIENT 0.25
|
||||||
#define WINDOW_REFLECTION_INTENSITY 0.55
|
#define WINDOW_REFLECTION_INTENSITY 0.30
|
||||||
|
|
||||||
struct PrintVolumeDetection
|
struct PrintVolumeDetection
|
||||||
{
|
{
|
||||||
@@ -164,12 +164,13 @@ vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
|
|||||||
// No bars - just pure circular glass
|
// No bars - just pure circular glass
|
||||||
float bars = 1.0;
|
float bars = 1.0;
|
||||||
|
|
||||||
// Fresnel effect for edge glow
|
|
||||||
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.0);
|
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.2);
|
||||||
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
|
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
|
||||||
|
|
||||||
float intensity = window_light * bars * (0.25 + 0.25 * fresnel) * facing;
|
|
||||||
intensity = clamp(intensity, 0.0, 0.45);
|
float intensity = window_light * bars * (0.15 + 0.15 * fresnel) * facing;
|
||||||
|
intensity = clamp(intensity, 0.0, 0.25);
|
||||||
|
|
||||||
return vec3(intensity);
|
return vec3(intensity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,8 @@
|
|||||||
<color type="primary" scheme_preference="dark">#00695C</color>
|
<color type="primary" scheme_preference="dark">#00695C</color>
|
||||||
</branding>
|
</branding>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="2.4.0-dev" date="2026-05-22" type="development">
|
<release version="2.4.0" date="2026-06-08" type="development">
|
||||||
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds</url>
|
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/2.4.0</url>
|
||||||
<description>
|
<description>
|
||||||
<p>See the release page for detailed changelog.</p>
|
<p>See the release page for detailed changelog.</p>
|
||||||
</description>
|
</description>
|
||||||
|
|||||||
@@ -276,6 +276,9 @@ void AppConfig::set_defaults()
|
|||||||
if (get(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS).empty())
|
if (get(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS).empty())
|
||||||
set_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS, false);
|
set_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS, false);
|
||||||
|
|
||||||
|
if (get(SETTING_OPENGL_PHONG_SMOOTH_NORMALS).empty())
|
||||||
|
set_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS, false);
|
||||||
|
|
||||||
if (get(SETTING_OPENGL_PHONG_SSAO).empty())
|
if (get(SETTING_OPENGL_PHONG_SSAO).empty())
|
||||||
set_bool(SETTING_OPENGL_PHONG_SSAO, false);
|
set_bool(SETTING_OPENGL_PHONG_SSAO, false);
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ using namespace nlohmann;
|
|||||||
#define SETTING_OPENGL_SHADING_MODEL "opengl_shading_model"
|
#define SETTING_OPENGL_SHADING_MODEL "opengl_shading_model"
|
||||||
#define SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS "opengl_phong_basic_plate_shadows"
|
#define SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS "opengl_phong_basic_plate_shadows"
|
||||||
#define SETTING_OPENGL_PHONG_SSAO "opengl_phong_ssao"
|
#define SETTING_OPENGL_PHONG_SSAO "opengl_phong_ssao"
|
||||||
|
#define SETTING_OPENGL_PHONG_SMOOTH_NORMALS "opengl_phong_smooth_normals"
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"
|
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"
|
||||||
|
|||||||
@@ -879,7 +879,9 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
|||||||
std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
||||||
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
||||||
std::vector<std::pair<ObjectID, unsigned int>> &objPrintVec,
|
std::vector<std::pair<ObjectID, unsigned int>> &objPrintVec,
|
||||||
std::vector<unsigned int>& printExtruders)
|
std::vector<unsigned int>& printExtruders,
|
||||||
|
std::map<ObjectID, ExPolygons>* objectBrimAreasOut,
|
||||||
|
std::map<ObjectID, ExPolygons>* supportBrimAreasOut)
|
||||||
{
|
{
|
||||||
std::map<ObjectID, double> brim_width_map;
|
std::map<ObjectID, double> brim_width_map;
|
||||||
std::map<ObjectID, ExPolygons> brimAreaMap;
|
std::map<ObjectID, ExPolygons> brimAreaMap;
|
||||||
@@ -928,12 +930,25 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
|||||||
for (size_t iia = 0; iia < islands_area.size(); ++iia)
|
for (size_t iia = 0; iia < islands_area.size(); ++iia)
|
||||||
islands_area[iia].translate(plate_shift);
|
islands_area[iia].translate(plate_shift);
|
||||||
|
|
||||||
|
// Orca: keep translated brim footprints for skirt grouping.
|
||||||
|
auto translate_area_map = [plate_shift](const std::map<ObjectID, ExPolygons>& src) {
|
||||||
|
std::map<ObjectID, ExPolygons> dst = src;
|
||||||
|
for (auto& [_, areas] : dst)
|
||||||
|
for (ExPolygon& area : areas)
|
||||||
|
area.translate(plate_shift);
|
||||||
|
return dst;
|
||||||
|
};
|
||||||
|
if (objectBrimAreasOut != nullptr)
|
||||||
|
*objectBrimAreasOut = translate_area_map(brimAreaMap);
|
||||||
|
if (supportBrimAreasOut != nullptr)
|
||||||
|
*supportBrimAreasOut = translate_area_map(supportBrimAreaMap);
|
||||||
|
|
||||||
const bool combine_brims = print.config().combine_brims.value;
|
const bool combine_brims = print.config().combine_brims.value;
|
||||||
const bool is_by_object = (print.config().print_sequence == PrintSequence::ByObject);
|
const bool is_by_object = (print.config().print_sequence == PrintSequence::ByObject);
|
||||||
const bool can_combine_brims = combine_brims && !is_by_object;
|
const bool can_combine_brims = combine_brims && !is_by_object;
|
||||||
|
|
||||||
if (!can_combine_brims) {
|
if (!can_combine_brims) {
|
||||||
// Orca: Generate brims separately for each object when multiple extruders are used
|
// Orca: Generate brims separately when brims cannot be combined.
|
||||||
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
||||||
if (!iter->second.empty()) {
|
if (!iter->second.empty()) {
|
||||||
brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef slic3r_Brim_hpp_
|
#ifndef slic3r_Brim_hpp_
|
||||||
#define slic3r_Brim_hpp_
|
#define slic3r_Brim_hpp_
|
||||||
|
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
|
|
||||||
#include<map>
|
#include<map>
|
||||||
@@ -19,7 +20,9 @@ void make_brim(const Print& print, PrintTryCancel try_cancel,
|
|||||||
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
||||||
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
||||||
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
||||||
std::vector<unsigned int>& printExtruders);
|
std::vector<unsigned int>& printExtruders,
|
||||||
|
std::map<ObjectID, ExPolygons>* objectBrimAreasOut = nullptr,
|
||||||
|
std::map<ObjectID, ExPolygons>* supportBrimAreasOut = nullptr);
|
||||||
|
|
||||||
// BBS: automatically make brim
|
// BBS: automatically make brim
|
||||||
ExtrusionEntityCollection make_brim_auto(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area);
|
ExtrusionEntityCollection make_brim_auto(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area);
|
||||||
|
|||||||
@@ -5151,44 +5151,30 @@ LayerResult GCode::process_layer(
|
|||||||
bool has_insert_wrapping_detection_gcode = false;
|
bool has_insert_wrapping_detection_gcode = false;
|
||||||
|
|
||||||
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
|
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
|
||||||
// Orca: Print unified global brim before any object.
|
|
||||||
// Only do this if `combine_brims` is enabled and we are printing by layer.
|
|
||||||
if (first_layer && sequence_by_layer && m_config.combine_brims && !print.m_brimMap.empty()) {
|
|
||||||
const ObjectID unified_object_id = [&]() -> ObjectID {
|
|
||||||
ObjectID id;
|
|
||||||
bool found = false;
|
|
||||||
for (const auto& [obj_id, brim] : print.m_brimMap) {
|
|
||||||
const bool has_printable_entities = std::any_of(brim.entities.begin(), brim.entities.end(),
|
|
||||||
[](const ExtrusionEntity* ee) { return ee != nullptr; });
|
|
||||||
if (!has_printable_entities)
|
|
||||||
continue;
|
|
||||||
if (found)
|
|
||||||
return ObjectID();
|
|
||||||
id = obj_id;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
return found ? id : ObjectID();
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (unified_object_id.valid()) {
|
|
||||||
const auto it = print.m_brimMap.find(unified_object_id);
|
|
||||||
if (it != print.m_brimMap.end()) {
|
|
||||||
this->set_origin(0., 0.);
|
|
||||||
for (const ExtrusionEntity* ee : it->second.entities)
|
|
||||||
if (ee != nullptr)
|
|
||||||
gcode += this->extrude_entity(*ee, "brim", m_config.support_speed.value);
|
|
||||||
|
|
||||||
// Mark brim as printed for this object to avoid per-object brim emission later.
|
|
||||||
this->m_objsWithBrim.erase(unified_object_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int extruder_id : layer_tools.extruders)
|
for (unsigned int extruder_id : layer_tools.extruders)
|
||||||
{
|
{
|
||||||
if (print.config().skirt_type == stCombined && !print.skirt().empty())
|
if ((print.config().skirt_type == stCombined ||
|
||||||
gcode += generate_skirt(print, print.skirt(), Point(0, 0), layer.object()->config().skirt_start_angle, layer_tools, layer,
|
(print.config().skirt_type == stPerObject && print.config().print_sequence == PrintSequence::ByLayer)) &&
|
||||||
extruder_id);
|
!print.skirt_groups().empty()) {
|
||||||
|
bool skirt_generated_for_current_print_z = false;
|
||||||
|
for (const ExtrusionEntityCollection& skirt_group : print.skirt_groups()) {
|
||||||
|
if (skirt_group.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Orca: each grouped skirt is emitted as its own collection so higher skirt layers
|
||||||
|
// follow the same per-group behavior as the first layer.
|
||||||
|
if (first_layer)
|
||||||
|
m_skirt_done.clear();
|
||||||
|
else if (skirt_generated_for_current_print_z && !m_skirt_done.empty())
|
||||||
|
m_skirt_done.pop_back();
|
||||||
|
|
||||||
|
std::string skirt_gcode = generate_skirt(print, skirt_group, Point(0, 0), layer.object()->config().skirt_start_angle,
|
||||||
|
layer_tools, layer, extruder_id);
|
||||||
|
if (!skirt_gcode.empty())
|
||||||
|
skirt_generated_for_current_print_z = true;
|
||||||
|
gcode += std::move(skirt_gcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (print.config().print_sequence == PrintSequence::ByLayer && m_enable_exclude_object && print.config().support_object_skip_flush.value) {
|
if (print.config().print_sequence == PrintSequence::ByLayer && m_enable_exclude_object && print.config().support_object_skip_flush.value) {
|
||||||
std::vector<size_t> filament_instances_id;
|
std::vector<size_t> filament_instances_id;
|
||||||
@@ -5289,6 +5275,37 @@ LayerResult GCode::process_layer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Orca: Print unified global brim after the skirt and before any object.
|
||||||
|
// Only do this if `combine_brims` is enabled and we are printing by layer.
|
||||||
|
if (first_layer && sequence_by_layer && m_config.combine_brims && !print.m_brimMap.empty()) {
|
||||||
|
const ObjectID unified_object_id = [&]() -> ObjectID {
|
||||||
|
ObjectID id;
|
||||||
|
for (const auto& [obj_id, brim] : print.m_brimMap) {
|
||||||
|
const bool has_printable_entities = std::any_of(brim.entities.begin(), brim.entities.end(),
|
||||||
|
[](const ExtrusionEntity* ee) { return ee != nullptr; });
|
||||||
|
if (!has_printable_entities)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (id.valid())
|
||||||
|
return ObjectID();
|
||||||
|
|
||||||
|
id = obj_id;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (unified_object_id.valid() && this->m_objsWithBrim.find(unified_object_id) != this->m_objsWithBrim.end()) {
|
||||||
|
const ExtrusionEntityCollection& unified_brim = print.m_brimMap.at(unified_object_id);
|
||||||
|
this->set_origin(0., 0.);
|
||||||
|
for (const ExtrusionEntity* ee : unified_brim.entities)
|
||||||
|
if (ee != nullptr)
|
||||||
|
gcode += this->extrude_entity(*ee, "brim", m_config.support_speed.value);
|
||||||
|
|
||||||
|
// Mark brim as printed for this object to avoid per-object brim emission later.
|
||||||
|
this->m_objsWithBrim.erase(unified_object_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
|
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
|
||||||
std::vector<ObjectByExtruder::Island::Region> by_region_per_copy_cache;
|
std::vector<ObjectByExtruder::Island::Region> by_region_per_copy_cache;
|
||||||
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <numeric>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
@@ -2442,7 +2443,10 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
start_time = (long long)Slic3r::Utils::get_current_time_utc();
|
start_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||||||
|
|
||||||
m_skirt.clear();
|
m_skirt.clear();
|
||||||
|
m_skirt_groups.clear();
|
||||||
m_skirt_convex_hull.clear();
|
m_skirt_convex_hull.clear();
|
||||||
|
m_objectBrimAreas.clear();
|
||||||
|
m_supportBrimAreas.clear();
|
||||||
m_first_layer_convex_hull.points.clear();
|
m_first_layer_convex_hull.points.clear();
|
||||||
for (PrintObject *object : m_objects) object->m_skirt.clear();
|
for (PrintObject *object : m_objects) object->m_skirt.clear();
|
||||||
|
|
||||||
@@ -2536,7 +2540,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
if (this->has_brim()) {
|
if (this->has_brim()) {
|
||||||
Polygons islands_area;
|
Polygons islands_area;
|
||||||
make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
|
make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
|
||||||
m_supportBrimMap, objPrintVec, printExtruders);
|
m_supportBrimMap, objPrintVec, printExtruders, &m_objectBrimAreas, &m_supportBrimAreas);
|
||||||
for (Polygon& poly_ex : islands_area)
|
for (Polygon& poly_ex : islands_area)
|
||||||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||||||
for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
|
for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
|
||||||
@@ -2658,11 +2662,13 @@ void Print::_make_skirt()
|
|||||||
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
|
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect points from all layers contained in skirt height.
|
struct ObjectSkirtHull {
|
||||||
Points points;
|
PrintObject* object;
|
||||||
|
Polygon hull;
|
||||||
|
};
|
||||||
|
|
||||||
// BBS
|
// Orca: build one local occupied hull per object from object and support geometry up to skirt height.
|
||||||
std::map<PrintObject*, Polygon> object_convex_hulls;
|
std::vector<ObjectSkirtHull> object_convex_hulls;
|
||||||
for (PrintObject *object : m_objects) {
|
for (PrintObject *object : m_objects) {
|
||||||
Points object_points;
|
Points object_points;
|
||||||
// Get object layers up to skirt_height_z.
|
// Get object layers up to skirt_height_z.
|
||||||
@@ -2680,30 +2686,13 @@ void Print::_make_skirt()
|
|||||||
layer->support_fills.collect_points(object_points);
|
layer->support_fills.collect_points(object_points);
|
||||||
}
|
}
|
||||||
|
|
||||||
object_convex_hulls.insert({ object, Slic3r::Geometry::convex_hull(object_points) });
|
object_convex_hulls.push_back({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||||||
|
|
||||||
// Repeat points for each object copy.
|
|
||||||
for (const PrintInstance &instance : object->instances()) {
|
|
||||||
Points copy_points = object_points;
|
|
||||||
for (Point &pt : copy_points)
|
|
||||||
pt += instance.shift;
|
|
||||||
append(points, copy_points);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include the wipe tower.
|
if (object_convex_hulls.empty())
|
||||||
append(points, this->first_layer_wipe_tower_corners());
|
|
||||||
|
|
||||||
// Unless draft shield is enabled, include all brims as well.
|
|
||||||
if (config().draft_shield == dsDisabled)
|
|
||||||
append(points, m_first_layer_convex_hull.points);
|
|
||||||
|
|
||||||
if (points.size() < 3)
|
|
||||||
// At least three points required for a convex hull.
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->throw_if_canceled();
|
this->throw_if_canceled();
|
||||||
Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
|
|
||||||
|
|
||||||
// Skirt may be printed on several layers, having distinct layer heights,
|
// Skirt may be printed on several layers, having distinct layer heights,
|
||||||
// but loops must be aligned so can't vary width/spacing
|
// but loops must be aligned so can't vary width/spacing
|
||||||
@@ -2725,13 +2714,13 @@ void Print::_make_skirt()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
// Initial skirt centerline offset from the occupied outline.
|
||||||
// The skirt will touch the brim if the brim is extruded.
|
// The skirt will touch the occupied outline if skirt_distance is zero.
|
||||||
auto distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
// Generate loops inward to outward; callers reverse them before G-code export.
|
||||||
// Draw outlines from outside to inside.
|
|
||||||
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
||||||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
auto append_skirt_loops_for_hull = [&](const Polygon& hull, ExtrusionEntityCollection& dst, bool collect_skirt_hull) {
|
||||||
if (m_config.skirt_type == stCombined) {
|
float distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||||
|
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||||||
for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
|
for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
|
||||||
this->throw_if_canceled();
|
this->throw_if_canceled();
|
||||||
// Offset the skirt outside.
|
// Offset the skirt outside.
|
||||||
@@ -2739,8 +2728,8 @@ void Print::_make_skirt()
|
|||||||
// Generate the skirt centerline.
|
// Generate the skirt centerline.
|
||||||
Polygon loop;
|
Polygon loop;
|
||||||
{
|
{
|
||||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
// Orca: the hull already represents the occupied outline used for this skirt.
|
||||||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
Polygons loops = offset(hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||||
if (loops.empty())
|
if (loops.empty())
|
||||||
break;
|
break;
|
||||||
@@ -2756,7 +2745,7 @@ void Print::_make_skirt()
|
|||||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||||
)));
|
)));
|
||||||
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
||||||
m_skirt.append(eloop);
|
dst.append(eloop);
|
||||||
if (m_config.min_skirt_length.value > 0) {
|
if (m_config.min_skirt_length.value > 0) {
|
||||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||||
@@ -2772,69 +2761,147 @@ void Print::_make_skirt()
|
|||||||
++ extruder_idx;
|
++ extruder_idx;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
// The skirt length is not limited, extrude the skirt with the 1st extruder only.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
m_skirt.clear();
|
|
||||||
}
|
|
||||||
// Brims were generated inside out, reverse to print the outmost contour first.
|
|
||||||
m_skirt.reverse();
|
|
||||||
|
|
||||||
// Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull.
|
if (collect_skirt_hull)
|
||||||
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
for (Polygon &poly : offset(hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||||||
append(m_skirt_convex_hull, std::move(poly.points));
|
append(m_skirt_convex_hull, std::move(poly.points));
|
||||||
|
};
|
||||||
|
|
||||||
if (m_config.skirt_type == stPerObject) {
|
m_skirt.clear();
|
||||||
// BBS
|
m_skirt_groups.clear();
|
||||||
for (auto obj_cvx_hull : object_convex_hulls) {
|
|
||||||
double object_skirt_distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
|
||||||
PrintObject* object = obj_cvx_hull.first;
|
|
||||||
object->m_skirt.clear();
|
|
||||||
extruded_length.assign(extruded_length.size(), 0.);
|
|
||||||
for (size_t i = m_config.skirt_loops.value, extruder_idx = 0; i > 0; -- i) {
|
|
||||||
object_skirt_distance += float(scale_(spacing));
|
|
||||||
Polygon loop;
|
|
||||||
{
|
|
||||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
|
||||||
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
|
|
||||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
|
||||||
if (loops.empty())
|
|
||||||
break;
|
|
||||||
loop = loops.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extrude the skirt loop.
|
if (m_config.skirt_type == stPerObject && m_config.print_sequence == PrintSequence::ByObject) {
|
||||||
ExtrusionLoop eloop(elrSkirt);
|
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||||
eloop.paths.emplace_back(ExtrusionPath(
|
object_hull.object->m_skirt.clear();
|
||||||
ExtrusionPath(
|
append_skirt_loops_for_hull(object_hull.hull, object_hull.object->m_skirt, false);
|
||||||
erSkirt,
|
object_hull.object->m_skirt.reverse();
|
||||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
}
|
||||||
flow.width(),
|
} else if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
|
||||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
struct SkirtGroupItem {
|
||||||
)));
|
Points occupied_points;
|
||||||
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
bool emits_skirt;
|
||||||
object->m_skirt.append(std::move(eloop));
|
};
|
||||||
if (m_config.min_skirt_length.value > 0) {
|
|
||||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
|
||||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
|
||||||
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
|
|
||||||
// Not extruded enough yet with the current extruder. Add another loop.
|
|
||||||
if (i == 1)
|
|
||||||
++ i;
|
|
||||||
} else {
|
|
||||||
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
|
|
||||||
// Enough extruded with the current extruder. Extrude with the next one,
|
|
||||||
// until the prescribed number of skirt loops is extruded.
|
|
||||||
if (extruder_idx + 1 < extruders.size())
|
|
||||||
++ extruder_idx;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Orca: group items represent occupied first-layer areas. Object items emit skirts;
|
||||||
|
// obstacle-only items, such as wipe tower, only force nearby object groups to merge.
|
||||||
|
std::vector<SkirtGroupItem> group_items;
|
||||||
|
const coord_t grouping_offset = scale_(m_config.skirt_distance.value + m_config.skirt_loops.value * spacing);
|
||||||
|
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||||
|
PrintObject* object = object_hull.object;
|
||||||
|
Points occupied_points;
|
||||||
|
for (const PrintInstance &instance : object->instances()) {
|
||||||
|
Points copy_points = object_hull.hull.points;
|
||||||
|
for (Point &pt : copy_points)
|
||||||
|
pt += instance.shift;
|
||||||
|
append(occupied_points, copy_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto append_brim_points = [&occupied_points](const ExPolygons& areas) {
|
||||||
|
for (const ExPolygon& area : areas)
|
||||||
|
append(occupied_points, area.contour.points);
|
||||||
|
};
|
||||||
|
if (auto it = m_objectBrimAreas.find(object->id()); it != m_objectBrimAreas.end())
|
||||||
|
append_brim_points(it->second);
|
||||||
|
if (auto it = m_supportBrimAreas.find(object->id()); it != m_supportBrimAreas.end())
|
||||||
|
append_brim_points(it->second);
|
||||||
|
if (occupied_points.size() < 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Orca: include the object's brim/support-brim footprint before checking skirt collisions.
|
||||||
|
group_items.push_back({ std::move(occupied_points), true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orca: the wipe tower contributes occupied area, but does not emit a skirt by itself.
|
||||||
|
Points wipe_tower_points = this->first_layer_wipe_tower_corners();
|
||||||
|
if (wipe_tower_points.size() >= 3)
|
||||||
|
group_items.push_back({ std::move(wipe_tower_points), false });
|
||||||
|
|
||||||
|
std::vector<size_t> parent(group_items.size());
|
||||||
|
std::iota(parent.begin(), parent.end(), 0);
|
||||||
|
// Orca: union-find keeps collision merging local without repeatedly rebuilding item lists.
|
||||||
|
auto find_parent = [&parent](size_t idx) {
|
||||||
|
while (parent[idx] != idx) {
|
||||||
|
parent[idx] = parent[parent[idx]];
|
||||||
|
idx = parent[idx];
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
};
|
||||||
|
auto unite = [&parent, &find_parent](size_t a, size_t b) {
|
||||||
|
a = find_parent(a);
|
||||||
|
b = find_parent(b);
|
||||||
|
if (a != b)
|
||||||
|
parent[b] = a;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Orca: combined skirt is the same grouping model with all items forced into one group.
|
||||||
|
if (m_config.skirt_type == stCombined && !group_items.empty())
|
||||||
|
for (size_t i = 1; i < group_items.size(); ++i)
|
||||||
|
unite(0, i);
|
||||||
|
|
||||||
|
auto build_grouped_points = [&]() {
|
||||||
|
struct GroupData {
|
||||||
|
Points points;
|
||||||
|
bool emits_skirt = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<size_t, GroupData> grouped;
|
||||||
|
for (size_t i = 0; i < group_items.size(); ++i) {
|
||||||
|
GroupData& group = grouped[find_parent(i)];
|
||||||
|
append(group.points, group_items[i].occupied_points);
|
||||||
|
group.emits_skirt = group.emits_skirt || group_items[i].emits_skirt;
|
||||||
|
}
|
||||||
|
return grouped;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool groups_changed = m_config.skirt_type == stPerObject;
|
||||||
|
while (groups_changed) {
|
||||||
|
groups_changed = false;
|
||||||
|
auto grouped_points = build_grouped_points();
|
||||||
|
std::vector<std::pair<size_t, Polygon>> group_envelopes;
|
||||||
|
for (const auto& [root, group] : grouped_points) {
|
||||||
|
if (group.points.size() < 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Orca: emitting groups are expanded to their final skirt reach; obstacle groups are not.
|
||||||
|
Polygon envelope = Geometry::convex_hull(group.points);
|
||||||
|
if (group.emits_skirt) {
|
||||||
|
// Orca: merge groups when a skirt envelope intersects another group or obstacle.
|
||||||
|
Polygons envelopes = offset(envelope, grouping_offset, ClipperLib::jtRound, float(scale_(0.1)));
|
||||||
|
if (envelopes.empty())
|
||||||
|
continue;
|
||||||
|
envelope = std::move(envelopes.front());
|
||||||
|
}
|
||||||
|
group_envelopes.emplace_back(root, std::move(envelope));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < group_envelopes.size(); ++i) {
|
||||||
|
for (size_t j = i + 1; j < group_envelopes.size(); ++j) {
|
||||||
|
const size_t root_i = find_parent(group_envelopes[i].first);
|
||||||
|
const size_t root_j = find_parent(group_envelopes[j].first);
|
||||||
|
if (root_i != root_j && !intersection(group_envelopes[i].second, group_envelopes[j].second).empty()) {
|
||||||
|
unite(root_i, root_j);
|
||||||
|
groups_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto grouped_points = build_grouped_points();
|
||||||
|
for (auto& [_, group] : grouped_points) {
|
||||||
|
if (!group.emits_skirt || group.points.size() < 3)
|
||||||
|
continue;
|
||||||
|
// Orca: after merging, use the occupied outline directly; do not add skirt distance twice.
|
||||||
|
ExtrusionEntityCollection group_skirt;
|
||||||
|
append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
|
||||||
|
if (!group_skirt.empty()) {
|
||||||
|
group_skirt.reverse();
|
||||||
|
// Orca: keep m_skirt as a flattened compatibility mirror for preview/extents.
|
||||||
|
m_skirt.append(group_skirt.entities);
|
||||||
|
m_skirt_groups.push_back(std::move(group_skirt));
|
||||||
}
|
}
|
||||||
object->m_skirt.reverse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -971,6 +971,7 @@ public:
|
|||||||
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
|
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
|
||||||
std::vector<size_t> layers_sorted_for_object(float start, float end, std::vector<LayerPtrs> &layers_of_objects, std::vector<BoundingBox> &boundingBox_for_objects, VecOfPoints& objects_instances_shift);
|
std::vector<size_t> layers_sorted_for_object(float start, float end, std::vector<LayerPtrs> &layers_of_objects, std::vector<BoundingBox> &boundingBox_for_objects, VecOfPoints& objects_instances_shift);
|
||||||
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
|
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
|
||||||
|
const std::vector<ExtrusionEntityCollection>& skirt_groups() const { return m_skirt_groups; }
|
||||||
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
|
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
|
||||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||||
// It does NOT encompass user extrusions generated by custom G-code,
|
// It does NOT encompass user extrusions generated by custom G-code,
|
||||||
@@ -1144,9 +1145,13 @@ private:
|
|||||||
|
|
||||||
// Ordered collections of extrusion paths to build skirt loops and brim.
|
// Ordered collections of extrusion paths to build skirt loops and brim.
|
||||||
ExtrusionEntityCollection m_skirt;
|
ExtrusionEntityCollection m_skirt;
|
||||||
|
std::vector<ExtrusionEntityCollection> m_skirt_groups;
|
||||||
// BBS: collecting extrusion paths to build brim by objs
|
// BBS: collecting extrusion paths to build brim by objs
|
||||||
std::map<ObjectID, ExtrusionEntityCollection> m_brimMap;
|
std::map<ObjectID, ExtrusionEntityCollection> m_brimMap;
|
||||||
std::map<ObjectID, ExtrusionEntityCollection> m_supportBrimMap;
|
std::map<ObjectID, ExtrusionEntityCollection> m_supportBrimMap;
|
||||||
|
// Orca: cached occupied brim footprints used when grouping per-object skirts.
|
||||||
|
std::map<ObjectID, ExPolygons> m_objectBrimAreas;
|
||||||
|
std::map<ObjectID, ExPolygons> m_supportBrimAreas;
|
||||||
// Convex hull of the 1st layer extrusions.
|
// Convex hull of the 1st layer extrusions.
|
||||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||||
// It does NOT encompass user extrusions generated by custom G-code,
|
// It does NOT encompass user extrusions generated by custom G-code,
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
|
|
||||||
// Enable rendering of objects using environment map
|
// Enable rendering of objects using environment map
|
||||||
#define ENABLE_ENVIRONMENT_MAP 0
|
#define ENABLE_ENVIRONMENT_MAP 0
|
||||||
// Enable smoothing of objects normals
|
|
||||||
#define ENABLE_SMOOTH_NORMALS 0
|
|
||||||
// Enable rendering markers for options in preview as fixed screen size points
|
// Enable rendering markers for options in preview as fixed screen size points
|
||||||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
|
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
|
||||||
|
|
||||||
|
|||||||
@@ -759,12 +759,8 @@ int GLVolumeCollection::load_object_volume(
|
|||||||
v.set_color(color_from_model_volume(*model_volume));
|
v.set_color(color_from_model_volume(*model_volume));
|
||||||
v.name = model_volume->name;
|
v.name = model_volume->name;
|
||||||
|
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
v.model.init_from(mesh, true);
|
|
||||||
#else
|
|
||||||
v.model.init_from(*mesh);
|
v.model.init_from(*mesh);
|
||||||
if (need_raycaster) { v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh); }
|
if (need_raycaster) { v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh); }
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
||||||
|
|
||||||
if (model_volume->is_model_part())
|
if (model_volume->is_model_part())
|
||||||
@@ -815,13 +811,9 @@ void GLVolumeCollection::load_object_auxiliary(
|
|||||||
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
|
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
|
||||||
this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
|
this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
|
||||||
GLVolume& v = *this->volumes.back();
|
GLVolume& v = *this->volumes.back();
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
v.model.init_from(mesh, true);
|
|
||||||
#else
|
|
||||||
v.model.init_from(mesh);
|
v.model.init_from(mesh);
|
||||||
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
|
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
|
||||||
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
|
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
|
v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
|
||||||
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
||||||
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||||
|
|||||||
@@ -2793,22 +2793,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
|
TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
|
||||||
assert(! mesh.empty());
|
assert(! mesh.empty());
|
||||||
mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse());
|
mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse());
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
volume.model.init_from(mesh, true);
|
|
||||||
#else
|
|
||||||
volume.model.init_from(mesh);
|
volume.model.init_from(mesh);
|
||||||
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
|
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Reload the original volume.
|
// Reload the original volume.
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
|
|
||||||
#else
|
|
||||||
const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh();
|
const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh();
|
||||||
volume.model.init_from(new_mesh);
|
volume.model.init_from(new_mesh);
|
||||||
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(new_mesh));
|
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(new_mesh));
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
|
//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
|
||||||
@@ -10001,11 +9993,7 @@ void GLCanvas3D::_load_sla_shells()
|
|||||||
const TriangleMesh& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) {
|
const TriangleMesh& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) {
|
||||||
m_volumes.volumes.emplace_back(new GLVolume(color));
|
m_volumes.volumes.emplace_back(new GLVolume(color));
|
||||||
GLVolume& v = *m_volumes.volumes.back();
|
GLVolume& v = *m_volumes.volumes.back();
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
v.model.init_from(mesh, true);
|
|
||||||
#else
|
|
||||||
v.model.init_from(mesh);
|
v.model.init_from(mesh);
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
||||||
v.composite_id.volume_id = volume_id;
|
v.composite_id.volume_id = volume_id;
|
||||||
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0));
|
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0));
|
||||||
|
|||||||
@@ -14,18 +14,20 @@
|
|||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
#if defined(L)
|
||||||
|
#undef L
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <igl/per_face_normals.h>
|
#include <igl/per_face_normals.h>
|
||||||
#include <igl/per_corner_normals.h>
|
#include <igl/per_corner_normals.h>
|
||||||
#include <igl/per_vertex_normals.h>
|
#include <igl/per_vertex_normals.h>
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
|
|
||||||
#include <glad/gl.h>
|
#include <glad/gl.h>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
|
||||||
static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals)
|
static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals)
|
||||||
{
|
{
|
||||||
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||||
@@ -41,7 +43,7 @@ static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_norm
|
|||||||
Eigen::Index(face_normals.size()), 3).cast<double>();
|
Eigen::Index(face_normals.size()), 3).cast<double>();
|
||||||
Eigen::MatrixXd out_normals;
|
Eigen::MatrixXd out_normals;
|
||||||
|
|
||||||
igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals);
|
igl::per_corner_normals(vertices, indices, 1.0, out_normals);
|
||||||
|
|
||||||
normals = std::vector<stl_normal>(mesh.its.vertices.size());
|
normals = std::vector<stl_normal>(mesh.its.vertices.size());
|
||||||
for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
|
for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
|
||||||
@@ -50,7 +52,6 @@ static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_norm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
|
|
||||||
void GLModel::Geometry::add_vertex(const Vec2f& position)
|
void GLModel::Geometry::add_vertex(const Vec2f& position)
|
||||||
{
|
{
|
||||||
@@ -455,17 +456,41 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
|||||||
data.reserve_vertices(3 * its.indices.size());
|
data.reserve_vertices(3 * its.indices.size());
|
||||||
data.reserve_indices(3 * its.indices.size());
|
data.reserve_indices(3 * its.indices.size());
|
||||||
|
|
||||||
// vertices + indices
|
// Read user preference: smooth normals enabled
|
||||||
unsigned int vertices_counter = 0;
|
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
|
||||||
for (uint32_t i = 0; i < its.indices.size(); ++i) {
|
const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
|
||||||
const stl_triangle_vertex_indices face = its.indices[i];
|
|
||||||
const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
|
if (realistic_mode && smooth_normals_enabled) {
|
||||||
const stl_vertex n = face_normal_normalized(vertex);
|
// Use per-corner smooth normals (via IGL)
|
||||||
for (size_t j = 0; j < 3; ++j) {
|
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||||
data.add_vertex(vertex[j], n);
|
using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||||
|
Eigen::MatrixXd vertices = MapMatrixXfUnaligned(its.vertices.front().data(), Eigen::Index(its.vertices.size()), 3).cast<double>();
|
||||||
|
Eigen::MatrixXi indices = MapMatrixXiUnaligned(its.indices.front().data(), Eigen::Index(its.indices.size()), 3);
|
||||||
|
Eigen::MatrixXd corner_normals;
|
||||||
|
igl::per_corner_normals(vertices, indices, 5.0, corner_normals);
|
||||||
|
|
||||||
|
unsigned int vertices_counter = 0;
|
||||||
|
for (uint32_t i = 0; i < its.indices.size(); ++i) {
|
||||||
|
const stl_triangle_vertex_indices face = its.indices[i];
|
||||||
|
for (size_t j = 0; j < 3; ++j) {
|
||||||
|
const Vec3f normal = corner_normals.row(Eigen::Index(i * 3 + j)).cast<float>();
|
||||||
|
data.add_vertex(its.vertices[face[j]], normal);
|
||||||
|
}
|
||||||
|
data.add_triangle(vertices_counter, vertices_counter + 1, vertices_counter + 2);
|
||||||
|
vertices_counter += 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Original flat (per-face) normals
|
||||||
|
unsigned int vertices_counter = 0;
|
||||||
|
for (uint32_t i = 0; i < its.indices.size(); ++i) {
|
||||||
|
const stl_triangle_vertex_indices face = its.indices[i];
|
||||||
|
const stl_vertex vertex[3] = {its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]]};
|
||||||
|
const stl_vertex n = face_normal_normalized(vertex);
|
||||||
|
for (size_t j = 0; j < 3; ++j)
|
||||||
|
data.add_vertex(vertex[j], n);
|
||||||
|
data.add_triangle(vertices_counter, vertices_counter + 1, vertices_counter + 2);
|
||||||
|
vertices_counter += 3;
|
||||||
}
|
}
|
||||||
vertices_counter += 3;
|
|
||||||
data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update bounding box
|
// update bounding box
|
||||||
|
|||||||
@@ -1578,6 +1578,15 @@ void PreferencesDialog::create_items()
|
|||||||
);
|
);
|
||||||
g_sizer->Add(item_realistic_shadows);
|
g_sizer->Add(item_realistic_shadows);
|
||||||
|
|
||||||
|
|
||||||
|
auto item_realistic_smooth_normals = create_item_checkbox(
|
||||||
|
_L("Smooth normals"),
|
||||||
|
_L("Applies smooth normals to the realistic view.\n\nRequires manual scene reload to take effect "
|
||||||
|
"(right-click on 3D view → \"Reload All\")."),
|
||||||
|
SETTING_OPENGL_PHONG_SMOOTH_NORMALS
|
||||||
|
);
|
||||||
|
g_sizer->Add(item_realistic_smooth_normals);
|
||||||
|
|
||||||
//// GRAPHICS > Anti-aliasing
|
//// GRAPHICS > Anti-aliasing
|
||||||
g_sizer->Add(create_item_title(_L("Anti-aliasing")), 1, wxEXPAND);
|
g_sizer->Add(create_item_title(_L("Anti-aliasing")), 1, wxEXPAND);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ set(SLIC3R_APP_KEY "OrcaSlicer")
|
|||||||
if(NOT DEFINED BBL_INTERNAL_TESTING)
|
if(NOT DEFINED BBL_INTERNAL_TESTING)
|
||||||
set(BBL_INTERNAL_TESTING "0")
|
set(BBL_INTERNAL_TESTING "0")
|
||||||
endif()
|
endif()
|
||||||
set(SoftFever_VERSION "2.4.0-dev")
|
set(SoftFever_VERSION "2.4.0")
|
||||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
||||||
SoftFever_VERSION_MATCH ${SoftFever_VERSION})
|
SoftFever_VERSION_MATCH ${SoftFever_VERSION})
|
||||||
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})
|
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
|
|||||||
Reference in New Issue
Block a user