diff --git a/Graphics/private/bindings/Canvas.cpp b/Graphics/private/bindings/Canvas.cpp index 15e0117..946407d 100644 --- a/Graphics/private/bindings/Canvas.cpp +++ b/Graphics/private/bindings/Canvas.cpp @@ -112,6 +112,145 @@ void Graphics::Canvas::text( popClamp(); } +void Graphics::Canvas::drawColorwheel(const RectF& rec, const RGB& col) { + + float const x = rec.x; + float const y = rec.y; + float const w = rec.z; + float const h = rec.w; + + HSV hsv = tp::HSV(col); + float const hue = hsv.h / (NVG_PI * 2); + + int i; + float r0, r1, ax, ay, bx, by, cx, cy, aeps, r; + NVGpaint paint; + + nvgSave(mContext->vg); + + cx = x + w * 0.5f; + cy = y + h * 0.5f; + r1 = (w < h ? w : h) * 0.5f - 5.0f; + r0 = r1 - 13.0f; + aeps = 0.5f / r1; // half a pixel arc length in radians (2pi cancels out). + + for (i = 0; i < 6; i++) { + float a0 = (float) i / 6.0f * NVG_PI * 2.0f - aeps; + float a1 = (float) (i + 1.0f) / 6.0f * NVG_PI * 2.0f + aeps; + nvgBeginPath(mContext->vg); + nvgArc(mContext->vg, cx, cy, r0, a0, a1, NVG_CW); + nvgArc(mContext->vg, cx, cy, r1, a1, a0, NVG_CCW); + nvgClosePath(mContext->vg); + ax = cx + cosf(a0) * (r0 + r1) * 0.5f; + ay = cy + sinf(a0) * (r0 + r1) * 0.5f; + bx = cx + cosf(a1) * (r0 + r1) * 0.5f; + by = cy + sinf(a1) * (r0 + r1) * 0.5f; + paint = nvgLinearGradient( + mContext->vg, + ax, + ay, + bx, + by, + nvgHSLA(a0 / (NVG_PI * 2), 1.0f, 0.55f, 255), + nvgHSLA(a1 / (NVG_PI * 2), 1.0f, 0.55f, 255) + ); + nvgFillPaint(mContext->vg, paint); + nvgFill(mContext->vg); + } + + nvgBeginPath(mContext->vg); + nvgCircle(mContext->vg, cx, cy, r0 - 0.5f); + nvgCircle(mContext->vg, cx, cy, r1 + 0.5f); + nvgStrokeColor(mContext->vg, nvgRGBA(0, 0, 0, 64)); + nvgStrokeWidth(mContext->vg, 1.0f); + nvgStroke(mContext->vg); + + // Selector + nvgSave(mContext->vg); + nvgTranslate(mContext->vg, cx, cy); + nvgRotate(mContext->vg, hue * NVG_PI * 2); + + // Marker on + nvgStrokeWidth(mContext->vg, 2.0f); + nvgBeginPath(mContext->vg); + nvgRect(mContext->vg, r0 - 1, -3, r1 - r0 + 2, 6); + nvgStrokeColor(mContext->vg, nvgRGBA(255, 255, 255, 192)); + nvgStroke(mContext->vg); + + paint = nvgBoxGradient(mContext->vg, r0 - 3, -5, r1 - r0 + 6, 10, 2, 4, nvgRGBA(0, 0, 0, 128), nvgRGBA(0, 0, 0, 0)); + nvgBeginPath(mContext->vg); + nvgRect(mContext->vg, r0 - 2 - 10, -4 - 10, r1 - r0 + 4 + 20, 8 + 20); + nvgRect(mContext->vg, r0 - 2, -4, r1 - r0 + 4, 8); + nvgPathWinding(mContext->vg, NVG_HOLE); + nvgFillPaint(mContext->vg, paint); + nvgFill(mContext->vg); + + // Center triangle + r = r0 - 6; + ax = cosf(120.0f / 180.0f * NVG_PI) * r; + ay = sinf(120.0f / 180.0f * NVG_PI) * r; + bx = cosf(-120.0f / 180.0f * NVG_PI) * r; + by = sinf(-120.0f / 180.0f * NVG_PI) * r; + nvgBeginPath(mContext->vg); + nvgMoveTo(mContext->vg, r, 0); + nvgLineTo(mContext->vg, ax, ay); + nvgLineTo(mContext->vg, bx, by); + nvgClosePath(mContext->vg); + paint = nvgLinearGradient(mContext->vg, r, 0, ax, ay, nvgHSLA(hue, 1.0f, 0.5f, 255), nvgRGBA(255, 255, 255, 255)); + nvgFillPaint(mContext->vg, paint); + nvgFill(mContext->vg); + paint = nvgLinearGradient(mContext->vg, (r + ax) * 0.5f, (0 + ay) * 0.5f, bx, by, nvgRGBA(0, 0, 0, 0), nvgRGBA(0, 0, 0, 255)); + nvgFillPaint(mContext->vg, paint); + nvgFill(mContext->vg); + nvgStrokeColor(mContext->vg, nvgRGBA(0, 0, 0, 64)); + nvgStroke(mContext->vg); + + // Select circle on triangle + float yt = hsv.v * hsv.s; + float xt = hsv.v - 0.5 * yt; + ay = sinf(120.0f / 180.0f * NVG_PI) * r * (-1.0f + xt * 2.0f); + ax = cosf(120.0f / 180.0f * NVG_PI) * r * (1.0f - yt * 3.f); + nvgStrokeWidth(mContext->vg, 2.0f); + nvgBeginPath(mContext->vg); + nvgCircle(mContext->vg, ax, ay, 5); + nvgStrokeColor(mContext->vg, nvgRGBA(255, 255, 255, 192)); + nvgStroke(mContext->vg); + + paint = nvgRadialGradient(mContext->vg, ax, ay, 7, 9, nvgRGBA(0, 0, 0, 64), nvgRGBA(0, 0, 0, 0)); + nvgBeginPath(mContext->vg); + nvgRect(mContext->vg, ax - 20, ay - 20, 40, 40); + nvgCircle(mContext->vg, ax, ay, 7); + nvgPathWinding(mContext->vg, NVG_HOLE); + nvgFillPaint(mContext->vg, paint); + nvgFill(mContext->vg); + + nvgRestore(mContext->vg); + + nvgRestore(mContext->vg); +} + +void Graphics::Canvas::drawImage(const RectF& rec, ImageHandle* image, halnf angle, halnf alpha, halnf rounding) { + auto imgPaint = nvgImagePattern(mContext->vg, rec.x, rec.y, rec.z, rec.w, angle, image->id, alpha); + nvgBeginPath(mContext->vg); + nvgRoundedRect(mContext->vg, rec.x, rec.y, rec.z, rec.w, rounding); + nvgFillPaint(mContext->vg, imgPaint); + nvgFill(mContext->vg); +} + + Graphics::Canvas::ImageHandle Graphics::Canvas::createImageFromTextId(ualni id, Vec2F size) { +#ifdef ENV_OS_ANDROID + return { (ualni) nvglCreateImageFromHandleGLES3(mContext->vg, id, size.x, size.y, 0) }; +#else + return { (ualni) nvglCreateImageFromHandleGL3(mContext->vg, id, size.x, size.y, 0) }; +#endif +} + +void Graphics::Canvas::deleteImageHandle(ImageHandle image) { + if (image.id) { + nvgDeleteImage(mContext->vg, image.id); + } +} + void Graphics::Canvas::proc() { nvgBeginFrame(mContext->vg, mWidth, mHeight, 1.0); } void Graphics::Canvas::draw() { nvgEndFrame(mContext->vg); } diff --git a/Graphics/public/Graphics.hpp b/Graphics/public/Graphics.hpp index 4885b90..c9e0bc0 100644 --- a/Graphics/public/Graphics.hpp +++ b/Graphics/public/Graphics.hpp @@ -81,12 +81,21 @@ namespace tp { RB = 0x0202, }; + struct ImageHandle { + ualni id = 0; + }; + RectF getAvaliableArea(); void pushClamp(const RectF& rec); void popClamp(); void rect(const RectF& rec, const RGBA& col, halnf round = 0); void text(const char*, const RectF&, halnf size, Align, halnf marging, const RGBA&); + void drawColorwheel(const RectF& rec, const RGB& col); + + ImageHandle createImageFromTextId(ualni id, Vec2F size); + void deleteImageHandle(ImageHandle image); + void drawImage(const RectF& rec, ImageHandle* image, halnf angle, halnf alpha, halnf rounding); // TODO : API private: diff --git a/Sketch3D/applications/Entry.cpp b/Sketch3D/applications/Entry.cpp index c97cb96..849fbd0 100644 --- a/Sketch3D/applications/Entry.cpp +++ b/Sketch3D/applications/Entry.cpp @@ -3,29 +3,31 @@ #include "Graphics.hpp" #include "Window.hpp" -#include "Widgets.hpp" +#include "Sketch3DWidget.hpp" void runApp() { tp::GlobalGUIConfig config; tp::gGlobalGUIConfig = &config; - tp::LabelWidget gui; - auto window = tp::Window::createWindow(800, 600, "Window 1"); - if (window) { - while (!window->shouldClose()) { - window->processEvents(); + { + tp::Sketch3DWidget gui(window->getCanvas(), {800, 1000}); - auto area = window->getCanvas().getAvaliableArea(); + if (window) { + while (!window->shouldClose()) { + window->processEvents(); - gui.proc(window->getEvents(), { area.x, area.y, area.z, area.w }, { area.x, area.y, area.z, area.w }); - gui.draw(window->getCanvas()); + auto area = window->getCanvas().getAvaliableArea(); - tp::sleep(100); + gui.proc(window->getEvents(), { area.x, area.y, area.z, area.w }, { area.x, area.y, area.z, area.w }); + gui.draw(window->getCanvas()); - window->draw(); + tp::sleep(100); + + window->draw(); + } } } diff --git a/Sketch3D/private/FrameBuffer.cpp b/Sketch3D/private/FrameBuffer.cpp index 355ff87..1f3a23b 100644 --- a/Sketch3D/private/FrameBuffer.cpp +++ b/Sketch3D/private/FrameBuffer.cpp @@ -2,7 +2,7 @@ #include "FrameBuffer.hpp" #include "GraphicsApi.hpp" -void glerr(GLenum type); +void glerr(GLenum type) { printf("GL ERROR\n"); } #define AssertGL(x) { x; GLenum __gle = glGetError(); if (__gle != GL_NO_ERROR) glerr(__gle); } using namespace tp; diff --git a/Sketch3D/private/Sketch3D.cpp b/Sketch3D/private/Sketch3D.cpp index 309867f..79de83f 100644 --- a/Sketch3D/private/Sketch3D.cpp +++ b/Sketch3D/private/Sketch3D.cpp @@ -1,7 +1,7 @@ #include "Sketch3D.hpp" -using namespace strokes; +using namespace tp; Stroke::GLHandles::GLHandles() { glGenVertexArrays(1, &VertexArrayID); @@ -300,6 +300,11 @@ Project::Project() { mActiveBrush = "pencil"; + Stroke* debug = new Stroke(); + debug->mPoints = { { { 0, 0, 0 }, 1 }, { { 1, 1, 1 } , 1} }; + debug->updateGpuBuffers(); + + mLayers.last()->strokes.pushBack(debug); auto vec = mCamera.project({0, 0, 0}); vec = mCamera.project({-1, 0, 0}); diff --git a/Sketch3D/public/Sketch3D.hpp b/Sketch3D/public/Sketch3D.hpp index 4695c7d..2cb4f9c 100644 --- a/Sketch3D/public/Sketch3D.hpp +++ b/Sketch3D/public/Sketch3D.hpp @@ -1,3 +1,4 @@ +#pragma once #include "Buffer.hpp" #include "List.hpp" @@ -10,7 +11,7 @@ #include "FrameBuffer.hpp" #include "Shader.hpp" -namespace strokes { +namespace tp { class Renderer; class Project; @@ -21,12 +22,12 @@ namespace strokes { public: struct Point { - tp::Vec3F pos; - tp::halnf thickness = NULL; + Vec3F pos = { 0, 0, 0 }; + halnf thickness = NULL; }; - tp::Buffer mPoints; - tp::RGBA mCol; + Buffer mPoints; + RGBA mCol; private: struct GLHandles { @@ -34,7 +35,7 @@ namespace strokes { GLuint vertexbuffer = 0; GLuint vbo_len = 0; GLHandles(); - void sendDataToGPU(tp::Buffer* mPoints); + void sendDataToGPU(Buffer* mPoints); ~GLHandles(); } mGl; @@ -42,57 +43,57 @@ namespace strokes { 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 denoisePos(halni passes); + void denoiseThickness(halni passes); + void compress(halnf factor); + void subdiv(halnf precition, Camera* cam, halni passes = 1); void updateGpuBuffers(); - tp::Buffer& buff(); + Buffer& buff(); }; class Brush { public: - tp::String mType = "equal"; + String mType = "equal"; Brush() {} - virtual void sample(Project* proj, tp::Vec2F crs, tp::halnf pressure) {} - virtual void draw(Renderer* render, tp::Camera* camera) {} + virtual void sample(Project* proj, Vec2F crs, halnf pressure) {} + virtual void draw(Renderer* render, Camera* camera) {} virtual ~Brush() {} }; class PencilBrush : public Brush { - tp::halnf mPrecision = 0.001f; + halnf mPrecision = 0.001f; - tp::halni mDenoisePassesPos = 1; - tp::halni mDenoisePassesThick = 3; - tp::halnf mCompressionFactor = 0.0001f; - tp::halni mSubdivPasses = 3; + halni mDenoisePassesPos = 1; + halni mDenoisePassesThick = 3; + halnf mCompressionFactor = 0.0001f; + halni mSubdivPasses = 3; bool mEnableCompression = true; - tp::halni mMaxPoints = 100; + halni mMaxPoints = 100; Stroke* mStroke = NULL; Stroke mShowStroke; - void unsureReady(Stroke* stroke, tp::Camera* cam, bool debug = false); + void unsureReady(Stroke* stroke, Camera* cam, bool debug = false); public: - tp::RGBA mCol = tp::RGBA(1.0f); - tp::halnf mSize = 0.01f; + RGBA mCol = RGBA(1.0f); + 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 void sample(Project* proj, Vec2F crs, halnf pressure) override; + virtual void draw(Renderer* render, 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 void sample(Project* proj, Vec2F crs, halnf pressure) override {} + virtual void draw(Renderer* render, Camera* camera) override {} virtual ~EraserBrush() {} }; @@ -101,29 +102,29 @@ namespace strokes { public: struct Layer { - tp::String name = "new layer"; - tp::List strokes; + String name = "new layer"; + List strokes; bool enabled = true; ~Layer(); }; - tp::Buffer mLayers; - tp::halni mActiveLayer = -1; + Buffer mLayers; + halni mActiveLayer = -1; - tp::Camera mCamera; - tp::RGBA mBackgroundColor = { 0.22f, 0.22f, 0.25f, 1.f }; + Camera mCamera; + RGBA mBackgroundColor = { 0.22f, 0.22f, 0.25f, 1.f }; - tp::Map mBrushes; - tp::String mActiveBrush; + Map mBrushes; + String mActiveBrush; Project(); ~Project(); }; class Renderer { - tp::RenderBuffer mBufferDowncast; - tp::RenderBuffer mBuffer; - tp::RenderShader mShader; + RenderBuffer mBufferDowncast; + RenderBuffer mBuffer; + RenderShader mShader; // shader uniforms GLuint mMatrixUniform = 0; @@ -133,64 +134,64 @@ namespace strokes { GLuint mBGColUniform = 0; public: - Renderer(tp::Vec2F size); + Renderer(Vec2F size); void renderBegin(); - void setViewport(tp::RectF viewport); - void drawStroke(Stroke* str, tp::Camera* camera); + void setViewport(RectF viewport); + void drawStroke(Stroke* str, Camera* camera); void renderEnd(); - void setClearCol(tp::RGBA col); - tp::uhalni getTextudeId(); - tp::RenderBuffer* getBuff(); + void setClearCol(RGBA col); + uhalni getTextudeId(); + RenderBuffer* getBuff(); ~Renderer(); }; }; /* -void Project::save(tp::File& file) { - file.write(&mCamera); - file.write(&mBackgroundColor); - file.write(&mActiveLayer); +void Project::save(File& file) { + file.write(&mCamera); + file.write(&mBackgroundColor); + file.write(&mActiveLayer); - tp::alni lay_len = mLayers.length(); - file.write(&lay_len); + alni lay_len = mLayers.length(); + file.write(&lay_len); for (auto layer : mLayers) { layer.data()->name.save(&file); file.write(&layer.data()->enabled); - tp::alni len = layer.data()->strokes.length(); - file.write(&len); + alni len = layer.data()->strokes.length(); + file.write(&len); for (auto stiter : layer.data()->strokes) { stiter->save(file); } } } -void Project::load(tp::File& file) { - file.read(&mCamera); - file.read(&mBackgroundColor); - file.read(&mActiveLayer); +void Project::load(File& file) { + file.read(&mCamera); + file.read(&mBackgroundColor); + file.read(&mActiveLayer); - tp::alni layers_len; - file.read(&layers_len); + alni layers_len; + file.read(&layers_len); mLayers.reserve(layers_len); - for (tp::alni idx = 0; idx < layers_len; idx++) { + for (alni idx = 0; idx < layers_len; idx++) { - tp::string key; key.load(&file); + string key; key.load(&file); auto layer = new Layer(); layer->name = key; mLayers[idx] = layer; file.read(&layer->enabled); - tp::alni len; - file.read(&len); + alni len; + file.read(&len); - for (tp::alni str_idx = 0; str_idx < len; str_idx++) { + for (alni str_idx = 0; str_idx < len; str_idx++) { auto str = new Stroke(); layer->strokes.pushBack(str); layer->strokes.last()->data->load(file); @@ -200,22 +201,22 @@ void Project::load(tp::File& file) { */ /* - void Stroke::save(tp::File& file) { - file.write(&mCol); + void Stroke::save(File& file) { + file.write(&mCol); - tp::alni length = mPoints.length(); - file.write(&length); + alni length = mPoints.length(); + file.write(&length); for (auto piter : mPoints) { file.write(&piter.data()); } } - void Stroke::load(tp::File& file) { - tp::rgba color; - file.read(&color); + void Stroke::load(File& file) { + rgba color; + file.read(&color); - tp::alni p_len; - file.read(&p_len); + alni p_len; + file.read(&p_len); mPoints.reserve(p_len); for (auto piter : mPoints) { diff --git a/Sketch3D/public/Sketch3DWidget.hpp b/Sketch3D/public/Sketch3DWidget.hpp index 6a7d273..5dc9a49 100644 --- a/Sketch3D/public/Sketch3DWidget.hpp +++ b/Sketch3D/public/Sketch3DWidget.hpp @@ -1,87 +1,86 @@ +#pragma once -// #include "Fram.h" +#include "Sketch3D.hpp" +#include "Widgets.hpp" -class Widget { - void destructor(StrokesWidget* self) { - self->mRenderer.~Renderer(); - self->mImage.free(self->mDrawer); - self->mImage.~ImageHandle(); - } +namespace tp { - void procInputs(StrokesWidget* self, nd::GUIInputs* inputs) { - auto proj = self->getTargetProject(); - if (!proj) { - return; + template + class Sketch3DWidget : public Widget { + public: + + Canvas* mCanvas = nullptr; + + Sketch3DWidget(Canvas& canvas, Vec2F renderResolution) : + mRenderer(renderResolution) { + mImage = canvas.createImageFromTextId(mRenderer.getBuff()->texId(), mRenderer.getBuff()->getSize()); + mCanvas = &canvas; } - auto rec = self->getRect(); - proj->mCamera.set_ratio(rec.w / rec.z); - - auto col_obj_bg = self->getMember("bg_color"); - if (col_obj_bg) { - proj->mBackgroundColor = col_obj_bg->mCol; + ~Sketch3DWidget() { + mCanvas->deleteImageHandle(mImage); } - if (!rec.inside(inputs->mCrsPrev)) { - return; - } + void proc(const Events& events, const RectF& areaParent, const RectF& area) override { - auto pressure = inputs->mPressure; - if (!inputs->Anticipating()) { - pressure = 0.f; - } + this->mArea = area; + this->mVisible = area.isOverlap(areaParent); + if (!this->mVisible) return; - 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; + mProject.mCamera.setRatio(this->mArea.w / this->mArea.z); - if (brush->mType == "pencil") { - auto col_obj = self->getMember("draw_color"); - if (col_obj) { - ((strokes::PencilBrush*) brush)->mCol = col_obj->mCol; - } + // mProject.mBackgroundColor = col_obj_bg->mCol; + + if (!this->mArea.isInside(events.getPos())) { + return; } - brush->sample(proj, crs, pressure); - } - } + halnf pressure = 0.f; - void presentOutput(StrokesWidget* self, nd::GUIdrawer* drawer) { - auto proj = self->getTargetProject(); - if (!proj) { - return; - } + auto idx = mProject.mBrushes.presents(mProject.mActiveBrush); + if (idx) { + auto brush = mProject.mBrushes.getSlotVal(idx); + auto crs = (events.getPos() - this->mArea.pos); + crs.x /= this->mArea.z; + crs.y /= this->mArea.w; + crs = (crs - 0.5) * 2; - 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); + if (brush->mType == "pencil") { + ((tp::PencilBrush*) brush)->mCol = RGBA(1.f); } + + brush->sample(&mProject, crs, pressure); } } - auto idx = proj->mBrushes.presents(proj->mActiveBrush); - if (idx) { - auto brush = proj->mBrushes.getSlotVal(idx); - brush->draw(&self->mRenderer, &proj->mCamera); + void draw(Canvas& canvas) override { + + mRenderer.setViewport({ 0, 0, this->mArea.z, this->mArea.w }); + mRenderer.setClearCol(mProject.mBackgroundColor); + mRenderer.renderBegin(); + + for (auto lay : mProject.mLayers) { + if (lay.data()->enabled) { + for (auto str : lay.data()->strokes) { + mRenderer.drawStroke(str.data(), &mProject.mCamera); + } + } + } + + auto idx = mProject.mBrushes.presents(mProject.mActiveBrush); + if (idx) { + auto brush = mProject.mBrushes.getSlotVal(idx); + brush->draw(&mRenderer, &mProject.mCamera); + } + + mRenderer.renderEnd(); + + canvas.drawImage(this->mArea, &mImage, 0, 1, 12); } - self->mRenderer.renderEnd(); - - drawer->drawImage(rec, &self->mImage, 0, 1, 12); - } -}; \ No newline at end of file + private: + Project mProject; + Renderer mRenderer; + Canvas::ImageHandle mImage; + }; +} \ No newline at end of file diff --git a/Widgets/public/WidgetBase.hpp b/Widgets/public/WidgetBase.hpp index e47cb6e..3cb8433 100644 --- a/Widgets/public/WidgetBase.hpp +++ b/Widgets/public/WidgetBase.hpp @@ -49,7 +49,7 @@ namespace tp { this->mArea = aArea; } - virtual void draw(Canvas& canvas) const { + virtual void draw(Canvas& canvas) { if (!mVisible) { return; }