/apps/tools/twndel: point selection, sounds and other assets
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,6 +18,8 @@
|
||||
#define INVALID_OBJECT (OBJECT_LIMIT+1)
|
||||
#define INVALID_TEXTURE (TEXTURE_LIMIT+1)
|
||||
|
||||
#define CAMERA_ROTATION_SPEED 0.04f
|
||||
#define SELECTION_SPHERE_RADIUS 32
|
||||
|
||||
typedef struct Operation {
|
||||
enum {
|
||||
@@ -40,7 +42,7 @@ typedef struct Point {
|
||||
/* lines have p2, p3 = INVALID_POINT */
|
||||
/* points have p1, p2, p3 = INVALID_POINT */
|
||||
typedef struct Face {
|
||||
uint16_t p0, p1, p2, p3;
|
||||
uint16_t p[4];
|
||||
uint8_t texture;
|
||||
} Face;
|
||||
|
||||
|
||||
+159
-26
@@ -54,6 +54,14 @@ static uint8_t push_texture(uint8_t object, char *texture) {
|
||||
}
|
||||
|
||||
|
||||
static inline Vec3 point_to_vec3(uint8_t object, uint16_t point) {
|
||||
Object *o = &state.objects[object];
|
||||
return (Vec3){ (o->position.x + state.points[point].x) / (float)POINTS_PER_METER,
|
||||
(o->position.y + state.points[point].y) / (float)POINTS_PER_METER,
|
||||
(o->position.z + state.points[point].z) / (float)POINTS_PER_METER };
|
||||
}
|
||||
|
||||
|
||||
static void render_object(uint8_t object) {
|
||||
Object *o = &state.objects[object];
|
||||
|
||||
@@ -63,24 +71,16 @@ static void render_object(uint8_t object) {
|
||||
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||
Face *f = &o->faces[fi];
|
||||
/* quads */
|
||||
if (f->p3 != INVALID_POINT) {
|
||||
Vec3 p0 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p0].x),
|
||||
(float)POINTS_PER_METER / (o->position.y + state.points[f->p0].y),
|
||||
(float)POINTS_PER_METER / (o->position.z + state.points[f->p0].z) };
|
||||
Vec3 p1 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p1].x),
|
||||
(float)POINTS_PER_METER / (o->position.y + state.points[f->p1].y),
|
||||
(float)POINTS_PER_METER / (o->position.z + state.points[f->p1].z) };
|
||||
Vec3 p2 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p2].x),
|
||||
(float)POINTS_PER_METER / (o->position.y + state.points[f->p2].y),
|
||||
(float)POINTS_PER_METER / (o->position.z + state.points[f->p2].z) };
|
||||
Vec3 p3 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p3].x),
|
||||
(float)POINTS_PER_METER / (o->position.y + state.points[f->p3].y),
|
||||
(float)POINTS_PER_METER / (o->position.z + state.points[f->p3].z) };
|
||||
if (f->p[3] != INVALID_POINT) {
|
||||
Vec3 p0 = point_to_vec3(object, f->p[0]);
|
||||
Vec3 p1 = point_to_vec3(object, f->p[1]);
|
||||
Vec3 p2 = point_to_vec3(object, f->p[2]);
|
||||
Vec3 p3 = point_to_vec3(object, f->p[3]);
|
||||
|
||||
if (state.solid_display_mode)
|
||||
draw_quad(o->textures[f->texture],
|
||||
p0, p1, p2, p3,
|
||||
(Rect){0},
|
||||
(Rect){0,0,32,32},
|
||||
(Color){255,255,255,255} );
|
||||
else {
|
||||
draw_line_3d(p0, p1, 1, (Color){255,255,255,255});
|
||||
@@ -109,36 +109,169 @@ static uint8_t new_cube(Point pos, Point size) {
|
||||
uint16_t p6 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
||||
uint16_t p7 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
||||
|
||||
uint8_t txt = push_texture(object, "/data/default.png");
|
||||
push_face(object, p0, p1, p2, p3, txt);
|
||||
push_face(object, p7, p6, p5, p4, txt);
|
||||
push_face(object, p4, p5, p1, p0, txt);
|
||||
push_face(object, p6, p7, p3, p2, txt);
|
||||
push_face(object, p2, p1, p5, p6, txt);
|
||||
push_face(object, p0, p3, p7, p4, txt);
|
||||
uint8_t tex = push_texture(object, "/data/placeholder.png");
|
||||
push_face(object, p0, p1, p2, p3, tex);
|
||||
push_face(object, p7, p6, p5, p4, tex);
|
||||
push_face(object, p4, p5, p1, p0, tex);
|
||||
push_face(object, p6, p7, p3, p2, tex);
|
||||
push_face(object, p2, p1, p5, p6, tex);
|
||||
push_face(object, p0, p3, p7, p4, tex);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
static void process_camera_movement(void) {
|
||||
input_action("camera_rotate_left", "A");
|
||||
input_action("camera_rotate_right", "D");
|
||||
input_action("camera_rotate_up", "W");
|
||||
input_action("camera_rotate_down", "S");
|
||||
|
||||
float horizontal_rotation = 0;
|
||||
float vertical_rotation = 0;
|
||||
|
||||
if (input_action_pressed("camera_rotate_left"))
|
||||
horizontal_rotation -= CAMERA_ROTATION_SPEED;
|
||||
if (input_action_pressed("camera_rotate_right"))
|
||||
horizontal_rotation += CAMERA_ROTATION_SPEED;
|
||||
if (input_action_pressed("camera_rotate_up"))
|
||||
vertical_rotation -= CAMERA_ROTATION_SPEED;
|
||||
if (input_action_pressed("camera_rotate_down"))
|
||||
vertical_rotation += CAMERA_ROTATION_SPEED;
|
||||
|
||||
Vec3 front = vec3_cross(state.camera_direction, (Vec3){0,1,0});
|
||||
|
||||
state.camera_position = vec3_rotate(state.camera_position, horizontal_rotation, (Vec3){0,1,0});
|
||||
state.camera_direction = vec3_rotate(state.camera_direction, horizontal_rotation, (Vec3){0,1,0});
|
||||
|
||||
Vec3 new_rot = vec3_rotate(state.camera_direction, vertical_rotation, front);
|
||||
|
||||
/* only apply if it's in limits */
|
||||
if (fabsf(vec3_dot(new_rot, (Vec3){0,-1,0})) <= 0.99f) {
|
||||
state.camera_position = vec3_rotate(state.camera_position, vertical_rotation, front);
|
||||
state.camera_direction = new_rot;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool find_closest_point(uint8_t* object_result, uint16_t *point_result) {
|
||||
DrawCameraUnprojectResult pos_and_ray = draw_camera_unproject(
|
||||
ctx.mouse_position,
|
||||
state.camera_position,
|
||||
state.camera_direction,
|
||||
(Vec3){0, 1, 0},
|
||||
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
||||
state.camera_zoom,
|
||||
100 );
|
||||
|
||||
/* step over every selectable object and find points closest to the view ray */
|
||||
/* by constructing triangles and finding their height, from perpendicular */
|
||||
uint16_t closest_point = INVALID_POINT;
|
||||
uint8_t closest_obj = INVALID_OBJECT;
|
||||
float closest_distance = INFINITY;
|
||||
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
||||
Object *o = &state.objects[obj];
|
||||
|
||||
if (o->is_invisible)
|
||||
continue;
|
||||
|
||||
/* TODO: is it possible to skip repeated points? does it matter? */
|
||||
/* as we limit the point could we could actually have bool array preallocated for this */
|
||||
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||
Face *f = &o->faces[fi];
|
||||
for (uint16_t pi = 0; pi < 4; ++pi) {
|
||||
Vec3 p = point_to_vec3(obj, f->p[pi]);
|
||||
Vec3 d = vec3_sub(pos_and_ray.position, p);
|
||||
Vec3 b = vec3_cross(d, pos_and_ray.direction);
|
||||
float ray_dist = vec3_length(b);
|
||||
if (ray_dist > ((float)SELECTION_SPHERE_RADIUS / POINTS_PER_METER))
|
||||
continue;
|
||||
Vec3 i = vec3_add(p, b);
|
||||
float dist = vec3_length(i);
|
||||
if (dist < closest_distance) {
|
||||
closest_distance = dist;
|
||||
closest_obj = obj;
|
||||
closest_point = f->p[pi];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_point == INVALID_POINT)
|
||||
return false;
|
||||
|
||||
if (object_result)
|
||||
*object_result = closest_obj;
|
||||
if (point_result)
|
||||
*point_result = closest_point;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void game_tick(void) {
|
||||
if (!init) {
|
||||
/* default state */
|
||||
new_cube((Point){0,0,0}, (Point){POINTS_PER_METER,POINTS_PER_METER,POINTS_PER_METER});
|
||||
state.camera_position = (Vec3){10,5,10};
|
||||
state.camera_direction = vec3_norm(((Vec3){-10,-5,-10}));
|
||||
state.camera_position = (Vec3){2,1,2};
|
||||
state.camera_direction = vec3_norm(((Vec3){-2,-1,-2}));
|
||||
state.camera_zoom = 1.0f;
|
||||
init = true;
|
||||
}
|
||||
|
||||
input_action("toggle_display_mode", "C");
|
||||
input_action("toggle_display_mode", "Q");
|
||||
input_action("toggle_projection", "E");
|
||||
|
||||
if (input_action_just_pressed("toggle_display_mode"))
|
||||
if (input_action_just_pressed("toggle_display_mode")) {
|
||||
audio_play("/data/click.wav", NULL, false, 0.7f, 0.0f);
|
||||
state.solid_display_mode = !state.solid_display_mode;
|
||||
}
|
||||
|
||||
if (input_action_just_pressed("toggle_projection")) {
|
||||
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||
state.camera_is_orthographic = !state.camera_is_orthographic;
|
||||
}
|
||||
|
||||
process_camera_movement();
|
||||
|
||||
uint16_t point_select; uint8_t obj_select;
|
||||
if (find_closest_point(&obj_select, &point_select))
|
||||
draw_billboard("/data/point.png",
|
||||
point_to_vec3(obj_select, point_select),
|
||||
(Vec2){0.05f, 0.05f},
|
||||
(Rect){0,0,16,16},
|
||||
(Color){255,255,255,255},
|
||||
false);
|
||||
|
||||
/* axis helpers */
|
||||
/* idea: black out inactives when dragging points */
|
||||
/* idea: double selection of axes for diagonal edits */
|
||||
draw_line_3d((Vec3){0}, (Vec3){0,0,256}, 1, (Color){255,0,0,125});
|
||||
draw_billboard("/data/x.png",
|
||||
(Vec3){0,0,2},
|
||||
(Vec2){0.1f, 0.1f},
|
||||
(Rect){0},
|
||||
(Color){255,0,0,255},
|
||||
false);
|
||||
draw_line_3d((Vec3){0}, (Vec3){256,0,0}, 1, (Color){0,0,255,125});
|
||||
draw_billboard("/data/y.png",
|
||||
(Vec3){2,0,0},
|
||||
(Vec2){0.1f, 0.1f},
|
||||
(Rect){0},
|
||||
(Color){0,0,255,255},
|
||||
false);
|
||||
draw_line_3d((Vec3){0}, (Vec3){0,256,0}, 1, (Color){75,125,25,125});
|
||||
draw_billboard("/data/z.png",
|
||||
(Vec3){0,1.5f,0},
|
||||
(Vec2){0.1f, 0.1f},
|
||||
(Rect){0},
|
||||
(Color){75,125,25,255},
|
||||
false);
|
||||
|
||||
for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
|
||||
render_object(obj);
|
||||
|
||||
draw_text("twndel\x03", (Vec2){0, 0}, 32, (Color){255,255,255,255}, NULL);
|
||||
draw_text("twndel\x03", (Vec2){0, 2}, 32, (Color){255,255,255,200}, NULL);
|
||||
draw_camera(
|
||||
state.camera_position,
|
||||
state.camera_direction,
|
||||
|
||||
Reference in New Issue
Block a user