Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add Listener2D #53429

Merged
merged 1 commit into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion doc/classes/Listener.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
</brief_description>
<description>
Once added to the scene tree and enabled using [method make_current], this node will override the location sounds are heard from. This can be used to listen from a location different from the [Camera].
[b]Note:[/b] There is no 2D equivalent for this node yet.
</description>
<tutorials>
</tutorials>
Expand Down
35 changes: 35 additions & 0 deletions doc/classes/Listener2D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Listener2D" inherits="Node2D" version="3.4">
<brief_description>
Overrides the location sounds are heard from.
</brief_description>
<description>
Once added to the scene tree and enabled using [method make_current], this node will override the location sounds are heard from. Only one [Listener2D] can be current. Using [method make_current] will disable the previous [Listener2D].
If there is no active [Listener2D] in the current [Viewport], center of the screen will be used as a hearing point for the audio. [Listener2D] needs to be inside [SceneTree] to function.
</description>
<tutorials>
</tutorials>
<methods>
<method name="clear_current">
<return type="void" />
<description>
Disables the [Listener2D]. If it's not set as current, this method will have no effect.
</description>
</method>
<method name="is_current" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this [Listener2D] is currently active.
</description>
</method>
<method name="make_current">
<return type="void" />
<description>
Makes the [Listener2D] active, setting it as the hearing point for the sounds. If there is already another active [Listener2D], it will be disabled.
This method will have no effect if the [Listener2D] is not added to [SceneTree].
</description>
</method>
</methods>
<constants>
</constants>
</class>
1 change: 1 addition & 0 deletions editor/icons/icon_listener_2d.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 15 additions & 9 deletions scene/2d/audio_stream_player_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "core/engine.h"
#include "scene/2d/area_2d.h"
#include "scene/2d/listener_2d.h"
#include "scene/main/viewport.h"

void AudioStreamPlayer2D::_mix_audio() {
Expand Down Expand Up @@ -208,13 +209,21 @@ void AudioStreamPlayer2D::_notification(int p_what) {
Viewport *vp = E->get();
if (vp->is_audio_listener_2d()) {
//compute matrix to convert to screen
Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform();
Vector2 screen_size = vp->get_visible_rect().size;
Vector2 listener_in_global;
Vector2 relative_to_listener;

Listener2D *listener = vp->get_listener_2d();
if (listener) {
listener_in_global = listener->get_global_position();
relative_to_listener = global_pos - listener_in_global;
} else {
Transform2D to_listener = vp->get_global_canvas_transform() * vp->get_canvas_transform();
listener_in_global = to_listener.affine_inverse().xform(screen_size * 0.5);
relative_to_listener = to_listener.xform(global_pos) - screen_size * 0.5;
}

//screen in global is used for attenuation
Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5);

float dist = global_pos.distance_to(screen_in_global); //distance to screen center
float dist = global_pos.distance_to(listener_in_global); // Distance to listener, or screen if none.

if (dist > max_distance) {
continue; //can't hear this sound in this viewport
Expand All @@ -223,10 +232,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {
float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
multiplier *= Math::db2linear(volume_db); //also apply player volume!

//point in screen is used for panning
Vector2 point_in_screen = to_screen.xform(global_pos);

float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0);
float pan = CLAMP((relative_to_listener.x + screen_size.x * 0.5) / screen_size.x, 0.0, 1.0);

float l = 1.0 - pan;
float r = pan;
Expand Down
112 changes: 112 additions & 0 deletions scene/2d/listener_2d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*************************************************************************/
/* listener_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/

#include "listener_2d.h"

bool Listener2D::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "current") {
if (p_value.operator bool()) {
make_current();
} else {
clear_current();
}
} else {
return false;
}
return true;
}

bool Listener2D::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "current") {
if (is_inside_tree() && get_tree()->is_node_being_edited(this)) {
r_ret = current;
} else {
r_ret = is_current();
}
} else {
return false;
}
return true;
}

void Listener2D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, "current"));
}

void Listener2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (!get_tree()->is_node_being_edited(this) && current) {
make_current();
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (!get_tree()->is_node_being_edited(this)) {
if (is_current()) {
clear_current();
current = true; // Keep it true.
} else {
current = false;
}
}
} break;
}
}

void Listener2D::make_current() {
current = true;
if (!is_inside_tree()) {
return;
}
get_viewport()->_listener_2d_set(this);
}

void Listener2D::clear_current() {
current = false;
if (!is_inside_tree()) {
return;
}
get_viewport()->_listener_2d_remove(this);
}

bool Listener2D::is_current() const {
if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) {
return get_viewport()->get_listener_2d() == this;
} else {
return current;
}
return false;
}

void Listener2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_current"), &Listener2D::make_current);
ClassDB::bind_method(D_METHOD("clear_current"), &Listener2D::clear_current);
ClassDB::bind_method(D_METHOD("is_current"), &Listener2D::is_current);
}
61 changes: 61 additions & 0 deletions scene/2d/listener_2d.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*************************************************************************/
/* listener_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/

#ifndef LISTENER_2D_H
#define LISTENER_2D_H

#include "scene/2d/node_2d.h"
#include "scene/main/viewport.h"

class Listener2D : public Node2D {
GDCLASS(Listener2D, Node2D);

private:
bool current = false;

friend class Viewport;

protected:
void _update_listener();

bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _notification(int p_what);

static void _bind_methods();

public:
void make_current();
void clear_current();
bool is_current() const;
};

#endif
21 changes: 20 additions & 1 deletion scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "core/os/os.h"
#include "core/project_settings.h"
#include "scene/2d/collision_object_2d.h"
#include "scene/2d/listener_2d.cpp"
#include "scene/3d/camera.h"
#include "scene/3d/collision_object.h"
#include "scene/3d/listener.h"
Expand Down Expand Up @@ -783,14 +784,17 @@ void Viewport::set_as_audio_listener_2d(bool p_enable) {
}

audio_listener_2d = p_enable;

_update_listener_2d();
}

bool Viewport::is_audio_listener_2d() const {
return audio_listener_2d;
}

Listener2D *Viewport::get_listener_2d() const {
return listener_2d;
}

void Viewport::enable_canvas_transform_override(bool p_enable) {
if (override_canvas_transform == p_enable) {
return;
Expand Down Expand Up @@ -977,6 +981,21 @@ void Viewport::_camera_make_next_current(Camera *p_exclude) {
}
#endif

void Viewport::_listener_2d_set(Listener2D *p_listener) {
if (listener_2d == p_listener) {
return;
} else if (listener_2d) {
listener_2d->clear_current();
}
listener_2d = p_listener;
}

void Viewport::_listener_2d_remove(Listener2D *p_listener) {
if (listener_2d == p_listener) {
listener_2d = nullptr;
}
}

void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) {
canvas_layers.insert(p_canvas_layer);
}
Expand Down
7 changes: 7 additions & 0 deletions scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
class Camera;
class Camera2D;
class Listener;
class Listener2D;
class Control;
class CanvasItem;
class CanvasLayer;
Expand Down Expand Up @@ -180,6 +181,7 @@ class Viewport : public Node {

Camera *camera;
Set<Camera *> cameras;
Listener2D *listener_2d = nullptr;
Set<CanvasLayer *> canvas_layers;

RID viewport;
Expand Down Expand Up @@ -400,6 +402,10 @@ class Viewport : public Node {
void _camera_remove(Camera *p_camera);
void _camera_make_next_current(Camera *p_exclude);

friend class Listener2D;
void _listener_2d_set(Listener2D *p_listener);
void _listener_2d_remove(Listener2D *p_listener);

friend class CanvasLayer;
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
Expand Down Expand Up @@ -436,6 +442,7 @@ class Viewport : public Node {
void set_as_audio_listener(bool p_enable);
bool is_audio_listener() const;

Listener2D *get_listener_2d() const;
void set_as_audio_listener_2d(bool p_enable);
bool is_audio_listener_2d() const;

Expand Down
2 changes: 2 additions & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "scene/2d/light_2d.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/line_2d.h"
#include "scene/2d/listener_2d.h"
#include "scene/2d/mesh_instance_2d.h"
#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/navigation_2d.h"
Expand Down Expand Up @@ -597,6 +598,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init

ClassDB::register_class<Camera2D>();
ClassDB::register_class<Listener2D>();
ClassDB::register_virtual_class<Joint2D>();
ClassDB::register_class<PinJoint2D>();
ClassDB::register_class<GrooveJoint2D>();
Expand Down