#include "pch.h" #include "log.h" #include "util.h" template<> std::vector poly_remove_duplicate(const std::vector& v, const float tollerance) { std::vector ret; for (size_t i = 0; i < v.size(); i++) { if (glm::distance2(v[i].pos, v[(i + 1) % v.size()].pos) > tollerance) ret.push_back(v[i]); } return ret; } bool point_in_rect(const glm::vec2& p, const glm::vec4& r) { return p.x > r.x && p.x < r.x+r.z && p.y > r.y && p.y < r.y+r.w; } // params and returns {origin, size} form glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b) { // convert from [x,y,w,h] to [x1,y1,x2,y1] a = glm::vec4(xy(a), xy(a) + zw(a)); b = glm::vec4(xy(b), xy(b) + zw(b)); // compute intersection auto o = glm::vec4(glm::max(xy(a), xy(b)), glm::min(zw(a), zw(b))); // back to rect form o = glm::vec4(xy(o), glm::max({ 0, 0 }, zw(o) - xy(o))); return o; } // params and returns {origin, size} form glm::vec4 rect_union(glm::vec4 a, glm::vec4 b) { // convert from rect [x,y,w,h] to bb [x1,y1,x2,y1] a = glm::vec4(xy(a), xy(a) + zw(a)); b = glm::vec4(xy(b), xy(b) + zw(b)); // compute union glm::vec4 o = { glm::min(xy(a), xy(b)), glm::max(zw(a), zw(b)) }; // back to rect form o = glm::vec4(xy(o), glm::max({ 0, 0 }, zw(o) - xy(o))); return o; } bool ray_intersect(glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin, glm::vec3 plane_normal, glm::vec3 plane_tangent, glm::vec3& out_hit, float& out_t) { float den = glm::dot(ray_dir, plane_normal); if (den == 0) return false; // no intersection float num = glm::dot(plane_origin - ray_origin, plane_normal); out_t = num / den; if (out_t > 0) out_hit = ray_origin + ray_dir * out_t; else // negative intersection return false; return true; }; // see: https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect bool segments_intersect(const glm::vec2& p0a, const glm::vec2& p0b, const glm::vec2& p1a, const glm::vec2& p1b, glm::vec2& out_pt, glm::vec2& out_hit_uv) { auto cross2d = [](const glm::vec2& v, const glm::vec2& w) { return (v.x * w.y) - (v.y * w.x); }; auto p = p0a; auto r = p0b - p0a; auto q = p1a; auto s = p1b - p1a; float den = cross2d(r, s); if (den == 0.f) { glm::vec4 is = rect_intersection({p, r}, {q, s}); out_pt = xy(is) + zw(is) * 0.5f; return glm::all(glm::greaterThan(zw(is), glm::vec2(0, 0))); } out_hit_uv.x = cross2d(q - p, s) / den; out_hit_uv.y = cross2d(q - p, r) / den; out_pt = p + out_hit_uv.x * r; if (out_hit_uv.x >= 0 && out_hit_uv.x <= 1 && out_hit_uv.y >= 0 && out_hit_uv.y <= 1) { return true; } return false; } // return true if the point p in the right halfspace of the ab line // computed using the 2d cross product bool point_side(glm::vec2 a, glm::vec2 b, glm::vec2 p) { return (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) >= 0.f; } // intersect 2 closed polygons // a is a convex polygon // a and b are a list of non repeating points // returns the resulting intersection polygon points std::vector poly_intersect(const std::vector& poly, const std::vector& clip) { // implementing the Sutherland-Hodgman algorithm // see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm std::vector ret = poly; for (int i = 0; i < clip.size(); i++) { std::vector tmp; glm::vec2 edge[2] = { clip[i], clip[(i + 1) % clip.size()] }; for (int j = 0; j < ret.size(); j++) { vertex_t s[2] = { ret[j], ret[(j + 1) % ret.size()] }; bool side0 = point_side(edge[0], edge[1], s[0].pos); bool side1 = point_side(edge[0], edge[1], s[1].pos); if (side0 != side1) // intersecting { glm::vec2 pt; glm::vec2 hit_uv; segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv); vertex_t v; v.pos = glm::lerp(s[0].pos, s[1].pos, hit_uv.y); v.uvs = glm::lerp(s[0].uvs, s[1].uvs, hit_uv.y); v.uvs2 = glm::lerp(s[0].uvs2, s[1].uvs2, hit_uv.y); if (side0) // outgoing { tmp.push_back(s[0]); tmp.push_back(v); } else // ingoing { tmp.push_back(v); } } else if (side0 && side1) { tmp.push_back(s[0]); } } ret = std::move(tmp); } return poly_remove_duplicate(ret); } std::vector poly_intersect(const std::vector& poly, const std::vector& clip) { // implementing the Sutherland-Hodgman algorithm // see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm std::vector ret = poly; for (int i = 0; i < clip.size(); i++) { std::vector tmp; glm::vec2 edge[2] = { clip[i], clip[(i + 1) % clip.size()] }; for (int j = 0; j < ret.size(); j++) { glm::vec2 s[2] = { ret[j], ret[(j + 1) % ret.size()] }; bool side0 = point_side(edge[0], edge[1], s[0]); bool side1 = point_side(edge[0], edge[1], s[1]); if (side0 != side1) // intersecting { glm::vec2 pt; glm::vec2 hit_uv; segments_intersect(edge[0], edge[1], s[0], s[1], pt, hit_uv); if (side0) // outgoing { tmp.push_back(s[0]); tmp.push_back(pt); } else // ingoing { tmp.push_back(pt); } } else if (side0 && side1) { tmp.push_back(s[0]); } } ret = std::move(tmp); } return poly_remove_duplicate(ret); } // clip the polygon to the near clip plane // poly is the polygon in camera coordinates std::vector poly_clip_near(const std::vector& poly, float near_plane_distance) { // implementing the Sutherland-Hodgman algorithm in 3D // see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm auto o = glm::vec3(0, 0, -near_plane_distance); auto n = glm::vec3(0, 0, -1); auto t = glm::vec3(0, 1, 0); std::vector ret; for (int j = 0; j < poly.size(); j++) { glm::vec3 s[2] = { poly[j], poly[(j + 1) % poly.size()] }; bool side0 = glm::dot(n, s[0] - o) >= 0.f; bool side1 = glm::dot(n, s[1] - o) >= 0.f; if (side0 != side1) // intersecting { glm::vec3 pt; float hit_t; if (!ray_intersect(s[0], glm::normalize(s[1] - s[0]), o, n, t, pt, hit_t)) { LOG("error ray_intersect"); } if (side0) // outgoing { ret.push_back(s[0]); ret.push_back(pt); } else // ingoing { ret.push_back(pt); } } else if (side0 && side1) { ret.push_back(s[0]); } } return poly_remove_duplicate(ret); } glm::vec4 rand_color() { float r = (rand() % 256) / 256.f; float g = (rand() % 256) / 256.f; float b = (rand() % 256) / 256.f; return { r, g, b, 1.f }; } glm::vec3 convert_hsv2rgb(const glm::vec3 c) { glm::vec4 K = glm::vec4(1.0f, 2.0f / 3.0f, 1.0f / 3.0f, 3.0f); glm::vec3 p = glm::abs(glm::fract(glm::vec3(c.x) + xyz(K)) * 6.0f - glm::vec3(K.w)); auto tmp = glm::clamp(p - glm::vec3(K.x), 0.0f, 1.0f); return c.z * glm::mix(glm::vec3(K.x), tmp, c.y); } glm::vec3 convert_rgb2hsv(const glm::vec3 c) { glm::vec4 K = glm::vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); //glm::vec4 p = mix(glm::vec4(c.bg, K.wz), glm::vec4(c.gb, K.xy), glm::step(c.b, c.g)); //glm::vec4 q = mix(glm::vec4(p.xyw, c.r), glm::vec4(c.r, p.yzx), glm::step(p.x, c.r)); glm::vec4 p = c.g < c.b ? glm::vec4(c.b, c.g, K.w, K.z) : glm::vec4(c.g, c.b, K.x, K.y); glm::vec4 q = c.r < p.x ? glm::vec4(p.x, p.y, p.w, c.r) : glm::vec4(c.r, p.y, p.z, p.x); float d = q.x - glm::min(q.w, q.y); float e = 1.0e-10f; return glm::vec3(fabs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } std::vector split(const std::string& subject, char d, int max_split/* = 0*/) { std::vector ret; int start = 0; int n = subject.find_first_of(d); while (n != std::string::npos) { ret.push_back(subject.substr(start, n - start)); start = n + 1; if (max_split && ret.size() == max_split) break; n = subject.find_first_of(d, start); } ret.push_back(subject.substr(start)); return ret; } std::string unescape(const std::string& s) { std::string res; std::string::const_iterator it = s.begin(); while (it != s.end()) { char c = *it++; if (c == '\\' && it != s.end()) { switch (*it++) { case '\\': c = '\\'; break; case 'n': c = '\n'; break; case 't': c = '\t'; break; // all other escapes default: // invalid escape sequence - skip it. alternatively you can copy it as is, throw an exception... continue; } } res += c; } return res; } std::wstring str2wstr(const std::string& str) { mbstate_t st = {}; std::wstring converted; converted.resize(str.size()); const char* ptr = str.c_str(); std::mbsrtowcs((wchar_t*)converted.data(), &ptr, converted.capacity(), &st); return converted; } std::string wstr2str(const std::wstring & wstr) { mbstate_t st = {}; std::string converted; converted.resize(wstr.size()); const wchar_t * wptr = wstr.c_str(); std::wcsrtombs((char*)converted.data(), &wptr, converted.capacity(), &st); return converted; } static const char* gl2str(GLenum err) { switch (err) { case GL_NO_ERROR: return "GL_NO_ERROR"; case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; default: return "Unknown"; } } double now_seconds() { time_t timer; struct tm y2k = { 0 }; double seconds; y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; time(&timer); /* get current time; same as: timer = time(NULL) */ seconds = difftime(timer, mktime(&y2k)); return seconds; } void check_OpenGLError(const char* stmt, const char* fname, int line) { GLenum err; while ((err = glGetError()) != GL_NO_ERROR) { LOG("OpenGL error %08x (%s), at %s:%i - for %s", err, gl2str(err), fname, line, stmt); } } size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp) { auto buffer = reinterpret_cast(userp); buffer->append((char*)contents, size * nmemb); return size * nmemb; } size_t curl_data_write(void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t written = fwrite(ptr, size, nmemb, stream); return written; }