mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-21 09:38:43 +03:00
Compare commits
16 Commits
| 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:
|
||||
strategy:
|
||||
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:
|
||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_check_cache.yml
|
||||
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 }}
|
||||
secrets: inherit
|
||||
build_windows:
|
||||
@@ -99,7 +111,9 @@ jobs:
|
||||
secrets: inherit
|
||||
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
|
||||
if: ${{ !cancelled() && success() }}
|
||||
steps:
|
||||
@@ -237,4 +251,3 @@ jobs:
|
||||
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
|
||||
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
|
||||
|
||||
|
||||
6
.github/workflows/build_check_cache.yml
vendored
6
.github/workflows/build_check_cache.yml
vendored
@@ -33,8 +33,10 @@ jobs:
|
||||
- name: set outputs
|
||||
id: set_outputs
|
||||
env:
|
||||
# Keep macOS cache keys and paths architecture-specific.
|
||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || 'linux-clang') }}
|
||||
# Keep macOS/Linux cache keys architecture-specific. amd64 Linux passes
|
||||
# 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' }}
|
||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||
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 "date=$(date +'%Y%m%d')" >> $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
|
||||
|
||||
- name: Get the version and date on Windows
|
||||
@@ -401,16 +406,23 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
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
|
||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
chmod +x ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
tar -cvpf build_tests.tar build/tests
|
||||
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
|
||||
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
|
||||
# and doesn't preserve file permissions
|
||||
- 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
|
||||
with:
|
||||
name: ${{ github.sha }}-tests
|
||||
@@ -420,7 +432,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Run external slicer regression tests
|
||||
if: runner.os == 'Linux'
|
||||
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
|
||||
timeout-minutes: 20
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -430,7 +442,7 @@ jobs:
|
||||
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
||||
|
||||
- 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
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -442,11 +454,11 @@ jobs:
|
||||
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
|
||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
|
||||
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
|
||||
|
||||
- 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
|
||||
with:
|
||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||
@@ -458,12 +470,12 @@ jobs:
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||
release_id: 137995723
|
||||
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.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 }}${{ env.arch_suffix }}_nightly.AppImage
|
||||
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
|
||||
- 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
|
||||
with:
|
||||
tag: "nightly-builds"
|
||||
@@ -472,7 +484,7 @@ jobs:
|
||||
message: "nightly-builds"
|
||||
|
||||
- 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
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||
@@ -483,7 +495,7 @@ jobs:
|
||||
max_releases: 1
|
||||
|
||||
- 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
|
||||
with:
|
||||
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
|
||||
type: string
|
||||
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
|
||||
type: string
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ It can also be installed through graphical software managers (KDE Discover, GNOM
|
||||
|
||||
### 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).
|
||||
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)
|
||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 128.0
|
||||
#define LIGHT_TOP_DIFFUSE (0.85 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.35 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 32.0
|
||||
|
||||
// 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);
|
||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SHININESS 64.0
|
||||
#define LIGHT_FRONT_DIFFUSE (0.35 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SPECULAR (0.12 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SHININESS 16.0
|
||||
|
||||
#define INTENSITY_AMBIENT 0.22
|
||||
#define WINDOW_REFLECTION_INTENSITY 0.55
|
||||
#define INTENSITY_AMBIENT 0.25
|
||||
#define WINDOW_REFLECTION_INTENSITY 0.30
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@@ -162,11 +162,11 @@ vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
|
||||
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 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);
|
||||
}
|
||||
|
||||
@@ -10,18 +10,18 @@ const float EPSILON = 0.0001;
|
||||
|
||||
// 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);
|
||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 128.0
|
||||
#define LIGHT_TOP_DIFFUSE (0.85 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.35 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 32.0
|
||||
|
||||
// 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);
|
||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SHININESS 64.0
|
||||
#define LIGHT_FRONT_DIFFUSE (0.35 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SPECULAR (0.12 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_FRONT_SHININESS 16.0
|
||||
|
||||
#define INTENSITY_AMBIENT 0.22
|
||||
#define WINDOW_REFLECTION_INTENSITY 0.55
|
||||
#define INTENSITY_AMBIENT 0.25
|
||||
#define WINDOW_REFLECTION_INTENSITY 0.30
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@@ -164,12 +164,13 @@ vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
|
||||
// No bars - just pure circular glass
|
||||
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 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);
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
<color type="primary" scheme_preference="dark">#00695C</color>
|
||||
</branding>
|
||||
<releases>
|
||||
<release version="2.4.0-dev" date="2026-05-22" type="development">
|
||||
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds</url>
|
||||
<release version="2.4.0" date="2026-06-08" type="development">
|
||||
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/2.4.0</url>
|
||||
<description>
|
||||
<p>See the release page for detailed changelog.</p>
|
||||
</description>
|
||||
|
||||
@@ -276,6 +276,9 @@ void AppConfig::set_defaults()
|
||||
if (get(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS).empty())
|
||||
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())
|
||||
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_PHONG_BASIC_PLATE_SHADOWS "opengl_phong_basic_plate_shadows"
|
||||
#define SETTING_OPENGL_PHONG_SSAO "opengl_phong_ssao"
|
||||
#define SETTING_OPENGL_PHONG_SMOOTH_NORMALS "opengl_phong_smooth_normals"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#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>& supportBrimMap,
|
||||
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, 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)
|
||||
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 is_by_object = (print.config().print_sequence == PrintSequence::ByObject);
|
||||
const bool can_combine_brims = combine_brims && !is_by_object;
|
||||
|
||||
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) {
|
||||
if (!iter->second.empty()) {
|
||||
brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_Brim_hpp_
|
||||
#define slic3r_Brim_hpp_
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include<map>
|
||||
@@ -19,7 +20,9 @@ void make_brim(const Print& print, PrintTryCancel try_cancel,
|
||||
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
||||
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
||||
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
|
||||
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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (print.config().skirt_type == stCombined && !print.skirt().empty())
|
||||
gcode += generate_skirt(print, print.skirt(), Point(0, 0), layer.object()->config().skirt_start_angle, layer_tools, layer,
|
||||
extruder_id);
|
||||
if ((print.config().skirt_type == stCombined ||
|
||||
(print.config().skirt_type == stPerObject && print.config().print_sequence == PrintSequence::ByLayer)) &&
|
||||
!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) {
|
||||
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):
|
||||
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) {
|
||||
@@ -5327,7 +5344,7 @@ LayerResult GCode::process_layer(
|
||||
gcode += std::move(skirt_gcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
|
||||
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
|
||||
// To control print speed of the 1st object layer printed over raft interface.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
#include <boost/filesystem/path.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();
|
||||
|
||||
m_skirt.clear();
|
||||
m_skirt_groups.clear();
|
||||
m_skirt_convex_hull.clear();
|
||||
m_objectBrimAreas.clear();
|
||||
m_supportBrimAreas.clear();
|
||||
m_first_layer_convex_hull.points.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()) {
|
||||
Polygons islands_area;
|
||||
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)
|
||||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
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);
|
||||
}
|
||||
|
||||
// Collect points from all layers contained in skirt height.
|
||||
Points points;
|
||||
struct ObjectSkirtHull {
|
||||
PrintObject* object;
|
||||
Polygon hull;
|
||||
};
|
||||
|
||||
// BBS
|
||||
std::map<PrintObject*, Polygon> object_convex_hulls;
|
||||
// Orca: build one local occupied hull per object from object and support geometry up to skirt height.
|
||||
std::vector<ObjectSkirtHull> object_convex_hulls;
|
||||
for (PrintObject *object : m_objects) {
|
||||
Points object_points;
|
||||
// Get object layers up to skirt_height_z.
|
||||
@@ -2680,30 +2686,13 @@ void Print::_make_skirt()
|
||||
layer->support_fills.collect_points(object_points);
|
||||
}
|
||||
|
||||
object_convex_hulls.insert({ 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);
|
||||
}
|
||||
object_convex_hulls.push_back({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||||
}
|
||||
|
||||
// Include the wipe tower.
|
||||
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.
|
||||
if (object_convex_hulls.empty())
|
||||
return;
|
||||
|
||||
this->throw_if_canceled();
|
||||
Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
|
||||
|
||||
// Skirt may be printed on several layers, having distinct layer heights,
|
||||
// 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).
|
||||
// The skirt will touch the brim if the brim is extruded.
|
||||
auto distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
// Draw outlines from outside to inside.
|
||||
// Initial skirt centerline offset from the occupied outline.
|
||||
// The skirt will touch the occupied outline if skirt_distance is zero.
|
||||
// Generate loops inward to outward; callers reverse them before G-code export.
|
||||
// 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.);
|
||||
if (m_config.skirt_type == stCombined) {
|
||||
auto append_skirt_loops_for_hull = [&](const Polygon& hull, ExtrusionEntityCollection& dst, bool collect_skirt_hull) {
|
||||
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) {
|
||||
this->throw_if_canceled();
|
||||
// Offset the skirt outside.
|
||||
@@ -2739,8 +2728,8 @@ void Print::_make_skirt()
|
||||
// Generate the skirt centerline.
|
||||
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(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
// Orca: the hull already represents the occupied outline used for this skirt.
|
||||
Polygons loops = offset(hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
@@ -2756,7 +2745,7 @@ void Print::_make_skirt()
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
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) {
|
||||
// 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];
|
||||
@@ -2772,69 +2761,147 @@ void Print::_make_skirt()
|
||||
++ extruder_idx;
|
||||
}
|
||||
} 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.
|
||||
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||||
append(m_skirt_convex_hull, std::move(poly.points));
|
||||
if (collect_skirt_hull)
|
||||
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));
|
||||
};
|
||||
|
||||
if (m_config.skirt_type == stPerObject) {
|
||||
// BBS
|
||||
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();
|
||||
}
|
||||
m_skirt.clear();
|
||||
m_skirt_groups.clear();
|
||||
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
||||
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.
|
||||
}
|
||||
if (m_config.skirt_type == stPerObject && m_config.print_sequence == PrintSequence::ByObject) {
|
||||
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||
object_hull.object->m_skirt.clear();
|
||||
append_skirt_loops_for_hull(object_hull.hull, object_hull.object->m_skirt, false);
|
||||
object_hull.object->m_skirt.reverse();
|
||||
}
|
||||
} else if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
|
||||
struct SkirtGroupItem {
|
||||
Points occupied_points;
|
||||
bool emits_skirt;
|
||||
};
|
||||
|
||||
// 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; }
|
||||
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 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.
|
||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||
// 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.
|
||||
ExtrusionEntityCollection m_skirt;
|
||||
std::vector<ExtrusionEntityCollection> m_skirt_groups;
|
||||
// BBS: collecting extrusion paths to build brim by objs
|
||||
std::map<ObjectID, ExtrusionEntityCollection> m_brimMap;
|
||||
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.
|
||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||
// It does NOT encompass user extrusions generated by custom G-code,
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
#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
|
||||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
|
||||
|
||||
|
||||
@@ -758,13 +758,9 @@ int GLVolumeCollection::load_object_volume(
|
||||
GLVolume& v = *this->volumes.back();
|
||||
v.set_color(color_from_model_volume(*model_volume));
|
||||
v.name = model_volume->name;
|
||||
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.model.init_from(mesh, true);
|
||||
#else
|
||||
|
||||
v.model.init_from(*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);
|
||||
|
||||
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];
|
||||
this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
|
||||
GLVolume& v = *this->volumes.back();
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.model.init_from(mesh, true);
|
||||
#else
|
||||
v.model.init_from(mesh);
|
||||
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));
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
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);
|
||||
// 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);
|
||||
assert(! mesh.empty());
|
||||
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.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
}
|
||||
else {
|
||||
// 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();
|
||||
volume.model.init_from(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
|
||||
@@ -10001,11 +9993,7 @@ void GLCanvas3D::_load_sla_shells()
|
||||
const TriangleMesh& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) {
|
||||
m_volumes.volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *m_volumes.volumes.back();
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.model.init_from(mesh, true);
|
||||
#else
|
||||
v.model.init_from(mesh);
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
||||
v.composite_id.volume_id = volume_id;
|
||||
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0));
|
||||
|
||||
@@ -14,18 +14,20 @@
|
||||
#include <boost/filesystem/operations.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_corner_normals.h>
|
||||
#include <igl/per_vertex_normals.h>
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if ENABLE_SMOOTH_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>>;
|
||||
@@ -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::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());
|
||||
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)
|
||||
{
|
||||
@@ -455,17 +456,41 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
||||
data.reserve_vertices(3 * its.indices.size());
|
||||
data.reserve_indices(3 * its.indices.size());
|
||||
|
||||
// vertices + indices
|
||||
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);
|
||||
// Read user preference: smooth normals enabled
|
||||
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
|
||||
const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
|
||||
|
||||
if (realistic_mode && smooth_normals_enabled) {
|
||||
// Use per-corner smooth normals (via IGL)
|
||||
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||
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
|
||||
|
||||
@@ -1578,6 +1578,15 @@ void PreferencesDialog::create_items()
|
||||
);
|
||||
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
|
||||
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)
|
||||
set(BBL_INTERNAL_TESTING "0")
|
||||
endif()
|
||||
set(SoftFever_VERSION "2.4.0-dev")
|
||||
set(SoftFever_VERSION "2.4.0")
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
||||
SoftFever_VERSION_MATCH ${SoftFever_VERSION})
|
||||
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
|
||||
Reference in New Issue
Block a user