static int l_gif_new(lua_State *L) { const char *filename = luaL_checkstring(L, 1); int w = luaL_checknumber(L, 2); int h = luaL_checknumber(L, 3); int ncolors = luaL_optnumber(L, 4, 63); Gif *self = lua_newuserdata(L, sizeof(*self)); luaL_setmetatable(L, CLASS_NAME); memset(self, 0, sizeof(*self)); self->state = STATE_INIT; self->w = w; self->h = h; self->buf = malloc(w * h * 4); ASSERT(self->buf); memset(self->buf, 0, w * h * 4); /* Activate gif */ self->gif = jo_gif_start(filename, self->w, self->h, 0, ncolors); self->state = STATE_ACTIVE; return 1; }
void gui_tick(VideoMode mode, r32 gui_time, r32 gui_dt) { persist bool flag_DrawDroneGoto = true; persist bool flag_DrawDrone = true; persist bool flag_DrawVisibleRegion = true; persist bool flag_DrawTargets = true; persist bool flag_DrawObstacles = true; persist bool flag_Paused = false; persist bool flag_Recording = false; persist bool flag_SetupRecord = false; persist int record_from = 0; persist int record_to = 0; persist int record_frame_skip = 1; persist int record_width = 0; persist int record_height = 0; persist float record_region_x = -1.0f; persist float record_region_y = -1.0f; persist float record_region_scale = 2.0f; persist jo_gif_t record_gif; persist int seek_cursor = 0; persist int selected_target = -1; persist Color color_Clear = { 0.00f, 0.00f, 0.00f, 1.00f }; persist Color color_Tiles = { 0.20f, 0.35f, 0.46f, 0.66f }; persist Color color_Grid = { 0.00f, 0.00f, 0.00f, 1.00f }; persist Color color_VisibleRegion = { 0.87f, 0.93f, 0.84f, 0.50f }; persist Color color_GreenLine = { 0.10f, 1.00f, 0.20f, 1.00f }; persist Color color_SelectedTarget = { 0.85f, 0.34f, 0.32f, 1.00f }; persist Color color_Targets = { 0.85f, 0.83f, 0.37f, 1.00f }; persist Color color_Obstacles = { 0.43f, 0.76f, 0.79f, 1.00f }; persist Color color_Drone = { 0.87f, 0.93f, 0.84f, 0.50f }; persist Color color_DroneGoto = { 0.87f, 0.93f, 0.84f, 0.50f }; #define RGBA(C) C.r, C.g, C.b, C.a persist float send_timer = 0.0f; persist float send_interval = 1.0f; // In simulation time units NDC_SCALE_X = (mode.height / (r32)mode.width) / 12.0f; NDC_SCALE_Y = 1.0f / 12.0f; if (flag_Recording || flag_SetupRecord) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float Ax = 2.0f / record_region_scale; float Bx = -1.0f - Ax*record_region_x; float Ay = 2.0f / record_region_scale; float By = -1.0f - Ay*record_region_y; float modelview[] = { Ax, 0.0f, 0.0f, 0.0f, 0.0f, Ay, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, Bx, By, 0.0f, 1.0f }; glLoadMatrixf(modelview); } else { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } if (!flag_Paused) { if (flag_Recording) { if (seek_cursor >= record_to) { flag_Paused = true; flag_Recording = false; seek_cursor = record_from; jo_gif_end(&record_gif); } else if (seek_cursor + record_frame_skip >= record_to) { // clamp to end seek_cursor = record_to; } else { seek_cursor += record_frame_skip; } } else if (seek_cursor < HISTORY_LENGTH-1) { seek_cursor++; } else { sim_Command cmd; if (!sim_recv_cmd(&cmd)) { cmd.type = sim_CommandType_NoCommand; cmd.x = 0.0f; cmd.y = 0.0f; cmd.i = 0; } STATE = sim_tick(STATE, cmd); add_history(cmd, STATE); seek_cursor = HISTORY_LENGTH-1; send_timer -= Sim_Timestep; if (send_timer <= 0.0f) { sim_send_state(&STATE); send_timer += send_interval; } } } sim_State draw_state = HISTORY_STATE[seek_cursor]; sim_Drone drone = draw_state.drone; sim_Robot *robots = draw_state.robots; sim_Robot *targets = draw_state.robots; sim_Robot *obstacles = draw_state.robots + Num_Targets; if (flag_Recording || flag_SetupRecord) { glViewport(0, 0, record_width, record_height); } else { glViewport(0, 0, mode.width, mode.height); } glClearColor(RGBA(color_Clear)); glClear(GL_COLOR_BUFFER_BIT); glLineWidth(2.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // draw grid tiles glBegin(GL_TRIANGLES); { color4f(color_Tiles); for (int yi = 0; yi < 20; yi++) for (int xi = 0; xi < 20; xi++) { r32 x = xi*1.0f; r32 y = yi*1.0f; fill_square(x, y, x+1.0f, y+1.0f); } } glEnd(); glBegin(GL_LINES); { // draw grid lines color4f(color_Grid); for (int i = 0; i <= 20; i++) { r32 x = (r32)i; draw_line(x, 0.0f, x, 20.0f); draw_line(0.0f, x, 20.0f, x); } // draw visible region if (flag_DrawVisibleRegion) { color4f(color_VisibleRegion); draw_circle(drone.x, drone.y, 2.5f); } // draw green line color4f(color_GreenLine); draw_line(0.0f, 20.0f, 20.0f, 20.0f); // draw targets if (flag_DrawTargets) { color4f(color_Targets); for (int i = 0; i < Num_Targets; i++) draw_robot(targets[i]); if (selected_target >= 0) { color4f(color_SelectedTarget); draw_robot(targets[selected_target]); } } // draw obstacles if (flag_DrawObstacles) { color4f(color_Obstacles); for (int i = 0; i < Num_Obstacles; i++) draw_robot(obstacles[i]); } // draw drone if (flag_DrawDrone) { color4f(color_Drone); draw_line(drone.x - 0.5f, drone.y, drone.x + 0.5f, drone.y); draw_line(drone.x, drone.y - 0.5f, drone.x, drone.y + 0.5f); } // draw drone goto if (flag_DrawDroneGoto) { color4f(color_DroneGoto); draw_circle(drone.xr, drone.yr, 0.45f); } // draw indicators of magnet or bumper activations for (int i = 0; i < Num_Targets; i++) { r32 x = targets[i].x; r32 y = targets[i].y; if (targets[i].action.was_bumped) { glColor4f(1.0f, 0.3f, 0.1f, 1.0f); draw_circle(x, y, 0.5f); } else if (targets[i].action.was_top_touched) { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); draw_circle(x, y, 0.5f); } } } glEnd(); // TODO: Change capture res? if (flag_Recording) { static unsigned char capture_data[1024*1024*4]; Assert(sizeof(capture_data) >= record_width*record_height*4); glReadPixels(0, 0, record_width, record_height, GL_RGBA, GL_UNSIGNED_BYTE, capture_data); jo_gif_frame(&record_gif, capture_data, 2, false); } ImGui_ImplSdl_NewFrame(mode.window); // DRAW FLAGS if (ImGui::CollapsingHeader("Rendering")) { ImGui::Checkbox("Drone goto", &flag_DrawDroneGoto); ImGui::Checkbox("Drone", &flag_DrawDrone); ImGui::Checkbox("Visible region", &flag_DrawVisibleRegion); ImGui::Checkbox("Targets", &flag_DrawTargets); ImGui::Checkbox("Obstacles", &flag_DrawObstacles); } // END DRAW FLAGS // COLORS if (ImGui::CollapsingHeader("Colors")) { ImGui::ColorEdit4("Clear", &color_Clear.r); ImGui::ColorEdit4("Grid", &color_Grid.r); ImGui::ColorEdit4("VisibleRegion", &color_VisibleRegion.r); ImGui::ColorEdit4("GreenLine", &color_GreenLine.r); ImGui::ColorEdit4("Targets", &color_Targets.r); ImGui::ColorEdit4("Obstacles", &color_Obstacles.r); ImGui::ColorEdit4("Drone", &color_Drone.r); ImGui::ColorEdit4("DroneGoto", &color_DroneGoto.r); } // END COLORS // REWIND HISTORY if (ImGui::CollapsingHeader("Seek##header")) { ImGui::Checkbox("Paused", &flag_Paused); ImGui::SliderInt("Seek##bar", &seek_cursor, 0, HISTORY_LENGTH-1); ImGui::InputInt("Seek frame", &seek_cursor); if (seek_cursor < 0) seek_cursor = 0; if (seek_cursor > HISTORY_LENGTH-1) seek_cursor = HISTORY_LENGTH-1; ImGui::Text("Time: %.2f seconds", (seek_cursor+1) * Sim_Timestep); } // END REWIND HISTORY // ROBOTS if (ImGui::CollapsingHeader("Robots")) { ImGui::Columns(4, "RobotsColumns"); ImGui::Separator(); ImGui::Text("ID"); ImGui::NextColumn(); ImGui::Text("X"); ImGui::NextColumn(); ImGui::Text("Y"); ImGui::NextColumn(); ImGui::Text("Angle"); ImGui::NextColumn(); ImGui::Separator(); for (int i = 0; i < Num_Targets; i++) { char label[32]; sprintf(label, "%02d", i); if (ImGui::Selectable(label, selected_target == i, ImGuiSelectableFlags_SpanAllColumns)) selected_target = i; ImGui::NextColumn(); ImGui::Text("%.2f", robots[i].x); ImGui::NextColumn(); ImGui::Text("%.2f", robots[i].y); ImGui::NextColumn(); ImGui::Text("%.2f", robots[i].q); ImGui::NextColumn(); } ImGui::Columns(1); ImGui::Separator(); } else { selected_target = -1; } // END ROBOTS // COMMUNICATION if (ImGui::CollapsingHeader("Communication")) { ImGui::TextWrapped("The rate at which the state is " "sent can be changed using this slider. " "The slider value represents the time " "interval (in simulation time) " "between each send."); ImGui::SliderFloat("Send interval", &send_interval, Sim_Timestep, 1.0f); ImGui::Separator(); ImGui::Text("Last 10 non-trivial commands received:"); ImGui::Columns(5, "CommunicationColumns"); ImGui::Separator(); ImGui::Text("Time"); ImGui::NextColumn(); ImGui::Text("type"); ImGui::NextColumn(); ImGui::Text("x"); ImGui::NextColumn(); ImGui::Text("y"); ImGui::NextColumn(); ImGui::Text("i"); ImGui::NextColumn(); ImGui::Separator(); int count = 0; for (int i = 0; count < 10 && i <= seek_cursor; i++) { sim_State state_i = HISTORY_STATE[seek_cursor-i]; sim_Command cmd_i = HISTORY_CMD[seek_cursor-i]; if (cmd_i.type == sim_CommandType_NoCommand) continue; char label[32]; sprintf(label, "%.2f", state_i.elapsed_time); ImGui::Selectable(label, false, ImGuiSelectableFlags_SpanAllColumns); ImGui::NextColumn(); switch (cmd_i.type) { case sim_CommandType_NoCommand: { ImGui::Text("Nothing"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); } break; case sim_CommandType_LandInFrontOf: { ImGui::Text("Land 180"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("%d", cmd_i.i); ImGui::NextColumn(); } break; case sim_CommandType_LandOnTopOf: { ImGui::Text("Land 45"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("%d", cmd_i.i); ImGui::NextColumn(); } break; case sim_CommandType_Track: { ImGui::Text("Track"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); ImGui::Text("%d", cmd_i.i); ImGui::NextColumn(); } break; case sim_CommandType_Search: { ImGui::Text("Search"); ImGui::NextColumn(); ImGui::Text("%.2f", cmd_i.x); ImGui::NextColumn(); ImGui::Text("%.2f", cmd_i.y); ImGui::NextColumn(); ImGui::Text("-"); ImGui::NextColumn(); } break; } count++; } ImGui::Columns(1); ImGui::Separator(); } // END COMMUNICATION // RECORDING GIFS if (ImGui::CollapsingHeader("Recording")) { flag_SetupRecord = true; if (ImGui::Button("Mark frame as begin")) { record_from = seek_cursor; } ImGui::SameLine(); ImGui::Text("Record from: %d", record_from); if (ImGui::Button("Mark frame as end")) { record_to = seek_cursor; } ImGui::SameLine(); ImGui::Text("Record to: %d", record_to); ImGui::InputInt("Frame skip", &record_frame_skip); ImGui::InputInt("Record width", &record_width); ImGui::InputInt("Record height", &record_height); if (record_width <= 0) record_width = mode.width; if (record_height <= 0) record_height = mode.height; ImGui::SliderFloat("Record x", &record_region_x, -1.0f, 1.0f); ImGui::SliderFloat("Record y", &record_region_y, -1.0f, 1.0f); ImGui::SliderFloat("Record scale", &record_region_scale, 0.0f, 2.0f); if (ImGui::Button("Start recording") && !flag_Recording) ImGui::OpenPopup("Save recording as?"); if (ImGui::BeginPopupModal("Save recording as?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { persist char filename[1024]; persist bool init_filename = true; if (init_filename) { sprintf(filename, "recording%u.gif", STATE.seed); init_filename = false; } ImGui::InputText("Filename", filename, sizeof(filename)); ImGui::Separator(); if (ImGui::Button("OK", ImVec2(120,0))) { flag_Recording = true; flag_Paused = false; seek_cursor = record_from-1; record_gif = jo_gif_start(filename, (short)record_width, (short)record_height, 0, 32); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } if (ImGui::Button("Stop recording") && flag_Recording) { flag_Recording = false; flag_Paused = true; jo_gif_end(&record_gif); } if (record_from < 0) record_from = 0; if (record_from > HISTORY_LENGTH-1) record_from = HISTORY_LENGTH-1; if (record_to < record_from) record_to = record_from; if (record_to > HISTORY_LENGTH-1) record_to = HISTORY_LENGTH-1; if (record_frame_skip < 1) record_frame_skip = 1; ImGui::Separator(); } else { flag_SetupRecord = false; } // END RECORDING GIFS // RESET AND SET SEED persist int custom_seed = 0; if (ImGui::Button("Reset")) { if (custom_seed > 0) STATE = sim_init((u32)custom_seed); else STATE = sim_init((u32)get_tick()); HISTORY_LENGTH = 0; sim_Command cmd; cmd.type = sim_CommandType_NoCommand; add_history(cmd, STATE); } ImGui::SameLine(); ImGui::InputInt("Seed", &custom_seed); // END RESET AND SET SEED // SAVE SIMULATION if (ImGui::Button("Save..")) ImGui::OpenPopup("Save as?"); if (ImGui::BeginPopupModal("Save as?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { persist char filename[1024]; persist bool init_filename = true; if (init_filename) { sprintf(filename, "simulation%u", STATE.seed); init_filename = false; } ImGui::InputText("Filename", filename, sizeof(filename)); ImGui::Separator(); if (ImGui::Button("OK", ImVec2(120,0))) { write_history(filename); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } // END SAVE SIMULATION ImGui::SameLine(); // SAVE SINGLE SNAPSHOT persist bool init_snapshot_filename = true; if (ImGui::Button("Save snapshot..")) ImGui::OpenPopup("Save snapshot as?"); if (ImGui::BeginPopupModal("Save snapshot as?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { persist char filename[1024]; ImGui::TextWrapped("The filename is relative to the executable," "unless you write an absolute path."); if (init_snapshot_filename) { sprintf(filename, "snapshot%u-%u", STATE.seed, seek_cursor); init_snapshot_filename = false; } ImGui::InputText("Filename", filename, sizeof(filename)); ImGui::Separator(); if (ImGui::Button("OK", ImVec2(120,0))) { sim_Observed_State snapshot = sim_observe_state(HISTORY_STATE[seek_cursor]); printf("%.2f\n", snapshot.obstacle_q[0]); sim_write_snapshot(filename, snapshot); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } else { init_snapshot_filename = true; } // END SAVE SIMULATION ImGui::SameLine(); // LOAD SIMULATION if (ImGui::Button("Load..")) ImGui::OpenPopup("Load file?"); if (ImGui::BeginPopupModal("Load file?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { persist char filename[1024]; persist bool init_filename = true; if (init_filename) { sprintf(filename, "simulation%u", STATE.seed); init_filename = false; } ImGui::InputText("Filename", filename, sizeof(filename)); ImGui::Separator(); if (ImGui::Button("OK", ImVec2(120,0))) { read_history(filename); seek_cursor = 0; flag_Paused = true; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } // END LOAD SIMULATION ImGui::Render(); } // END gui_tick