/apps/tools/twndel: texture painting, face selection
This commit is contained in:
parent
b256fc903a
commit
0e075ec334
@ -29,6 +29,7 @@
|
|||||||
typedef struct Operation {
|
typedef struct Operation {
|
||||||
enum {
|
enum {
|
||||||
OPERATION_MOVE_POINT,
|
OPERATION_MOVE_POINT,
|
||||||
|
OPERATION_SET_TEXTURE,
|
||||||
} kind;
|
} kind;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
@ -38,6 +39,12 @@ typedef struct Operation {
|
|||||||
int16_t delta_z;
|
int16_t delta_z;
|
||||||
uint8_t object;
|
uint8_t object;
|
||||||
} move_point;
|
} move_point;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t face;
|
||||||
|
int16_t delta_texture;
|
||||||
|
uint8_t object;
|
||||||
|
} set_texture;
|
||||||
} data;
|
} data;
|
||||||
} Operation;
|
} Operation;
|
||||||
|
|
||||||
@ -61,7 +68,7 @@ typedef struct Object {
|
|||||||
char *name;
|
char *name;
|
||||||
bool is_invisible;
|
bool is_invisible;
|
||||||
Point position;
|
Point position;
|
||||||
char *textures[TEXTURE_LIMIT];
|
char *textures[TEXTURE_LIMIT + 1];
|
||||||
uint8_t textures_sz;
|
uint8_t textures_sz;
|
||||||
Point rotation;
|
Point rotation;
|
||||||
Face faces[FACE_LIMIT];
|
Face faces[FACE_LIMIT];
|
||||||
|
@ -58,8 +58,13 @@ static uint16_t push_face(uint8_t object,
|
|||||||
|
|
||||||
|
|
||||||
static uint8_t push_texture(uint8_t object, char *texture) {
|
static uint8_t push_texture(uint8_t object, char *texture) {
|
||||||
/* TODO: search and combine if it already exists */
|
|
||||||
Object *o = &state.objects[object];
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
|
/* check whether it's already here */
|
||||||
|
for (uint8_t i = 0; i < o->textures_sz; ++i)
|
||||||
|
if (SDL_strcmp(o->textures[i], texture) == 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
o->textures_sz++;
|
o->textures_sz++;
|
||||||
o->textures[o->textures_sz-1] = SDL_strdup(texture);
|
o->textures[o->textures_sz-1] = SDL_strdup(texture);
|
||||||
return o->textures_sz-1;
|
return o->textures_sz-1;
|
||||||
@ -114,7 +119,7 @@ static void render_object(uint8_t object) {
|
|||||||
Vec2 tul = project_texture_coordinate(to, n, p0, (float)f->tex_scale);
|
Vec2 tul = project_texture_coordinate(to, n, p0, (float)f->tex_scale);
|
||||||
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
||||||
|
|
||||||
draw_quad(state.current_texture,
|
draw_quad(o->textures[f->texture],
|
||||||
p0, p1, p2, p3,
|
p0, p1, p2, p3,
|
||||||
(Rect){tul.x, tdr.x, tdr.x - tul.x, tdr.y - tul.y},
|
(Rect){tul.x, tdr.x, tdr.x - tul.x, tdr.y - tul.y},
|
||||||
(Color){255,255,255,255} );
|
(Color){255,255,255,255} );
|
||||||
@ -138,7 +143,7 @@ static void render_object(uint8_t object) {
|
|||||||
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
||||||
|
|
||||||
if (state.solid_display_mode)
|
if (state.solid_display_mode)
|
||||||
draw_triangle(state.current_texture,
|
draw_triangle(o->textures[f->texture],
|
||||||
p0, p1, p2,
|
p0, p1, p2,
|
||||||
tul, tdl, tdr,
|
tul, tdl, tdr,
|
||||||
(Color){255,255,255,255},
|
(Color){255,255,255,255},
|
||||||
@ -337,6 +342,76 @@ static bool vector_plane_intersection(Vec3 o, Vec3 v, Vec3 p, Vec3 n, Vec3 *out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool find_closest_face(uint8_t* object_result, uint16_t *face_result) {
|
||||||
|
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
|
||||||
|
|
||||||
|
uint8_t closest_obj = INVALID_OBJECT;
|
||||||
|
uint16_t closest_face = INVALID_FACE;
|
||||||
|
float closest_distance = INFINITY;
|
||||||
|
|
||||||
|
for (uint8_t oi = 0; oi < state.objects_sz; ++oi) {
|
||||||
|
Object *o = &state.objects[oi];
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
|
||||||
|
if (f->p[1] == INVALID_POINT) continue;
|
||||||
|
|
||||||
|
Vec3 p0 = point_to_vec3(oi, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(oi, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(oi, f->p[2]);
|
||||||
|
|
||||||
|
Vec3 n = vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0));
|
||||||
|
|
||||||
|
/* culling */
|
||||||
|
if (vec3_dot(state.camera_direction, n) > 0) continue;
|
||||||
|
|
||||||
|
Vec3 i;
|
||||||
|
if (!vector_plane_intersection(cam.position, cam.direction, p0, n, &i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = vec3_length(vec3_sub(p0, i));
|
||||||
|
if (dist >= closest_distance) continue;
|
||||||
|
|
||||||
|
/* left normals are used to determine whether point lies to the left for all forming lines */
|
||||||
|
Vec3 ln0 = vec3_cross(n, vec3_sub(p1, p0));
|
||||||
|
if (vec3_dot(ln0, vec3_sub(p0, i)) > 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln1 = vec3_cross(n, vec3_sub(p2, p1));
|
||||||
|
if (vec3_dot(ln1, vec3_sub(p1, i)) > 0) continue;
|
||||||
|
|
||||||
|
if (f->p[3] == INVALID_POINT) {
|
||||||
|
/* triangle */
|
||||||
|
Vec3 ln2 = vec3_cross(n, vec3_sub(p0, p2));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) > 0) continue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* quad */
|
||||||
|
Vec3 p3 = point_to_vec3(oi, f->p[3]);
|
||||||
|
Vec3 ln2 = vec3_cross(n, vec3_sub(p3, p2));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) > 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln3 = vec3_cross(n, vec3_sub(p0, p3));
|
||||||
|
if (vec3_dot(ln3, vec3_sub(p3, i)) > 0) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
closest_distance = dist;
|
||||||
|
closest_face = fi;
|
||||||
|
closest_obj = oi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_face == INVALID_FACE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object_result)
|
||||||
|
*object_result = closest_obj;
|
||||||
|
if (face_result)
|
||||||
|
*face_result = closest_face;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void show_snap_lines(Vec3 p) {
|
static void show_snap_lines(Vec3 p) {
|
||||||
float step = 1.0f / ((float)POINTS_PER_METER / state.grid_snap_granularity);
|
float step = 1.0f / ((float)POINTS_PER_METER / state.grid_snap_granularity);
|
||||||
int const lines_per_side = (SNAP_LINES_SHOW - 1) / 2;
|
int const lines_per_side = (SNAP_LINES_SHOW - 1) / 2;
|
||||||
@ -448,6 +523,15 @@ static void reverse_operation_move_point(Operation *op) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_operation_set_texture(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_SET_TEXTURE);
|
||||||
|
Object *o = &state.objects[op->data.set_texture.object];
|
||||||
|
o->faces[op->data.set_texture.face].texture += op->data.set_texture.delta_texture;
|
||||||
|
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: reverse of this */
|
||||||
static void try_subdividing_from_moving(uint8_t object, uint16_t point) {
|
static void try_subdividing_from_moving(uint8_t object, uint16_t point) {
|
||||||
Object *o = &state.objects[object];
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
@ -487,7 +571,11 @@ static void process_operations(void) {
|
|||||||
switch (op->kind) {
|
switch (op->kind) {
|
||||||
case OPERATION_MOVE_POINT: {
|
case OPERATION_MOVE_POINT: {
|
||||||
reverse_operation_move_point(op);
|
reverse_operation_move_point(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE: {
|
||||||
|
reverse_operation_set_texture(op);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,20 +585,45 @@ static void process_operations(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!state.op_active) {
|
if (!state.op_active) {
|
||||||
uint16_t point_select; uint8_t obj_select;
|
/* point dragging */
|
||||||
if (find_closest_point(&obj_select, &point_select)) {
|
if (!state.solid_display_mode) {
|
||||||
draw_billboard("/data/point.png",
|
uint16_t point_select; uint8_t obj_select;
|
||||||
point_to_vec3(obj_select, point_select),
|
if (find_closest_point(&obj_select, &point_select)) {
|
||||||
(Vec2){0.05f, 0.05f},
|
draw_billboard("/data/point.png",
|
||||||
(Rect){0,0,16,16},
|
point_to_vec3(obj_select, point_select),
|
||||||
(Color){255,255,255,255},
|
(Vec2){0.05f, 0.05f},
|
||||||
false);
|
(Rect){0,0,16,16},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
if (input_action_just_pressed("select"))
|
if (input_action_just_pressed("select"))
|
||||||
push_operation((Operation){
|
push_operation((Operation){
|
||||||
.kind = OPERATION_MOVE_POINT,
|
.kind = OPERATION_MOVE_POINT,
|
||||||
.data = { .move_point = { .point = point_select, .object = obj_select } },
|
.data = { .move_point = { .point = point_select, .object = obj_select } },
|
||||||
}, true );
|
}, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* texture setting */
|
||||||
|
} else if (input_action_pressed("select")) {
|
||||||
|
uint8_t obj_select; uint16_t face_select;
|
||||||
|
if (find_closest_face(&obj_select, &face_select)) {
|
||||||
|
uint8_t new_tex = push_texture(obj_select, state.current_texture);
|
||||||
|
uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
|
||||||
|
|
||||||
|
if (new_tex != cur_tex) {
|
||||||
|
state.objects[obj_select].faces[face_select].texture = new_tex;
|
||||||
|
audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
|
||||||
|
|
||||||
|
push_operation((Operation){
|
||||||
|
.kind = OPERATION_SET_TEXTURE,
|
||||||
|
.data = { .set_texture = {
|
||||||
|
.face = face_select,
|
||||||
|
.object = obj_select,
|
||||||
|
.delta_texture = cur_tex - new_tex,
|
||||||
|
}},
|
||||||
|
}, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,6 +639,7 @@ static void process_operations(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE:
|
||||||
default:
|
default:
|
||||||
(void)0;
|
(void)0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user