|
|
|
@ -55,15 +55,49 @@ VkResult set_vertex_height(float height, uint32_t region, uint32_t hex, uint32_t
|
|
|
|
&context->render);
|
|
|
|
&context->render);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// vertex is 1-6 (the hex's 6 outer corners - matches height[]'s indexing).
|
|
|
|
VkResult set_vertex_color(vec4 color, uint32_t region, uint32_t hex, uint32_t vertex, ClientContext* context) {
|
|
|
|
VkResult set_vertex_color(vec4 color, uint32_t region, uint32_t hex, uint32_t vertex, ClientContext* context) {
|
|
|
|
context->hex.regions[region]->data.hexes[hex].color[vertex] = hex_color(color);
|
|
|
|
context->hex.regions[region]->data.hexes[hex].outer_colors[vertex-1] = hex_color(color);
|
|
|
|
return add_transfer(
|
|
|
|
return add_transfer(
|
|
|
|
&context->hex.regions[region]->data.hexes[hex].color[vertex],
|
|
|
|
&context->hex.regions[region]->data.hexes[hex].outer_colors[vertex-1],
|
|
|
|
context->hex.regions[region]->region,
|
|
|
|
context->hex.regions[region]->region,
|
|
|
|
offsetof(GPUHexRegion, hexes)
|
|
|
|
offsetof(GPUHexRegion, hexes)
|
|
|
|
+ sizeof(GPUHex)*hex
|
|
|
|
+ sizeof(GPUHex)*hex
|
|
|
|
+ offsetof(GPUHex, color)
|
|
|
|
+ offsetof(GPUHex, outer_colors)
|
|
|
|
+ sizeof(uint32_t)*vertex,
|
|
|
|
+ sizeof(uint32_t)*(vertex-1),
|
|
|
|
|
|
|
|
sizeof(uint32_t),
|
|
|
|
|
|
|
|
context->render.current_frame,
|
|
|
|
|
|
|
|
&context->render);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// triangle is 0-5 (one of the hex's 6 wedges - see hex.vert's `wedge`).
|
|
|
|
|
|
|
|
VkResult set_hex_triangle_color(vec4 color, uint32_t region, uint32_t hex, uint32_t triangle, ClientContext* context) {
|
|
|
|
|
|
|
|
context->hex.regions[region]->data.hexes[hex].inner_colors[triangle] = hex_color(color);
|
|
|
|
|
|
|
|
return add_transfer(
|
|
|
|
|
|
|
|
&context->hex.regions[region]->data.hexes[hex].inner_colors[triangle],
|
|
|
|
|
|
|
|
context->hex.regions[region]->region,
|
|
|
|
|
|
|
|
offsetof(GPUHexRegion, hexes)
|
|
|
|
|
|
|
|
+ sizeof(GPUHex)*hex
|
|
|
|
|
|
|
|
+ offsetof(GPUHex, inner_colors)
|
|
|
|
|
|
|
|
+ sizeof(uint32_t)*triangle,
|
|
|
|
|
|
|
|
sizeof(uint32_t),
|
|
|
|
|
|
|
|
context->render.current_frame,
|
|
|
|
|
|
|
|
&context->render);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sets (rather than toggles) wedge `triangle`'s flat bit, so callers that
|
|
|
|
|
|
|
|
// already know the target state (editor_selection_toggle_flat) don't need
|
|
|
|
|
|
|
|
// a read-modify-write of their own on top of this one.
|
|
|
|
|
|
|
|
VkResult set_hex_flat(bool flat, uint32_t region, uint32_t hex, uint32_t triangle, ClientContext* context) {
|
|
|
|
|
|
|
|
uint32_t* mask = &context->hex.regions[region]->data.hexes[hex].flat_mask;
|
|
|
|
|
|
|
|
if(flat) *mask |= (1u << triangle);
|
|
|
|
|
|
|
|
else *mask &= ~(1u << triangle);
|
|
|
|
|
|
|
|
return add_transfer(
|
|
|
|
|
|
|
|
mask,
|
|
|
|
|
|
|
|
context->hex.regions[region]->region,
|
|
|
|
|
|
|
|
offsetof(GPUHexRegion, hexes)
|
|
|
|
|
|
|
|
+ sizeof(GPUHex)*hex
|
|
|
|
|
|
|
|
+ offsetof(GPUHex, flat_mask),
|
|
|
|
sizeof(uint32_t),
|
|
|
|
sizeof(uint32_t),
|
|
|
|
context->render.current_frame,
|
|
|
|
context->render.current_frame,
|
|
|
|
&context->render);
|
|
|
|
&context->render);
|
|
|
|
@ -75,13 +109,19 @@ uint32_t add_hex_region(ClientContext* context) {
|
|
|
|
allocate_hex_region(0, 0, 0, context->hex.data.current_map, ®ion, &context->hex, &context->render);
|
|
|
|
allocate_hex_region(0, 0, 0, context->hex.data.current_map, ®ion, &context->hex, &context->render);
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t hex = 0; hex < REGION_HEX_COUNT; hex++) {
|
|
|
|
for(uint32_t hex = 0; hex < REGION_HEX_COUNT; hex++) {
|
|
|
|
region->data.hexes[hex].color[0] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[0] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[1] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[1] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[2] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[2] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[3] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[3] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[4] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[4] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[5] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].inner_colors[5] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].color[6] = 0xFFFFFFFF;
|
|
|
|
region->data.hexes[hex].outer_colors[0] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].outer_colors[1] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].outer_colors[2] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].outer_colors[3] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].outer_colors[4] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].outer_colors[5] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
region->data.hexes[hex].flat_mask = 0;
|
|
|
|
|
|
|
|
|
|
|
|
region->data.hexes[hex].height[0] = 0;
|
|
|
|
region->data.hexes[hex].height[0] = 0;
|
|
|
|
region->data.hexes[hex].height[1] = 0;
|
|
|
|
region->data.hexes[hex].height[1] = 0;
|
|
|
|
@ -261,15 +301,6 @@ void resize_selected(EditorData* data, unsigned int size) {
|
|
|
|
data->selected_max = size;
|
|
|
|
data->selected_max = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t find_selected_hex(EditorData* data, uint32_t region, uint32_t hex) {
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
|
|
|
|
if(data->selected_regions[i] == region && data->selected_hexes[i] == hex) {
|
|
|
|
|
|
|
|
return (int32_t)i;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int32_t find_selected_vertex(EditorData* data, uint32_t region, uint32_t hex, uint32_t vertex) {
|
|
|
|
int32_t find_selected_vertex(EditorData* data, uint32_t region, uint32_t hex, uint32_t vertex) {
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
if(data->selected_regions[i] == region
|
|
|
|
if(data->selected_regions[i] == region
|
|
|
|
@ -301,23 +332,129 @@ void remove_selected_at(EditorData* data, uint32_t index) {
|
|
|
|
data->selected_count--;
|
|
|
|
data->selected_count--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool editor_pick(ClientContext* context, double cursor[2], uint32_t* rid, uint32_t* hid, uint32_t* vertex, bool edge_only) {
|
|
|
|
// Resolves (region, hex, vertex) plus up to 2 other hexes sharing that same
|
|
|
|
|
|
|
|
// world corner (a hex corner touches up to 3 hexes): hex_qr/region_qr/
|
|
|
|
|
|
|
|
// hex_add build the hex's absolute world coordinate, hex_vertex_neighbors
|
|
|
|
|
|
|
|
// finds the corner's other hexes in world coordinates, and
|
|
|
|
|
|
|
|
// first_matching_region resolves each back to a loaded region index (a
|
|
|
|
|
|
|
|
// neighbor that isn't loaded is skipped). Writes up to 3 entries (self
|
|
|
|
|
|
|
|
// first, then any loaded neighbors) into the out arrays and returns the
|
|
|
|
|
|
|
|
// count. Only meaningful for real vertex indices (1-6) - Hex-mode wedge
|
|
|
|
|
|
|
|
// selections are hex-local, so nothing calls this for those.
|
|
|
|
|
|
|
|
static uint32_t resolve_vertex_group(
|
|
|
|
|
|
|
|
ClientContext* context, uint32_t region, uint32_t hex, uint32_t vertex,
|
|
|
|
|
|
|
|
uint32_t out_region[3], uint32_t out_hex[3], uint32_t out_vertex[3]) {
|
|
|
|
|
|
|
|
out_region[0] = region;
|
|
|
|
|
|
|
|
out_hex[0] = hex;
|
|
|
|
|
|
|
|
out_vertex[0] = vertex;
|
|
|
|
|
|
|
|
uint32_t count = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HexRegion* r = context->hex.regions[region];
|
|
|
|
|
|
|
|
HexCoord world;
|
|
|
|
|
|
|
|
region_qr(r->data.position, &world);
|
|
|
|
|
|
|
|
HexCoord hex_local;
|
|
|
|
|
|
|
|
hex_qr(hex, &hex_local);
|
|
|
|
|
|
|
|
hex_add(hex_local, &world);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t n_vertex[2];
|
|
|
|
|
|
|
|
HexCoord n_region_coord[2];
|
|
|
|
|
|
|
|
uint32_t n_hex[2];
|
|
|
|
|
|
|
|
hex_vertex_neighbors(vertex, world, n_vertex, n_region_coord, n_hex);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < 2; i++) {
|
|
|
|
|
|
|
|
uint32_t actual_region;
|
|
|
|
|
|
|
|
first_matching_region(n_region_coord[i], r->data.y, &actual_region, &context->hex);
|
|
|
|
|
|
|
|
if(actual_region == MAX_LOADED_REGIONS) continue;
|
|
|
|
|
|
|
|
out_region[count] = actual_region;
|
|
|
|
|
|
|
|
out_hex[count] = n_hex[i];
|
|
|
|
|
|
|
|
out_vertex[count] = n_vertex[i];
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// editor.set_color (editor_lua.c) - Vertex mode paints the selected corner
|
|
|
|
|
|
|
|
// and any loaded neighbors sharing it (so adjacent hexes stay seamless);
|
|
|
|
|
|
|
|
// Hex mode paints just the selected wedge's own inner color.
|
|
|
|
|
|
|
|
void editor_selection_set_color(ClientContext* context, vec4 color) {
|
|
|
|
|
|
|
|
EditorData* data = context->app_data;
|
|
|
|
|
|
|
|
EditorMode mode = current_mode(context);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
|
|
|
|
if(mode == MODE_VERTEX) {
|
|
|
|
|
|
|
|
uint32_t g_region[3], g_hex[3], g_vertex[3];
|
|
|
|
|
|
|
|
uint32_t count = resolve_vertex_group(
|
|
|
|
|
|
|
|
context, data->selected_regions[i], data->selected_hexes[i], data->selected_vertices[i],
|
|
|
|
|
|
|
|
g_region, g_hex, g_vertex);
|
|
|
|
|
|
|
|
for(uint32_t g = 0; g < count; g++) {
|
|
|
|
|
|
|
|
set_vertex_color(color, g_region[g], g_hex[g], g_vertex[g], context);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if(mode == MODE_HEX) {
|
|
|
|
|
|
|
|
set_hex_triangle_color(color, data->selected_regions[i], data->selected_hexes[i], data->selected_vertices[i], context);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// editor.nudge_height (editor_lua.c) - Vertex mode only; a wedge index
|
|
|
|
|
|
|
|
// (Hex mode) isn't a valid height-vertex index, so this no-ops there.
|
|
|
|
|
|
|
|
void editor_selection_nudge_height(ClientContext* context, float delta) {
|
|
|
|
|
|
|
|
EditorData* data = context->app_data;
|
|
|
|
|
|
|
|
if(current_mode(context) != MODE_VERTEX) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
|
|
|
|
uint32_t g_region[3], g_hex[3], g_vertex[3];
|
|
|
|
|
|
|
|
uint32_t count = resolve_vertex_group(
|
|
|
|
|
|
|
|
context, data->selected_regions[i], data->selected_hexes[i], data->selected_vertices[i],
|
|
|
|
|
|
|
|
g_region, g_hex, g_vertex);
|
|
|
|
|
|
|
|
for(uint32_t g = 0; g < count; g++) {
|
|
|
|
|
|
|
|
float height = context->hex.regions[g_region[g]]->data.hexes[g_hex[g]].height[g_vertex[g]-1];
|
|
|
|
|
|
|
|
set_vertex_height(height + delta, g_region[g], g_hex[g], g_vertex[g], context);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// editor.toggle_flat (editor_lua.c) - Hex mode only; flips each selected
|
|
|
|
|
|
|
|
// wedge's own flat bit independently (no neighbor concept - inner colors
|
|
|
|
|
|
|
|
// and flat_mask are hex-local, unlike outer_colors/height).
|
|
|
|
|
|
|
|
void editor_selection_toggle_flat(ClientContext* context) {
|
|
|
|
|
|
|
|
EditorData* data = context->app_data;
|
|
|
|
|
|
|
|
if(current_mode(context) != MODE_HEX) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < data->selected_count; i++) {
|
|
|
|
|
|
|
|
uint32_t region = data->selected_regions[i];
|
|
|
|
|
|
|
|
uint32_t hex = data->selected_hexes[i];
|
|
|
|
|
|
|
|
uint32_t triangle = data->selected_vertices[i];
|
|
|
|
|
|
|
|
uint32_t mask = context->hex.regions[region]->data.hexes[hex].flat_mask;
|
|
|
|
|
|
|
|
bool currently_flat = (mask >> triangle) & 1u;
|
|
|
|
|
|
|
|
set_hex_flat(!currently_flat, region, hex, triangle, context);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool editor_pick(ClientContext* context, double cursor[2], uint32_t* rid, uint32_t* hid, uint32_t* vertex, uint32_t* triangle, bool edge_only) {
|
|
|
|
vec4 start, end;
|
|
|
|
vec4 start, end;
|
|
|
|
float distance;
|
|
|
|
float distance;
|
|
|
|
cursor_to_world_ray(&context->render, context->hex.inverse, cursor, start, end);
|
|
|
|
cursor_to_world_ray(&context->render, context->hex.inverse, cursor, start, end);
|
|
|
|
return ray_world_intersect(&distance, vertex, rid, hid, start, end, edge_only, &context->hex);
|
|
|
|
return ray_world_intersect(&distance, vertex, triangle, rid, hid, start, end, edge_only, &context->hex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Selection uses all highlight/point slots except the last, which is
|
|
|
|
// Selection uses all highlight/point slots except the last, which is
|
|
|
|
// reserved for the hover preview (see refresh_hover_visuals).
|
|
|
|
// reserved for the hover preview (see refresh_hover_visuals).
|
|
|
|
void sync_hex_highlights(EditorData* data, ClientContext* context) {
|
|
|
|
void sync_hex_highlights(EditorData* data, ClientContext* context) {
|
|
|
|
|
|
|
|
EditorMode mode = current_mode(context);
|
|
|
|
for(uint32_t i = 0; i < MAX_HIGHLIGHTS - 1; i++) {
|
|
|
|
for(uint32_t i = 0; i < MAX_HIGHLIGHTS - 1; i++) {
|
|
|
|
if(i < data->selected_count) {
|
|
|
|
if(i < data->selected_count) {
|
|
|
|
GPUHighlight temp = {
|
|
|
|
GPUHighlight temp = {
|
|
|
|
.color = {1.0f, 0.85f, 0.05f, 1.0f},
|
|
|
|
// 0.25 alpha (was 1.0) so a color applied under the selection
|
|
|
|
|
|
|
|
// overlay is actually visible while still selected.
|
|
|
|
|
|
|
|
.color = {1.0f, 0.85f, 0.05f, 0.25f},
|
|
|
|
.region = data->selected_regions[i],
|
|
|
|
.region = data->selected_regions[i],
|
|
|
|
.hex = data->selected_hexes[i],
|
|
|
|
.hex = data->selected_hexes[i],
|
|
|
|
.offset = SELECTION_HIGHLIGHT_OFFSET,
|
|
|
|
.offset = SELECTION_HIGHLIGHT_OFFSET,
|
|
|
|
|
|
|
|
// Hex mode selects one wedge at a time - highlight just that
|
|
|
|
|
|
|
|
// triangle so it's clear which one a color/flat edit will affect.
|
|
|
|
|
|
|
|
// Vertex mode keeps highlighting the whole hex as spatial context.
|
|
|
|
|
|
|
|
.triangle = (mode == MODE_HEX) ? data->selected_vertices[i] : 0xFFFFFFFF,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
add_transfers(&temp, context->hex.highlights, sizeof(GPUHighlight)*i, sizeof(GPUHighlight), &context->render);
|
|
|
|
add_transfers(&temp, context->hex.highlights, sizeof(GPUHighlight)*i, sizeof(GPUHighlight), &context->render);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@ -328,10 +465,14 @@ void sync_hex_highlights(EditorData* data, ClientContext* context) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sync_vertex_points(EditorData* data, ClientContext* context) {
|
|
|
|
void sync_vertex_points(EditorData* data, ClientContext* context) {
|
|
|
|
|
|
|
|
EditorMode mode = current_mode(context);
|
|
|
|
for(uint32_t i = 0; i < MAX_POINTS - 1; i++) {
|
|
|
|
for(uint32_t i = 0; i < MAX_POINTS - 1; i++) {
|
|
|
|
if(i < data->selected_count) {
|
|
|
|
// Hex-mode selections are wedges, not vertices - selected_vertices[i]
|
|
|
|
|
|
|
|
// isn't a real vertex index there, so there's nothing for a point
|
|
|
|
|
|
|
|
// marker to draw (the wedge highlight above is the only visual).
|
|
|
|
|
|
|
|
if(mode == MODE_VERTEX && i < data->selected_count) {
|
|
|
|
GPUPoint temp = {
|
|
|
|
GPUPoint temp = {
|
|
|
|
.color = {1.0f, 0.85f, 0.05f, 1.0f},
|
|
|
|
.color = {1.0f, 0.85f, 0.05f, 0.25f},
|
|
|
|
.region = data->selected_regions[i],
|
|
|
|
.region = data->selected_regions[i],
|
|
|
|
.hex = data->selected_hexes[i],
|
|
|
|
.hex = data->selected_hexes[i],
|
|
|
|
.vertex = data->selected_vertices[i],
|
|
|
|
.vertex = data->selected_vertices[i],
|
|
|
|
@ -352,19 +493,18 @@ void sync_vertex_points(EditorData* data, ClientContext* context) {
|
|
|
|
void refresh_hover_visuals(EditorData* data, ClientContext* context) {
|
|
|
|
void refresh_hover_visuals(EditorData* data, ClientContext* context) {
|
|
|
|
EditorMode mode = current_mode(context);
|
|
|
|
EditorMode mode = current_mode(context);
|
|
|
|
uint32_t highlight_slot = MAX_HIGHLIGHTS - 1;
|
|
|
|
uint32_t highlight_slot = MAX_HIGHLIGHTS - 1;
|
|
|
|
// In vertex mode the hex highlight is just spatial context for the
|
|
|
|
// would_remove and the hover highlight's shape (whole hex vs. one wedge)
|
|
|
|
// hovered vertex, so it mirrors the vertex's own add/remove color rather
|
|
|
|
// both key off the same mode-dependent hover_vertex meaning that
|
|
|
|
// than tracking hex selection (vertex mode doesn't select whole hexes).
|
|
|
|
// find_selected_vertex/sync_hex_highlights use.
|
|
|
|
if((mode == MODE_HEX || mode == MODE_VERTEX) && data->hover_valid) {
|
|
|
|
if((mode == MODE_HEX || mode == MODE_VERTEX) && data->hover_valid) {
|
|
|
|
bool would_remove = (mode == MODE_HEX)
|
|
|
|
bool would_remove = find_selected_vertex(data, data->hover_region, data->hover_hex, data->hover_vertex) != -1;
|
|
|
|
? find_selected_hex(data, data->hover_region, data->hover_hex) != -1
|
|
|
|
|
|
|
|
: find_selected_vertex(data, data->hover_region, data->hover_hex, data->hover_vertex) != -1;
|
|
|
|
|
|
|
|
GPUHighlight temp;
|
|
|
|
GPUHighlight temp;
|
|
|
|
if(would_remove) temp = (GPUHighlight){.color = HOVER_REMOVE_COLOR};
|
|
|
|
if(would_remove) temp = (GPUHighlight){.color = HOVER_REMOVE_COLOR};
|
|
|
|
else temp = (GPUHighlight){.color = HOVER_ADD_COLOR};
|
|
|
|
else temp = (GPUHighlight){.color = HOVER_ADD_COLOR};
|
|
|
|
temp.region = data->hover_region;
|
|
|
|
temp.region = data->hover_region;
|
|
|
|
temp.hex = data->hover_hex;
|
|
|
|
temp.hex = data->hover_hex;
|
|
|
|
temp.offset = HOVER_HIGHLIGHT_OFFSET;
|
|
|
|
temp.offset = HOVER_HIGHLIGHT_OFFSET;
|
|
|
|
|
|
|
|
temp.triangle = (mode == MODE_HEX) ? data->hover_vertex : 0xFFFFFFFF;
|
|
|
|
add_transfers(&temp, context->hex.highlights, sizeof(GPUHighlight)*highlight_slot, sizeof(GPUHighlight), &context->render);
|
|
|
|
add_transfers(&temp, context->hex.highlights, sizeof(GPUHighlight)*highlight_slot, sizeof(GPUHighlight), &context->render);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
uint32_t disabled = 0xFFFFFFFF;
|
|
|
|
uint32_t disabled = 0xFFFFFFFF;
|
|
|
|
@ -446,17 +586,18 @@ void editor_button_callback(ClientContext* context, float x, float y, int button
|
|
|
|
v = data->hover_vertex;
|
|
|
|
v = data->hover_vertex;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
double cursor[2] = {x, y};
|
|
|
|
double cursor[2] = {x, y};
|
|
|
|
uint32_t vertex;
|
|
|
|
uint32_t vertex, triangle;
|
|
|
|
// Vertex mode requests edge-only picking so a click always resolves to a
|
|
|
|
// Vertex mode requests edge-only picking so a click always resolves to a
|
|
|
|
// real, editable vertex instead of frequently landing on the hex center
|
|
|
|
// real, editable vertex instead of frequently landing on the hex center
|
|
|
|
// (vertex 0) and giving no feedback.
|
|
|
|
// (vertex 0) and giving no feedback.
|
|
|
|
if(!editor_pick(context, cursor, &rid, &hid, &vertex, mode == MODE_VERTEX)) return;
|
|
|
|
if(!editor_pick(context, cursor, &rid, &hid, &vertex, &triangle, mode == MODE_VERTEX)) return;
|
|
|
|
v = (mode == MODE_VERTEX) ? vertex : 0;
|
|
|
|
v = (mode == MODE_VERTEX) ? vertex : triangle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t idx = (mode == MODE_VERTEX)
|
|
|
|
// find_selected_vertex is a plain 3-tuple match despite its name - used
|
|
|
|
? find_selected_vertex(data, rid, hid, v)
|
|
|
|
// here for Hex mode too now that the third field is a wedge index, so two
|
|
|
|
: find_selected_hex(data, rid, hid);
|
|
|
|
// different wedges of the same hex are distinct selections.
|
|
|
|
|
|
|
|
int32_t idx = find_selected_vertex(data, rid, hid, v);
|
|
|
|
|
|
|
|
|
|
|
|
// Left click adds to the selection; Shift is reserved for camera movement
|
|
|
|
// Left click adds to the selection; Shift is reserved for camera movement
|
|
|
|
// (GLFW_KEY_LEFT_SHIFT) so it carries no selection meaning here. Ctrl
|
|
|
|
// (GLFW_KEY_LEFT_SHIFT) so it carries no selection meaning here. Ctrl
|
|
|
|
@ -483,15 +624,17 @@ void editor_cursor_callback(ClientContext* context, float x, float y) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double cursor[2] = {x, y};
|
|
|
|
double cursor[2] = {x, y};
|
|
|
|
uint32_t rid, hid, vertex;
|
|
|
|
uint32_t rid, hid, vertex, triangle;
|
|
|
|
bool hit = editor_pick(context, cursor, &rid, &hid, &vertex, mode == MODE_VERTEX);
|
|
|
|
bool hit = editor_pick(context, cursor, &rid, &hid, &vertex, &triangle, mode == MODE_VERTEX);
|
|
|
|
uint32_t v = (mode == MODE_VERTEX) ? vertex : 0;
|
|
|
|
uint32_t v = (mode == MODE_VERTEX) ? vertex : triangle;
|
|
|
|
|
|
|
|
|
|
|
|
// Skip the GPU write unless the hovered element actually changed; cursor
|
|
|
|
// Skip the GPU write unless the hovered element actually changed; cursor
|
|
|
|
// motion fires far more often than the hover target changes.
|
|
|
|
// motion fires far more often than the hover target changes. v is now a
|
|
|
|
|
|
|
|
// meaningful varying value in both modes (a real vertex in Vertex mode, a
|
|
|
|
|
|
|
|
// wedge in Hex mode), so it's always part of the comparison.
|
|
|
|
bool changed = hit != data->hover_valid
|
|
|
|
bool changed = hit != data->hover_valid
|
|
|
|
|| (hit && (data->hover_region != rid || data->hover_hex != hid
|
|
|
|
|| (hit && (data->hover_region != rid || data->hover_hex != hid
|
|
|
|
|| (mode == MODE_VERTEX && data->hover_vertex != v)));
|
|
|
|
|| data->hover_vertex != v));
|
|
|
|
if(!changed) return;
|
|
|
|
if(!changed) return;
|
|
|
|
|
|
|
|
|
|
|
|
data->hover_valid = hit;
|
|
|
|
data->hover_valid = hit;
|
|
|
|
@ -524,6 +667,8 @@ void editor_startup(ClientContext* context) {
|
|
|
|
ui_lua_run_script(context->ui.lua, &context->ui, &context->render, "script/editor_ui.lua");
|
|
|
|
ui_lua_run_script(context->ui.lua, &context->ui, &context->render, "script/editor_ui.lua");
|
|
|
|
// Input->state-machine trigger: writes editor.mode on V/N/H/R/Escape
|
|
|
|
// Input->state-machine trigger: writes editor.mode on V/N/H/R/Escape
|
|
|
|
ui_lua_run_script(context->ui.lua, &context->ui, &context->render, "script/editor_mode.lua");
|
|
|
|
ui_lua_run_script(context->ui.lua, &context->ui, &context->render, "script/editor_mode.lua");
|
|
|
|
|
|
|
|
// Height nudge (=/-) and flat toggle (F) for the current selection
|
|
|
|
|
|
|
|
ui_lua_run_script(context->ui.lua, &context->ui, &context->render, "script/editor_paint.lua");
|
|
|
|
|
|
|
|
|
|
|
|
// Main 3D view: a full-window container hosting the interactive camera,
|
|
|
|
// Main 3D view: a full-window container hosting the interactive camera,
|
|
|
|
// same as every other overlay - see container_set_camera (ui.h). Screen-
|
|
|
|
// same as every other overlay - see container_set_camera (ui.h). Screen-
|
|
|
|
|