mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-30 21:21:10 +00:00 
			
		
		
		
	Added 3D freelook navigation mode
- Triggered by holding RMB - Can look around in FPS style - Can move with WASD - Movement speed accelerates over time - Can multiply speed with a modifier key to go faster or slower - Configurable in editor settings and shortcuts
This commit is contained in:
		
							parent
							
								
									f527a69462
								
							
						
					
					
						commit
						442006b4bf
					
				
					 3 changed files with 123 additions and 124 deletions
				
			
		|  | @ -58,15 +58,7 @@ void SpatialEditorViewport::_update_camera() { | |||
| 	} else | ||||
| 		camera->set_perspective(get_fov(), get_znear(), get_zfar()); | ||||
| 
 | ||||
| 	Transform camera_transform; | ||||
| 	camera_transform.translate(cursor.pos); | ||||
| 	camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); | ||||
| 	camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); | ||||
| 
 | ||||
| 	if (orthogonal) | ||||
| 		camera_transform.translate(0, 0, 4096); | ||||
| 	else | ||||
| 		camera_transform.translate(0, 0, cursor.distance); | ||||
| 	Transform camera_transform = to_camera_transform(cursor); | ||||
| 
 | ||||
| 	if (camera->get_global_transform() != camera_transform) { | ||||
| 		camera->set_global_transform(camera_transform); | ||||
|  | @ -74,6 +66,20 @@ void SpatialEditorViewport::_update_camera() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| Transform SpatialEditorViewport::to_camera_transform(const Cursor &p_cursor) const { | ||||
| 	Transform camera_transform; | ||||
| 	camera_transform.translate(p_cursor.pos); | ||||
| 	camera_transform.basis.rotate(Vector3(1, 0, 0), -p_cursor.x_rot); | ||||
| 	camera_transform.basis.rotate(Vector3(0, 1, 0), -p_cursor.y_rot); | ||||
| 
 | ||||
| 	if (orthogonal) | ||||
| 		camera_transform.translate(0, 0, 4096); | ||||
| 	else | ||||
| 		camera_transform.translate(0, 0, p_cursor.distance); | ||||
| 
 | ||||
| 	return camera_transform; | ||||
| } | ||||
| 
 | ||||
| String SpatialEditorGizmo::get_handle_name(int p_idx) const { | ||||
| 
 | ||||
| 	if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) | ||||
|  | @ -669,8 +675,7 @@ void SpatialEditorViewport::_list_select(InputEventMouseButton b) { | |||
| 			selection_menu->add_item(spat->get_name()); | ||||
| 			selection_menu->set_item_icon(i, icon); | ||||
| 			selection_menu->set_item_metadata(i, node_path); | ||||
| 			selection_menu->set_item_tooltip(i, String(spat->get_name()) + | ||||
| 														"\nType: " + spat->get_class() + "\nPath: " + node_path); | ||||
| 			selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path); | ||||
| 		} | ||||
| 
 | ||||
| 		selection_menu->set_global_position(Vector2(b.global_x, b.global_y)); | ||||
|  | @ -729,76 +734,6 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { | |||
| 
 | ||||
| 					if (_edit.mode == TRANSFORM_NONE && b.pressed) { | ||||
| 
 | ||||
| 						Plane cursor_plane(cursor.cursor_pos, _get_camera_normal()); | ||||
| 
 | ||||
| 						Vector3 ray_origin = _get_ray_pos(Vector2(b.x, b.y)); | ||||
| 						Vector3 ray_dir = _get_ray(Vector2(b.x, b.y)); | ||||
| 
 | ||||
| 						//gizmo modify
 | ||||
| 
 | ||||
| 						if (b.mod.control) { | ||||
| 
 | ||||
| 							Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(ray_origin, ray_dir, get_tree()->get_root()->get_world()->get_scenario()); | ||||
| 
 | ||||
| 							Plane p(ray_origin, _get_camera_normal()); | ||||
| 
 | ||||
| 							float min_d = 1e10; | ||||
| 							bool found = false; | ||||
| 
 | ||||
| 							for (int i = 0; i < instances.size(); i++) { | ||||
| 
 | ||||
| 								Object *obj = ObjectDB::get_instance(instances[i]); | ||||
| 
 | ||||
| 								if (!obj) | ||||
| 									continue; | ||||
| 
 | ||||
| 								VisualInstance *vi = obj->cast_to<VisualInstance>(); | ||||
| 								if (!vi) | ||||
| 									continue; | ||||
| 
 | ||||
| 								//optimize by checking AABB (although should pre sort by distance)
 | ||||
| 								Rect3 aabb = vi->get_global_transform().xform(vi->get_aabb()); | ||||
| 								if (p.distance_to(aabb.get_support(-ray_dir)) > min_d) | ||||
| 									continue; | ||||
| 
 | ||||
| 								PoolVector<Face3> faces = vi->get_faces(VisualInstance::FACES_SOLID); | ||||
| 								int c = faces.size(); | ||||
| 								if (c > 0) { | ||||
| 									PoolVector<Face3>::Read r = faces.read(); | ||||
| 
 | ||||
| 									for (int j = 0; j < c; j++) { | ||||
| 
 | ||||
| 										Vector3 inters; | ||||
| 										if (r[j].intersects_ray(ray_origin, ray_dir, &inters)) { | ||||
| 
 | ||||
| 											float d = p.distance_to(inters); | ||||
| 											if (d < 0) | ||||
| 												continue; | ||||
| 
 | ||||
| 											if (d < min_d) { | ||||
| 												min_d = d; | ||||
| 												found = true; | ||||
| 											} | ||||
| 										} | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 
 | ||||
| 							if (found) { | ||||
| 
 | ||||
| 								//cursor.cursor_pos=ray_origin+ray_dir*min_d;
 | ||||
| 								//VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos));
 | ||||
| 							} | ||||
| 
 | ||||
| 						} else { | ||||
| 							Vector3 new_pos; | ||||
| 							if (cursor_plane.intersects_ray(ray_origin, ray_dir, &new_pos)) { | ||||
| 
 | ||||
| 								//cursor.cursor_pos=new_pos;
 | ||||
| 								//VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos));
 | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						if (b.mod.alt) { | ||||
| 
 | ||||
| 							if (nav_scheme == NAVIGATION_MAYA) | ||||
|  | @ -1344,6 +1279,8 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { | |||
| 
 | ||||
| 				if (nav_scheme == NAVIGATION_MAYA && m.mod.alt) { | ||||
| 					nav_mode = NAVIGATION_ZOOM; | ||||
| 				} else { | ||||
| 					nav_mode = NAVIGATION_LOOK; | ||||
| 				} | ||||
| 
 | ||||
| 			} else if (m.button_mask & 4) { | ||||
|  | @ -1453,6 +1390,29 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { | |||
| 					_update_name(); | ||||
| 				} break; | ||||
| 
 | ||||
| 				case NAVIGATION_LOOK: { | ||||
| 					// Freelook only works properly in perspective.
 | ||||
| 					// It technically works too in ortho, but it's awful for a user due to fov being near zero
 | ||||
| 					if (!orthogonal) { | ||||
| 						cursor.x_rot += m.relative_y / 120.0; | ||||
| 						cursor.y_rot += m.relative_x / 120.0; | ||||
| 						if (cursor.x_rot > Math_PI / 2.0) | ||||
| 							cursor.x_rot = Math_PI / 2.0; | ||||
| 						if (cursor.x_rot < -Math_PI / 2.0) | ||||
| 							cursor.x_rot = -Math_PI / 2.0; | ||||
| 
 | ||||
| 						// Look is like Orbit, except the cursor translates, not the camera
 | ||||
| 						Transform camera_transform = to_camera_transform(cursor); | ||||
| 						Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); | ||||
| 						Vector3 diff = camera->get_translation() - pos; | ||||
| 						cursor.pos += diff; | ||||
| 
 | ||||
| 						name = ""; | ||||
| 						_update_name(); | ||||
| 					} | ||||
| 
 | ||||
| 				} break; | ||||
| 
 | ||||
| 				default: {} | ||||
| 			} | ||||
| 		} break; | ||||
|  | @ -1543,6 +1503,75 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SpatialEditorViewport::_update_freelook(real_t delta) { | ||||
| 
 | ||||
| 	const Input &input = *Input::get_singleton(); | ||||
| 
 | ||||
| 	if (!input.is_mouse_button_pressed(BUTTON_RIGHT)) | ||||
| 		return; | ||||
| 
 | ||||
| 	Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); | ||||
| 	Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); | ||||
| 	Vector3 up = Vector3(0, 1, 0); | ||||
| 
 | ||||
| 	int key_left = ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), KEY_A)->get_shortcut().key.scancode; | ||||
| 	int key_right = ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), KEY_D)->get_shortcut().key.scancode; | ||||
| 	int key_forward = ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), KEY_W)->get_shortcut().key.scancode; | ||||
| 	int key_backwards = ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), KEY_S)->get_shortcut().key.scancode; | ||||
| 	int key_up = ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_Q)->get_shortcut().key.scancode; | ||||
| 	int key_down = ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_E)->get_shortcut().key.scancode; | ||||
| 	int key_speed_modifier = ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT)->get_shortcut().key.scancode; | ||||
| 
 | ||||
| 	Vector3 velocity; | ||||
| 	bool pressed = false; | ||||
| 	bool speed_modifier = false; | ||||
| 
 | ||||
| 	if (input.is_key_pressed(key_left)) { | ||||
| 		velocity -= right; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_right)) { | ||||
| 		velocity += right; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_forward)) { | ||||
| 		velocity += forward; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_backwards)) { | ||||
| 		velocity -= forward; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_up)) { | ||||
| 		velocity += up; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_down)) { | ||||
| 		velocity -= up; | ||||
| 		pressed = true; | ||||
| 	} | ||||
| 	if (input.is_key_pressed(key_speed_modifier)) { | ||||
| 		speed_modifier = true; | ||||
| 	} | ||||
| 
 | ||||
| 	const EditorSettings &s = *EditorSettings::get_singleton(); | ||||
| 
 | ||||
| 	real_t base_speed = s.get("editors/3d/freelook_base_speed"); | ||||
| 	real_t acceleration = s.get("editors/3d/freelook_acceleration"); | ||||
| 	real_t max_speed = s.get("editors/3d/freelook_max_speed"); | ||||
| 	real_t modifier_speed_factor = s.get("editors/3d/freelook_modifier_speed_factor"); | ||||
| 
 | ||||
| 	if (pressed) { | ||||
| 		velocity.normalize(); | ||||
| 		freelook_speed += acceleration * delta; | ||||
| 		if (freelook_speed > max_speed) | ||||
| 			freelook_speed = max_speed; | ||||
| 		cursor.pos += velocity * ((freelook_speed * (speed_modifier ? modifier_speed_factor : 1.0) * delta)); | ||||
| 	} else { | ||||
| 		freelook_speed = base_speed; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SpatialEditorViewport::set_message(String p_message, float p_time) { | ||||
| 
 | ||||
| 	message = p_message; | ||||
|  | @ -1579,6 +1608,8 @@ void SpatialEditorViewport::_notification(int p_what) { | |||
| 		} | ||||
| 		*/ | ||||
| 
 | ||||
| 		_update_freelook(get_tree()->get_idle_process_time()); | ||||
| 
 | ||||
| 		_update_camera(); | ||||
| 
 | ||||
| 		Map<Node *, Object *> &selection = editor_selection->get_selection(); | ||||
|  | @ -2204,6 +2235,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed | |||
| 	previewing = NULL; | ||||
| 	preview = NULL; | ||||
| 	gizmo_scale = 1.0; | ||||
| 	freelook_speed = 0; | ||||
| 
 | ||||
| 	selection_menu = memnew(PopupMenu); | ||||
| 	add_child(selection_menu); | ||||
|  | @ -3149,36 +3181,6 @@ void SpatialEditor::_finish_indicators() { | |||
| 	VisualServer::get_singleton()->free(cursor_mesh); | ||||
| } | ||||
| 
 | ||||
| void SpatialEditor::_instance_scene() { | ||||
| #if 0 | ||||
| 	EditorNode *en = get_scene()->get_root_node()->cast_to<EditorNode>(); | ||||
| 	ERR_FAIL_COND(!en); | ||||
| 	String path = en->get_filesystem_dock()->get_selected_path(); | ||||
| 	if (path=="") { | ||||
| 		set_message(TTR("No scene selected to instance!")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	undo_redo->create_action(TTR("Instance at Cursor")); | ||||
| 
 | ||||
| 	Node* scene = en->request_instance_scene(path); | ||||
| 
 | ||||
| 	if (!scene) { | ||||
| 		set_message(TTR("Could not instance scene!")); | ||||
| 		undo_redo->commit_action(); //bleh
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	Spatial *s = scene->cast_to<Spatial>(); | ||||
| 	if (s) { | ||||
| 
 | ||||
| 		undo_redo->add_do_method(s,"set_global_transform",Transform(Matrix3(),cursor.cursor_pos)); | ||||
| 	} | ||||
| 
 | ||||
| 	undo_redo->commit_action(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void SpatialEditor::_unhandled_key_input(InputEvent p_event) { | ||||
| 
 | ||||
| 	if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack()) | ||||
|  | @ -3242,8 +3244,6 @@ void SpatialEditor::_notification(int p_what) { | |||
| 		tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons")); | ||||
| 		tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons")); | ||||
| 		tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons")); | ||||
| 		instance_button->set_icon(get_icon("SpatialAdd", "EditorIcons")); | ||||
| 		instance_button->hide(); | ||||
| 
 | ||||
| 		view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_icon("Panels1", "EditorIcons")); | ||||
| 		view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_icon("Panels2", "EditorIcons")); | ||||
|  | @ -3383,7 +3383,6 @@ void SpatialEditor::_bind_methods() { | |||
| 	ClassDB::bind_method("_node_removed", &SpatialEditor::_node_removed); | ||||
| 	ClassDB::bind_method("_menu_item_pressed", &SpatialEditor::_menu_item_pressed); | ||||
| 	ClassDB::bind_method("_xform_dialog_action", &SpatialEditor::_xform_dialog_action); | ||||
| 	ClassDB::bind_method("_instance_scene", &SpatialEditor::_instance_scene); | ||||
| 	ClassDB::bind_method("_get_editor_data", &SpatialEditor::_get_editor_data); | ||||
| 	ClassDB::bind_method("_request_gizmo", &SpatialEditor::_request_gizmo); | ||||
| 	ClassDB::bind_method("_default_light_angle_input", &SpatialEditor::_default_light_angle_input); | ||||
|  | @ -3520,12 +3519,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { | |||
| 	tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds); | ||||
| 	tool_button[TOOL_MODE_SCALE]->set_tooltip(TTR("Scale Mode (R)")); | ||||
| 
 | ||||
| 	instance_button = memnew(Button); | ||||
| 	hbc_menu->add_child(instance_button); | ||||
| 	instance_button->set_flat(true); | ||||
| 	instance_button->connect("pressed", this, "_instance_scene"); | ||||
| 	instance_button->hide(); | ||||
| 
 | ||||
| 	VSeparator *vs = memnew(VSeparator); | ||||
| 	hbc_menu->add_child(vs); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Marc Gilleron
						Marc Gilleron