Initial Commit
This commit is contained in:
commit
6088fc29ed
978 changed files with 353647 additions and 0 deletions
399
src/DrawComponents/DrawBrushStroke.cpp
Normal file
399
src/DrawComponents/DrawBrushStroke.cpp
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
#include "DrawBrushStroke.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <limits>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <Eigen/Geometry>
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "../TimePoint.hpp"
|
||||
#include <include/core/SkVertices.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
#define AABB_PRECHECK_BVH_LEVELS 2
|
||||
|
||||
void DrawBrushStroke::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
void DrawBrushStroke::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
DrawComponentType DrawBrushStroke::get_type() const {
|
||||
return DRAWCOMPONENT_BRUSHSTROKE;
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawBrushStroke::copy() const {
|
||||
auto a = std::make_shared<DrawBrushStroke>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::update_from_delayed_ptr() {
|
||||
std::shared_ptr<DrawBrushStroke> newPtr = std::static_pointer_cast<DrawBrushStroke>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
Vector2f two_dim_vec_slerp(const Vector2f& v1, const Vector2f& v2) {
|
||||
Vector2f v1Norm = v1.normalized();
|
||||
Vector2f v2Norm = v2.normalized();
|
||||
Vector2f avg = (v1Norm + v2Norm) * 0.5f;
|
||||
if(std::fabs(avg.x()) < 0.00001f && std::fabs(avg.y()) < 0.00001f)
|
||||
return perpendicular_vec2(v1Norm).normalized();
|
||||
else
|
||||
return avg.normalized();
|
||||
}
|
||||
|
||||
void DrawBrushStroke::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
//bool deepPrecheck = false;
|
||||
//for(auto& aabb : precheckAABBLevels) {
|
||||
// if(SCollision::collide(drawData.cam.viewingAreaGenerousCollider, aabb)) {
|
||||
// deepPrecheck = true;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//if(!deepPrecheck && !precheckAABBLevels.empty())
|
||||
// return;
|
||||
|
||||
SkPaint paint;
|
||||
if(d.color.w() == 1.0) // Fully opaque, dont bother with blending
|
||||
canvas->save();
|
||||
else
|
||||
canvas->saveLayerAlphaf(nullptr, d.color.w());
|
||||
paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), 1.0f});
|
||||
canvas_do_calculated_transform(canvas);
|
||||
canvas->drawVertices(vertices, SkBlendMode::kClear, paint);
|
||||
//SkPaint p2(SkColor4f{1, 0, 0, 1});
|
||||
//p2.setStroke(true);
|
||||
//SkPaint p3(SkColor4f{0, 1, 0, 1});
|
||||
//SkPaint p4(SkColor4f{0, 0, 1, 1});
|
||||
//p3.setStroke(true);
|
||||
//p4.setStroke(true);
|
||||
//for(auto& precheckAABBLevel : precheckAABBLevels)
|
||||
// draw_collider(canvas, drawData, precheckAABBLevel, p4);
|
||||
//draw_collider(canvas, drawData, collisionTree, p3);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::initialize_draw_data(DrawingProgram& drawP) {
|
||||
std::vector<SkPoint> vertexData;
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
vertexData.emplace_back(a.x(), a.y());
|
||||
vertexData.emplace_back(b.x(), b.y());
|
||||
vertexData.emplace_back(c.x(), c.y());
|
||||
return false;
|
||||
});
|
||||
vertices_to_draw_data(vertexData);
|
||||
}
|
||||
|
||||
std::vector<size_t> DrawBrushStroke::get_wedge_indices(const std::vector<DrawBrushStrokePoint>& points) {
|
||||
if(points.size() < 2)
|
||||
return {};
|
||||
std::vector<size_t> toRet;
|
||||
toRet.emplace_back(0);
|
||||
for(size_t i = 1; i < points.size() - 1; i++) {
|
||||
Vector2f v1Norm = (points[i - 1].pos - points[i].pos).normalized();
|
||||
Vector2f v2Norm = (points[i + 1].pos - points[i].pos).normalized();
|
||||
if(v1Norm.dot(v2Norm) > -0.6f)
|
||||
toRet.emplace_back(i);
|
||||
}
|
||||
toRet.emplace_back(points.size() - 1);
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::vector<DrawBrushStrokePoint> DrawBrushStroke::smooth_points(size_t beginIndex, size_t endIndex) {
|
||||
std::vector<DrawBrushStrokePoint>& points = d.points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<DrawBrushStrokePoint> toRet;
|
||||
|
||||
DrawBrushStrokePoint p0 = points[beginIndex];
|
||||
p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized() * DRAW_MINIMUM_LIMIT;
|
||||
DrawBrushStrokePoint p1 = points[beginIndex];
|
||||
DrawBrushStrokePoint p2 = points[beginIndex + 1];
|
||||
DrawBrushStrokePoint p3;
|
||||
|
||||
for(size_t i = beginIndex; i < endIndex; i++) {
|
||||
|
||||
if(i == endIndex - 1) {
|
||||
p3 = points[i + 1];
|
||||
p3.pos = p3.pos + p3.width * (p3.pos - points[i].pos).normalized() * DRAW_MINIMUM_LIMIT;
|
||||
}
|
||||
else
|
||||
p3 = points[i + 2];
|
||||
|
||||
toRet.emplace_back(p1);
|
||||
|
||||
const unsigned numOfDivisions = 5; // How smooth the stroke should be
|
||||
|
||||
float tMove = 1.0 / static_cast<float>(numOfDivisions);
|
||||
for(unsigned d = 1; d < numOfDivisions; d++) {
|
||||
float t = tMove * static_cast<float>(d);
|
||||
DrawBrushStrokePoint pToAdd;
|
||||
pToAdd.pos = catmull_rom(p0.pos, p1.pos, p2.pos, p3.pos, t);
|
||||
pToAdd.width = std::lerp(p1.width, p2.width, t);
|
||||
if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values
|
||||
toRet.emplace_back(pToAdd);
|
||||
}
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
p2 = p3;
|
||||
}
|
||||
toRet.emplace_back(points[endIndex]);
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::vector<DrawBrushStrokePoint> DrawBrushStroke::smooth_points_avg(size_t beginIndex, size_t endIndex) {
|
||||
std::vector<DrawBrushStrokePoint>& points = d.points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
return {points[beginIndex], points[endIndex]};
|
||||
|
||||
std::vector<DrawBrushStrokePoint> toRet;
|
||||
|
||||
for(size_t i = beginIndex; i < endIndex; i++) {
|
||||
const DrawBrushStrokePoint& p1 = points[i];
|
||||
const DrawBrushStrokePoint& p2 = points[i + 1];
|
||||
|
||||
const unsigned numOfDivisions = 5; // How smooth the stroke should be
|
||||
|
||||
float tMove = 1.0 / static_cast<float>(numOfDivisions);
|
||||
for(unsigned d = 0; d < numOfDivisions; d++) {
|
||||
float t = tMove * static_cast<float>(d);
|
||||
DrawBrushStrokePoint pToAdd;
|
||||
pToAdd.pos = lerp_vec(p1.pos, p2.pos, t);
|
||||
pToAdd.width = std::lerp(p1.width, p2.width, t);
|
||||
if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values
|
||||
toRet.emplace_back(pToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
toRet.emplace_back(points[endIndex]);
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc) {
|
||||
const int ARC_SMOOTHNESS = 10;
|
||||
const int CIRCLE_SMOOTHNESS = 20;
|
||||
std::vector<DrawBrushStrokePoint>& pointsN = d.points;
|
||||
|
||||
if(pointsN.size() == 1 && d.hasRoundCaps) {
|
||||
std::vector<Vector2f> circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS);
|
||||
for(size_t i = 0; i < circlePoints.size(); i++)
|
||||
if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return;
|
||||
}
|
||||
|
||||
if(pointsN.size() < 2)
|
||||
return;
|
||||
|
||||
std::vector<DrawBrushStrokePoint> points = smooth_points(0, pointsN.size() - 1);
|
||||
std::vector<size_t> wedgeIndices = get_wedge_indices(points);
|
||||
for(size_t i = 0; i < wedgeIndices.size() - 1; i++) {
|
||||
size_t pointsBegin = wedgeIndices[i];
|
||||
size_t pointsEnd = wedgeIndices[i + 1];
|
||||
|
||||
std::array<Vector2f, 3> vertArray;
|
||||
size_t newIndex = 0;
|
||||
|
||||
Vector2f perpPrev = perpendicular_vec2((points[pointsBegin + 1].pos - points[pointsBegin].pos).eval());
|
||||
perpPrev.normalize();
|
||||
|
||||
vertArray[0] = points[pointsBegin].pos + perpPrev * points[pointsBegin].width * 0.5f;
|
||||
vertArray[1] = points[pointsBegin].pos - perpPrev * points[pointsBegin].width * 0.5f;
|
||||
newIndex = 2;
|
||||
|
||||
if(d.hasRoundCaps && pointsBegin == 0) {
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[0]) - points[0].pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[1]) - points[0].pos).normalized();
|
||||
Vector2f arcDirCenter = (points[0].pos - points[1].pos).normalized();
|
||||
std::vector<Vector2f> arcPoints = arc_vec<float>(points[0].pos, arcDirStart, arcDirEnd, arcDirCenter, points[0].width * 0.5f, ARC_SMOOTHNESS);
|
||||
arcPoints.emplace(arcPoints.begin(), convert_vec2<Vector2f>(vertArray[0]));
|
||||
arcPoints.emplace_back(convert_vec2<Vector2f>(vertArray[1]));
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++)
|
||||
if(passTriangleFunc(points[0].pos, arcPoints[i], arcPoints[i + 1])) return;
|
||||
}
|
||||
|
||||
for(size_t j = pointsBegin + 1; j < pointsEnd; j++) {
|
||||
Vector2f v1 = points[j - 1].pos - points[j].pos;
|
||||
Vector2f v2 = points[j].pos - points[j + 1].pos;
|
||||
Vector2f perp = perpendicular_vec2((v1 + v2).eval()).normalized();
|
||||
if(std::isnan(perp.dot(perpPrev))) {
|
||||
std::cout << "NAN FOUND" << std::endl;
|
||||
std::cout << v1 << std::endl;
|
||||
std::cout << v2 << std::endl;
|
||||
std::cout << perp << std::endl;
|
||||
std::cout << perpPrev << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
if(perp.dot(perpPrev) < 0.0)
|
||||
perp = -perp;
|
||||
|
||||
vertArray[newIndex] = points[j].pos + perp * 0.5f * points[j].width;
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
vertArray[newIndex] = points[j].pos - perp * 0.5f * points[j].width;
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
perpPrev = perp;
|
||||
}
|
||||
|
||||
Vector2f rectDir = points[pointsEnd - 1].pos - points[pointsEnd].pos;
|
||||
rectDir.normalize();
|
||||
Vector2f perp = perpendicular_vec2(rectDir);
|
||||
if(perp.dot(perpPrev) < 0.0)
|
||||
perp = -perp;
|
||||
vertArray[newIndex] = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5;
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
vertArray[newIndex] = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5;
|
||||
newIndex = (newIndex + 1) % 3;
|
||||
if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return;
|
||||
|
||||
// Render wedge
|
||||
if(pointsEnd != points.size() - 1) {
|
||||
// Find 4 points involved in intersection
|
||||
Vector2f currentP1 = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5;
|
||||
Vector2f currentP2 = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5;
|
||||
Vector2f nextRectDir = points[pointsEnd + 1].pos - points[pointsEnd].pos;
|
||||
nextRectDir.normalize();
|
||||
Vector2f nextPerp = perpendicular_vec2(nextRectDir);
|
||||
Vector2f nextP1 = points[pointsEnd].pos - nextPerp * points[pointsEnd].width * 0.5;
|
||||
Vector2f nextP2 = points[pointsEnd].pos + nextPerp * points[pointsEnd].width * 0.5;
|
||||
|
||||
// Find center of wedge
|
||||
Vector2f wedgeCenter = line_line_intersection(currentP1, currentP2, nextP1, nextP2);
|
||||
|
||||
Vector2f wedgeP1 = ((currentP1 - currentP2).dot(nextRectDir)) <= 0.0 ? currentP1 : currentP2;
|
||||
Vector2f wedgeP2 = ((nextP1 - nextP2).dot(rectDir)) <= 0.0 ? nextP1 : nextP2;
|
||||
|
||||
float arcRadius;
|
||||
|
||||
std::vector<Vector2f> arcPoints;
|
||||
|
||||
if(wedgeCenter.x() == std::numeric_limits<float>::max()) {
|
||||
wedgeCenter = (currentP1 + currentP2) * 0.5;
|
||||
wedgeP1 = currentP1;
|
||||
wedgeP2 = currentP2;
|
||||
arcRadius = points[pointsEnd].width * 0.5;
|
||||
arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), (-rectDir).eval(), arcRadius, ARC_SMOOTHNESS);
|
||||
}
|
||||
else {
|
||||
arcRadius = (wedgeCenter - wedgeP1).norm();
|
||||
arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), lerp_vec((-rectDir).eval(), (-nextRectDir).eval(), 0.5).normalized(), arcRadius, ARC_SMOOTHNESS);
|
||||
}
|
||||
|
||||
arcPoints.emplace(arcPoints.begin(), wedgeP1);
|
||||
arcPoints.emplace_back(wedgeP2);
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++)
|
||||
if(passTriangleFunc(wedgeCenter, arcPoints[i], arcPoints[i + 1])) return;
|
||||
}
|
||||
else if(d.hasRoundCaps) { // It's the last point, cap it off
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[(newIndex + 2) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirCenter = -rectDir;
|
||||
std::vector<Vector2f> arcPoints = arc_vec<float>(points.back().pos, arcDirStart, arcDirEnd, arcDirCenter, points.back().width * 0.5f, ARC_SMOOTHNESS);
|
||||
arcPoints.emplace(arcPoints.begin(), convert_vec2<Vector2f>(vertArray[(newIndex + 1) % 3]));
|
||||
arcPoints.emplace_back(convert_vec2<Vector2f>(vertArray[(newIndex + 2) % 3]));
|
||||
for(size_t i = 0; i < arcPoints.size() - 1; i++)
|
||||
if(passTriangleFunc(points.back().pos, arcPoints[i], arcPoints[i + 1])) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::create_collider(bool colliderAllocated) {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
strokeObjects.triangle.emplace_back(a, b, c);
|
||||
return false;
|
||||
});
|
||||
strokeObjects.recalculate_bounds();
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
precheckAABBLevels.clear();
|
||||
add_precheck_aabb_level(0, collisionTree.children);
|
||||
if(!colliderAllocated) {
|
||||
collisionTree.objects.aabb.clear();
|
||||
collisionTree.objects.circle.clear();
|
||||
collisionTree.objects.triangle.clear();
|
||||
collisionTree.children.clear();
|
||||
}
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawBrushStroke::add_precheck_aabb_level(size_t level, const std::vector<SCollision::BVHContainer<float>>& levelArray) {
|
||||
for(const auto& a : levelArray) {
|
||||
if(level == AABB_PRECHECK_BVH_LEVELS || a.children.empty())
|
||||
precheckAABBLevels.emplace_back(a.objects.bounds);
|
||||
else
|
||||
add_precheck_aabb_level(level + 1, a.children);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBrushStroke::vertices_to_draw_data(const std::vector<SkPoint>& vertexData) {
|
||||
if((d.points.size() == 1 && d.hasRoundCaps) || d.points.size() >= 2)
|
||||
vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, vertexData.size(), vertexData.data(), nullptr, nullptr);
|
||||
else
|
||||
vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 0, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void DrawBrushStroke::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
bool DrawBrushStroke::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) {
|
||||
if(colliderAllocated)
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
|
||||
if(!SCollision::collide(checkAgainst, collisionTree.objects.bounds))
|
||||
return false;
|
||||
|
||||
bool deepPrecheck = false;
|
||||
for(auto& aabb : precheckAABBLevels) {
|
||||
if(SCollision::collide(checkAgainst, aabb)) {
|
||||
deepPrecheck = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!deepPrecheck && !precheckAABBLevels.empty())
|
||||
return false;
|
||||
|
||||
bool toRet = false;
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
toRet = SCollision::collide(checkAgainst, SCollision::Triangle<float>{a, b, c});
|
||||
return toRet;
|
||||
});
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void DrawBrushStroke::free_collider() {
|
||||
collisionTree.objects.aabb.clear();
|
||||
collisionTree.objects.circle.clear();
|
||||
collisionTree.objects.triangle.clear();
|
||||
collisionTree.children.clear();
|
||||
}
|
||||
|
||||
void DrawBrushStroke::allocate_collider() {
|
||||
create_collider(true);
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawBrushStroke::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
62
src/DrawComponents/DrawBrushStroke.hpp
Normal file
62
src/DrawComponents/DrawBrushStroke.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
#define DRAW_MINIMUM_LIMIT 0.75
|
||||
|
||||
|
||||
struct DrawBrushStrokePoint {
|
||||
Vector2f pos;
|
||||
float width;
|
||||
|
||||
template <typename Archive> void serialize(Archive& a) {
|
||||
a(pos, width);
|
||||
}
|
||||
};
|
||||
|
||||
class DrawBrushStroke : public DrawComponent {
|
||||
public:
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
|
||||
struct Data {
|
||||
std::vector<DrawBrushStrokePoint> points;
|
||||
Vector4f color;
|
||||
bool hasRoundCaps;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy() const override;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) override;
|
||||
virtual void create_collider(bool colliderAllocated) override;
|
||||
virtual void update_from_delayed_ptr() override;
|
||||
|
||||
virtual void free_collider() override;
|
||||
virtual void allocate_collider() override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
sk_sp<SkVertices> vertices;
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
|
||||
private:
|
||||
void add_precheck_aabb_level(size_t level, const std::vector<SCollision::BVHContainer<float>>& levelArray);
|
||||
std::vector<SCollision::AABB<float>> precheckAABBLevels;
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc);
|
||||
void vertices_to_draw_data(const std::vector<SkPoint>& vertexData);
|
||||
std::vector<size_t> get_wedge_indices(const std::vector<DrawBrushStrokePoint>& points);
|
||||
std::vector<DrawBrushStrokePoint> smooth_points(size_t beginIndex, size_t endIndex);
|
||||
std::vector<DrawBrushStrokePoint> smooth_points_avg(size_t beginIndex, size_t endIndex);
|
||||
#endif
|
||||
};
|
||||
|
||||
208
src/DrawComponents/DrawComponent.cpp
Normal file
208
src/DrawComponents/DrawComponent.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#include "DrawComponent.hpp"
|
||||
#include "DrawBrushStroke.hpp"
|
||||
#include "DrawEllipse.hpp"
|
||||
#include "DrawRectangle.hpp"
|
||||
#include "DrawTextBox.hpp"
|
||||
#include "DrawImage.hpp"
|
||||
#include "Helpers/Networking/NetLibrary.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../Server/CommandList.hpp"
|
||||
#include <chrono>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../MainProgram.hpp"
|
||||
#endif
|
||||
|
||||
std::shared_ptr<DrawComponent> DrawComponent::allocate_comp_type(DrawComponentType type) {
|
||||
switch(type) {
|
||||
case DRAWCOMPONENT_BRUSHSTROKE:
|
||||
return std::make_shared<DrawBrushStroke>();
|
||||
case DRAWCOMPONENT_RECTANGLE:
|
||||
return std::make_shared<DrawRectangle>();
|
||||
case DRAWCOMPONENT_ELLIPSE:
|
||||
return std::make_shared<DrawEllipse>();
|
||||
case DRAWCOMPONENT_TEXTBOX:
|
||||
return std::make_shared<DrawTextBox>();
|
||||
case DRAWCOMPONENT_IMAGE:
|
||||
return std::make_shared<DrawImage>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_place(MainServer& server, ServerClientID id, uint64_t placement) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_PLACE_COMPONENT, placement, get_type(), id, this->coords, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_erase(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_ERASE_COMPONENT, id);
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_update_temp(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_UPDATE_COMPONENT, true, id, *this);
|
||||
tempServerUpdateTimer = std::chrono::steady_clock::now();
|
||||
serverIsTempUpdate = true;
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_update_final(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_UPDATE_COMPONENT, false, id, *this);
|
||||
serverIsTempUpdate = false;
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_transform_temp(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, true, id, this->coords);
|
||||
tempServerUpdateTimer = std::chrono::steady_clock::now();
|
||||
serverIsTempUpdate = true;
|
||||
}
|
||||
|
||||
void DrawComponent::server_send_transform_final(MainServer& server, ServerClientID id) {
|
||||
server.netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_TRANSFORM_COMPONENT, false, id, this->coords);
|
||||
serverIsTempUpdate = false;
|
||||
}
|
||||
|
||||
void DrawComponent::server_update(MainServer& server, ServerClientID id) {
|
||||
if(serverIsTempUpdate) {
|
||||
float timeSince = std::chrono::duration_cast<std::chrono::duration<float>>(std::chrono::steady_clock::now() - tempServerUpdateTimer).count();
|
||||
if(timeSince >= 1.0f) {
|
||||
server_send_update_final(server, id);
|
||||
serverIsTempUpdate = false;
|
||||
}
|
||||
}
|
||||
if(serverIsTempTransform) {
|
||||
float timeSince = std::chrono::duration_cast<std::chrono::duration<float>>(std::chrono::steady_clock::now() - tempServerTransformTimer).count();
|
||||
if(timeSince >= 1.0f) {
|
||||
server_send_transform_final(server, id);
|
||||
serverIsTempTransform = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::get_used_resources(std::unordered_set<ServerClientID>& v) const {
|
||||
}
|
||||
|
||||
void DrawComponent::remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) {
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
|
||||
bool DrawComponent::bounds_draw_check(const DrawData& drawData) const {
|
||||
bool a = !drawData.clampDrawBetween || ((drawData.clampDrawMaximum >= coords.inverseScale) && (drawData.clampDrawMinimum < coords.inverseScale));
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawComponent::check_timers(DrawingProgram& drawP) {
|
||||
if(delayedCoordinateSpace) {
|
||||
float transformDur = std::chrono::duration_cast<std::chrono::duration<float>>(std::chrono::steady_clock::now() - lastTransformTime).count();
|
||||
if(transformDur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) {
|
||||
coords = *delayedCoordinateSpace;
|
||||
delayedCoordinateSpace = nullptr;
|
||||
finalize_update(drawP.colliderAllocated);
|
||||
}
|
||||
}
|
||||
if(delayedUpdatePtr) {
|
||||
float updateDur = std::chrono::duration_cast<std::chrono::duration<float>>(std::chrono::steady_clock::now() - lastUpdateTime).count();
|
||||
if(updateDur > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) {
|
||||
update_from_delayed_ptr();
|
||||
delayedUpdatePtr = nullptr;
|
||||
final_update(drawP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::temp_update(DrawingProgram& drawP) {
|
||||
initialize_draw_data(drawP);
|
||||
updateDraw = true;
|
||||
}
|
||||
|
||||
void DrawComponent::final_update(DrawingProgram& drawP) {
|
||||
initialize_draw_data(drawP);
|
||||
finalize_update(drawP.colliderAllocated);
|
||||
updateDraw = true;
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_place(DrawingProgram& drawP, uint64_t placement) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_COMPONENT, placement, get_type(), this->coords, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_erase(DrawingProgram& drawP, ServerClientID id) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_ERASE_COMPONENT, id);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_update_temp(DrawingProgram& drawP, ServerClientID id) {
|
||||
drawP.world.con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, true, id, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_update_final(DrawingProgram& drawP, ServerClientID id) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_UPDATE_COMPONENT, false, id, *this);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_transform_temp(DrawingProgram& drawP, ServerClientID id) {
|
||||
drawP.world.con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, true, id, this->coords);
|
||||
}
|
||||
|
||||
void DrawComponent::client_send_transform_final(DrawingProgram& drawP, ServerClientID id) {
|
||||
drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, false, id, this->coords);
|
||||
}
|
||||
|
||||
void DrawComponent::finalize_update(bool colliderAllocated) {
|
||||
create_collider(colliderAllocated);
|
||||
updateDraw = true;
|
||||
}
|
||||
|
||||
bool DrawComponent::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, bool colliderAllocated) {
|
||||
return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords<SCollision::ColliderCollection<float>>(checkAgainstWorld), colliderAllocated);
|
||||
}
|
||||
|
||||
bool DrawComponent::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam, bool colliderAllocated) {
|
||||
return collides_with(camCoords, camCoords.collider_to_world<SCollision::ColliderCollection<WorldScalar>>(checkAgainstCam), checkAgainstCam, colliderAllocated);
|
||||
}
|
||||
|
||||
// We could just send one of the checkAgainst colliders, since they represent the same thing, but sending both saves on redundant transformations, since we can save both of the versions on the executor side
|
||||
bool DrawComponent::collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam, bool colliderAllocated) {
|
||||
if((camCoords.inverseScale << DRAWCOMP_MAX_SHIFT_BEFORE_DISAPPEAR) < coords.inverseScale) // Object is too large, just dismiss the collision
|
||||
return false;
|
||||
else if((camCoords.inverseScale >> DRAWCOMP_MIN_SHIFT_BEFORE_DISAPPEAR) >= coords.inverseScale) {
|
||||
if(!worldAABB)
|
||||
return false;
|
||||
return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB->min));
|
||||
}
|
||||
else {
|
||||
auto c = checkAgainstWorld.transform<float>(
|
||||
[&coords = coords](const WorldVec& a) {
|
||||
return coords.to_space(a);
|
||||
},
|
||||
[&coords = coords](const WorldScalar& a) {
|
||||
return coords.scalar_to_space(a);
|
||||
});
|
||||
return collides_within_coords(c, colliderAllocated);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::canvas_do_calculated_transform(SkCanvas* canvas) {
|
||||
canvas->scale(drawSetupData.transformData.scale, drawSetupData.transformData.scale);
|
||||
canvas->rotate(drawSetupData.transformData.rotation);
|
||||
canvas->translate(drawSetupData.transformData.translation.x(), drawSetupData.transformData.translation.y());
|
||||
}
|
||||
|
||||
void DrawComponent::calculate_draw_transform(const DrawData& drawData) {
|
||||
drawSetupData.shouldDraw = !drawData.clampDrawBetween || ((drawData.clampDrawMaximum >= coords.inverseScale) && (drawData.clampDrawMinimum < coords.inverseScale));
|
||||
if(drawSetupData.shouldDraw) {
|
||||
drawSetupData.shouldDraw = updateDraw || !worldAABB || SCollision::collide(*worldAABB, drawData.cam.viewingAreaGenerousCollider);
|
||||
if(drawSetupData.shouldDraw) {
|
||||
drawSetupData.transformData.translation = -coords.to_space(drawData.cam.c.pos);
|
||||
drawSetupData.transformData.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi;
|
||||
drawSetupData.transformData.scale = static_cast<float>(coords.inverseScale / drawData.cam.c.inverseScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawComponent::calculate_world_bounds() {
|
||||
worldAABB = std::optional<SCollision::AABB<WorldScalar>>(coords.collider_to_world<SCollision::AABB<WorldScalar>, SCollision::AABB<float>>(get_obj_coord_bounds()));
|
||||
}
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> DrawComponent::get_world_bounds() const {
|
||||
return worldAABB;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DrawComponent::~DrawComponent() { }
|
||||
109
src/DrawComponents/DrawComponent.hpp
Normal file
109
src/DrawComponents/DrawComponent.hpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <Helpers/Hashes.hpp>
|
||||
#include "cereal/archives/portable_binary.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
#include "../Server/MainServer.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawData.hpp"
|
||||
#include <chrono>
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include <include/core/SkVertices.h>
|
||||
#endif
|
||||
|
||||
#define CLIENT_DRAWCOMP_DELAY_TIMER_DURATION 5.0f
|
||||
|
||||
#define DRAWCOMP_MAX_SHIFT_BEFORE_DISAPPEAR 20
|
||||
#define DRAWCOMP_MIN_SHIFT_BEFORE_DISAPPEAR 9
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
class DrawComponent {
|
||||
public:
|
||||
static std::shared_ptr<DrawComponent> allocate_comp_type(DrawComponentType type);
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const = 0;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) = 0;
|
||||
virtual DrawComponentType get_type() const = 0;
|
||||
virtual void get_used_resources(std::unordered_set<ServerClientID>& v) const;
|
||||
virtual void remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs);
|
||||
virtual ~DrawComponent();
|
||||
|
||||
// NOTE: Permanantly altering the coordinates during transformation can lead to loss of detail for smaller objects
|
||||
CoordSpaceHelper coords;
|
||||
|
||||
struct DrawSetupData {
|
||||
struct TransformDrawData {
|
||||
Vector2f translation;
|
||||
float rotation;
|
||||
float scale;
|
||||
} transformData;
|
||||
bool shouldDraw = true;
|
||||
} drawSetupData;
|
||||
|
||||
void canvas_do_calculated_transform(SkCanvas* canvas);
|
||||
void calculate_draw_transform(const DrawData& drawData);
|
||||
|
||||
void server_send_place(MainServer& server, ServerClientID id, uint64_t placement);
|
||||
static void server_send_erase(MainServer& server, ServerClientID id);
|
||||
void server_send_update_temp(MainServer& server, ServerClientID id);
|
||||
void server_send_update_final(MainServer& server, ServerClientID id);
|
||||
void server_send_transform_temp(MainServer& server, ServerClientID id);
|
||||
void server_send_transform_final(MainServer& server, ServerClientID id);
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> tempServerUpdateTimer;
|
||||
bool serverIsTempUpdate = false;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> tempServerTransformTimer;
|
||||
bool serverIsTempTransform = false;
|
||||
|
||||
void server_update(MainServer& server, ServerClientID id);
|
||||
|
||||
#ifndef IS_SERVER
|
||||
bool selected = false;
|
||||
bool updateDraw = true;
|
||||
|
||||
bool globalCollisionCheck = false;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastTransformTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastUpdateTime;
|
||||
|
||||
std::shared_ptr<DrawComponent> delayedUpdatePtr = nullptr;
|
||||
std::shared_ptr<CoordSpaceHelper> delayedCoordinateSpace = nullptr;
|
||||
|
||||
bool bounds_draw_check(const DrawData& drawData) const;
|
||||
|
||||
void check_timers(DrawingProgram& drawP);
|
||||
void client_send_place(DrawingProgram& drawP, uint64_t placement);
|
||||
static void client_send_erase(DrawingProgram& drawP, ServerClientID id);
|
||||
void client_send_update_temp(DrawingProgram& drawP, ServerClientID id);
|
||||
void client_send_update_final(DrawingProgram& drawP, ServerClientID id);
|
||||
void client_send_transform_temp(DrawingProgram& drawP, ServerClientID id);
|
||||
void client_send_transform_final(DrawingProgram& drawP, ServerClientID id);
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> worldAABB;
|
||||
|
||||
std::optional<SCollision::AABB<WorldScalar>> get_world_bounds() const;
|
||||
void calculate_world_bounds();
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const = 0;
|
||||
|
||||
virtual std::shared_ptr<DrawComponent> copy() const = 0;
|
||||
virtual void create_collider(bool colliderAllocated) = 0;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) = 0;
|
||||
void temp_update(DrawingProgram& drawP);
|
||||
void final_update(DrawingProgram& drawP);
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
|
||||
virtual void finalize_update(bool colliderAllocated);
|
||||
virtual void update(DrawingProgram& drawP) = 0;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) = 0;
|
||||
bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, bool colliderAllocated);
|
||||
bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam, bool colliderAllocated);
|
||||
bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam, bool colliderAllocated);
|
||||
virtual void update_from_delayed_ptr() = 0;
|
||||
|
||||
virtual void free_collider() = 0;
|
||||
virtual void allocate_collider() = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
141
src/DrawComponents/DrawEllipse.cpp
Normal file
141
src/DrawComponents/DrawEllipse.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#include "DrawEllipse.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawEllipse::get_type() const {
|
||||
return DRAWCOMPONENT_ELLIPSE;
|
||||
}
|
||||
|
||||
void DrawEllipse::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawEllipse::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawEllipse::copy() const {
|
||||
auto a = std::make_shared<DrawEllipse>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawEllipse::update_from_delayed_ptr() {
|
||||
std::shared_ptr<DrawEllipse> newPtr = std::static_pointer_cast<DrawEllipse>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawEllipse::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kFill_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.fillColor));
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.strokeColor));
|
||||
p.setStrokeWidth(d.strokeWidth);
|
||||
canvas->drawPath(ellipsePath, p);
|
||||
}
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEllipse::create_draw_data() {
|
||||
ellipsePath = SkPath();
|
||||
SkRect newRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
ellipsePath.addOval(newRect);
|
||||
}
|
||||
|
||||
void DrawEllipse::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
}
|
||||
|
||||
void DrawEllipse::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
bool DrawEllipse::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawEllipse::create_collider(bool colliderAllocated) {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
|
||||
if(d.fillStrokeMode == 0) {
|
||||
Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5;
|
||||
float a = ellipseCenter.x() - d.p1.x();
|
||||
float b = ellipseCenter.y() - d.p1.y();
|
||||
Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)};
|
||||
unsigned numOfSegments = 20;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i <= numOfSegments; i++) {
|
||||
Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)};
|
||||
strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint);
|
||||
prevPoint = nextPoint;
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
}
|
||||
else if(d.fillStrokeMode == 1) {
|
||||
std::vector<Vector2f> points;
|
||||
|
||||
Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5;
|
||||
float a = ellipseCenter.x() - d.p1.x();
|
||||
float b = ellipseCenter.y() - d.p1.y();
|
||||
unsigned numOfSegments = 40;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i < numOfSegments; i++) {
|
||||
points.emplace_back(ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)});
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
|
||||
}
|
||||
else if(d.fillStrokeMode == 2) {
|
||||
float strokeRadius = d.strokeWidth * 0.5f;
|
||||
Vector2f wNewP1 = d.p1 - Vector2f{strokeRadius, strokeRadius};
|
||||
Vector2f wNewP2 = d.p2 + Vector2f{strokeRadius, strokeRadius};
|
||||
Vector2f ellipseCenter = (wNewP1 + wNewP2) * 0.5;
|
||||
float a = ellipseCenter.x() - wNewP1.x();
|
||||
float b = ellipseCenter.y() - wNewP1.y();
|
||||
Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)};
|
||||
unsigned numOfSegments = 20;
|
||||
float segmentStep = 1.0 / numOfSegments;
|
||||
float t = 0.0;
|
||||
for(unsigned i = 0; i <= numOfSegments; i++) {
|
||||
Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)};
|
||||
strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint);
|
||||
prevPoint = nextPoint;
|
||||
t += segmentStep * std::numbers::pi * 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawEllipse::free_collider() {
|
||||
}
|
||||
|
||||
void DrawEllipse::allocate_collider() {
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawEllipse::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
49
src/DrawComponents/DrawEllipse.hpp
Normal file
49
src/DrawComponents/DrawEllipse.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawEllipse : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector4f strokeColor;
|
||||
Vector4f fillColor;
|
||||
float strokeWidth;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
uint8_t fillStrokeMode;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy() const override;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) override;
|
||||
virtual void update_from_delayed_ptr() override;
|
||||
void create_draw_data();
|
||||
virtual void create_collider(bool colliderAllocated) override;
|
||||
|
||||
virtual void free_collider() override;
|
||||
virtual void allocate_collider() override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkPath ellipsePath;
|
||||
|
||||
// Cache
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
private:
|
||||
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc);
|
||||
#endif
|
||||
};
|
||||
102
src/DrawComponents/DrawImage.cpp
Normal file
102
src/DrawComponents/DrawImage.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#include "DrawImage.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "../World.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkSamplingOptions.h>
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawImage::get_type() const {
|
||||
return DRAWCOMPONENT_IMAGE;
|
||||
}
|
||||
|
||||
void DrawImage::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.p1, d.p2, d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::get_used_resources(std::unordered_set<ServerClientID>& v) const {
|
||||
v.emplace(d.imageID);
|
||||
}
|
||||
|
||||
void DrawImage::remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) {
|
||||
auto it = newIDs.find(d.imageID);
|
||||
if(it != newIDs.end())
|
||||
d.imageID = it->second;
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawImage::copy() const {
|
||||
auto a = std::make_shared<DrawImage>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawImage::update_from_delayed_ptr() {
|
||||
std::shared_ptr<DrawImage> newPtr = std::static_pointer_cast<DrawImage>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawImage::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
std::shared_ptr<ResourceDisplay> display = drawData.rMan->get_display_data(d.imageID);
|
||||
if(display)
|
||||
display->draw(canvas, drawData, imRect);
|
||||
else
|
||||
canvas->drawRect(imRect, SkPaint({0.5f, 0.5f, 0.5f, 0.5f}));
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawImage::update(DrawingProgram& drawP) {
|
||||
std::shared_ptr<ResourceDisplay> display = drawP.world.drawData.rMan->get_display_data(d.imageID);
|
||||
if(display) {
|
||||
if(display->update_draw())// && bounds_draw_check(drawP.world.drawData, collisionTree))
|
||||
updateDraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawImage::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
}
|
||||
|
||||
bool DrawImage::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawImage::create_draw_data() {
|
||||
imRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
}
|
||||
|
||||
void DrawImage::create_collider(bool colliderAllocated) {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawImage::free_collider() {
|
||||
}
|
||||
|
||||
void DrawImage::allocate_collider() {
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawImage::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
46
src/DrawComponents/DrawImage.hpp
Normal file
46
src/DrawComponents/DrawImage.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawImage : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void get_used_resources(std::unordered_set<ServerClientID>& v) const override;
|
||||
virtual void remap_resource_ids(std::unordered_map<ServerClientID, ServerClientID>& newIDs) override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
|
||||
ServerClientID imageID;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy() const override;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) override;
|
||||
void create_draw_data();
|
||||
virtual void create_collider(bool colliderAllocated) override;
|
||||
virtual void update_from_delayed_ptr() override;
|
||||
|
||||
virtual void free_collider() override;
|
||||
virtual void allocate_collider() override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkRect imRect;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
|
||||
124
src/DrawComponents/DrawRectangle.cpp
Normal file
124
src/DrawComponents/DrawRectangle.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include "DrawRectangle.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include "Helpers/MathExtras.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <include/core/SkPaint.h>
|
||||
#include "../SharedTypes.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawRectangle::get_type() const {
|
||||
return DRAWCOMPONENT_RECTANGLE;
|
||||
}
|
||||
|
||||
void DrawRectangle::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void DrawRectangle::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawRectangle::copy() const {
|
||||
auto a = std::make_shared<DrawRectangle>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawRectangle::update_from_delayed_ptr() {
|
||||
std::shared_ptr<DrawRectangle> newPtr = std::static_pointer_cast<DrawRectangle>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
}
|
||||
|
||||
void DrawRectangle::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kFill_Style);
|
||||
p.setStrokeCap(SkPaint::kRound_Cap);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.fillColor));
|
||||
canvas->drawPath(rectPath, p);
|
||||
}
|
||||
if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setStrokeCap(SkPaint::kRound_Cap);
|
||||
p.setColor4f(convert_vec4<SkColor4f>(d.strokeColor));
|
||||
p.setStrokeWidth(d.strokeWidth);
|
||||
canvas->drawPath(rectPath, p);
|
||||
}
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectangle::update(DrawingProgram& drawP) {
|
||||
}
|
||||
|
||||
void DrawRectangle::initialize_draw_data(DrawingProgram& drawP) {
|
||||
create_draw_data();
|
||||
}
|
||||
|
||||
bool DrawRectangle::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawRectangle::create_draw_data() {
|
||||
rectPath = SkPath();
|
||||
if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) {
|
||||
rectPath.moveTo(convert_vec2<SkPoint>(d.p1));
|
||||
rectPath.lineTo(convert_vec2<SkPoint>(d.p2));
|
||||
}
|
||||
else {
|
||||
SkRect newRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
rectPath.addRoundRect(newRect, d.cornerRadius, d.cornerRadius);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectangle::create_collider(bool colliderAllocated) {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
if(d.fillStrokeMode == 0) {
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
}
|
||||
else if(d.fillStrokeMode == 1) {
|
||||
if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) {
|
||||
std::vector<Vector2f> points = {d.p1, d.p2};
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
}
|
||||
else {
|
||||
std::array<Vector2f, 4> pointArr = triangle_from_rect_points(d.p1, d.p2);
|
||||
std::vector<Vector2f> points(pointArr.begin(), pointArr.end());
|
||||
generate_polyline(strokeObjects, points, d.strokeWidth, true);
|
||||
}
|
||||
}
|
||||
else if(d.fillStrokeMode == 2) {
|
||||
float strokeRadius = d.strokeWidth * 0.5f;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points((d.p1 - Vector2f{strokeRadius, strokeRadius}).eval(), (d.p2 + Vector2f{strokeRadius, strokeRadius}).eval());
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
}
|
||||
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawRectangle::free_collider() {
|
||||
}
|
||||
|
||||
void DrawRectangle::allocate_collider() {
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawRectangle::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
48
src/DrawComponents/DrawRectangle.hpp
Normal file
48
src/DrawComponents/DrawRectangle.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#endif
|
||||
|
||||
class DrawRectangle : public DrawComponent {
|
||||
public:
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual DrawComponentType get_type() const override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector4f strokeColor;
|
||||
Vector4f fillColor;
|
||||
float cornerRadius;
|
||||
float strokeWidth;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
uint8_t fillStrokeMode;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy() const override;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) override;
|
||||
void create_draw_data();
|
||||
virtual void create_collider(bool colliderAllocated) override;
|
||||
virtual void update_from_delayed_ptr() override;
|
||||
|
||||
virtual void free_collider() override;
|
||||
virtual void allocate_collider() override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
SkPath rectPath;
|
||||
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
|
||||
134
src/DrawComponents/DrawTextBox.cpp
Normal file
134
src/DrawComponents/DrawTextBox.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include "DrawTextBox.hpp"
|
||||
#include "Helpers/ConvertVec.hpp"
|
||||
#include <cereal/types/string.hpp>
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "../MainProgram.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include "../DrawingProgram/DrawingProgram.hpp"
|
||||
#include "../DrawCollision.hpp"
|
||||
#endif
|
||||
|
||||
DrawComponentType DrawTextBox::get_type() const {
|
||||
return DRAWCOMPONENT_TEXTBOX;
|
||||
}
|
||||
|
||||
void DrawTextBox::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d.editing, d.p1, d.p2, d.textColor, d.textSize, d.cursor, d.currentText);
|
||||
}
|
||||
|
||||
void DrawTextBox::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d.editing, d.p1, d.p2, d.textColor, d.textSize, d.cursor, d.currentText);
|
||||
#ifndef IS_SERVER
|
||||
textboxUpdate = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef IS_SERVER
|
||||
std::shared_ptr<DrawComponent> DrawTextBox::copy() const {
|
||||
auto a = std::make_shared<DrawTextBox>();
|
||||
a->d = d;
|
||||
a->coords = coords;
|
||||
a->textboxUpdate = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
void DrawTextBox::update_from_delayed_ptr() {
|
||||
std::shared_ptr<DrawTextBox> newPtr = std::static_pointer_cast<DrawTextBox>(delayedUpdatePtr);
|
||||
d = newPtr->d;
|
||||
textboxUpdate = true;
|
||||
}
|
||||
|
||||
void DrawTextBox::init_text_box(DrawingProgram& drawP) {
|
||||
textBox.setFontMgr(drawP.world.main.fonts.mgr);
|
||||
SkFont f(drawP.world.main.fonts.map["Roboto"], d.textSize);
|
||||
f.setLinearMetrics(true);
|
||||
f.setHinting(SkFontHinting::kNormal);
|
||||
//font.setForceAutoHinting(true);
|
||||
f.setSubpixel(true);
|
||||
f.setBaselineSnap(true);
|
||||
f.setEdging(SkFont::Edging::kSubpixelAntiAlias);
|
||||
textBox.setFont(f);
|
||||
textBox.setWidth(d.p2.x() - d.p1.x());
|
||||
textBox.fLines.clear();
|
||||
textBox.insert({0, 0}, d.currentText);
|
||||
}
|
||||
|
||||
void DrawTextBox::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
if(drawSetupData.shouldDraw) {
|
||||
canvas->save();
|
||||
canvas_do_calculated_transform(canvas);
|
||||
SkRect clipR = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
|
||||
if(d.editing) {
|
||||
SkPaint p;
|
||||
p.setStyle(SkPaint::kStroke_Style);
|
||||
p.setStrokeWidth(0.0f);
|
||||
p.setColor4f({0.5f, 0.5f, 0.5f, 1.0f});
|
||||
canvas->drawRect(clipR, p);
|
||||
}
|
||||
canvas->clipRect(clipR);
|
||||
canvas->translate(d.p1.x(), d.p1.y());
|
||||
|
||||
CollabTextBox::Editor::PaintOpts paintOpts;
|
||||
paintOpts.fForegroundColor = convert_vec4<SkColor4f>(d.textColor);
|
||||
paintOpts.fBackgroundColor = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
paintOpts.cursorColor = {0.2f, 0.2f, 0.7f, 1.0f};
|
||||
paintOpts.showCursor = d.editing;
|
||||
paintOpts.cursor = d.cursor;
|
||||
|
||||
textBox.paint(canvas, paintOpts);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTextBox::update(DrawingProgram& drawP) {
|
||||
if(textboxUpdate)
|
||||
set_textbox_string(d.currentText);
|
||||
}
|
||||
|
||||
void DrawTextBox::set_textbox_string(const std::string& str) {
|
||||
d.currentText = str;
|
||||
textBox.fLines.clear();
|
||||
textBox.insert({0, 0}, d.currentText);
|
||||
textboxUpdate = false;
|
||||
}
|
||||
|
||||
void DrawTextBox::update_contained_string() {
|
||||
d.currentText = textBox.get_string();
|
||||
updateDraw = true;
|
||||
}
|
||||
|
||||
Vector2f DrawTextBox::get_mouse_pos(DrawingProgram& drawP) {
|
||||
return coords.get_mouse_pos(drawP.world) - d.p1;
|
||||
}
|
||||
|
||||
void DrawTextBox::initialize_draw_data(DrawingProgram& drawP) {
|
||||
init_text_box(drawP);
|
||||
}
|
||||
|
||||
bool DrawTextBox::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) {
|
||||
return collisionTree.is_collide(checkAgainst);
|
||||
}
|
||||
|
||||
void DrawTextBox::create_collider(bool colliderAllocated) {
|
||||
using namespace SCollision;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
|
||||
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
|
||||
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
|
||||
collisionTree.clear();
|
||||
collisionTree.calculate_bvh_recursive(strokeObjects);
|
||||
calculate_world_bounds();
|
||||
}
|
||||
|
||||
void DrawTextBox::free_collider() {
|
||||
}
|
||||
|
||||
void DrawTextBox::allocate_collider() {
|
||||
}
|
||||
|
||||
SCollision::AABB<float> DrawTextBox::get_obj_coord_bounds() const {
|
||||
return collisionTree.objects.bounds;
|
||||
}
|
||||
|
||||
#endif
|
||||
52
src/DrawComponents/DrawTextBox.hpp
Normal file
52
src/DrawComponents/DrawTextBox.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "DrawComponent.hpp"
|
||||
#include "../CollabTextBox/CollabTextBoxCursor.hpp"
|
||||
#include "../CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include <include/core/SkPath.h>
|
||||
#include "../CollabTextBox/CollabTextBox.hpp"
|
||||
#endif
|
||||
|
||||
class DrawTextBox : public DrawComponent {
|
||||
public:
|
||||
virtual DrawComponentType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
|
||||
// User input data
|
||||
struct Data {
|
||||
Vector4f textColor;
|
||||
float textSize = -100000.0f;
|
||||
Vector2f p1;
|
||||
Vector2f p2;
|
||||
std::string currentText;
|
||||
CollabTextBox::Cursor cursor;
|
||||
bool editing = false;
|
||||
bool operator==(const Data& o) const = default;
|
||||
} d;
|
||||
|
||||
#ifndef IS_SERVER
|
||||
virtual std::shared_ptr<DrawComponent> copy() const override;
|
||||
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
|
||||
virtual void initialize_draw_data(DrawingProgram& drawP) override;
|
||||
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst, bool colliderAllocated) override;
|
||||
virtual void update(DrawingProgram& drawP) override;
|
||||
virtual void create_collider(bool colliderAllocated) override;
|
||||
virtual void update_from_delayed_ptr() override;
|
||||
void init_text_box(DrawingProgram& drawP);
|
||||
void update_contained_string();
|
||||
void set_textbox_string(const std::string& str);
|
||||
Vector2f get_mouse_pos(DrawingProgram& drawP);
|
||||
|
||||
virtual void free_collider() override;
|
||||
virtual void allocate_collider() override;
|
||||
|
||||
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
|
||||
|
||||
bool textboxUpdate = false;
|
||||
CollabTextBox::Editor textBox;
|
||||
SCollision::BVHContainer<float> collisionTree;
|
||||
#endif
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue