refactor to have a single global current brush on Canvas::m_current_brush and make panels read from it for drawing instead of relying on their own copy
This commit is contained in:
@@ -256,6 +256,11 @@ void App::update(float dt)
|
||||
layout[main_id]->find<NodeButton>("btn-touchlock")->set_color(
|
||||
canvas->m_canvas->m_touch_lock ? color_button_hlight : color_button_normal);
|
||||
|
||||
stroke->update_controls();
|
||||
auto pix = ui::Canvas::I->m_current_brush.m_tip_color;
|
||||
auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2]));
|
||||
color->m_hue->set_value(hsv.x);
|
||||
color->m_quad->set_value(1.f - hsv.y, 1.f - hsv.z);
|
||||
|
||||
auto observer = [this](Node* n)
|
||||
{
|
||||
|
||||
@@ -53,9 +53,6 @@ public:
|
||||
NodeCanvas* canvas;
|
||||
Node* current_panel = nullptr;
|
||||
NodeScroll* panels;
|
||||
std::function<void(int)> on_brush_select;
|
||||
std::function<void(glm::vec4 color)> on_color_change;
|
||||
std::function<void()> on_stroke_change;
|
||||
const uint16_t main_id = const_hash("main");
|
||||
std::string doc_name;
|
||||
float width;
|
||||
|
||||
@@ -90,12 +90,16 @@ void App::dialog_open()
|
||||
{
|
||||
canvas->reset_camera();
|
||||
layers->clear();
|
||||
canvas->m_canvas->project_open(dialog->selected_path);
|
||||
doc_name = dialog->selected_name;
|
||||
if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
|
||||
docname->set_text(("Panodoc: " + doc_name).c_str());
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
canvas->m_canvas->project_open(dialog->selected_path, [this] {
|
||||
// on complete
|
||||
async_start();
|
||||
if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
|
||||
docname->set_text(("Panodoc: " + doc_name).c_str());
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
async_end();
|
||||
});
|
||||
dialog->destroy();
|
||||
ActionManager::clear();
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ bool App::mouse_scroll(float x, float y, float delta)
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseScroll;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_scroll_delta = delta * 0.1f;
|
||||
e.m_scroll_delta = delta;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
|
||||
@@ -128,47 +128,34 @@ void App::init_sidebar()
|
||||
presets->create();
|
||||
presets->loaded();
|
||||
|
||||
if (canvas)
|
||||
{
|
||||
stroke->m_canvas->m_brush.m_tip_color = color->m_color;
|
||||
stroke->m_canvas->draw_stroke();
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
}
|
||||
// if (canvas)
|
||||
// {
|
||||
// ui::Canvas::I->m_current_brush.m_tip_color = color->m_color;
|
||||
// stroke->m_canvas->draw_stroke();
|
||||
// }
|
||||
|
||||
brushes->on_brush_changed = [this](Node* target, int index) {
|
||||
stroke->m_canvas->m_brush.m_tex_id = brushes->get_texture_id(index);
|
||||
stroke->m_canvas->m_brush.id = brushes->get_brush_id(index);
|
||||
stroke->m_canvas->draw_stroke();
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
if (on_brush_select)
|
||||
on_brush_select(index);
|
||||
};
|
||||
// brushes->on_brush_changed = [this](Node* target, int index) {
|
||||
// ui::Canvas::I->m_current_brush.m_tex_id = brushes->get_texture_id(index);
|
||||
// ui::Canvas::I->m_current_brush.id = brushes->get_brush_id(index);
|
||||
// stroke->m_canvas->draw_stroke();
|
||||
// };
|
||||
presets->on_brush_changed = [this](Node* target, int index) {
|
||||
auto b = presets->get_brush(index);
|
||||
// don't change some params
|
||||
b.m_tip_size = stroke->m_canvas->m_brush.m_tip_size;
|
||||
b.m_tip_color = stroke->m_canvas->m_brush.m_tip_color;
|
||||
stroke->set_params(b);
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
if (on_brush_select)
|
||||
on_brush_select(index);
|
||||
b.m_tip_size = ui::Canvas::I->m_current_brush.m_tip_size;
|
||||
b.m_tip_color = ui::Canvas::I->m_current_brush.m_tip_color;
|
||||
ui::Canvas::I->m_current_brush = b;
|
||||
};
|
||||
|
||||
color->on_color_changed = [this](Node* target, glm::vec4 color) {
|
||||
stroke->m_canvas->m_brush.m_tip_color = color;
|
||||
// stroke->m_canvas->draw_stroke();
|
||||
if (canvas)
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
if (on_color_change)
|
||||
on_color_change(color);
|
||||
};
|
||||
|
||||
stroke->on_stroke_change = [this](Node*target) {
|
||||
if (canvas)
|
||||
canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
if (on_stroke_change)
|
||||
on_stroke_change();
|
||||
};
|
||||
// color->on_color_changed = [this](Node* target, glm::vec4 color) {
|
||||
// ui::Canvas::I->m_current_brush.m_tip_color = color;
|
||||
// // stroke->m_canvas->draw_stroke();
|
||||
// };
|
||||
//
|
||||
// stroke->on_stroke_change = [this](Node*target) {
|
||||
// if (canvas)
|
||||
// canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
// };
|
||||
|
||||
layers->on_layer_add = [this](Node*) {
|
||||
canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str());
|
||||
@@ -329,7 +316,7 @@ void App::init_toolbar_draw()
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-bucket"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
canvas->m_canvas->clear(canvas->m_brush.m_tip_color);
|
||||
canvas->m_canvas->clear(ui::Canvas::I->m_current_brush.m_tip_color);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -427,8 +414,8 @@ void App::init_menu_edit()
|
||||
|
||||
void App::brush_update()
|
||||
{
|
||||
brushes->select_brush(canvas->m_brush.id);
|
||||
stroke->set_params(canvas->m_brush);
|
||||
// brushes->select_brush(canvas->m_brush.id);
|
||||
// stroke->set_params(canvas->m_brush);
|
||||
}
|
||||
|
||||
void App::init_menu_layer()
|
||||
@@ -552,8 +539,7 @@ void App::initLayout()
|
||||
b.m_tip_flow = .5f;
|
||||
b.m_tip_spacing = .1f;
|
||||
b.m_tip_opacity = 1.f;
|
||||
stroke->m_canvas->m_brush = b;
|
||||
canvas->m_brush = b;
|
||||
ui::Canvas::I->m_current_brush = b;
|
||||
|
||||
brush_update();
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ public:
|
||||
glm::vec3 m_cam_pos;
|
||||
float m_cam_fov = 85;
|
||||
|
||||
Brush m_current_brush;
|
||||
|
||||
enum class kCanvasMode { Draw, Erase, Line, Camera, Grid, Fill, MaskFree, MaskLine, COUNT };
|
||||
kCanvasMode m_state{ kCanvasMode::Draw };
|
||||
static std::vector<CanvasMode*> modes[];
|
||||
|
||||
@@ -36,7 +36,7 @@ void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
break;
|
||||
case kEventType::MouseScroll:
|
||||
m_zoom_canvas += me->m_scroll_delta * 0.1f;
|
||||
canvas->m_cam_fov -= me->m_scroll_delta * 20.1f;
|
||||
canvas->m_cam_fov -= me->m_scroll_delta * 2.0f;
|
||||
App::I.brush_update();
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
@@ -79,11 +79,10 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
m_picking = true;
|
||||
canvas->pick_start();
|
||||
//canvas->m_show_tmp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->stroke_start(loc, me->m_pressure, node->m_brush);
|
||||
canvas->stroke_start(loc, me->m_pressure, canvas->m_current_brush);
|
||||
}
|
||||
m_dragging = true;
|
||||
node->mouse_capture();
|
||||
@@ -97,12 +96,8 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
if (m_dragging && m_picking)
|
||||
{
|
||||
node->mouse_release();
|
||||
//canvas->m_show_tmp = false;
|
||||
|
||||
glm::vec4 pix = canvas->pick_get(loc);
|
||||
auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2]));
|
||||
App::I.color->m_hue->set_value(hsv.x);
|
||||
App::I.color->m_quad->set_value(1.f - hsv.y, 1.f - hsv.z);
|
||||
canvas->m_current_brush.m_tip_color = pix;
|
||||
}
|
||||
m_dragging = false;
|
||||
m_picking = false;
|
||||
@@ -113,9 +108,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
if (m_dragging && m_picking)
|
||||
{
|
||||
glm::vec4 pix = canvas->pick_get(loc);
|
||||
auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2]));
|
||||
App::I.color->m_hue->set_value(hsv.x);
|
||||
App::I.color->m_quad->set_value(1.f - hsv.y, 1.f - hsv.z);
|
||||
canvas->m_current_brush.m_tip_color = pix;
|
||||
}
|
||||
m_cur_pos = loc;
|
||||
break;
|
||||
@@ -143,8 +136,8 @@ void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::StrokePreview);
|
||||
ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, node->m_brush.m_tip_flow);
|
||||
auto tip_color = glm::vec4(glm::vec3(node->m_brush.m_tip_color), 1);
|
||||
ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_current_brush.m_tip_flow);
|
||||
auto tip_color = glm::vec4(glm::vec3(canvas->m_current_brush.m_tip_color), 1);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color);
|
||||
//ui::ShaderManager::u_int(ui::kShaderUniform::Highlight, 0);
|
||||
float tip_scale = 1.f / glm::tan(glm::radians(ui::Canvas::I->m_cam_fov * 0.5f));
|
||||
@@ -152,11 +145,11 @@ void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const
|
||||
glm::scale(glm::vec3(1, -1, 1)) *
|
||||
ortho *
|
||||
glm::translate(glm::vec3(m_cur_pos, 0)) *
|
||||
glm::scale(glm::vec3(node->m_brush.m_tip_size * 800.f * tip_scale))
|
||||
glm::scale(glm::vec3(canvas->m_current_brush.m_tip_size * 800.f * tip_scale))
|
||||
);
|
||||
glEnable(GL_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
auto& tex = TextureManager::get(node->m_brush.m_tex_id);
|
||||
auto& tex = TextureManager::get(canvas->m_current_brush.m_tex_id);
|
||||
tex.bind();
|
||||
canvas->m_sampler_brush.bind(0);
|
||||
canvas->m_plane.draw_fill();
|
||||
@@ -167,19 +160,19 @@ void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const
|
||||
|
||||
void CanvasModePen::leave()
|
||||
{
|
||||
m_brush = node->m_brush;
|
||||
m_brush = canvas->m_current_brush;
|
||||
}
|
||||
|
||||
void CanvasModePen::enter()
|
||||
{
|
||||
if (m_valid_brush)
|
||||
{
|
||||
node->m_brush = m_brush;
|
||||
canvas->m_current_brush = m_brush;
|
||||
App::I.brush_update();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_brush = node->m_brush;
|
||||
m_brush = canvas->m_current_brush;
|
||||
m_valid_brush = true;
|
||||
}
|
||||
}
|
||||
@@ -200,7 +193,7 @@ void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
node->mouse_release();
|
||||
if (m_dragging)
|
||||
{
|
||||
canvas->stroke_start(m_drag_start, 1.f, node->m_brush);
|
||||
canvas->stroke_start(m_drag_start, 1.f, canvas->m_current_brush);
|
||||
canvas->stroke_update(m_drag_pos, 1.f);
|
||||
canvas->stroke_end();
|
||||
}
|
||||
@@ -225,7 +218,7 @@ void CanvasModeLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, cons
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, node->m_brush.m_tip_color);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, canvas->m_current_brush.m_tip_color);
|
||||
static glm::vec4 AB[2];
|
||||
AB[0] = { m_drag_start, 0, 1 };
|
||||
AB[1] = { m_drag_pos, 0, 1 };
|
||||
|
||||
@@ -562,7 +562,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
pt.y = GET_Y_LPARAM(lp);
|
||||
ScreenToClient(hWnd, &pt);
|
||||
App::I.mouse_scroll((float)pt.x, (float)pt.y,
|
||||
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA * 10.f);
|
||||
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
|
||||
break;
|
||||
}
|
||||
case WM_POINTERUPDATE:
|
||||
|
||||
@@ -7,7 +7,6 @@ class NodeCanvas : public Node
|
||||
public:
|
||||
std::string data_path;
|
||||
std::unique_ptr<ui::Canvas> m_canvas;
|
||||
ui::Brush m_brush;
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_linear;
|
||||
Sampler m_sampler_stencil;
|
||||
|
||||
@@ -62,10 +62,8 @@ public:
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
void handle_click(Node* target);
|
||||
std::vector<std::string> FindAllBrushes(const std::string& folder);
|
||||
uint16_t get_texture_id(int index) const;
|
||||
ui::Brush get_brush(int index) const;
|
||||
int get_brush_id(int index) const;
|
||||
void select_brush(int brush_id);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_stroke.h"
|
||||
#include "canvas.h"
|
||||
|
||||
Node* NodePanelStroke::clone_instantiate() const
|
||||
{
|
||||
@@ -19,8 +20,9 @@ void NodePanelStroke::init()
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodePanelStroke::set_params(const ui::Brush &b)
|
||||
void NodePanelStroke::update_controls()
|
||||
{
|
||||
const auto& b = ui::Canvas::I->m_current_brush;
|
||||
m_tip_size->m_value.x = glm::pow(b.m_tip_size, 1.f/3.f);
|
||||
m_tip_spacing->m_value.x = glm::pow(b.m_tip_spacing, 1.f/2.f) / 4.f;
|
||||
m_tip_flow->m_value.x = glm::pow(b.m_tip_flow, 1.f/2.f);
|
||||
@@ -34,13 +36,13 @@ void NodePanelStroke::set_params(const ui::Brush &b)
|
||||
m_tip_angle_follow->checked = b.m_tip_angle_follow;
|
||||
m_tip_flow_pressure->checked = b.m_tip_flow_pressure;
|
||||
m_tip_size_pressure->checked = b.m_tip_size_pressure;
|
||||
m_canvas->m_brush = b;
|
||||
m_canvas->draw_stroke();
|
||||
m_preview->m_brush = b;
|
||||
m_preview->draw_stroke();
|
||||
}
|
||||
|
||||
void NodePanelStroke::init_controls()
|
||||
{
|
||||
m_canvas = find<NodeStrokePreview>("canvas");
|
||||
m_preview = find<NodeStrokePreview>("canvas");
|
||||
|
||||
init_slider(m_tip_size, "tip-size", &ui::Brush::m_tip_size);
|
||||
init_slider(m_tip_spacing, "tip-spacing", &ui::Brush::m_tip_spacing);
|
||||
@@ -61,7 +63,8 @@ void NodePanelStroke::init_controls()
|
||||
init_checkbox(m_tip_flow_pressure, "tip-flow-pressure", &ui::Brush::m_tip_flow_pressure);
|
||||
init_checkbox(m_tip_size_pressure, "tip-size-pressure", &ui::Brush::m_tip_size_pressure);
|
||||
|
||||
//m_canvas->draw_stroke();
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
}
|
||||
|
||||
void NodePanelStroke::init_slider(NodeSliderH*& target, const char* id, float ui::Brush::* prop)
|
||||
@@ -75,8 +78,9 @@ void NodePanelStroke::init_slider(NodeSliderH*& target, const char* id, float ui
|
||||
void NodePanelStroke::handle_slide(float ui::Brush::* prop, Node* target, float value)
|
||||
{
|
||||
auto curve = m_curves.find((NodeSliderH*)target);
|
||||
m_canvas->m_brush.*prop = curve != m_curves.end() ? curve->second(value) : value;
|
||||
m_canvas->draw_stroke();
|
||||
ui::Canvas::I->m_current_brush.*prop = curve != m_curves.end() ? curve->second(value) : value;
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
if (on_stroke_change)
|
||||
on_stroke_change(this);
|
||||
}
|
||||
@@ -86,13 +90,14 @@ void NodePanelStroke::init_checkbox(NodeCheckBox*& target, const char* id, bool
|
||||
target = find<NodeCheckBox>(id);
|
||||
target->on_value_changed = std::bind(&NodePanelStroke::handle_checkbox,
|
||||
this, prop, std::placeholders::_1, std::placeholders::_2);
|
||||
m_canvas->m_brush.*prop = target->checked;
|
||||
ui::Canvas::I->m_current_brush.*prop = target->checked;
|
||||
}
|
||||
|
||||
void NodePanelStroke::handle_checkbox(bool ui::Brush::* prop, Node *target, bool value)
|
||||
{
|
||||
m_canvas->m_brush.*prop = value;
|
||||
m_canvas->draw_stroke();
|
||||
ui::Canvas::I->m_current_brush.*prop = value;
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
if (on_stroke_change)
|
||||
on_stroke_change(this);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
class NodePanelStroke : public Node
|
||||
{
|
||||
public:
|
||||
NodeStrokePreview* m_canvas;
|
||||
NodeStrokePreview* m_preview;
|
||||
NodeSliderH* m_tip_size;
|
||||
NodeSliderH* m_tip_spacing;
|
||||
NodeSliderH* m_tip_flow;
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
void set_params(const ui::Brush& b);
|
||||
void update_controls();
|
||||
|
||||
void init_slider(NodeSliderH*& slider, const char* id, float ui::Brush::* prop);
|
||||
void handle_slide(float ui::Brush::* prop, Node* target, float value);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "bezier.h"
|
||||
#include "canvas.h"
|
||||
|
||||
Node* NodeStrokePreview::clone_instantiate() const
|
||||
{
|
||||
@@ -32,8 +33,8 @@ void NodeStrokePreview::init_controls()
|
||||
m_sampler.create(GL_LINEAR, GL_REPEAT);
|
||||
m_sampler_brush.create();
|
||||
m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
|
||||
TextureManager::load("data/thumbs/Round-Hard.png");
|
||||
m_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
|
||||
// TextureManager::load("data/thumbs/Round-Hard.png");
|
||||
// ui::Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
|
||||
}
|
||||
|
||||
void NodeStrokePreview::restore_context()
|
||||
@@ -73,7 +74,7 @@ void NodeStrokePreview::draw_stroke()
|
||||
if (!m_stroke.m_keypoints.empty())
|
||||
m_stroke.m_prev_sample.origin = m_stroke.m_keypoints[0].pos;
|
||||
auto samples = m_stroke.compute_samples();
|
||||
auto& tex = TextureManager::get(m_brush.m_tex_id);
|
||||
auto& tex = TextureManager::get(b.m_tex_id);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
tex.bind();
|
||||
m_sampler_brush.bind(0);
|
||||
@@ -91,7 +92,7 @@ void NodeStrokePreview::draw_stroke()
|
||||
m_mesh.shader.u_int(kShaderUniform::TexStencil, 1); // stencil
|
||||
m_mesh.shader.u_vec2(kShaderUniform::Resolution, { m_rtt.getWidth(), m_rtt.getHeight() });
|
||||
m_mesh.shader.u_vec2(kShaderUniform::StencilOffset, glm::vec2(0));
|
||||
m_mesh.shader.u_float(kShaderUniform::StencilAlpha, m_brush.m_tip_stencil);
|
||||
m_mesh.shader.u_float(kShaderUniform::StencilAlpha, b.m_tip_stencil);
|
||||
m_mesh.draw(samples, proj);
|
||||
}
|
||||
//else
|
||||
|
||||
Reference in New Issue
Block a user