Files
academy2/modules/morpher/triangle.h
2021-07-31 03:37:28 +03:00

258 lines
8.4 KiB
C++

#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <core/bind/core_bind.h>
#include <core/os/file_access.h>
#include <core/reference.h>
#include <core/resource.h>
#include <scene/resources/mesh.h>
#include <cassert>
class TriangleSet : public Reference {
GDCLASS(TriangleSet, Reference)
protected:
PoolVector<Vector3> vertices;
PoolVector<Vector3> normals;
PoolVector<Vector2> uvs1;
PoolVector<Vector2> uvs2;
PoolVector<int> indices;
static void _bind_methods();
float minp[3];
float maxp[3];
float cd[3];
float minn[3];
float maxn[3];
float cdn[3];
int minx, miny, maxx, maxy;
PoolVector<uint8_t> get_data(const Ref<Image> &vimage, const Ref<Image> &nimage) {
int i, j, width = vimage->get_width(), height = vimage->get_height();
const PoolVector<uint8_t> &vdata = vimage->get_data(), &ndata = nimage->get_data();
assert(vdata.size() == ndata.size());
uint32_t *h_table = memnew_arr(uint32_t, height + 1);
h_table[0] = (uint32_t)height;
uint64_t *src_buffer = memnew_arr(uint64_t, width);
uint8_t *dst_buffer = memnew_arr(uint8_t, width * 9);
PoolVector<uint8_t> ret;
if (minx == maxx || miny == maxy)
return ret;
/* map of height offsets */
ret.resize(ret.size() + height * 4 + 4);
const uint32_t *pxdata = (const uint32_t *)vdata.read().ptr();
const uint32_t *npxdata = (const uint32_t *)vdata.read().ptr();
int out_count = ret.size();
for (i = miny; i < maxy + 1; i++) {
h_table[i + 1] = out_count;
for (j = minx; j < maxx + 1; j++) {
uint64_t data_l = (pxdata[i * width + j] & 0xFFFFFFU);
uint64_t data_h = (npxdata[i * width + j] & 0xFFFFFFU);
src_buffer[j] = (data_l | (data_h << 24)) & 0xFFFFFFFFFFFFULL;
}
uint8_t count = 1;
uint64_t cur = src_buffer[0];
int dst_count = 0;
for (j = minx + 1; j < maxx + 1; j++) {
if (cur == src_buffer[j] && count < 255 && j < maxx)
count++;
else {
int k;
for (k = 0; k < 6; k++) {
dst_buffer[dst_count++] = (uint8_t)(cur & 0xff);
cur >>= 8;
}
dst_buffer[dst_count++] = count;
cur = src_buffer[j];
count = 1;
}
}
if (ret.size() < out_count + dst_count)
ret.resize(out_count + dst_count * 2);
uint8_t *dptr = ret.write().ptr();
memcpy(&dptr[out_count], dst_buffer, dst_count);
out_count += dst_count;
}
uint32_t *table_p = (uint32_t *)ret.read().ptr();
memcpy(table_p, h_table, (height + 1) * 4);
return ret;
}
static inline void draw_hline(Image *image, const float *v1, const float *v2) {
if (v1[0] < 0 && v2[0] < 0)
return;
if (v1[0] >= v2[0])
return;
if (v1[0] >= image->get_width())
return;
float l = (v2[0] - v1[0]);
Color c;
for (int i = MAX(0, (int)v1[0] - 1); i <= MIN(image->get_width() - 1, (int)v2[0] + 1); i++) {
float t = ((float)i - v1[0] + 1) / (l + 2.0f);
t = CLAMP(t, 0.0f, 1.0f);
c.r = Math::lerp(v1[2], v2[2], t);
c.g = Math::lerp(v1[3], v2[3], t);
c.b = Math::lerp(v1[4], v2[4], t);
image->set_pixel(i, v1[1], c);
}
}
static inline void flat_bottom_triangle(Image *image,
const float *v1, const float *v2, const float *v3) {
if ((v2[1] - v1[1]) < 1.0)
return;
double bdiv = (v2[1] - v1[1]);
for (int scanlineY = v1[1]; scanlineY <= v2[1]; scanlineY++) {
float t = ((double)((double)scanlineY - v1[1])) / bdiv;
t = CLAMP(t, 0.0f, 1.0f);
if (scanlineY < 0 || scanlineY >= image->get_height())
continue;
float cx1[5], cx2[5];
cx1[0] = Math::lerp(v1[0], v2[0], t);
cx1[1] = scanlineY;
cx1[2] = Math::lerp(v1[2], v2[2], t);
cx1[3] = Math::lerp(v1[3], v2[3], t);
cx1[4] = Math::lerp(v1[4], v2[4], t);
cx2[0] = Math::lerp(v1[0], v3[0], t);
cx2[1] = scanlineY;
cx2[2] = Math::lerp(v1[2], v3[2], t);
cx2[3] = Math::lerp(v1[3], v3[3], t);
cx2[4] = Math::lerp(v1[4], v3[4], t);
draw_hline(image, cx1, cx2);
}
}
static inline void flat_top_triangle(Image *image,
const float *v1, const float *v2, const float *v3) {
if ((v3[1] - v1[1]) < 1.0)
return;
double bdiv = (v3[1] - v1[1]);
for (int scanlineY = v3[1]; scanlineY > v1[1] - 1; scanlineY--) {
float t = (double)(v3[1] - (double)scanlineY) / bdiv;
t = CLAMP(t, 0.0f, 1.0f);
if (scanlineY < 0 || scanlineY >= image->get_height())
continue;
float cx1[5], cx2[5];
cx1[0] = Math::lerp(v3[0], v1[0], t);
cx1[1] = scanlineY;
cx1[2] = Math::lerp(v3[2], v1[2], t);
cx1[3] = Math::lerp(v3[3], v1[3], t);
cx1[4] = Math::lerp(v3[4], v1[4], t);
cx2[0] = Math::lerp(v3[0], v2[0], t);
cx2[1] = scanlineY;
cx2[2] = Math::lerp(v3[2], v2[2], t);
cx2[3] = Math::lerp(v3[3], v2[3], t);
cx2[4] = Math::lerp(v3[4], v2[4], t);
draw_hline(image, cx1, cx2);
}
}
inline float distance_squared(const float *v1, const float *v2) {
return Vector2(v1[0], v1[1]).distance_squared_to(Vector2(v2[0], v2[1]));
}
inline void draw_triangle(Image *image,
const float *v1,
const float *v2,
const float *v3) {
if (v1[1] == v2[1] && v1[1] == v3[1])
return;
float d12 = distance_squared(v1, v2);
float d13 = distance_squared(v1, v3);
const float *points[] = { v1, v2, v3 };
int i, j;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (i == j)
continue;
if (points[i][1] < points[j][1])
SWAP(points[i], points[j]);
}
if (points[0][1] == points[1][1]) {
if (points[2][0] - points[0][0] < points[2][0] - points[1][0])
flat_top_triangle(image, points[1], points[0], points[2]);
else
flat_top_triangle(image, points[0], points[1], points[2]);
} else if (points[1][1] == points[2][1]) {
if (points[1][0] - points[0][0] > points[2][0] - points[0][0])
flat_bottom_triangle(image, points[0], points[2], points[1]);
else
flat_bottom_triangle(image, points[0], points[1], points[2]);
} else {
float y01 = points[1][1] - points[0][1];
float y02 = points[2][1] - points[0][1];
float p4[5];
// Vector2 p4(points[0][0] + (y01 / y02) * (points[2][0] - points[0][0]), points[1][1]);
float t = y01 / y02;
assert(t <= 1.0f && t >= 0.0f);
p4[0] = Math::lerp(points[0][0], points[2][0], t);
p4[1] = points[1][1];
p4[2] = Math::lerp(points[0][2], points[2][2], t);
p4[3] = Math::lerp(points[0][3], points[2][3], t);
p4[4] = Math::lerp(points[0][4], points[2][4], t);
if (points[1][0] - points[0][0] > p4[0] - points[0][0])
flat_bottom_triangle(image, points[0], p4, points[1]);
else
flat_bottom_triangle(image, points[0], points[1], p4);
if (points[2][0] - points[1][0] < points[2][0] - p4[0])
flat_top_triangle(image, p4, points[1], points[2]);
else
flat_top_triangle(image, points[1], p4, points[2]);
}
}
public:
TriangleSet() {
}
~TriangleSet() {
}
void normalize_deltas();
static inline float get_area(Vector3 p1, Vector3 p2, Vector3 p3) {
return (p2 - p1).cross(p3 - p1).length() / 2.0;
}
static inline Vector3 get_baricentric(Vector3 pt, Vector3 p1, Vector3 p2, Vector3 p3) {
Vector3 p;
float area = get_area(p1, p2, p3);
if (area == 0.0) {
printf("bad triangle %ls %ls %ls\n", String(p1).c_str(), String(p2).c_str(), String(p3).c_str());
return Vector3(-1, -1, -1);
}
Vector3 n = (p2 - p1).cross(p3 - p1);
float d = n.dot(p1);
float denom = n.dot(n);
if (denom != 0.0) {
float t = (-n.dot(pt) + d) / denom;
p = pt + n * t;
} else
p = pt;
float c = get_area(p2, p3, p);
float u = c / area;
float e = get_area(p3, p1, p);
float v = e / area;
float w = 1.0f - u - v;
if (!(u < 0 || v < 0 || w < 0))
printf("%f %f %f %f %f %f %ls %f %f %f\n", u, v, w, d, denom, n.length(), String(p).c_str(), c, e, area);
return Vector3(u, v, w);
}
/* Same topology */
void create_from_array_shape(const Array &arrays_base, const Array &arrays_shape);
/* Close but not the same topology */
void create_from_array_difference(const Array &arrays_base, int uv_index1, const Array &arrays_shape, int uv_index2);
inline void update_bounds(float x, float y) {
if (minx > (int)x)
minx = (int)x;
if (miny > (int)y)
miny = (int)y;
if (maxx < ceil(x))
maxx = (int)(ceil(x));
if (maxy < ceil(y))
maxy = (int)(ceil(y));
}
void draw(Ref<Image> vimage, Ref<Image> nimage, int uv_index);
Vector3 get_min() {
return Vector3(minp[0], minp[1], minp[2]);
}
Vector3 get_max() {
return Vector3(maxp[0], maxp[1], maxp[2]);
}
Vector3 get_min_normal() {
return Vector3(minn[0], minn[1], minn[2]);
}
Vector3 get_max_normal() {
return Vector3(maxn[0], maxn[1], maxn[2]);
}
void save(Ref<_File> fd, const String &shape_name, Ref<Image> vimage, Ref<Image> nimage);
};
#endif