#include "freespace.h" #include "asteroid/asteroid.h" #include "globalincs/vmallocator.h" #include "graphics/2d.h" #include "graphics/light.h" #include "graphics/matrix.h" #include "lab/labv2_internal.h" #include "lab/renderer/lab_renderer.h" #include "lighting/lighting_profiles.h" #include "math/bitarray.h" #include "model/modelrender.h" #include "nebula/neb.h" #include "parse/parselo.h" #include "particle/particle.h" #include "starfield/starfield.h" #include "starfield/nebula.h" #include "prop/prop.h" #include "missionui/missionscreencommon.h" #include "tracing/tracing.h" void LabRenderer::onFrame(float frametime) { GR_DEBUG_SCOPE("Lab Frame"); gr_reset_clip(); gr_clear(); if (!renderFlags[LabRenderFlag::TimeStopped]) Missiontime += Frametime; // render our particular thing if (getLabManager()->CurrentObject >= 0) { int w, h; renderModel(frametime); // print out the current pof filename, to help with... something if (strlen(getLabManager()->ModelFilename.c_str())) { SCP_string lab_text = "POF File: " + getLabManager()->ModelFilename + " Detail Level: " + std::to_string(Lab_object_detail_level); gr_get_string_size(&w, &h, lab_text.c_str()); gr_set_color_fast(&Color_white); gr_string(gr_screen.center_offset_x + gr_screen.center_w - w - 20, // add a little padding to the right gr_screen.center_offset_y + gr_screen.center_h - h, lab_text.c_str(), GR_RESIZE_NONE); } } renderHud(frametime); // Normally, we would call gr_flip here, but because wmcgui conflates rendering and input gathering, this is done at the end of // the LabManager::onFrame method } namespace ltp = lighting_profiles; void LabRenderer::resetGraphicsSettings(gfx_options settings) { ltp::lab_set_ambient(settings.ambient_factor); ltp::lab_set_emissive(settings.emissive_factor); ltp::lab_set_exposure(settings.exposure_level); ltp::lab_set_light(settings.light_factor); ltp::lab_set_ppc(settings.ppcv); ltp::lab_set_tonemapper(settings.tonemapper); gr_set_bloom_intensity(settings.bloom_level); Gr_aa_mode = settings.aa_mode; } void LabRenderer::resetView() { getLabManager()->CurrentOrientation = vmd_identity_matrix; labCamera->resetView(); } void LabRenderer::renderModel(float frametime) { GR_DEBUG_SCOPE("Lab Render Model"); auto lab_debris_override_save = Motion_debris_enabled; auto lab_envmap_override_save = Envmap_override; auto lab_emissive_light_save = Cmdline_emissive; light_reset(); if (currentMissionBackground == LAB_MISSION_NONE_STRING) { common_setup_room_lights(); } Cmdline_emissive = renderFlags[LabRenderFlag::ShowEmissiveLighting]; object* obj = &Objects[getLabManager()->CurrentObject]; // Ships && Objects stay put. Weapons are allowed to move so particle effects look correct if (obj->type == OBJ_SHIP || obj->type == OBJ_ASTEROID) { obj->pos = getLabManager()->CurrentPosition; } obj->orient = getLabManager()->CurrentOrientation; Envmap_override = renderFlags[LabRenderFlag::NoEnvMap]; Glowpoint_override = renderFlags[LabRenderFlag::NoGlowpoints]; PostProcessing_override = renderFlags[LabRenderFlag::HidePostProcessing]; if (obj->type == OBJ_SHIP) { Ships[obj->instance].flags.set(Ship::Ship_Flags::Subsystem_movement_locked, !renderFlags[LabRenderFlag::MoveSubsystems]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_miscmap, renderFlags[LabRenderFlag::NoMiscMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_weapons, !renderFlags[LabRenderFlag::ShowWeapons]); Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); Ships[obj->instance].flags.set(Ship::Ship_Flags::No_insignias, !renderFlags[LabRenderFlag::ShowInsignia]); Ships[obj->instance].team_name = currentTeamColor; if (renderFlags[LabRenderFlag::ShowDamageLightning]) { obj->hull_strength = 1.0f; } else { obj->hull_strength = Ship_info[Ships[obj->instance].ship_info_index].max_hull_strength; } } if (obj->type == OBJ_PROP) { prop* propp = prop_id_lookup(obj->instance); propp->flags.set(Prop::Prop_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); propp->flags.set(Prop::Prop_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); propp->flags.set(Prop::Prop_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); propp->flags.set(Prop::Prop_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); propp->flags.set(Prop::Prop_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); propp->flags.set(Prop::Prop_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); propp->flags.set(Prop::Prop_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); propp->flags.set(Prop::Prop_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); propp->flags.set(Prop::Prop_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); propp->flags.set(Prop::Prop_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); } if (obj->type == OBJ_WEAPON) { Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); } if (obj->type == OBJ_RAW_POF) { Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); } if (obj->type == OBJ_ASTEROID) { Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); Asteroids[obj->instance].render_flags.set(Object::Raw_Pof_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); } if (renderFlags[LabRenderFlag::ShowWireframe]) model_render_set_wireframe_color(&Color_white); if (renderFlags[LabRenderFlag::ShowThrusters] || renderFlags[LabRenderFlag::ShowAfterburners]) { obj->phys_info.linear_thrust.xyz.z = 1.0f; if (obj->type == OBJ_SHIP) { Ships[obj->instance].flags.remove(Ship::Ship_Flags::No_thrusters); } else if (obj->type == OBJ_WEAPON) { Weapons[obj->instance].weapon_flags.remove(Weapon::Weapon_Flags::No_thruster); } if (renderFlags[LabRenderFlag::ShowAfterburners]) { obj->phys_info.flags |= PF_AFTERBURNER_ON; // Keep the AB topped off Ships[obj->instance].afterburner_fuel = Ship_info[Ships[obj->instance].ship_info_index].afterburner_fuel_capacity; } else obj->phys_info.flags &= ~PF_AFTERBURNER_ON; } else { obj->phys_info.linear_thrust.xyz.z = 0.0f; if (obj->type == OBJ_SHIP) { Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters); } else if (obj->type == OBJ_WEAPON) { Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::No_thruster); } } // Prevent weapons from destroying themselves if (!getLabManager()->AllowWeaponDestruction) { if (obj->type == OBJ_WEAPON) { weapon* wep = &Weapons[obj->instance]; weapon_info* wip = &Weapon_info[wep->weapon_info_index]; if (wip != nullptr && !wip->wi_flags[Weapon::Info_Flags::Beam]) { wep->lifeleft = wip->lifetime; } } else if (obj->type == OBJ_BEAM) { beam* b = &Beams[obj->instance]; //Little hack to keep targeting beams going if (b->type == BeamType::TARGETING) { b->life_left = 0.1f; } else { b->life_left = b->life_total; } } } obj_move_all(frametime); // Force the camera to follow our current object labCamera->updateCamera(); if (!renderFlags[LabRenderFlag::NoParticles]) { particle::move_all(frametime); particle::ParticleManager::get()->doFrame(frametime); } shockwave_move_all(frametime); if (renderFlags[LabRenderFlag::UseOrthographicProjection]) { float dist = labCamera->getCameraDistance(); if (dist > 0.0f) gr_activate_ortho_proj_override(dist); } Trail_render_override = true; game_render_frame(labCamera->FS_camera); Trail_render_override = false; gr_deactivate_ortho_proj_override(); Motion_debris_enabled = lab_debris_override_save; Envmap_override = lab_envmap_override_save; Cmdline_emissive = lab_emissive_light_save; gr_reset_clip(); gr_set_color_fast(&HUD_color_debug); if (Cmdline_frame_profile) { tracing::frame_profile_process_frame(); gr_string(gr_screen.center_offset_x + 20, gr_screen.center_offset_y + 100 + gr_get_font_height() + 1, tracing::get_frame_profile_output().c_str(), GR_RESIZE_NONE); } } SCP_string get_rot_mode_string(LabRotationMode rotmode) { switch (rotmode) { case LabRotationMode::Both: return "Pitch and Yaw"; case LabRotationMode::Pitch: return "Pitch"; case LabRotationMode::Yaw: return "Yaw"; case LabRotationMode::Roll: return "Roll"; default: return "HOW DID THIS HAPPEN? Ask a coder!"; } } SCP_string get_rot_speed_string(float speed_divisor) { auto exp = std::lroundf(log10f(speed_divisor)); switch (exp) { case 2: return "Fast"; case 3: return "Slow"; case 4: return "Slowest"; default: return "HOW DID THIS HAPPEN? Ask a coder!"; } } void LabRenderer::renderHud(float) { GR_DEBUG_SCOPE("Lab Render HUD"); // print FPS at bottom left, might be helpful extern void game_get_framerate(); extern float frametotal; extern float Framerate; game_get_framerate(); gr_set_color_fast(&Color_white); if (frametotal != 0.0f) { gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - gr_get_font_height(), "FPS: %3i %s", (int)std::lround(Framerate), labCamera->getOnFrameInfo().c_str()); } else { gr_string(gr_screen.center_offset_x + 10, gr_screen.center_offset_y + gr_screen.center_h - gr_get_font_height(), "FPS: ?", GR_RESIZE_NONE); } //Print FXAA preset if ((gr_is_fxaa_mode(Gr_aa_mode) || gr_is_smaa_mode(Gr_aa_mode)) && !PostProcessing_override) { const char* aa_mode; switch (Gr_aa_mode) { case AntiAliasMode::FXAA_Low: aa_mode = "FXAA Low"; break; case AntiAliasMode::FXAA_Medium: aa_mode = "FXAA Medium"; break; case AntiAliasMode::FXAA_High: aa_mode = "FXAA High"; break; case AntiAliasMode::SMAA_Low: aa_mode = "SMAA Low"; break; case AntiAliasMode::SMAA_Medium: aa_mode = "SMAA Medium"; break; case AntiAliasMode::SMAA_High: aa_mode = "SMAA High"; break; case AntiAliasMode::SMAA_Ultra: aa_mode = "SMAA Ultra"; break; default: aa_mode = "None"; break; } gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 2) - 3, "AA Preset: %s", aa_mode); } // Controls info gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 7) - 3, "Open Options -> Controls reference for the full object and camera controls list."); // Rotation mode gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 5) - 3, "Model rotation axis limit: %s, Rotation speed: %s", get_rot_mode_string(getLabManager()->RotationMode).c_str(), get_rot_speed_string(getLabManager()->RotationSpeedDivisor).c_str()); // Print current Team Color setting, if any if (currentTeamColor != LAB_TEAM_COLOR_NONE) { gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 4) - 3, "Current Team Color: %s", currentTeamColor.c_str()); } if (showOrientationWidget) { labCamera->renderOverlay(); } } void LabRenderer::useBackground(const SCP_string& mission_name) { matrix skybox_orientation; char skybox_model[MAX_FILENAME_LEN]; int skybox_flags; int ambient_light_level; extern const char* Neb2_filenames[]; char envmap_name[MAX_FILENAME_LEN] = {0}; SCP_string ltp_name; currentMissionBackground = mission_name; stars_pre_level_init(true); vm_set_identity(&skybox_orientation); // (DahBlount) - Remember to load the debris anims stars_load_debris(false); if (mission_name != LAB_MISSION_NONE_STRING) { read_file_text((mission_name + ".fs2").c_str(), CF_TYPE_MISSIONS); reset_parse(); flagset flags; skip_to_start_of_string("+Flags"); if (optional_string("+Flags:")) stuff_flagset(&flags); skip_to_start_of_string_one_of(SCP_vector{ "+Volumetric Nebula:", "$Skybox Model:", "$Lighting Profile:", "#Background bitmaps" }); if (optional_string("+Volumetric Nebula:")) { //Rendering usually happens in post-mission-init, just do it now in the lab The_mission.volumetrics.emplace().parse_volumetric_nebula().renderVolumeBitmap(); } else { volumetrics_level_close(); } // Are we using a skybox? //skip will skip to the end of the file (or to the 'end' string) if any string is absent, //so be sure to include any section that might be found skip_to_start_of_string_one_of(SCP_vector{ "$Skybox Model:", "$Lighting Profile:", "#Background bitmaps" }); strcpy_s(skybox_model, ""); if (optional_string("$Skybox Model:")) { stuff_string(skybox_model, F_NAME, MAX_FILENAME_LEN); if (optional_string("+Skybox Orientation:")) { stuff_matrix(&skybox_orientation); } if (optional_string("+Skybox Flags:")) { skybox_flags = 0; stuff_int(&skybox_flags); } else { skybox_flags = DEFAULT_NMODEL_FLAGS; } stars_set_background_model(skybox_model, nullptr, skybox_flags); stars_set_background_orientation(&skybox_orientation); } skip_to_start_of_string_either("$Lighting Profile:", "#Background bitmaps"); ltp_name = ltp::default_name(); if(optional_string("$Lighting Profile:")){ stuff_string(ltp_name,F_NAME); if (ltp_name.empty()) ltp_name = ltp::default_name(); } if (ltp_name != ltp::current()->name) { ltp::switch_to(ltp_name); } // Mission headers include additional fields between lighting profile and the // background section. If we stopped at the lighting profile, we need to seek again. skip_to_start_of_string("#Background bitmaps"); if (optional_string("#Background bitmaps")) { required_string("$Num stars:"); stuff_int(&Num_stars); if (Num_stars >= MAX_STARS) Num_stars = MAX_STARS; required_string("$Ambient light level:"); stuff_int(&ambient_light_level); if (ambient_light_level == 0) { ambient_light_level = DEFAULT_AMBIENT_LIGHT_LEVEL; } gr_set_ambient_light(ambient_light_level & 0xff, (ambient_light_level >> 8) & 0xff, (ambient_light_level >> 16) & 0xff); strcpy_s(Neb2_texture_name, ""); clear_all_bits(Neb2_poof_flags.get(), Poof_info.size()); bool nebula = false; if (optional_string("+Neb2:")) { nebula = true; stuff_string(Neb2_texture_name, F_NAME, MAX_FILENAME_LEN); } if (optional_string("+Neb2Color:")) { nebula = true; int neb_colors[3]; stuff_int_list(neb_colors, 3, ParseLookupType::RAW_INTEGER_TYPE); Neb2_fog_color[0] = (ubyte)neb_colors[0]; Neb2_fog_color[1] = (ubyte)neb_colors[1]; Neb2_fog_color[2] = (ubyte)neb_colors[2]; flags |= Mission::Mission_Flags::Neb2_fog_color_override; } if (nebula){ // Obsolete and only for backwards compatibility if (optional_string("+Neb2Flags:")) { int temp; stuff_int(&temp); bit_array_set_from_int(Neb2_poof_flags.get(), Poof_info.size(), temp); } // Get poofs by name if (optional_string("+Neb2 Poofs List:")) { SCP_vector poofs_list; stuff_string_list(poofs_list); neb2_set_poof_bits(poofs_list); } if (flags[Mission::Mission_Flags::Fullneb]) { neb2_post_level_init(flags[Mission::Mission_Flags::Neb2_fog_color_override]); } } if (flags[Mission::Mission_Flags::Fullneb]) { // no regular nebula stuff nebula_close(); } else { Nebula_index = -1; if (optional_string("+Nebula:")) { char str[MAX_FILENAME_LEN]; int z; stuff_string(str, F_NAME, MAX_FILENAME_LEN); // parse the proper nebula type (full or not) for (z = 0; z < NUM_NEBULAS; z++) { if (flags[Mission::Mission_Flags::Fullneb]) { if (!stricmp(str, Neb2_filenames[z])) { Nebula_index = z; break; } } else { if (!stricmp(str, Nebula_filenames[z])) { Nebula_index = z; break; } } } if (z == NUM_NEBULAS) WarningEx(LOCATION, "Unknown nebula %s!", str); if (optional_string("+Color:")) { stuff_string(str, F_NAME, MAX_FILENAME_LEN); for (z = 0; z < NUM_NEBULA_COLORS; z++) { if (!stricmp(str, Nebula_colors[z])) { Mission_palette = z; break; } } } if (z == NUM_NEBULA_COLORS) WarningEx(LOCATION, "Unknown nebula color %s!", str); if (optional_string("+Pitch:")) { stuff_int(&Nebula_pitch); } else { Nebula_pitch = 0; } if (optional_string("+Bank:")) { stuff_int(&Nebula_bank); } else { Nebula_bank = 0; } if (optional_string("+Heading:")) { stuff_int(&Nebula_heading); } else { Nebula_heading = 0; } } nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); } stars_load_debris(flags[Mission::Mission_Flags::Fullneb]); extern void parse_one_background(background_t * background); while (optional_string("$Bitmap List:") || check_for_string("$Sun:") || check_for_string("$Starbitmap:")) { stars_add_blank_background(false); parse_one_background(&Backgrounds.back()); } stars_load_first_valid_background(); if (optional_string("$Environment Map:")) { stuff_string(envmap_name, F_NAME, MAX_FILENAME_LEN); } const int size = 512; int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP | BMP_FLAG_RENDER_TARGET_MIPMAP); if (!Cmdline_env) { return; } if (gr_screen.envmap_render_target >= 0) { if (!bm_release(gr_screen.envmap_render_target, 1)) { Warning(LOCATION, "Unable to release environment map render target."); } gr_screen.envmap_render_target = -1; } if (strlen(envmap_name)) { // Load the mission map so we can use it later ENVMAP = bm_load(The_mission.envmap_name); // Load may fail, if so, don't exit early. Proceed to make render target. if (ENVMAP > 1) { return; } } gr_screen.envmap_render_target = bm_make_render_target(size, size, gen_flags); } } else { // (DahBlount) - This spot should be used to disable rendering features that only apply to missions. Motion_debris_override = true; Num_stars = 0; } } std::unique_ptr &LabRenderer::getCurrentCamera() { return labCamera; } void LabRenderer::setCurrentCamera(std::unique_ptr &newcam) { labCamera = std::move(newcam); }