ENH:enhance render performance
jira: none Change-Id: I23ba97d0a00752a131b719436bd062cd0b79e9d2
This commit is contained in:
parent
072f4ed43e
commit
4bf4e1bb91
|
@ -10970,7 +10970,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
|||
|
||||
// Basic info
|
||||
Text("Dear ImGui %s", GetVersion());
|
||||
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
//Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
|
||||
Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
|
||||
Text("%d active allocations", io.MetricsActiveAllocations);
|
||||
|
|
|
@ -172,6 +172,8 @@ void AppConfig::set_defaults()
|
|||
|
||||
if (get("zoom_to_mouse").empty())
|
||||
set_bool("zoom_to_mouse", false);
|
||||
if (get("enable_lod").empty())
|
||||
set_bool("enable_lod", true);
|
||||
if (get("user_bed_type").empty())
|
||||
set_bool("user_bed_type", true);
|
||||
if (get("grabber_size_factor").empty())
|
||||
|
@ -306,7 +308,7 @@ void AppConfig::set_defaults()
|
|||
if (get("mouse_wheel").empty()) {
|
||||
set("mouse_wheel", "0");
|
||||
}
|
||||
|
||||
|
||||
if (get("max_recent_count").empty()) {
|
||||
set("max_recent_count", "18");
|
||||
}
|
||||
|
|
|
@ -108,6 +108,8 @@ set(lisbslic3r_sources
|
|||
Fill/FillRectilinear.hpp
|
||||
Flow.cpp
|
||||
Flow.hpp
|
||||
Frustum.cpp
|
||||
Frustum.hpp
|
||||
FlushVolCalc.cpp
|
||||
FlushVolCalc.hpp
|
||||
format.hpp
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#include "Frustum.hpp"
|
||||
#include <cmath>
|
||||
namespace Slic3r {
|
||||
Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const BoundingBoxf3 &box) const
|
||||
{
|
||||
Vec3f center = ((box.min + box.max) * 0.5f).cast<float>();
|
||||
Vec3f extent = ((box.max - box.min) * 0.5f).cast<float>();
|
||||
float d = distance(center);
|
||||
float r = fabsf(extent.x() * normal_.x()) + fabsf(extent.y() * normal_.y()) + fabsf(extent.z() * normal_.z());
|
||||
if (d == r) {
|
||||
return Plane::Intersects_Tangent;
|
||||
} else if (std::abs(d) < r) {
|
||||
return Plane::Intersects_Cross;
|
||||
}
|
||||
return (d > 0.0f) ? Plane::Intersects_Front : Plane::Intersects_Back;
|
||||
}
|
||||
Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0) const
|
||||
{
|
||||
float d = distance(p0);
|
||||
if (d == 0) {
|
||||
return Plane::Intersects_Tangent;
|
||||
}
|
||||
return (d > 0.0f) ? Plane::Intersects_Front : Plane::Intersects_Back;
|
||||
}
|
||||
Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0, const Vec3f &p1) const
|
||||
{
|
||||
Plane::PlaneIntersects state0 = intersects(p0);
|
||||
Plane::PlaneIntersects state1 = intersects(p1);
|
||||
if (state0 == state1) {
|
||||
return state0;
|
||||
}
|
||||
if (state0 == Plane::Intersects_Tangent || state1 == Plane::Intersects_Tangent) {
|
||||
return Plane::Intersects_Tangent;
|
||||
}
|
||||
|
||||
return Plane::Intersects_Cross;
|
||||
}
|
||||
Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const
|
||||
{
|
||||
Plane::PlaneIntersects state0 = intersects(p0, p1);
|
||||
Plane::PlaneIntersects state1 = intersects(p0, p2);
|
||||
Plane::PlaneIntersects state2 = intersects(p1, p2);
|
||||
|
||||
if (state0 == state1 && state0 == state2) {
|
||||
return state0; }
|
||||
|
||||
if (state0 == Plane::Intersects_Cross || state1 == Plane::Intersects_Cross || state2 == Plane::Intersects_Cross) {
|
||||
return Plane::Intersects_Cross;
|
||||
}
|
||||
|
||||
return Plane::Intersects_Tangent;
|
||||
}
|
||||
|
||||
bool Frustum::intersects(const BoundingBoxf3 &box) const {
|
||||
for (auto &plane : planes) {
|
||||
if (plane.intersects(box) == Plane::Intersects_Back) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check box intersects
|
||||
if (!bbox.intersects(box)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::intersects(const Vec3f &p0) const {
|
||||
for (auto &plane : planes) {
|
||||
if (plane.intersects(p0) == Plane::Intersects_Back) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::intersects(const Vec3f &p0, const Vec3f &p1) const
|
||||
{
|
||||
for (auto &plane : planes) {
|
||||
if (plane.intersects(p0, p1) == Plane::Intersects_Back) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const
|
||||
{
|
||||
for (auto &plane : planes) {
|
||||
if (plane.intersects(p0, p1, p2) == Plane::Intersects_Back) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef slic3r_Frustum_hpp_
|
||||
#define slic3r_Frustum_hpp_
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
namespace Slic3r {
|
||||
class Frustum
|
||||
{
|
||||
public:
|
||||
Frustum()=default;
|
||||
~Frustum() = default;
|
||||
|
||||
class Plane {
|
||||
public:
|
||||
enum PlaneIntersects { Intersects_Cross = 0, Intersects_Tangent = 1, Intersects_Front = 2, Intersects_Back = 3 };
|
||||
void set(const Vec3f &n, const Vec3f &pt)
|
||||
{
|
||||
normal_ = n.normalized();
|
||||
center_ = pt;
|
||||
d_ = -normal_.dot(pt);
|
||||
}
|
||||
|
||||
float distance(const Vec3f &pt) const { return normal_.dot(pt) + d_; }
|
||||
|
||||
inline const Vec3f &getNormal() const { return normal_; }
|
||||
|
||||
Plane::PlaneIntersects intersects(const BoundingBoxf3 &box) const;
|
||||
//// check intersect with point (world space)
|
||||
Plane::PlaneIntersects intersects(const Vec3f &p0) const;
|
||||
// check intersect with line segment (world space)
|
||||
Plane::PlaneIntersects intersects(const Vec3f &p0, const Vec3f &p1) const;
|
||||
// check intersect with triangle (world space)
|
||||
Plane::PlaneIntersects intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const;
|
||||
private:
|
||||
Vec3f normal_;
|
||||
Vec3f center_;
|
||||
float d_ = 0;
|
||||
};
|
||||
|
||||
bool intersects(const BoundingBoxf3 &box) const;
|
||||
// check intersect with point (world space)
|
||||
bool intersects(const Vec3f &p0) const;
|
||||
// check intersect with line segment (world space)
|
||||
bool intersects(const Vec3f &p0, const Vec3f &p1) const;
|
||||
// check intersect with triangle (world space)
|
||||
bool intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const;
|
||||
|
||||
Plane planes[6];
|
||||
/* corners[0]: nearTopLeft;
|
||||
* corners[1]: nearTopRight;
|
||||
* corners[2]: nearBottomLeft;
|
||||
* corners[3]: nearBottomRight;
|
||||
* corners[4]: farTopLeft;
|
||||
* corners[5]: farTopRight;
|
||||
* corners[6]: farBottomLeft;
|
||||
* corners[7]: farBottomRight;
|
||||
*/
|
||||
Vec3f corners[8];
|
||||
|
||||
BoundingBoxf3 bbox;
|
||||
};
|
||||
|
||||
enum FrustumClipMask {
|
||||
POSITIVE_X = 1 << 0,
|
||||
NEGATIVE_X = 1 << 1,
|
||||
POSITIVE_Y = 1 << 2,
|
||||
NEGATIVE_Y = 1 << 3,
|
||||
POSITIVE_Z = 1 << 4,
|
||||
NEGATIVE_Z = 1 << 5,
|
||||
};
|
||||
|
||||
const int FrustumClipMaskArray[6] = {
|
||||
FrustumClipMask::POSITIVE_X, FrustumClipMask::NEGATIVE_X, FrustumClipMask::POSITIVE_Y, FrustumClipMask::NEGATIVE_Y, FrustumClipMask::POSITIVE_Z, FrustumClipMask::NEGATIVE_Z,
|
||||
};
|
||||
|
||||
const Vec4f FrustumClipPlane[6] = {{-1, 0, 0, 1}, {1, 0, 0, 1}, {0, -1, 0, 1}, {0, 1, 0, 1}, {0, 0, -1, 1}, {0, 0, 1, 1}};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,6 +10,7 @@
|
|||
#include "GLShader.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Colors.hpp"
|
||||
//#include "Camera.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/QuadricEdgeCollapse.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -415,6 +417,45 @@ std::array<std::array<float, 4>, 5> GLVolume::MODEL_COLOR = { {
|
|||
{ 1.0f, 1.0f, 0.0f, 1.f }
|
||||
} };
|
||||
|
||||
float GLVolume::LOD_HIGH_ZOOM = 3.5f;
|
||||
float GLVolume::LOD_MIDDLE_ZOOM = 2.8f;
|
||||
float GLVolume::LOD_SMALL_ZOOM = 1.4f;
|
||||
float GLVolume::LAST_CAMERA_ZOOM_VALUE = 0.0f;
|
||||
const float ZOOM_THRESHOLD = 0.3f;
|
||||
const unsigned char LOD_UPDATE_FREQUENCY = 20;
|
||||
const Vec2i LOD_SCREEN_MIN = Vec2i(45, 35);
|
||||
const Vec2i LOD_SCREEN_MAX = Vec2i(70, 55);
|
||||
|
||||
Vec2f calc_pt_in_screen(const Vec3d &pt, const Transform3d &world_tran, const Matrix4d &view_proj_mat, int window_width, int window_height)
|
||||
{
|
||||
auto tran = (view_proj_mat * world_tran);
|
||||
Vec4d temp_center(pt.x(), pt.y(), pt.z(), 1.0);
|
||||
Vec4d temp_ndc = tran * temp_center;
|
||||
Vec3d screen_box_center = Vec3d(temp_ndc.x(), temp_ndc.y(), temp_ndc.z()) / temp_ndc.w();
|
||||
|
||||
float x = 0.5f * (1 + screen_box_center(0)) * window_width;
|
||||
float y = 0.5f * (1 - screen_box_center(1)) * window_height;
|
||||
return Vec2f(x, y);
|
||||
}
|
||||
|
||||
LOD_LEVEL calc_volume_box_in_screen_bigger_than_threshold(
|
||||
const BoundingBoxf3 &v_box, const Transform3d &world_tran, const Matrix4d &view_proj_mat,
|
||||
int window_width, int window_height)
|
||||
{
|
||||
auto s_min = calc_pt_in_screen(v_box.min, world_tran, view_proj_mat, window_width, window_height);
|
||||
auto s_max = calc_pt_in_screen(v_box.max, world_tran, view_proj_mat, window_width, window_height);
|
||||
auto size_x = abs(s_max.x() - s_min.x());
|
||||
auto size_y = abs(s_max.y() - s_min.y());
|
||||
if (size_x >= LOD_SCREEN_MAX.x() || size_y >= LOD_SCREEN_MAX.y()) {
|
||||
return LOD_LEVEL::HIGH;
|
||||
}
|
||||
if (size_x <= LOD_SCREEN_MIN.x() && size_y <= LOD_SCREEN_MIN.y()) {
|
||||
return LOD_LEVEL::SMALL;
|
||||
} else {
|
||||
return LOD_LEVEL::MIDDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void GLVolume::update_render_colors()
|
||||
{
|
||||
GLVolume::DISABLED_COLOR = GLColor(RenderColor::colors[RenderCol_Model_Disable]);
|
||||
|
@ -463,6 +504,8 @@ GLVolume::GLVolume(float r, float g, float b, float a, bool create_index_data)
|
|||
, force_sinking_contours(false)
|
||||
, tverts_range(0, size_t(-1))
|
||||
, qverts_range(0, size_t(-1))
|
||||
, tverts_range_lod(0, size_t(-1))
|
||||
, qverts_range_lod(0, size_t(-1))
|
||||
{
|
||||
color = { r, g, b, a };
|
||||
set_render_color(color);
|
||||
|
@ -580,6 +623,91 @@ std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume)
|
|||
return color;
|
||||
}
|
||||
|
||||
bool GLVolume::simplify_mesh(const TriangleMesh &mesh, GLIndexedVertexArray &va, LOD_LEVEL lod) const
|
||||
{
|
||||
return simplify_mesh(mesh.its,va,lod);
|
||||
}
|
||||
#define SUPER_LARGE_FACES 500000
|
||||
#define LARGE_FACES 100000
|
||||
bool GLVolume::simplify_mesh(const indexed_triangle_set &_its, GLIndexedVertexArray &va, LOD_LEVEL lod) const
|
||||
{
|
||||
if (_its.indices.size() == 0 || _its.vertices.size() == 0) { return false; }
|
||||
bool enable_lod = GUI::wxGetApp().app_config->get("enable_lod") == "true";
|
||||
if (!enable_lod) {
|
||||
return false;
|
||||
}
|
||||
auto its = std::make_unique<indexed_triangle_set>(_its);
|
||||
auto m_state = std::make_unique<State>();
|
||||
if (lod == LOD_LEVEL::MIDDLE) {
|
||||
m_state->config.max_error = 0.5f;
|
||||
if (_its.indices.size() > SUPER_LARGE_FACES) {
|
||||
m_state->config.max_error = 0.4f;
|
||||
} else if (_its.indices.size() > LARGE_FACES) {
|
||||
m_state->config.max_error = 0.3f;
|
||||
}
|
||||
}
|
||||
if (lod == LOD_LEVEL::SMALL) {
|
||||
m_state->config.max_error = 0.1f;
|
||||
if (_its.indices.size() > SUPER_LARGE_FACES) {
|
||||
m_state->config.max_error = 0.08f;
|
||||
} else if (_its.indices.size() > LARGE_FACES) {
|
||||
m_state->config.max_error = 0.05f;
|
||||
}
|
||||
}
|
||||
|
||||
//std::mutex m_state_mutex;
|
||||
std::thread m_worker = std::thread(
|
||||
[&va](std::unique_ptr<indexed_triangle_set> its, std::unique_ptr<State> state) {
|
||||
// Checks that the UI thread did not request cancellation, throws if so.
|
||||
std::function<void(void)> throw_on_cancel = []() {
|
||||
};
|
||||
std::function<void(int)> statusfn = [&state](int percent) {
|
||||
state->progress = percent;
|
||||
};
|
||||
// Initialize.
|
||||
uint32_t triangle_count = 0;
|
||||
float max_error = std::numeric_limits<float>::max();
|
||||
{
|
||||
if (state->config.use_count)
|
||||
triangle_count = state->config.wanted_count;
|
||||
if (!state->config.use_count)
|
||||
max_error = state->config.max_error;
|
||||
state->progress = 0;
|
||||
state->result.reset();
|
||||
state->status = State::Status::running;
|
||||
}
|
||||
TriangleMesh origin_mesh(*its);
|
||||
try { // Start the actual calculation.
|
||||
its_quadric_edge_collapse(*its, triangle_count, &max_error, throw_on_cancel, statusfn);
|
||||
} catch (std::exception&) {
|
||||
state->status = State::idle;
|
||||
}
|
||||
if (state->status == State::Status::running) {
|
||||
// We were not cancelled, the result is valid.
|
||||
state->status = State::Status::idle;
|
||||
state->result = std::move(its);
|
||||
}
|
||||
if (state->result) {
|
||||
TriangleMesh mesh(*state->result);
|
||||
float eps = 1.0f;
|
||||
Vec3f origin_min = origin_mesh.stats().min - Vec3f(eps, eps, eps);
|
||||
Vec3f origin_max = origin_mesh.stats().max + Vec3f(eps, eps, eps);
|
||||
if (origin_min.x() < mesh.stats().min.x() && origin_min.y() < mesh.stats().min.y() && origin_min.z() < mesh.stats().min.z()&&
|
||||
origin_max.x() > mesh.stats().max.x() && origin_max.y() > mesh.stats().max.y() && origin_max.z() > mesh.stats().max.z()) {
|
||||
va.load_mesh(mesh);
|
||||
}
|
||||
else {
|
||||
state->status = State::cancelling;
|
||||
}
|
||||
}
|
||||
},
|
||||
std::move(its),std::move(m_state));
|
||||
if (m_worker.joinable()) {
|
||||
m_worker.detach();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Transform3d GLVolume::world_matrix() const
|
||||
{
|
||||
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
|
||||
|
@ -687,7 +815,11 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
glFrontFace(GL_CW);
|
||||
glsafe(::glCullFace(GL_BACK));
|
||||
glsafe(::glPushMatrix());
|
||||
|
||||
auto camera = GUI::wxGetApp().plater()->get_camera();
|
||||
auto zoom = camera.get_zoom();
|
||||
Transform3d vier_mat = camera.get_view_matrix();
|
||||
Matrix4d vier_proj_mat = camera.get_projection_matrix().matrix() * vier_mat.matrix();
|
||||
const std::array<int, 4> &viewport = camera.get_viewport();
|
||||
// BBS: add logic for mmu segmentation rendering
|
||||
auto render_body = [&]() {
|
||||
bool color_volume = false;
|
||||
|
@ -716,7 +848,6 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
mmuseg_ivas[idx].load_its_flat_shading(its_per_color[idx]);
|
||||
mmuseg_ivas[idx].finalize_geometry(true);
|
||||
}
|
||||
|
||||
mmuseg_ts = mv->mmu_segmentation_facets.timestamp();
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, new mmuseg_ts %3%, new color size %4%")
|
||||
%this %this->name %mmuseg_ts %mmuseg_ivas.size();
|
||||
|
@ -734,8 +865,8 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
}
|
||||
glsafe(::glMultMatrixd(world_matrix().data()));
|
||||
for (int idx = 0; idx < mmuseg_ivas.size(); idx++) {
|
||||
GLIndexedVertexArray& iva = mmuseg_ivas[idx];
|
||||
if (iva.triangle_indices_size == 0 && iva.quad_indices_size == 0)
|
||||
GLIndexedVertexArray* iva = &mmuseg_ivas[idx];
|
||||
if (iva->triangle_indices_size == 0 && iva->quad_indices_size == 0)
|
||||
continue;
|
||||
|
||||
if (shader) {
|
||||
|
@ -763,7 +894,7 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
}
|
||||
}
|
||||
}
|
||||
iva.render(this->tverts_range, this->qverts_range);
|
||||
iva->render(this->tverts_range, this->qverts_range);
|
||||
/*if (force_native_color && (render_color[3] < 1.0)) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, tverts_range {%3,%4}, qverts_range{%5%, %6%}")
|
||||
%this %this->name %this->tverts_range.first %this->tverts_range.second
|
||||
|
@ -773,7 +904,32 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
}
|
||||
else {
|
||||
glsafe(::glMultMatrixd(world_matrix().data()));
|
||||
this->indexed_vertex_array->render(this->tverts_range, this->qverts_range);
|
||||
auto render_which = [this](std::shared_ptr<GLIndexedVertexArray> cur) {
|
||||
if (cur->vertices_and_normals_interleaved_VBO_id > 0) {
|
||||
cur->render(tverts_range_lod, qverts_range_lod);
|
||||
} else {// if (cur->vertices_and_normals_interleaved_VBO_id == 0)
|
||||
if (cur->triangle_indices.size() > 0) {
|
||||
cur->finalize_geometry(true);
|
||||
cur->render(tverts_range_lod, qverts_range_lod);
|
||||
} else {
|
||||
indexed_vertex_array->render(this->tverts_range, this->qverts_range);
|
||||
}
|
||||
}
|
||||
};
|
||||
Transform3d world_tran = world_matrix();
|
||||
m_lod_update_index++;
|
||||
if (abs(zoom - LAST_CAMERA_ZOOM_VALUE) > ZOOM_THRESHOLD || m_lod_update_index >= LOD_UPDATE_FREQUENCY){
|
||||
m_lod_update_index = 0;
|
||||
LAST_CAMERA_ZOOM_VALUE = zoom;
|
||||
m_cur_lod_level = calc_volume_box_in_screen_bigger_than_threshold(bounding_box(), world_tran, vier_proj_mat, viewport[2], viewport[3]);
|
||||
}
|
||||
if (m_cur_lod_level == LOD_LEVEL::SMALL && indexed_vertex_array_small) {
|
||||
render_which(indexed_vertex_array_small);
|
||||
} else if (m_cur_lod_level == LOD_LEVEL::MIDDLE && indexed_vertex_array_middle) {
|
||||
render_which(indexed_vertex_array_middle);
|
||||
} else {
|
||||
this->indexed_vertex_array->render(this->tverts_range, this->qverts_range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -887,9 +1043,22 @@ void GLVolume::render(bool with_outline, const std::array<float, 4>& body_color)
|
|||
glsafe(::glPushMatrix());
|
||||
|
||||
Transform3d matrix = world_matrix();
|
||||
Transform3d world_tran = matrix;
|
||||
matrix.scale(scale);
|
||||
glsafe(::glMultMatrixd(matrix.data()));
|
||||
this->indexed_vertex_array->render(this->tverts_range, this->qverts_range);
|
||||
m_lod_update_index++;
|
||||
if (abs(zoom - LAST_CAMERA_ZOOM_VALUE) > ZOOM_THRESHOLD || m_lod_update_index >= LOD_UPDATE_FREQUENCY) {
|
||||
m_lod_update_index = 0;
|
||||
LAST_CAMERA_ZOOM_VALUE = zoom;
|
||||
m_cur_lod_level = calc_volume_box_in_screen_bigger_than_threshold(bounding_box(), world_tran, vier_proj_mat, viewport[2], viewport[3]);
|
||||
}
|
||||
if (m_cur_lod_level == LOD_LEVEL::SMALL && indexed_vertex_array_small && indexed_vertex_array_small->vertices_and_normals_interleaved_VBO_id > 0) {
|
||||
this->indexed_vertex_array_small->render(this->tverts_range_lod, this->qverts_range_lod);
|
||||
} else if (m_cur_lod_level == LOD_LEVEL::MIDDLE && indexed_vertex_array_middle && indexed_vertex_array_middle->vertices_and_normals_interleaved_VBO_id > 0) {
|
||||
this->indexed_vertex_array_middle->render(this->tverts_range_lod, this->qverts_range_lod);
|
||||
} else {
|
||||
this->indexed_vertex_array->render(this->tverts_range, this->qverts_range);
|
||||
}
|
||||
//BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render for body, shader name %2%")%__LINE__ %shader->get_name();
|
||||
shader->set_uniform("is_outline", false);
|
||||
|
||||
|
@ -998,6 +1167,14 @@ void GLVolume::simple_render(GLShaderProgram *shader, ModelObjectPtrs &model_obj
|
|||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
void GLVolume::set_bounding_boxes_as_dirty()
|
||||
{
|
||||
m_lod_update_index = LOD_UPDATE_FREQUENCY;
|
||||
m_transformed_bounding_box.reset();
|
||||
m_transformed_convex_hull_bounding_box.reset();
|
||||
m_transformed_non_sinking_bounding_box.reset();
|
||||
}
|
||||
|
||||
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
|
||||
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
|
||||
|
||||
|
@ -1112,6 +1289,8 @@ int GLVolumeCollection::load_object_volume(
|
|||
else {
|
||||
GLVolume* first_volume = *(volume_set.begin());
|
||||
new_volume->indexed_vertex_array = first_volume->indexed_vertex_array;
|
||||
new_volume->indexed_vertex_array_middle = first_volume->indexed_vertex_array_middle;
|
||||
new_volume->indexed_vertex_array_small = first_volume->indexed_vertex_array_small;
|
||||
need_create_mesh = false;
|
||||
}
|
||||
volume_set.emplace(new_volume);
|
||||
|
@ -1127,6 +1306,10 @@ int GLVolumeCollection::load_object_volume(
|
|||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.indexed_vertex_array->load_mesh(mesh, true);
|
||||
#else
|
||||
if (v.indexed_vertex_array_middle == nullptr) { v.indexed_vertex_array_middle = std::make_shared<GLIndexedVertexArray>(); }
|
||||
v.simplify_mesh(mesh, *v.indexed_vertex_array_middle, LOD_LEVEL::MIDDLE); // include finalize_geometry
|
||||
if (v.indexed_vertex_array_small == nullptr) { v.indexed_vertex_array_small = std::make_shared<GLIndexedVertexArray>(); }
|
||||
v.simplify_mesh(mesh, *v.indexed_vertex_array_small, LOD_LEVEL::SMALL);
|
||||
v.indexed_vertex_array->load_mesh(mesh);
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
v.indexed_vertex_array->finalize_geometry(opengl_initialized);
|
||||
|
@ -1369,7 +1552,12 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
|||
if (disable_cullface)
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
auto camera = GUI::wxGetApp().plater()->get_camera();
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
auto world_box = volume.first->transformed_bounding_box();
|
||||
if (!camera.getFrustum().intersects(world_box)) {
|
||||
continue;
|
||||
}
|
||||
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
||||
if (type == ERenderType::Transparent) {
|
||||
volume.first->force_transparent = true;
|
||||
|
|
|
@ -256,8 +256,20 @@ public:
|
|||
private:
|
||||
BoundingBox m_bounding_box;
|
||||
};
|
||||
enum LOD_LEVEL {
|
||||
HIGH, // Origin data
|
||||
MIDDLE,
|
||||
SMALL,
|
||||
};
|
||||
|
||||
class GLVolume {
|
||||
static float LOD_HIGH_ZOOM;
|
||||
static float LOD_MIDDLE_ZOOM;
|
||||
static float LOD_SMALL_ZOOM;
|
||||
static float LAST_CAMERA_ZOOM_VALUE;
|
||||
mutable LOD_LEVEL m_cur_lod_level = LOD_LEVEL::HIGH;
|
||||
mutable unsigned char m_lod_update_index = 0;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
bool is_text_shape{false};
|
||||
|
@ -292,6 +304,9 @@ public:
|
|||
virtual ~GLVolume() = default;
|
||||
|
||||
// BBS
|
||||
bool simplify_mesh(const TriangleMesh &mesh, GLIndexedVertexArray &va, LOD_LEVEL lod) const;
|
||||
bool simplify_mesh(const indexed_triangle_set &_its, GLIndexedVertexArray &va, LOD_LEVEL lod) const;
|
||||
|
||||
protected:
|
||||
Geometry::Transformation m_instance_transformation;
|
||||
Geometry::Transformation m_volume_transformation;
|
||||
|
@ -327,6 +342,30 @@ protected:
|
|||
|
||||
SinkingContours m_sinking_contours;
|
||||
|
||||
// guards m_state
|
||||
struct Configuration
|
||||
{
|
||||
bool use_count = false;//diff with glgizmoSimplify
|
||||
float decimate_ratio = 50.f; // in percent
|
||||
uint32_t wanted_count = 0; // initialize by percents
|
||||
float max_error = 1.; // maximal quadric error
|
||||
|
||||
bool operator==(const Configuration &rhs)
|
||||
{
|
||||
return (use_count == rhs.use_count && decimate_ratio == rhs.decimate_ratio && wanted_count == rhs.wanted_count && max_error == rhs.max_error);
|
||||
}
|
||||
bool operator!=(const Configuration &rhs) { return !(*this == rhs); }
|
||||
};
|
||||
struct State
|
||||
{
|
||||
enum Status { idle, running, cancelling };
|
||||
|
||||
Status status = idle;
|
||||
int progress = 0; // percent of done work
|
||||
Configuration config; // Configuration we started with.
|
||||
const ModelVolume * mv = nullptr;
|
||||
std::unique_ptr<indexed_triangle_set> result;
|
||||
};
|
||||
public:
|
||||
// Color of the triangles / quads held by this volume.
|
||||
std::array<float, 4> color;
|
||||
|
@ -400,7 +439,9 @@ public:
|
|||
EHoverState hover;
|
||||
|
||||
// Interleaved triangles & normals with indexed triangles & quads.
|
||||
std::shared_ptr<GLIndexedVertexArray> indexed_vertex_array;
|
||||
std::shared_ptr<GLIndexedVertexArray> indexed_vertex_array;
|
||||
std::shared_ptr<GLIndexedVertexArray> indexed_vertex_array_middle;
|
||||
std::shared_ptr<GLIndexedVertexArray> indexed_vertex_array_small;
|
||||
const TriangleMesh * ori_mesh{nullptr};
|
||||
// BBS
|
||||
mutable std::vector<GLIndexedVertexArray> mmuseg_ivas;
|
||||
|
@ -409,7 +450,8 @@ public:
|
|||
// Ranges of triangle and quad indices to be rendered.
|
||||
std::pair<size_t, size_t> tverts_range;
|
||||
std::pair<size_t, size_t> qverts_range;
|
||||
|
||||
std::pair<size_t, size_t> tverts_range_lod;
|
||||
std::pair<size_t, size_t> qverts_range_lod;
|
||||
// If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
|
||||
// of the extrusions per layer.
|
||||
std::vector<coordf_t> print_zs;
|
||||
|
@ -533,11 +575,7 @@ public:
|
|||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array->finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array->release_geometry(); }
|
||||
|
||||
void set_bounding_boxes_as_dirty() {
|
||||
m_transformed_bounding_box.reset();
|
||||
m_transformed_convex_hull_bounding_box.reset();
|
||||
m_transformed_non_sinking_bounding_box.reset();
|
||||
}
|
||||
void set_bounding_boxes_as_dirty();
|
||||
|
||||
bool is_sla_support() const;
|
||||
bool is_sla_pad() const;
|
||||
|
@ -573,6 +611,7 @@ typedef std::vector<GLVolume*> GLVolumePtrs;
|
|||
typedef std::pair<GLVolume*, std::pair<unsigned int, double>> GLVolumeWithIdAndZ;
|
||||
typedef std::vector<GLVolumeWithIdAndZ> GLVolumeWithIdAndZList;
|
||||
|
||||
|
||||
class GLVolumeCollection
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -56,7 +56,7 @@ void Camera::select_next_type()
|
|||
void Camera::translate(const Vec3d& displacement) {
|
||||
if (!displacement.isApprox(Vec3d::Zero())) {
|
||||
m_view_matrix.translate(-displacement);
|
||||
update_target();
|
||||
update_target();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,104 @@ void Camera::select_view(const std::string& direction)
|
|||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||
}
|
||||
}
|
||||
//how to use
|
||||
//BoundingBox bbox = mesh.aabb.transform(transform);
|
||||
//return camera_->getFrustum().intersects(bbox);
|
||||
void Camera::update_frustum()
|
||||
{
|
||||
auto eye_ = get_position().cast<float>();
|
||||
auto center_ = get_target().cast<float>();
|
||||
auto up_ = get_dir_up().cast<float>();
|
||||
float near_ = m_frustrum_zs.first;
|
||||
float far_ = m_frustrum_zs.second;
|
||||
float aspect_ = m_viewport[2] / (double)m_viewport[3];
|
||||
float fov_ = (float) Geometry::deg2rad(get_fov());
|
||||
float eps = 0.01;
|
||||
if (m_last_eye.isApprox(eye_) && m_last_center.isApprox(center_) && m_last_up.isApprox(up_) &&
|
||||
abs(m_last_near - near_) > eps && abs(m_last_far - far_) > eps &&
|
||||
abs(m_last_aspect - aspect_) > eps && abs(m_last_fov - fov_) > eps) {
|
||||
return;
|
||||
}
|
||||
m_last_eye = eye_;
|
||||
m_last_center = center_;
|
||||
m_last_up = up_;
|
||||
m_last_near = near_;
|
||||
m_last_far = far_;
|
||||
m_last_aspect = aspect_;
|
||||
m_last_fov = fov_;
|
||||
Vec3f forward((center_ - eye_).normalized());
|
||||
Vec3f side((forward.cross(up_)).normalized());
|
||||
Vec3f up((side.cross(forward)).normalized());
|
||||
|
||||
float nearHeightHalf = near_ * std::tan(fov_ / 2.f);
|
||||
float farHeightHalf = far_ * std::tan(fov_ / 2.f);
|
||||
float nearWidthHalf = nearHeightHalf * aspect_;
|
||||
float farWidthHalf = farHeightHalf * aspect_;
|
||||
|
||||
// near plane
|
||||
Vec3f nearCenter = eye_ + forward * near_;
|
||||
Vec3f nearNormal = forward;
|
||||
m_frustum.planes[0].set(nearNormal, nearCenter);
|
||||
|
||||
// far plane
|
||||
Vec3f farCenter = eye_ + forward * far_;
|
||||
Vec3f farNormal = -forward;
|
||||
m_frustum.planes[1].set(farNormal, farCenter);
|
||||
|
||||
// top plane
|
||||
Vec3f topCenter = nearCenter + up * nearHeightHalf;
|
||||
Vec3f topNormal = (topCenter - eye_).normalized().cross(side);
|
||||
m_frustum.planes[2].set(topNormal, topCenter);
|
||||
|
||||
// bottom plane
|
||||
Vec3f bottomCenter = nearCenter - up * nearHeightHalf;
|
||||
Vec3f bottomNormal = side.cross((bottomCenter - eye_).normalized());
|
||||
m_frustum.planes[3].set(bottomNormal, bottomCenter);
|
||||
|
||||
// left plane
|
||||
Vec3f leftCenter = nearCenter - side * nearWidthHalf;
|
||||
Vec3f leftNormal = (leftCenter - eye_).normalized().cross(up);
|
||||
m_frustum.planes[4].set(leftNormal, leftCenter);
|
||||
|
||||
// right plane
|
||||
Vec3f rightCenter = nearCenter + side * nearWidthHalf;
|
||||
Vec3f rightNormal = up.cross((rightCenter - eye_).normalized());
|
||||
m_frustum.planes[5].set(rightNormal, rightCenter);
|
||||
|
||||
//// 8 corners
|
||||
Vec3f nearTopLeft = nearCenter + up * nearHeightHalf - side * nearWidthHalf;
|
||||
Vec3f nearTopRight = nearCenter + up * nearHeightHalf + side * nearWidthHalf;
|
||||
Vec3f nearBottomLeft = nearCenter - up * nearHeightHalf - side * nearWidthHalf;
|
||||
Vec3f nearBottomRight = nearCenter - up * nearHeightHalf + side * nearWidthHalf;
|
||||
|
||||
Vec3f farTopLeft = farCenter + up * farHeightHalf - side * farWidthHalf;
|
||||
Vec3f farTopRight = farCenter + up * farHeightHalf + side * farWidthHalf;
|
||||
Vec3f farBottomLeft = farCenter - up * farHeightHalf - side * farWidthHalf;
|
||||
Vec3f farBottomRight = farCenter - up * farHeightHalf + side * farWidthHalf;
|
||||
|
||||
m_frustum.corners[0] = nearTopLeft;
|
||||
m_frustum.corners[1] = nearTopRight;
|
||||
m_frustum.corners[2] = nearBottomLeft;
|
||||
m_frustum.corners[3] = nearBottomRight;
|
||||
m_frustum.corners[4] = farTopLeft;
|
||||
m_frustum.corners[5] = farTopRight;
|
||||
m_frustum.corners[6] = farBottomLeft;
|
||||
m_frustum.corners[7] = farBottomRight;
|
||||
// bounding box
|
||||
auto double_min = std::numeric_limits<double>::min();
|
||||
auto double_max = std::numeric_limits<double>::max();
|
||||
m_frustum.bbox.min = Vec3d(double_max, double_max, double_max);
|
||||
m_frustum.bbox.max = Vec3d(double_min, double_min, double_min);
|
||||
for (auto &corner : m_frustum.corners) {
|
||||
m_frustum.bbox.min[0] = std::min((float)m_frustum.bbox.min.x(), corner.x());
|
||||
m_frustum.bbox.min[1] = std::min((float)m_frustum.bbox.min.y(), corner.y());
|
||||
m_frustum.bbox.min[2] = std::min((float)m_frustum.bbox.min.z(), corner.z());
|
||||
|
||||
m_frustum.bbox.max[0] = std::max((float)m_frustum.bbox.max.x(), corner.x());
|
||||
m_frustum.bbox.max[1] = std::max((float)m_frustum.bbox.max.y(), corner.y());
|
||||
m_frustum.bbox.max[2] = std::max((float)m_frustum.bbox.max.z(), corner.z());
|
||||
}
|
||||
}
|
||||
|
||||
double Camera::get_fov() const
|
||||
{
|
||||
|
@ -220,7 +318,7 @@ void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor)
|
|||
}
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void Camera::debug_render() const
|
||||
void Camera::debug_render()
|
||||
{
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
@ -520,7 +618,7 @@ void Camera::set_distance(double distance)
|
|||
if (m_distance != distance) {
|
||||
m_view_matrix.translate((distance - m_distance) * get_dir_forward());
|
||||
m_distance = distance;
|
||||
|
||||
|
||||
update_target();
|
||||
}
|
||||
}
|
||||
|
@ -626,7 +724,7 @@ void Camera::update_target() {
|
|||
Vec3d temptarget = get_position() + m_distance * get_dir_forward();
|
||||
if (!(temptarget-m_target).isApprox(Vec3d::Zero())){
|
||||
m_target = temptarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_Camera_hpp_
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Frustum.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include <array>
|
||||
|
||||
|
@ -55,6 +56,9 @@ private:
|
|||
std::pair<double, double> m_frustrum_zs;
|
||||
|
||||
BoundingBoxf3 m_scene_box;
|
||||
Frustum m_frustum;
|
||||
Vec3f m_last_eye, m_last_center, m_last_up;
|
||||
float m_last_near, m_last_far, m_last_aspect, m_last_fov;
|
||||
|
||||
public:
|
||||
Camera() { set_default_orientation(); }
|
||||
|
@ -69,7 +73,7 @@ public:
|
|||
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
|
||||
|
||||
void translate(const Vec3d& displacement);
|
||||
const Vec3d& get_target() {
|
||||
const Vec3d& get_target() {
|
||||
update_target();
|
||||
return m_target; }
|
||||
void set_target(const Vec3d& target);
|
||||
|
@ -100,9 +104,9 @@ public:
|
|||
Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); }
|
||||
Vec3d get_dir_forward() const { return -m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
|
||||
|
||||
|
||||
Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); }
|
||||
|
||||
const Frustum & getFrustum() const { return m_frustum; }
|
||||
void update_frustum();
|
||||
double get_near_z() const { return m_frustrum_zs.first; }
|
||||
double get_far_z() const { return m_frustrum_zs.second; }
|
||||
const std::pair<double, double>& get_z_range() const { return m_frustrum_zs; }
|
||||
|
@ -119,7 +123,7 @@ public:
|
|||
void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor);
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
void debug_render() const;
|
||||
void debug_render();
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
// translate the camera in world space
|
||||
|
|
|
@ -1991,7 +1991,7 @@ void GLCanvas3D::render(bool only_init)
|
|||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
_render_camera_target();
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
|
||||
camera.update_frustum();
|
||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||
m_rectangle_selection.render(*this);
|
||||
|
||||
|
|
|
@ -805,6 +805,28 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa
|
|||
}
|
||||
}
|
||||
|
||||
if (param == "enable_lod") {
|
||||
if (wxGetApp().plater()->is_project_dirty()) {
|
||||
auto result = MessageDialog(static_cast<wxWindow *>(this), _L("The current project has unsaved changes, save it before continuing?"),
|
||||
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxYES_DEFAULT | wxCENTRE)
|
||||
.ShowModal();
|
||||
if (result == wxID_YES) {
|
||||
wxGetApp().plater()->save_project();
|
||||
}
|
||||
}
|
||||
MessageDialog msg_wingow(nullptr, _L("Please note that the model show will undergo certain changes at small pixels case.\nEnabled LOD requires application restart.") + "\n" + _L("Do you want to continue?"), _L("Enable LOD"),
|
||||
wxYES| wxYES_DEFAULT | wxCANCEL | wxCENTRE);
|
||||
if (msg_wingow.ShowModal() == wxID_YES) {
|
||||
Close();
|
||||
GetParent()->RemoveChild(this);
|
||||
wxGetApp().recreate_GUI(_L("Enable LOD"));
|
||||
} else {
|
||||
checkbox->SetValue(!checkbox->GetValue());
|
||||
app_config->set_bool(param, checkbox->GetValue());
|
||||
app_config->save();
|
||||
}
|
||||
}
|
||||
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
|
@ -1107,6 +1129,9 @@ wxWindow* PreferencesDialog::create_general_page()
|
|||
auto item_mouse_zoom_settings = create_item_checkbox(_L("Zoom to mouse position"), page,
|
||||
_L("Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center."), 50,
|
||||
"zoom_to_mouse");
|
||||
auto enable_lod_settings = create_item_checkbox(_L("Improve rendering performance by lod"), page,
|
||||
_L("Improved rendering performance under the scene of multiple plates and many models."), 50,
|
||||
"enable_lod");
|
||||
float range_min = 1.0, range_max = 2.5;
|
||||
auto item_grabber_size_settings = create_item_range_input(_L("Grabber scale"), page,
|
||||
_L("Set grabber size for move,rotate,scale tool.") + _L("Value range") + ":[" + std::to_string(range_min) + "," +
|
||||
|
@ -1188,6 +1213,7 @@ wxWindow* PreferencesDialog::create_general_page()
|
|||
sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(_3d_settings, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||
sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(enable_lod_settings, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_grabber_size_settings, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||
sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3));
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
void OnPaint(wxPaintEvent &evt);
|
||||
virtual ~ProgressDialog();
|
||||
|
||||
virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO);
|
||||
virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO)override;
|
||||
bool Create(const wxString &title, const wxString &message, int maximum = 100, wxWindow *parent = NULL, int style = wxPD_APP_MODAL | wxPD_AUTO_HIDE);
|
||||
|
||||
virtual bool Update(int value, const wxString &newmsg = wxEmptyString, bool *skip = NULL);
|
||||
|
|
Loading…
Reference in New Issue