Compare commits

...

16 Commits
main ... v2.4.0

Author SHA1 Message Date
SoftFever
6d9eb1792f Add Linux ARM64 (aarch64) AppImage build
Build the Linux AppImage for ARM64 (aarch64) alongside x86_64: the Linux CI
job now matrixes over both architectures, with arch-aware deps caching and
artifact/asset names (amd64 keeps its existing names). The aarch64 AppImage is
published to the nightly and release pages like the x86_64 one.

Run the unit-test suite on the aarch64 runner (faster GitHub arm runner); the
tests are built on that leg. Self-hosted keeps tests on the amd64 server.
2026-06-19 17:38:34 +08:00
Kiss Lorand
691d97867c Skirt overhaul (#14130)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-06-19 11:51:41 +08:00
Rodrigo Faselli
da7e0540a9 ENABLE_SMOOTH_NORMALS (#14080)
* ENABLE_SMOOTH_NORMALS

* Remove definition of macro L if defined

* Update GLModel.cpp

* suavizado ajustado en 5 grados

5 grados

3,5 grados

* Ajuste de brillo menos intenso

* opcion smooth normals

Update GLModel.cpp

test

test 3

* cleaning macros

* tooltip

* Apply suggestion from @RF47

* Apply suggestion from @RF47

* Apply suggestions from code review

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-06-19 10:46:57 +08:00
SoftFever
70fd764e7d bump version to 2.4.0 official release 2026-06-18 00:42:10 +08:00
SoftFever
ed2e7d02d0 Merge branch 'main' into release/v2.4 2026-06-18 00:41:14 +08:00
SoftFever
75cd880336 Merge branch 'main' into release/v2.4 2026-06-18 00:01:28 +08:00
SoftFever
8e4b188473 Merge branch 'main' into release/v2.4 2026-06-12 00:08:02 +08:00
SoftFever
fc9a8aa93f Merge branch 'main' into release/v2.4 2026-06-09 02:34:53 +08:00
SoftFever
21f48a2654 bump metainfo version 2026-06-08 22:21:24 +08:00
SoftFever
548f291a81 Merge branch 'main' into release/v2.4 2026-06-08 22:19:50 +08:00
SoftFever
bc3af3d00f Merge branch 'main' into release/v2.4 2026-06-08 00:37:07 +08:00
SoftFever
269eb81028 bump version to 2.4.0-beta 2026-06-08 00:11:18 +08:00
SoftFever
08d4ae7dbd Merge branch 'main' into release/v2.4 2026-06-08 00:10:46 +08:00
SoftFever
b1411e253f Merge branch 'main' into release/v2.4 2026-06-01 00:59:02 +08:00
SoftFever
42cce5399c bump flatpak version 2026-05-23 18:14:45 +08:00
SoftFever
b17f5e3946 bump version to 2.4.0-alpha 2026-05-23 18:14:45 +08:00
21 changed files with 368 additions and 214 deletions

View File

@@ -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

View File

@@ -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: |

View File

@@ -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}

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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"

View File

@@ -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)));

View File

@@ -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);

View File

@@ -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.

View File

@@ -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();
}
}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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.

View File

@@ -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));

View File

@@ -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

View File

@@ -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);

View File

@@ -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})