Sketch ini
This commit is contained in:
parent
be0177b1b0
commit
b8bab2264f
16 changed files with 2510 additions and 0 deletions
|
|
@ -26,3 +26,4 @@ add_subdirectory(DataAnalysis)
|
||||||
add_subdirectory(Objects)
|
add_subdirectory(Objects)
|
||||||
add_subdirectory(Widgets)
|
add_subdirectory(Widgets)
|
||||||
add_subdirectory(LibraryViewer)
|
add_subdirectory(LibraryViewer)
|
||||||
|
add_subdirectory(Sketch3D)
|
||||||
22
Sketch3D/CMakeLists.txt
Normal file
22
Sketch3D/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
project(Sketch3Dlib)
|
||||||
|
|
||||||
|
### ---------------------- Externals --------------------- ###
|
||||||
|
set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
|
||||||
|
set(BINDINGS_LIBS glfw Imgui ${GLEW_LIB})
|
||||||
|
|
||||||
|
### ---------------------- Static Library --------------------- ###
|
||||||
|
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
|
||||||
|
file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE} ./ext/)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets Math Strings)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
|
||||||
|
|
||||||
|
file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
|
||||||
|
|
||||||
|
### -------------------------- Applications -------------------------- ###
|
||||||
|
add_executable(Sketch3D ./applications/Entry.cpp)
|
||||||
|
target_link_libraries(Sketch3D ${PROJECT_NAME})
|
||||||
|
|
||||||
34
Sketch3D/applications/Entry.cpp
Normal file
34
Sketch3D/applications/Entry.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
#include "nodes.h"
|
||||||
|
#include "StrokesWidget.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
|
auto nodes = (argc == 2) ? nd::NodesCore::createFast(argv[1]) : nd::NodesCore::createFast();
|
||||||
|
|
||||||
|
tp::init_utils();
|
||||||
|
|
||||||
|
obj::NDO->define(&obj::StrokesObject::TypeData);
|
||||||
|
obj::NDO->define(&nd::StrokesWidget::TypeData);
|
||||||
|
obj::NDO->type_groups.addType(&obj::StrokesObject::TypeData, { "Strokes", "StrokesProject" });
|
||||||
|
obj::NDO->type_groups.addType(&nd::StrokesWidget::TypeData, { "Strokes", "StrokesWidget" });
|
||||||
|
|
||||||
|
nd::StrokesWidget::OpsInit();
|
||||||
|
nd::StrokesWidget::addShortcuts(nodes);
|
||||||
|
|
||||||
|
auto root_widget = NDO_CAST(nd::Widget, obj::NDO->create("RootWidget"));
|
||||||
|
|
||||||
|
auto childs = root_widget->getMember<obj::ListObject>("childs");
|
||||||
|
|
||||||
|
childs->pushBack(obj::NDO->create("StrokesWidget"));
|
||||||
|
|
||||||
|
nodes->bindRootWindow(root_widget);
|
||||||
|
|
||||||
|
nodes->run();
|
||||||
|
|
||||||
|
nd::StrokesWidget::OpsUnInit();
|
||||||
|
tp::finalize_utils();
|
||||||
|
|
||||||
|
nodes->destroyFast();
|
||||||
|
tp::terminate();
|
||||||
|
}
|
||||||
260
Sketch3D/private/StrokesWidget.cpp
Normal file
260
Sketch3D/private/StrokesWidget.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
|
||||||
|
#include "StrokesWidget.h"
|
||||||
|
#include "operator.h"
|
||||||
|
|
||||||
|
using namespace nd;
|
||||||
|
|
||||||
|
void StrokesWidget::constructor(StrokesWidget* self) {
|
||||||
|
new (&self->mRenderer) strokes::Renderer({ 1980, 1020 });
|
||||||
|
self->createMember("link", "TargetProject");
|
||||||
|
self->createMember("StrokesProject", "default");
|
||||||
|
self->createMember("rgba", "draw_color");
|
||||||
|
self->addMember(obj::ColorObject::create({ 0.13f, 0.13f, 0.13f, 1.f }), "bg_color");
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::destructor(StrokesWidget* self) {
|
||||||
|
self->mRenderer.~Renderer();
|
||||||
|
self->mImage.free(self->mDrawer);
|
||||||
|
self->mImage.~ImageHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::copy(Widget* self, const Widget* in) {}
|
||||||
|
|
||||||
|
strokes::Project* StrokesWidget::getTargetProject() {
|
||||||
|
auto link = getMember<obj::LinkObject>("TargetProject");
|
||||||
|
if (!link) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
auto obj = link->getLink();
|
||||||
|
if (!obj) {
|
||||||
|
link->setLink(getMember<obj::StrokesObject>("default"));
|
||||||
|
return getTargetProject();
|
||||||
|
}
|
||||||
|
auto strokes = NDO_CAST(obj::StrokesObject, obj);
|
||||||
|
if (!strokes) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &strokes->mProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::procInputs(StrokesWidget* self, nd::GUIInputs* inputs) {
|
||||||
|
auto proj = self->getTargetProject();
|
||||||
|
if (!proj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rec = self->getRect();
|
||||||
|
proj->mCamera.set_ratio(rec.w / rec.z);
|
||||||
|
|
||||||
|
auto col_obj_bg = self->getMember<obj::ColorObject>("bg_color");
|
||||||
|
if (col_obj_bg) {
|
||||||
|
proj->mBackgroundColor = col_obj_bg->mCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rec.inside(inputs->mCrsPrev)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pressure = inputs->mPressure;
|
||||||
|
if (!inputs->Anticipating()) {
|
||||||
|
pressure = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto idx = proj->mBrushes.presents(proj->mActiveBrush);
|
||||||
|
if (idx) {
|
||||||
|
auto brush = proj->mBrushes.getSlotVal(idx);
|
||||||
|
auto crs = (inputs->mCrs - rec.pos);
|
||||||
|
crs.x /= rec.z;
|
||||||
|
crs.y /= rec.w;
|
||||||
|
crs = (crs - 0.5) * 2;
|
||||||
|
|
||||||
|
if (brush->mType == "pencil") {
|
||||||
|
auto col_obj = self->getMember<obj::ColorObject>("draw_color");
|
||||||
|
if (col_obj) {
|
||||||
|
((strokes::PencilBrush*)brush)->mCol = col_obj->mCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
brush->sample(proj, crs, pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::presentOutput(StrokesWidget* self, nd::GUIdrawer* drawer) {
|
||||||
|
auto proj = self->getTargetProject();
|
||||||
|
if (!proj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->mImage.mId) {
|
||||||
|
self->mDrawer = drawer;
|
||||||
|
self->mImage.createFromBuff(self->mRenderer.getBuff(), drawer);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rec = self->getRect();
|
||||||
|
self->mRenderer.setViewport({ 0, 0, rec.z, rec.w });
|
||||||
|
self->mRenderer.setClearCol(proj->mBackgroundColor);
|
||||||
|
self->mRenderer.renderBegin();
|
||||||
|
|
||||||
|
for (auto lay : proj->mLayers) {
|
||||||
|
if (lay.data()->enabled) {
|
||||||
|
for (auto str : lay.data()->strokes) {
|
||||||
|
self->mRenderer.drawStroke(str.data(), &proj->mCamera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto idx = proj->mBrushes.presents(proj->mActiveBrush);
|
||||||
|
if (idx) {
|
||||||
|
auto brush = proj->mBrushes.getSlotVal(idx);
|
||||||
|
brush->draw(&self->mRenderer, &proj->mCamera);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->mRenderer.renderEnd();
|
||||||
|
|
||||||
|
drawer->drawImage(rec, &self->mImage, 0, 1, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct obj::ObjectType StrokesWidget::TypeData = {
|
||||||
|
.base = &Widget::TypeData,
|
||||||
|
.constructor = (obj::object_constructor)StrokesWidget::constructor,
|
||||||
|
.destructor = (obj::object_destructor)StrokesWidget::destructor,
|
||||||
|
.copy = (obj::object_copy)StrokesWidget::copy,
|
||||||
|
.size = sizeof(StrokesWidget),
|
||||||
|
.name = "StrokesWidget",
|
||||||
|
.vtable = &StrokesWidget::vtable
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StrokesWidget::Vtable StrokesWidget::vtable = {
|
||||||
|
(void (*)(Widget*, nd::GUIInputs*)) & StrokesWidget::procInputs,
|
||||||
|
(void (*)(Widget*, nd::GUIdrawer*)) & StrokesWidget::presentOutput,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StrokesWidgetOps : public nd::TypeOperators {
|
||||||
|
|
||||||
|
struct CameraOrbit : public nd::Operator {
|
||||||
|
tp::vec2f prev_crs;
|
||||||
|
struct Invoke : public nd::OpCallBack {
|
||||||
|
Invoke() {
|
||||||
|
auto transf = obj::EnumObject::create({ "move", "rotate" , "scale" });
|
||||||
|
arg_interface.put("Gui", obj::NDO->create("link"));
|
||||||
|
arg_interface.put("transf", transf);
|
||||||
|
|
||||||
|
func = exec;
|
||||||
|
description = "invoke";
|
||||||
|
}
|
||||||
|
static void exec(Operator* op, obj::DictObject* args) {
|
||||||
|
auto active = OpCallBack::getActiveWidget<StrokesWidget>(args);
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto proj = active->getTargetProject();
|
||||||
|
if (!proj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rec = active->getRect();
|
||||||
|
|
||||||
|
auto gui = OpCallBack::getGUI(args);
|
||||||
|
auto prev_crs = ((CameraOrbit*)op)->prev_crs;
|
||||||
|
prev_crs = tp::vec2f(prev_crs.x / rec.size.x, prev_crs.y / rec.size.y);
|
||||||
|
prev_crs = (prev_crs - 0.5f) * 2.f;
|
||||||
|
auto crs = tp::vec2f(gui->mInputs.mCrs.x / rec.size.x, gui->mInputs.mCrs.y / rec.size.y);
|
||||||
|
crs = (crs - 0.5f) * 2.f;
|
||||||
|
auto delta = (crs - prev_crs);
|
||||||
|
|
||||||
|
if (tp::abs(delta.x) < 0.1 && tp::abs(delta.y) < 0.1) {
|
||||||
|
|
||||||
|
auto type = OpCallBack::getArg<obj::EnumObject>(args, "transf");
|
||||||
|
switch (type->active) {
|
||||||
|
case 0: {
|
||||||
|
proj->mCamera.move(crs, prev_crs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
proj->mCamera.rotate(-delta.x * 5, -delta.y * 5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
proj->mCamera.zoom((crs.y + 1.f) / (prev_crs.y + 1.f));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
((CameraOrbit*)op)->prev_crs = gui->mInputs.mCrs;
|
||||||
|
}
|
||||||
|
} invoke_callback;
|
||||||
|
|
||||||
|
CameraOrbit() {
|
||||||
|
description = "Orbits camera";
|
||||||
|
callbacks.put("invoke", &invoke_callback);
|
||||||
|
}
|
||||||
|
} camera_orbit_op;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StrokesWidgetOps() {
|
||||||
|
operators.put("camera_orbit", &camera_orbit_op);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void StrokesWidget::OpsInit() {
|
||||||
|
assert(!StrokesWidget::TypeData.nodes_custom_data);
|
||||||
|
StrokesWidget::TypeData.nodes_custom_data = new StrokesWidgetOps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::OpsUnInit() {
|
||||||
|
assert(StrokesWidget::TypeData.nodes_custom_data);
|
||||||
|
auto del = (StrokesWidgetOps*)StrokesWidget::TypeData.nodes_custom_data;
|
||||||
|
StrokesWidget::TypeData.nodes_custom_data = NULL;
|
||||||
|
delete del;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesWidget::addShortcuts(nd::NodesCore* nodes) {
|
||||||
|
auto uis_list = nodes->getMemberAssert<obj::ListObject>("uis");
|
||||||
|
NDO_CASTV(nd::GUI, uis_list->getItems()[0], gui);
|
||||||
|
NDO_CASTV(nd::TUI, uis_list->getItems()[1], tui);
|
||||||
|
|
||||||
|
auto shortcuts = tui->getMemberAssert<obj::ListObject>("shortcuts");
|
||||||
|
auto inputs = tui->getMemberAssert<obj::DictObject>("inputs");
|
||||||
|
auto input_states = tui->getMemberAssert<obj::DictObject>("input_states");
|
||||||
|
auto input_state_hold = input_states->get("hold");
|
||||||
|
|
||||||
|
{ // orbit
|
||||||
|
auto shortcut = nd::Shortcut::createShortcut("StrokesWidget", "camera_orbit");
|
||||||
|
auto args = NDO_CAST(obj::DictObject, shortcut->callbacks_arguments->get("invoke"));
|
||||||
|
NDO_CAST(obj::LinkObject, args->get("Gui"))->setLink(gui);
|
||||||
|
NDO_CAST(obj::EnumObject, args->get("transf"))->active = 1;
|
||||||
|
|
||||||
|
shortcuts->pushBack(shortcut);
|
||||||
|
|
||||||
|
auto trigger = shortcut->addTrigger("invoke");
|
||||||
|
auto back = tui->getInput(tp::Keycode::LEFT_ALT)->getMember<obj::EnumObject>("state");
|
||||||
|
trigger->addComparator("comp", back, input_state_hold);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // zoom
|
||||||
|
auto shortcut = nd::Shortcut::createShortcut("StrokesWidget", "camera_orbit");
|
||||||
|
auto args = NDO_CAST(obj::DictObject, shortcut->callbacks_arguments->get("invoke"));
|
||||||
|
NDO_CAST(obj::LinkObject, args->get("Gui"))->setLink(gui);
|
||||||
|
NDO_CAST(obj::EnumObject, args->get("transf"))->active = 2;
|
||||||
|
shortcuts->pushBack(shortcut);
|
||||||
|
|
||||||
|
auto trigger = shortcut->addTrigger("invoke");
|
||||||
|
auto back = tui->getInput(tp::Keycode::LEFT_CONTROL)->getMember<obj::EnumObject>("state");
|
||||||
|
trigger->addComparator("comp", back, input_state_hold);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // pan
|
||||||
|
auto shortcut = nd::Shortcut::createShortcut("StrokesWidget", "camera_orbit");
|
||||||
|
auto args = NDO_CAST(obj::DictObject, shortcut->callbacks_arguments->get("invoke"));
|
||||||
|
NDO_CAST(obj::LinkObject, args->get("Gui"))->setLink(gui);
|
||||||
|
shortcuts->pushBack(shortcut);
|
||||||
|
|
||||||
|
auto trigger = shortcut->addTrigger("invoke");
|
||||||
|
auto back = tui->getInput(tp::Keycode::SPACE)->getMember<obj::EnumObject>("state");
|
||||||
|
trigger->addComparator("comp", back, input_state_hold);
|
||||||
|
}
|
||||||
|
}
|
||||||
501
Sketch3D/private/strokes.cpp
Normal file
501
Sketch3D/private/strokes.cpp
Normal file
|
|
@ -0,0 +1,501 @@
|
||||||
|
|
||||||
|
#include "Strokes.h"
|
||||||
|
|
||||||
|
using namespace strokes;
|
||||||
|
|
||||||
|
Stroke::GLHandles::GLHandles() {
|
||||||
|
glGenVertexArrays(1, &VertexArrayID);
|
||||||
|
glBindVertexArray(VertexArrayID);
|
||||||
|
|
||||||
|
glGenBuffers(1, &vertexbuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::GLHandles::sendDataToGPU(tp::Array<Point>* mPoints) {
|
||||||
|
glBindVertexArray(VertexArrayID);
|
||||||
|
vbo_len = mPoints->length();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(mPoints[0]) * vbo_len, mPoints->buff(), GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stroke::GLHandles::~GLHandles() {
|
||||||
|
glDeleteBuffers(1, &vertexbuffer);
|
||||||
|
glDeleteVertexArrays(1, &VertexArrayID);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stroke::Stroke() {}
|
||||||
|
|
||||||
|
tp::Array<Stroke::Point>& Stroke::buff() {
|
||||||
|
return mPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::updateGpuBuffers() {
|
||||||
|
mGl.sendDataToGPU(&mPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::denoisePos(tp::halni passes) {
|
||||||
|
for (auto pass : tp::Range(passes)) {
|
||||||
|
for (auto pi : tp::Range(mPoints.length() - 2)) {
|
||||||
|
mPoints[pi + 1].pos = (mPoints[pi + 1].pos + mPoints[pi].pos + mPoints[pi + 2].pos) / 3.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::denoiseThickness(tp::halni passes) {
|
||||||
|
for (auto pass : tp::Range(passes)) {
|
||||||
|
for (auto pi : tp::Range(mPoints.length() - 2)) {
|
||||||
|
mPoints[pi + 1].thickness = (mPoints[pi].thickness + mPoints[pi + 2].thickness) / 2.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::compress(tp::halnf factor) {
|
||||||
|
if (mPoints.length() < 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::List<Point> passed_poits;
|
||||||
|
|
||||||
|
for (auto idx : tp::Range(mPoints.length())) {
|
||||||
|
passed_poits.pushBack(mPoints[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::ListNode<Point>* min_node = NULL;
|
||||||
|
do {
|
||||||
|
min_node = NULL;
|
||||||
|
tp::halnf min_factor = factor;
|
||||||
|
|
||||||
|
tp::ListNode<Point>* iter = passed_poits.first()->next;
|
||||||
|
for (; iter->next; iter = iter->next) {
|
||||||
|
tp::vec3f dir1 = (iter->data.pos - iter->prev->data.pos).normalize();
|
||||||
|
tp::vec3f dir2 = (iter->next->data.pos - iter->data.pos).normalize();
|
||||||
|
tp::halnf factor = 1 - dir1.dot(dir2);
|
||||||
|
|
||||||
|
if (factor < min_factor) {
|
||||||
|
min_node = iter;
|
||||||
|
min_factor = factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_node) {
|
||||||
|
passed_poits.delNode(min_node);
|
||||||
|
}
|
||||||
|
} while (min_node);
|
||||||
|
|
||||||
|
|
||||||
|
mPoints.reserve(passed_poits.length());
|
||||||
|
for (auto point : passed_poits) {
|
||||||
|
mPoints[point.idx()] = point.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::subdiv(tp::halnf precision, tp::Camera* cam, tp::halni passes) {
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
if (mPoints.length() < 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::List<Point> new_points;
|
||||||
|
for (auto idx : tp::Range(mPoints.length())) {
|
||||||
|
new_points.pushBack(mPoints[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto viewmat = cam->viewmat();
|
||||||
|
auto projmat = cam->projmat();
|
||||||
|
|
||||||
|
for (auto i : tp::Range(passes)) {
|
||||||
|
|
||||||
|
auto n_points = new_points.length();
|
||||||
|
|
||||||
|
auto p0 = new_points.first();
|
||||||
|
auto p1 = p0->next;
|
||||||
|
auto p2 = p1->next;
|
||||||
|
auto p3 = p2->next;
|
||||||
|
|
||||||
|
while (p3) {
|
||||||
|
|
||||||
|
auto p1_2d = cam->project(p1->data.pos, viewmat, projmat);
|
||||||
|
auto p2_2d = cam->project(p2->data.pos, viewmat, projmat);
|
||||||
|
|
||||||
|
auto len = (p1_2d - p2_2d).length();
|
||||||
|
|
||||||
|
if (len > precision * 1.5f) {
|
||||||
|
auto const a = (p1->data.pos - p0->data.pos).unitv();
|
||||||
|
auto const b = (p2->data.pos - p3->data.pos).unitv();
|
||||||
|
auto const l = p1->data.pos - p2->data.pos;
|
||||||
|
auto const l_len = l.length();
|
||||||
|
auto const ab = a.dot(b);
|
||||||
|
auto const la = l.dot(a);
|
||||||
|
auto const lb = l.dot(b);
|
||||||
|
tp::vec3f mid;
|
||||||
|
|
||||||
|
if (1 - tp::abs(ab) < 0.001f) {
|
||||||
|
goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k = (lb - ab * la) / (1 - ab * ab);
|
||||||
|
auto t = ab * k - la;
|
||||||
|
|
||||||
|
if (k < 0 || t < 0) {
|
||||||
|
goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k > l_len) {
|
||||||
|
k = l_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t > l_len) {
|
||||||
|
t = l_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const p1_mid = p1->data.pos + (a * t);
|
||||||
|
auto const p2_mid = p2->data.pos + (b * k);
|
||||||
|
|
||||||
|
mid = (p1_mid + p2_mid) / 2.f;
|
||||||
|
{
|
||||||
|
auto const p0 = ((p2->data.pos - p1->data.pos) / 2.f) + p1->data.pos;
|
||||||
|
mid = ((mid - p0) / 3.f) + p0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto node = new_points.newNode();
|
||||||
|
node->data.pos = mid;
|
||||||
|
node->data.thickness = (p1->data.thickness + p2->data.thickness) / 2.f;
|
||||||
|
|
||||||
|
auto insert_after = p1;
|
||||||
|
|
||||||
|
p0 = p1;
|
||||||
|
p1 = p2;
|
||||||
|
p2 = p3;
|
||||||
|
p3 = p3->next;
|
||||||
|
|
||||||
|
new_points.attach(node, insert_after);
|
||||||
|
p0 = p0->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SKIP:
|
||||||
|
p0 = p1;
|
||||||
|
p1 = p2;
|
||||||
|
p2 = p3;
|
||||||
|
p3 = p3->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_points == new_points.length()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_points.length() != mPoints.length()) {
|
||||||
|
mPoints.reserve(new_points.length());
|
||||||
|
for (auto point : new_points) {
|
||||||
|
mPoints[point.idx()] = point.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::halni Stroke::saveSize() {
|
||||||
|
tp::halni out = 0;
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
out += mPoints.length() * sizeof(Point);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::save(tp::File& file) {
|
||||||
|
file.write<tp::rgba>(&mCol);
|
||||||
|
|
||||||
|
tp::alni length = mPoints.length();
|
||||||
|
file.write<tp::alni>(&length);
|
||||||
|
for (auto piter : mPoints) {
|
||||||
|
file.write<Point>(&piter.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::load(tp::File& file) {
|
||||||
|
tp::rgba color;
|
||||||
|
file.read<tp::rgba>(&color);
|
||||||
|
|
||||||
|
tp::alni p_len;
|
||||||
|
file.read<tp::alni>(&p_len);
|
||||||
|
|
||||||
|
mPoints.reserve(p_len);
|
||||||
|
for (auto piter : mPoints) {
|
||||||
|
file.read<Point>(&piter.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGpuBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
PencilBrush::PencilBrush() {
|
||||||
|
mType = "pencil";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PencilBrush::unsureReady(Stroke* stroke, tp::Camera* cam, bool debug) {
|
||||||
|
if (stroke->buff().length() == 1) {
|
||||||
|
auto new_point = stroke->buff()[0];
|
||||||
|
new_point.pos += 0.00001f;
|
||||||
|
stroke->buff().pushBack(new_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEnableCompression && !debug) {
|
||||||
|
stroke->subdiv(mPrecision, cam, mSubdivPasses);
|
||||||
|
stroke->denoisePos(mDenoisePassesPos);
|
||||||
|
stroke->compress(mCompressionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke->updateGpuBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PencilBrush::sample(Project* proj, tp::vec2f crs, tp::halnf pressure) {
|
||||||
|
if (proj->mActiveLayer == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool max_level = mStroke && mStroke->mPoints.length() > mMaxPoints;
|
||||||
|
if (!pressure || max_level) {
|
||||||
|
if (mStroke) {
|
||||||
|
unsureReady(mStroke, &proj->mCamera);
|
||||||
|
proj->mLayers[proj->mActiveLayer]->strokes.pushBack(mStroke);
|
||||||
|
mStroke = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_level) {
|
||||||
|
mStroke = new Stroke();
|
||||||
|
mStroke->mCol = mCol;
|
||||||
|
|
||||||
|
mStroke->mPoints.pushBack(proj->mLayers[proj->mActiveLayer]->strokes.last()->data->mPoints.last());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pressure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thickness = pressure * (proj->mCamera.project({ 0.f, 0.f }) - proj->mCamera.project({ mSize, 0.f })).length();
|
||||||
|
bool point_passed = true;
|
||||||
|
|
||||||
|
if (mStroke && mStroke->buff().length()) {
|
||||||
|
auto last_point_2d = proj->mCamera.project(mStroke->buff().last().pos);
|
||||||
|
point_passed = (crs - last_point_2d).length() > mPrecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!point_passed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mStroke) {
|
||||||
|
mStroke = new Stroke();
|
||||||
|
mStroke->mCol = mCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto point_coords = proj->mCamera.project(crs);
|
||||||
|
mStroke->buff().pushBack({ point_coords, (tp::halnf) thickness });
|
||||||
|
mStroke->updateGpuBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PencilBrush::draw(Renderer* render, tp::Camera* camera) {
|
||||||
|
if (mStroke) {
|
||||||
|
if (mEnableCompression) {
|
||||||
|
mShowStroke.buff() = mStroke->buff();
|
||||||
|
mShowStroke.mCol = mStroke->mCol;
|
||||||
|
unsureReady(&mShowStroke, camera, 0);
|
||||||
|
render->drawStroke(&mShowStroke, camera);
|
||||||
|
} else {
|
||||||
|
render->drawStroke(mStroke, camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PencilBrush::~PencilBrush() {
|
||||||
|
if (mStroke) delete mStroke;
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::Layer::~Layer() {
|
||||||
|
for (auto str : strokes) {
|
||||||
|
delete str.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::Project() {
|
||||||
|
mCamera.lookat({ 0.0f, 0.0f, 0.0f }, { 2.0f, 0.0f, 0.f }, { 0, 0, 1 });
|
||||||
|
|
||||||
|
auto lay = new Layer();
|
||||||
|
lay->enabled = true;
|
||||||
|
mLayers.pushBack(lay);
|
||||||
|
mActiveLayer = 0;
|
||||||
|
|
||||||
|
mBrushes.put("eraser", new EraserBrush());
|
||||||
|
mBrushes.put("pencil", new PencilBrush());
|
||||||
|
|
||||||
|
mActiveBrush = "pencil";
|
||||||
|
|
||||||
|
|
||||||
|
auto vec = mCamera.project({0, 0, 0});
|
||||||
|
vec = mCamera.project({-1, 0, 0});
|
||||||
|
vec = mCamera.project({1, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::~Project() {
|
||||||
|
for (auto brush : mBrushes) {
|
||||||
|
delete brush->val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::alni Project::saveSize() {
|
||||||
|
tp::alni out = 0;
|
||||||
|
out += sizeof(tp::Camera);
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
out += sizeof(tp::halni);
|
||||||
|
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
for (auto layer : mLayers) {
|
||||||
|
out += layer.data()->name.save_size();
|
||||||
|
out += sizeof(bool);
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
for (auto stiter : layer.data()->strokes) {
|
||||||
|
out += stiter->saveSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Project::save(tp::File& file) {
|
||||||
|
file.write<tp::Camera>(&mCamera);
|
||||||
|
file.write<tp::rgba>(&mBackgroundColor);
|
||||||
|
file.write<tp::halni>(&mActiveLayer);
|
||||||
|
|
||||||
|
tp::alni lay_len = mLayers.length();
|
||||||
|
file.write<tp::alni>(&lay_len);
|
||||||
|
for (auto layer : mLayers) {
|
||||||
|
layer.data()->name.save(&file);
|
||||||
|
|
||||||
|
file.write<bool>(&layer.data()->enabled);
|
||||||
|
|
||||||
|
tp::alni len = layer.data()->strokes.length();
|
||||||
|
file.write<tp::alni>(&len);
|
||||||
|
for (auto stiter : layer.data()->strokes) {
|
||||||
|
stiter->save(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Project::load(tp::File& file) {
|
||||||
|
file.read<tp::Camera>(&mCamera);
|
||||||
|
file.read<tp::rgba>(&mBackgroundColor);
|
||||||
|
file.read<tp::halni>(&mActiveLayer);
|
||||||
|
|
||||||
|
tp::alni layers_len;
|
||||||
|
file.read<tp::alni>(&layers_len);
|
||||||
|
mLayers.reserve(layers_len);
|
||||||
|
|
||||||
|
for (tp::alni idx = 0; idx < layers_len; idx++) {
|
||||||
|
|
||||||
|
tp::string key; key.load(&file);
|
||||||
|
auto layer = new Layer();
|
||||||
|
layer->name = key;
|
||||||
|
mLayers[idx] = layer;
|
||||||
|
|
||||||
|
file.read<bool>(&layer->enabled);
|
||||||
|
|
||||||
|
tp::alni len;
|
||||||
|
file.read<tp::alni>(&len);
|
||||||
|
|
||||||
|
for (tp::alni str_idx = 0; str_idx < len; str_idx++) {
|
||||||
|
auto str = new Stroke();
|
||||||
|
layer->strokes.pushBack(str);
|
||||||
|
layer->strokes.last()->data->load(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::Renderer(tp::vec2f size) :
|
||||||
|
mBuffer(size, { 0.f, 0.f, 1.f, 1.f }, 4),
|
||||||
|
mBufferDowncast(size, { 0.f, 0.f, 1.f, 1.f }),
|
||||||
|
mShader(".\\rsc\\shaders\\stroke", ".\\rsc\\shaders\\stroke", ".\\rsc\\shaders\\stroke")
|
||||||
|
{
|
||||||
|
mMatrixUniform = mShader.getu("MVP");
|
||||||
|
mColorUniform = mShader.getu("Color");
|
||||||
|
mRatioUniform = mShader.getu("Ratio");
|
||||||
|
mTargetUniform = mShader.getu("Target");
|
||||||
|
mBGColUniform = mShader.getu("BGCol");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::renderBegin() {
|
||||||
|
mBuffer.begin_draw();
|
||||||
|
mBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setViewport(tp::rectf viewport) {
|
||||||
|
mBuffer.setViewport(viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::drawStroke(Stroke* str, tp::Camera* camera) {
|
||||||
|
//return;
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
|
GLint val;
|
||||||
|
glGetIntegerv(GL_SAMPLES, &val);
|
||||||
|
|
||||||
|
glBindVertexArray(str->mGl.VertexArrayID);
|
||||||
|
mShader.bind();
|
||||||
|
|
||||||
|
auto cam_mat = camera->transform_mat().transpose();
|
||||||
|
glUniformMatrix4fv(mMatrixUniform, 1, GL_FALSE, &cam_mat[0][0]);
|
||||||
|
|
||||||
|
glUniform4fv(mColorUniform, 1, &str->mCol.r);
|
||||||
|
|
||||||
|
glUniform4fv(mBGColUniform, 1, &mBuffer.col_clear.r);
|
||||||
|
|
||||||
|
auto ratio = camera->get_ratio();
|
||||||
|
glUniform1fv(mRatioUniform, 1, &ratio);
|
||||||
|
|
||||||
|
auto target = tp::halnf(((camera->get_target() - camera->get_pos()).length() - camera->get_near())
|
||||||
|
/ (camera->get_far() - camera->get_near()));
|
||||||
|
|
||||||
|
glUniform1fv(mTargetUniform, 1, &target);
|
||||||
|
|
||||||
|
// 1st attribute buffer : vertices
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, str->mGl.vertexbuffer);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||||||
|
|
||||||
|
glDrawArrays(GL_LINE_STRIP, 0, str->mGl.vbo_len);
|
||||||
|
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
|
||||||
|
mShader.unbind();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::renderEnd() {
|
||||||
|
mBuffer.end_draw();
|
||||||
|
|
||||||
|
mBufferDowncast.begin_draw();
|
||||||
|
|
||||||
|
auto size = mBufferDowncast.getSize();
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mBuffer.buffId());
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mBufferDowncast.buffId());
|
||||||
|
mBufferDowncast.clear();
|
||||||
|
glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
|
mBufferDowncast.end_draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::uhalni Renderer::getTextudeId() {
|
||||||
|
return mBufferDowncast.texId();
|
||||||
|
}
|
||||||
|
|
||||||
|
tp::ogl::fbuffer* Renderer::getBuff() {
|
||||||
|
return &mBufferDowncast;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setClearCol(tp::rgba col) {
|
||||||
|
mBuffer.col_clear = col;
|
||||||
|
mBufferDowncast.col_clear = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::~Renderer() {
|
||||||
|
}
|
||||||
42
Sketch3D/private/strokesobject.cpp
Normal file
42
Sketch3D/private/strokesobject.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
#include "strokesobject.h"
|
||||||
|
|
||||||
|
using namespace tp;
|
||||||
|
using namespace obj;
|
||||||
|
|
||||||
|
void StrokesObject::constructor(StrokesObject* self) {
|
||||||
|
new (&self->mProject) strokes::Project();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesObject::copy(StrokesObject* self, const StrokesObject* in) {
|
||||||
|
self->mProject = in->mProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokesObject::destructor(StrokesObject* self) {
|
||||||
|
self->mProject.~Project();
|
||||||
|
}
|
||||||
|
|
||||||
|
static alni save_size(StrokesObject* self) {
|
||||||
|
return self->mProject.saveSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save(StrokesObject* self, File& file_self) {
|
||||||
|
self->mProject.save(file_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load(File& file_self, StrokesObject* self) {
|
||||||
|
new (&self->mProject) strokes::Project();
|
||||||
|
self->mProject.load(file_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjectType obj::StrokesObject::TypeData = {
|
||||||
|
.base = NULL,
|
||||||
|
.constructor = (object_constructor) StrokesObject::constructor,
|
||||||
|
.destructor = (object_destructor) StrokesObject::destructor,
|
||||||
|
.copy = (object_copy) StrokesObject::copy,
|
||||||
|
.size = sizeof(StrokesObject),
|
||||||
|
.name = "StrokesProject",
|
||||||
|
.save_size = (object_save_size) save_size,
|
||||||
|
.save = (object_save) save,
|
||||||
|
.load = (object_load) load,
|
||||||
|
};
|
||||||
1049
Sketch3D/private/temp.cpp
Normal file
1049
Sketch3D/private/temp.cpp
Normal file
File diff suppressed because it is too large
Load diff
29
Sketch3D/public/StrokesWidget.h
Normal file
29
Sketch3D/public/StrokesWidget.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
#include "gui/Widgets.h"
|
||||||
|
|
||||||
|
#include "nodes.h"
|
||||||
|
#include "StrokesObject.h"
|
||||||
|
|
||||||
|
namespace nd {
|
||||||
|
struct StrokesWidget : Widget {
|
||||||
|
static obj::ObjectType TypeData;
|
||||||
|
static struct Vtable : Widget::Vtable {} vtable;
|
||||||
|
|
||||||
|
strokes::Renderer mRenderer;
|
||||||
|
nd::GUIdrawer::ImageHandle mImage;
|
||||||
|
nd::GUIdrawer* mDrawer = NULL;
|
||||||
|
|
||||||
|
static void constructor(StrokesWidget* self);
|
||||||
|
static void destructor(StrokesWidget* self);
|
||||||
|
static void copy(Widget* self, const Widget* in);
|
||||||
|
static void procInputs(StrokesWidget* self, nd::GUIInputs* inputs);
|
||||||
|
static void presentOutput(StrokesWidget* self, nd::GUIdrawer* drawer);
|
||||||
|
|
||||||
|
strokes::Project* getTargetProject();
|
||||||
|
|
||||||
|
static void OpsInit();
|
||||||
|
static void OpsUnInit();
|
||||||
|
|
||||||
|
static void addShortcuts(nd::NodesCore* core);
|
||||||
|
};
|
||||||
|
};
|
||||||
153
Sketch3D/public/strokes.h
Normal file
153
Sketch3D/public/strokes.h
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
|
||||||
|
#include "Buffer.hpp"
|
||||||
|
#include "List.hpp"
|
||||||
|
#include "Map.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Color.hpp"
|
||||||
|
#include "Strings.hpp"
|
||||||
|
|
||||||
|
namespace strokes {
|
||||||
|
|
||||||
|
class Renderer;
|
||||||
|
class Project;
|
||||||
|
|
||||||
|
class Stroke {
|
||||||
|
|
||||||
|
friend Renderer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Point {
|
||||||
|
tp::vec3f pos;
|
||||||
|
tp::halnf thickness = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
tp::Array<Point> mPoints;
|
||||||
|
tp::rgba mCol;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GLHandles {
|
||||||
|
GLuint VertexArrayID = 0;
|
||||||
|
GLuint vertexbuffer = 0;
|
||||||
|
GLuint vbo_len = 0;
|
||||||
|
GLHandles();
|
||||||
|
void sendDataToGPU(tp::Array<Point>* mPoints);
|
||||||
|
~GLHandles();
|
||||||
|
} mGl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Stroke();
|
||||||
|
|
||||||
|
void denoisePos(tp::halni passes);
|
||||||
|
void denoiseThickness(tp::halni passes);
|
||||||
|
void compress(tp::halnf factor);
|
||||||
|
void subdiv(tp::halnf precition, tp::Camera* cam, tp::halni passes = 1);
|
||||||
|
|
||||||
|
void updateGpuBuffers();
|
||||||
|
|
||||||
|
tp::Array<Point>& buff();
|
||||||
|
|
||||||
|
tp::halni saveSize();
|
||||||
|
void save(tp::File& file);
|
||||||
|
void load(tp::File& file);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Brush {
|
||||||
|
public:
|
||||||
|
tp::string mType = "equal";
|
||||||
|
Brush() {}
|
||||||
|
virtual void sample(Project* proj, tp::vec2f crs, tp::halnf pressure) {}
|
||||||
|
virtual void draw(Renderer* render, tp::Camera* camera) {}
|
||||||
|
virtual ~Brush() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PencilBrush : public Brush {
|
||||||
|
|
||||||
|
tp::halnf mPrecision = 0.001f;
|
||||||
|
|
||||||
|
tp::halni mDenoisePassesPos = 1;
|
||||||
|
tp::halni mDenoisePassesThick = 3;
|
||||||
|
tp::halnf mCompressionFactor = 0.0001f;
|
||||||
|
tp::halni mSubdivPasses = 3;
|
||||||
|
bool mEnableCompression = true;
|
||||||
|
|
||||||
|
tp::halni mMaxPoints = 100;
|
||||||
|
|
||||||
|
Stroke* mStroke = NULL;
|
||||||
|
Stroke mShowStroke;
|
||||||
|
|
||||||
|
void unsureReady(Stroke* stroke, tp::Camera* cam, bool debug = false);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
tp::rgba mCol = tp::rgba(1.0f);
|
||||||
|
tp::halnf mSize = 0.01f;
|
||||||
|
|
||||||
|
PencilBrush();
|
||||||
|
virtual void sample(Project* proj, tp::vec2f crs, tp::halnf pressure) override;
|
||||||
|
virtual void draw(Renderer* render, tp::Camera* camera) override;
|
||||||
|
virtual ~PencilBrush();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EraserBrush : public Brush {
|
||||||
|
EraserBrush() { mType = "eraser"; }
|
||||||
|
virtual void sample(Project* proj, tp::vec2f crs, tp::halnf pressure) override {}
|
||||||
|
virtual void draw(Renderer* render, tp::Camera* camera) override {}
|
||||||
|
virtual ~EraserBrush() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Project {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Layer {
|
||||||
|
tp::string name = "new layer";
|
||||||
|
tp::List<Stroke*> strokes;
|
||||||
|
bool enabled = true;
|
||||||
|
~Layer();
|
||||||
|
};
|
||||||
|
|
||||||
|
tp::Array<Layer*> mLayers;
|
||||||
|
tp::halni mActiveLayer = -1;
|
||||||
|
|
||||||
|
tp::Camera mCamera;
|
||||||
|
tp::rgba mBackgroundColor = { 0.22f, 0.22f, 0.25f, 1.f };
|
||||||
|
|
||||||
|
tp::HashMap<Brush*, tp::string> mBrushes;
|
||||||
|
tp::string mActiveBrush;
|
||||||
|
|
||||||
|
Project();
|
||||||
|
~Project();
|
||||||
|
|
||||||
|
tp::alni saveSize();
|
||||||
|
void save(tp::File& file);
|
||||||
|
void load(tp::File& file);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
tp::ogl::fbuffer mBufferDowncast;
|
||||||
|
tp::ogl::fbuffer mBuffer;
|
||||||
|
tp::ogl::shader mShader;
|
||||||
|
|
||||||
|
// shader uniforms
|
||||||
|
GLuint mMatrixUniform = 0;
|
||||||
|
GLuint mColorUniform = 0;
|
||||||
|
GLuint mRatioUniform = 0;
|
||||||
|
GLuint mTargetUniform = 0;
|
||||||
|
GLuint mBGColUniform = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer(tp::vec2f size);
|
||||||
|
|
||||||
|
void renderBegin();
|
||||||
|
void setViewport(tp::rectf viewport);
|
||||||
|
void drawStroke(Stroke* str, tp::Camera* camera);
|
||||||
|
void renderEnd();
|
||||||
|
|
||||||
|
void setClearCol(tp::rgba col);
|
||||||
|
tp::uhalni getTextudeId();
|
||||||
|
tp::ogl::fbuffer* getBuff();
|
||||||
|
|
||||||
|
~Renderer();
|
||||||
|
};
|
||||||
|
};
|
||||||
178
Sketch3D/public/strokes_versions.h
Normal file
178
Sketch3D/public/strokes_versions.h
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "strokes.h"
|
||||||
|
|
||||||
|
struct ProjectInfo {
|
||||||
|
char name[10] = {0};
|
||||||
|
char version[10] = {0};
|
||||||
|
|
||||||
|
void init(tp::alni v) {
|
||||||
|
tp::string version_s(v);
|
||||||
|
tp::memcp(name, "strokes", tp::slen("strokes"));
|
||||||
|
tp::memcp(version, version_s.cstr(), version_s.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace StrokesVersion0 {
|
||||||
|
|
||||||
|
tp::alni save_size(strokes_project* proj) {
|
||||||
|
tp::alni out = 0;
|
||||||
|
out += sizeof(tp::Camera);
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
for (auto layer : proj->layers) {
|
||||||
|
for (auto stiter : layer->strokes) {
|
||||||
|
stroke* str = &stiter.data();
|
||||||
|
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
out += str->points.length() * sizeof(stroke_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(tp::File& file, strokes_project* proj) {
|
||||||
|
|
||||||
|
file.write<tp::Camera>(&proj->cam);
|
||||||
|
file.write<tp::rgba>(&proj->canvas_color);
|
||||||
|
|
||||||
|
drawlayer base_layer;
|
||||||
|
proj->append_layers(&base_layer);
|
||||||
|
|
||||||
|
tp::alni len = base_layer.strokes.length();
|
||||||
|
file.write<tp::alni>(&len);
|
||||||
|
for (auto stiter : base_layer.strokes) {
|
||||||
|
stroke* str = &stiter.data();
|
||||||
|
|
||||||
|
tp::alni length = str->points.length();
|
||||||
|
file.write<tp::alni>(&length);
|
||||||
|
file.write<tp::rgba>(&str->mesh.color);
|
||||||
|
|
||||||
|
for (auto piter : str->points) {
|
||||||
|
file.write<stroke_point>(&piter.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(tp::File& file, strokes_project* proj) {
|
||||||
|
file.read<tp::Camera>(&proj->cam);
|
||||||
|
|
||||||
|
file.read<tp::rgba>(&proj->canvas_color);
|
||||||
|
|
||||||
|
drawlayer* base = proj->get_base_layer();
|
||||||
|
|
||||||
|
tp::alni len;
|
||||||
|
file.read<tp::alni>(&len);
|
||||||
|
|
||||||
|
for (tp::alni str_idx = 0; str_idx < len; str_idx++) {
|
||||||
|
stroke str = stroke();
|
||||||
|
|
||||||
|
tp::alni p_len; file.read<tp::alni>(&p_len);
|
||||||
|
tp::rgba color; file.read<tp::rgba>(&color);
|
||||||
|
|
||||||
|
str.points.reserve(p_len);
|
||||||
|
for (auto piter : str.points) {
|
||||||
|
file.read<stroke_point>(&piter.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
str.mesh.color = color;
|
||||||
|
base->add_stroke(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace StrokesVersion1 {
|
||||||
|
|
||||||
|
tp::alni save_size(strokes_project* proj) {
|
||||||
|
tp::alni out = 0;
|
||||||
|
out += sizeof(tp::Camera);
|
||||||
|
out += sizeof(tp::halnf);
|
||||||
|
out += sizeof(tp::halnf);
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
for (auto layer : proj->layers) {
|
||||||
|
out += layer->name.save_size();
|
||||||
|
out += sizeof(bool);
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
for (auto stiter : layer->strokes) {
|
||||||
|
stroke* str = &stiter.data();
|
||||||
|
out += sizeof(tp::rgba);
|
||||||
|
out += sizeof(tp::alni);
|
||||||
|
out += str->points.length() * sizeof(stroke_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(tp::File& file, strokes_project* proj) {
|
||||||
|
|
||||||
|
file.write<tp::Camera>(&proj->cam);
|
||||||
|
file.write<tp::halnf>(&proj->sampler.eraser_size);
|
||||||
|
file.write<tp::halnf>(&proj->sampler.screen_thikness);
|
||||||
|
file.write<tp::rgba>(&proj->canvas_color);
|
||||||
|
|
||||||
|
tp::alni lay_len = proj->layers.length();
|
||||||
|
file.write<tp::alni>(&lay_len);
|
||||||
|
for (auto layer : proj->layers) {
|
||||||
|
layer->name.save(&file);
|
||||||
|
|
||||||
|
file.write<bool>(&layer->hiden);
|
||||||
|
|
||||||
|
tp::alni len = layer->strokes.length();
|
||||||
|
file.write<tp::alni>(&len);
|
||||||
|
for (auto stiter : layer->strokes) {
|
||||||
|
stroke* str = &stiter.data();
|
||||||
|
|
||||||
|
file.write<tp::rgba>(&str->mesh.color);
|
||||||
|
|
||||||
|
tp::alni length = str->points.length();
|
||||||
|
file.write<tp::alni>(&length);
|
||||||
|
for (auto piter : str->points) {
|
||||||
|
file.write<stroke_point>(&piter.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(tp::File& file, strokes_project* proj) {
|
||||||
|
file.read<tp::Camera>(&proj->cam);
|
||||||
|
|
||||||
|
file.read<tp::halnf>(&proj->sampler.eraser_size);
|
||||||
|
file.read<tp::halnf>(&proj->sampler.screen_thikness);
|
||||||
|
file.read<tp::rgba>(&proj->canvas_color);
|
||||||
|
|
||||||
|
tp::alni layers_len;
|
||||||
|
file.read<tp::alni>(&layers_len);
|
||||||
|
for (tp::alni str_idx = 0; str_idx < layers_len; str_idx++) {
|
||||||
|
|
||||||
|
tp::string key; key.load(&file);
|
||||||
|
drawlayer* layer = new drawlayer();
|
||||||
|
layer->name = key;
|
||||||
|
proj->layers.pushBack(layer);
|
||||||
|
|
||||||
|
file.read<bool>(&layer->hiden);
|
||||||
|
|
||||||
|
tp::alni len;
|
||||||
|
file.read<tp::alni>(&len);
|
||||||
|
|
||||||
|
for (tp::alni str_idx = 0; str_idx < len; str_idx++) {
|
||||||
|
stroke str = stroke();
|
||||||
|
|
||||||
|
tp::rgba color; file.read<tp::rgba>(&color);
|
||||||
|
|
||||||
|
tp::alni p_len; file.read<tp::alni>(&p_len);
|
||||||
|
str.points.reserve(p_len);
|
||||||
|
for (auto piter : str.points) {
|
||||||
|
file.read<stroke_point>(&piter.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
str.mesh.color = color;
|
||||||
|
layer->add_stroke(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
BIN
Sketch3D/rsc/Font.ttf
Normal file
BIN
Sketch3D/rsc/Font.ttf
Normal file
Binary file not shown.
15
Sketch3D/rsc/shaders/Texture.frag
Normal file
15
Sketch3D/rsc/shaders/Texture.frag
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 UV;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
uniform sampler2D renderedTexture;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
vec4 texColor = texture(renderedTexture, UV);
|
||||||
|
if(texColor.a < 0.2)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
color = texColor;
|
||||||
|
}
|
||||||
12
Sketch3D/rsc/shaders/Texture.vert
Normal file
12
Sketch3D/rsc/shaders/Texture.vert
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
// Input vertex data, different for all executions of this shader.
|
||||||
|
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||||
|
|
||||||
|
// Output data ; will be interpolated for each fragment.
|
||||||
|
out vec2 UV;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
gl_Position = vec4(vertexPosition_modelspace.x, vertexPosition_modelspace.y, -0.99, 1);
|
||||||
|
UV = (vertexPosition_modelspace.xy + vec2(1, 1)) / 2.0;
|
||||||
|
}
|
||||||
28
Sketch3D/rsc/shaders/stroke.frag
Normal file
28
Sketch3D/rsc/shaders/stroke.frag
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform vec4 Color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 color;
|
||||||
|
|
||||||
|
uniform float Target;
|
||||||
|
uniform vec4 BGCol;
|
||||||
|
|
||||||
|
#define A 0.1f
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float x = gl_FragCoord.z;
|
||||||
|
|
||||||
|
float val = A;
|
||||||
|
|
||||||
|
float f = Target * 2;
|
||||||
|
|
||||||
|
if (x < f) {
|
||||||
|
val = ((A - 1) / f) * x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = clamp(val, A, 1);
|
||||||
|
|
||||||
|
|
||||||
|
color = vec4((Color.xyz - BGCol.xyz) * val + BGCol.xyz, 1.f);
|
||||||
|
}
|
||||||
170
Sketch3D/rsc/shaders/stroke.geom
Normal file
170
Sketch3D/rsc/shaders/stroke.geom
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
#define PI 3.14
|
||||||
|
|
||||||
|
layout (lines) in;
|
||||||
|
layout (triangle_strip, max_vertices = 16) out;
|
||||||
|
|
||||||
|
in float thickness[];
|
||||||
|
|
||||||
|
uniform float Ratio;
|
||||||
|
|
||||||
|
void main2() {
|
||||||
|
|
||||||
|
float r = sqrt(Ratio);
|
||||||
|
vec4 l1 = gl_in[0].gl_Position;
|
||||||
|
vec4 l2 = gl_in[1].gl_Position;
|
||||||
|
|
||||||
|
vec2 l1p = l1.xy / l1.w;
|
||||||
|
vec2 l2p = l2.xy / l2.w;
|
||||||
|
|
||||||
|
float t1 = thickness[0] / l1.w;
|
||||||
|
float t2 = thickness[1] / l2.w;
|
||||||
|
|
||||||
|
vec2 l = normalize(vec2(l2p.x / r, l2p.y * r) - vec2(l1p.x / r, l1p.y * r));
|
||||||
|
vec2 tmp = vec2(-l.y, l.x);
|
||||||
|
|
||||||
|
vec2 tmp_poj = vec2(tmp.x * r, tmp.y / r);
|
||||||
|
vec2 l_poj = vec2(l.x * r, l.y / r);
|
||||||
|
|
||||||
|
vec2 p1 = l1p.xy + (tmp_poj * t1);
|
||||||
|
vec2 p2 = l1p.xy - (tmp_poj * t1);
|
||||||
|
vec2 p3 = l2p.xy + (tmp_poj * t2);
|
||||||
|
vec2 p4 = l2p.xy - (tmp_poj * t2);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x, p1.y , l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x, p2.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = vec4(p3.x, p3.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x, p4.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
|
||||||
|
p1 = l1p.xy + (tmp_poj * t1);
|
||||||
|
p2 = l1p.xy - (tmp_poj * t1);
|
||||||
|
p3 = l1p.xy + (tmp_poj * t1) - (l_poj * t1) / 2.f;
|
||||||
|
p4 = l1p.xy - (tmp_poj * t1) - (l_poj * t1) / 2.f;
|
||||||
|
vec2 p5 = l1p.xy + (tmp_poj * t1) / 2.f - (l_poj * t1);
|
||||||
|
vec2 p6 = l1p.xy - (tmp_poj * t1) / 2.f - (l_poj * t1);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x, p1.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x, p2.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p3.x, p3.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x, p4.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p5.x, p5.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p6.x, p6.y, l1.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
p1 = l2p.xy + (tmp_poj * t2);
|
||||||
|
p2 = l2p.xy - (tmp_poj * t2);
|
||||||
|
p3 = l2p.xy + (tmp_poj * t2) + (l_poj * t2) / 2.f;
|
||||||
|
p4 = l2p.xy - (tmp_poj * t2) + (l_poj * t2) / 2.f;
|
||||||
|
p5 = l2p.xy + (tmp_poj * t2) / 2.f + (l_poj * t2);
|
||||||
|
p6 = l2p.xy - (tmp_poj * t2) / 2.f + (l_poj * t2);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x, p1.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x, p2.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p3.x, p3.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x, p4.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p5.x, p5.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p6.x, p6.y, l2.z, 1);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
float r = sqrt(Ratio);
|
||||||
|
vec4 l1 = gl_in[0].gl_Position;
|
||||||
|
vec4 l2 = gl_in[1].gl_Position;
|
||||||
|
|
||||||
|
vec2 l1p = l1.xy / l1.w;
|
||||||
|
vec2 l2p = l2.xy / l2.w;
|
||||||
|
|
||||||
|
float t1 = thickness[0] / l1.w;
|
||||||
|
float t2 = thickness[1] / l2.w;
|
||||||
|
|
||||||
|
vec2 l = normalize(vec2(l2p.x / r, l2p.y * r) - vec2(l1p.x / r, l1p.y * r));
|
||||||
|
vec2 tmp = vec2(-l.y, l.x);
|
||||||
|
|
||||||
|
vec2 tmp_poj = vec2(tmp.x * r, tmp.y / r);
|
||||||
|
vec2 l_poj = vec2(l.x * r, l.y / r);
|
||||||
|
|
||||||
|
vec2 p1 = l1p.xy + (tmp_poj * t1);
|
||||||
|
vec2 p2 = l1p.xy - (tmp_poj * t1);
|
||||||
|
vec2 p3 = l2p.xy + (tmp_poj * t2);
|
||||||
|
vec2 p4 = l2p.xy - (tmp_poj * t2);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x * l1.w, p1.y * l1.w, l1.z, l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x * l1.w, p2.y * l1.w, l1.z, l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = vec4(p3.x * l2.w, p3.y * l2.w, l2.z, l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x * l2.w, p4.y * l2.w, l2.z, l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
|
||||||
|
p1 = l1p.xy + (tmp_poj * t1);
|
||||||
|
p2 = l1p.xy - (tmp_poj * t1);
|
||||||
|
p3 = l1p.xy + (tmp_poj * t1) - (l_poj * t1) / 2.f;
|
||||||
|
p4 = l1p.xy - (tmp_poj * t1) - (l_poj * t1) / 2.f;
|
||||||
|
vec2 p5 = l1p.xy + (tmp_poj * t1) / 2.f - (l_poj * t1);
|
||||||
|
vec2 p6 = l1p.xy - (tmp_poj * t1) / 2.f - (l_poj * t1);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x * l1.w, p1.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x * l1.w, p2.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p3.x * l1.w, p3.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x * l1.w, p4.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p5.x * l1.w, p5.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p6.x * l1.w, p6.y * l1.w, l1.z, 1 * l1.w);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
p1 = l2p.xy + (tmp_poj * t2);
|
||||||
|
p2 = l2p.xy - (tmp_poj * t2);
|
||||||
|
p3 = l2p.xy + (tmp_poj * t2) + (l_poj * t2) / 2.f;
|
||||||
|
p4 = l2p.xy - (tmp_poj * t2) + (l_poj * t2) / 2.f;
|
||||||
|
p5 = l2p.xy + (tmp_poj * t2) / 2.f + (l_poj * t2);
|
||||||
|
p6 = l2p.xy - (tmp_poj * t2) / 2.f + (l_poj * t2);
|
||||||
|
|
||||||
|
gl_Position = vec4(p1.x * l2.w, p1.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p2.x * l2.w, p2.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p3.x * l2.w, p3.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p4.x * l2.w, p4.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p5.x * l2.w, p5.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(p6.x * l2.w, p6.y * l2.w, l2.z, 1 * l2.w);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
16
Sketch3D/rsc/shaders/stroke.vert
Normal file
16
Sketch3D/rsc/shaders/stroke.vert
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 point;
|
||||||
|
|
||||||
|
out float thickness;
|
||||||
|
|
||||||
|
uniform mat4 MVP;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
thickness = point.w;
|
||||||
|
// gl_Position = MVP * vec4(point.xyz, 1);
|
||||||
|
|
||||||
|
vec4 vec = MVP * vec4(point.xyz, 1);
|
||||||
|
vec.z *= vec.w;
|
||||||
|
gl_Position = vec;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue