Skip to content

Commit

Permalink
wip taking snapshot and compare working
Browse files Browse the repository at this point in the history
  • Loading branch information
leanmendoza committed Nov 23, 2023
1 parent 6b8b07c commit c0b084f
Show file tree
Hide file tree
Showing 17 changed files with 294 additions and 29 deletions.
6 changes: 6 additions & 0 deletions godot/.godot/global_script_class_cache.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ list=Array[Dictionary]([{
"language": &"GDScript",
"path": "res://src/test/avatar/spawn_and_move.gd"
}, {
"base": &"DclTestingTools",
"class": &"TestingTools",
"icon": "",
"language": &"GDScript",
"path": "res://src/test/testing_tool.gd"
}, {
"base": &"Control",
"class": &"VirtualJoystick",
"icon": "",
Expand Down
4 changes: 4 additions & 0 deletions godot/src/global.gd
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func _ready():

self.realm = Realm.new()
self.realm.set_name("realm")

self.testing_tools = TestingTools.new()
self.testing_tools.set_name("testing_tool")

self.content_manager = ContentManager.new()
self.content_manager.set_name("content_manager")
Expand All @@ -69,6 +72,7 @@ func _ready():
get_tree().root.add_child.call_deferred(self.comms)
get_tree().root.add_child.call_deferred(self.avatars)
get_tree().root.add_child.call_deferred(self.portable_experience_controller)
get_tree().root.add_child.call_deferred(self.testing_tools)

# TODO: enable raycast debugger
add_child(raycast_debugger)
Expand Down
8 changes: 5 additions & 3 deletions godot/src/logic/realm.gd
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,11 @@ func set_realm(new_realm_string: String) -> void:
if parsed_urn != null:
realm_global_scene_urns.push_back(parsed_urn)

realm_city_loader_content_base_url = Realm.ensure_ends_with_slash(
configuration.get("cityLoaderContentServer", "")
)
realm_city_loader_content_base_url = configuration.get("cityLoaderContentServer", "")
if not realm_city_loader_content_base_url.is_empty():
realm_city_loader_content_base_url = Realm.ensure_ends_with_slash(
configuration.get("cityLoaderContentServer", "")
)

realm_name = configuration.get("realmName", "no_realm_name")

Expand Down
65 changes: 65 additions & 0 deletions godot/src/test/testing_tool.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
extends DclTestingTools
class_name TestingTools

func take_and_compare_snapshot(id: String, camera_position: Vector3, camera_target: Vector3, snapshot_frame_size: Vector2, tolerance: float, dcl_rpc_sender: DclRpcSender):
prints("take_and_compare_snapshot", id, camera_position, camera_target, snapshot_frame_size, tolerance, dcl_rpc_sender)

# TODO: make this configurable
var hide_player := true
var update_snapshot := false
var create_snapshot_if_does_not_exist := true

var snapshot_path := "user://snapshot_" + id.replace(" ", "_") + ".png"

var existing_snapshot: Image = null
if not update_snapshot and FileAccess.file_exists(snapshot_path):
existing_snapshot = Image.load_from_file(snapshot_path)


RenderingServer.set_default_clear_color(Color(0, 0, 0, 0))
var viewport = get_viewport()
var camera = viewport.get_camera_3d()
var previous_camera_position = camera.global_position
var previous_camera_rotation = camera.global_rotation
var previous_viewport_size = viewport.size

viewport.size = snapshot_frame_size
camera.global_position = camera_position
camera.look_at(camera_target)

get_node("/root/explorer").set_visible_ui(false)
if hide_player:
get_node("/root/explorer/Player").hide()

await get_tree().process_frame
await get_tree().process_frame
await get_tree().process_frame

var viewport_img := viewport.get_texture().get_image()

get_node("/root/explorer").set_visible_ui(true)
if hide_player:
get_node("/root/explorer/Player").show()

viewport.size = previous_viewport_size
camera.global_position = previous_camera_position
camera.global_rotation = previous_camera_rotation

var similarity := 0.0
var updated := false

if existing_snapshot != null:
similarity = self.compute_image_similarity(existing_snapshot, viewport_img)
prints("similarity factor ", similarity)

if update_snapshot or (existing_snapshot == null and create_snapshot_if_does_not_exist):
viewport_img.save_png(snapshot_path)
updated = true

dcl_rpc_sender.send({
"is_match": similarity >= tolerance,
"similarity": similarity,
"was_exist": existing_snapshot != null,
"replaced": updated,
})

8 changes: 8 additions & 0 deletions godot/src/ui/explorer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,11 @@ func capture_mouse():
func release_mouse():
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
label_crosshair.hide()

func set_visible_ui(value: bool):
if value:
$UI.show()
$voice_chat.show()
else:
$UI.hide()
$voice_chat.hide()
7 changes: 5 additions & 2 deletions godot/src/ui/explorer.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ texture_filter = 0

[sub_resource type="Theme" id="Theme_1ufu0"]

[sub_resource type="ButtonGroup" id="ButtonGroup_5lp3f"]
[sub_resource type="ButtonGroup" id="ButtonGroup_nn4lh"]
resource_name = "Tabs"

[node name="explorer" type="Node3D"]
Expand Down Expand Up @@ -173,7 +173,7 @@ layout_mode = 2
[node name="Control_Menu" parent="UI" instance=ExtResource("5_mso44")]
visible = false
layout_mode = 1
group = SubResource("ButtonGroup_5lp3f")
group = SubResource("ButtonGroup_nn4lh")

[node name="DialogStack" parent="UI" instance=ExtResource("10_y1lkn")]
layout_mode = 1
Expand All @@ -196,6 +196,9 @@ grow_horizontal = 2
grow_vertical = 0
mouse_filter = 1

[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.30259, 1.22605, 2.26942)

[connection signal="timeout" from="UI/Timer_FPSLabel" to="." method="_on_timer_timeout"]
[connection signal="request_open_map" from="UI/Control_Minimap" to="." method="_on_control_minimap_request_open_map"]
[connection signal="submit_message" from="UI/LineEdit_Command" to="." method="_on_line_edit_command_submit_message"]
Expand Down
4 changes: 2 additions & 2 deletions rust/decentraland-godot-lib/src/dcl/js/js_modules/Testing.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ function emptyTesting() {
}

function testingModule() {
function takeAndCompareSnapshot(body) {
async function takeAndCompareSnapshot(body) {
const { id, cameraPosition, cameraTarget, snapshotFrameSize, tolerance } = body

return Deno.core.ops.op_take_and_compare_snapshot(
return await Deno.core.ops.op_take_and_compare_snapshot(
id,
[cameraPosition.x, cameraPosition.y, cameraPosition.z],
[cameraTarget.x, cameraTarget.y, cameraTarget.z],
Expand Down
7 changes: 6 additions & 1 deletion rust/decentraland-godot-lib/src/dcl/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ pub(crate) fn scene_thread(
state.borrow_mut().put(Vec::<RpcCall>::new());
state.borrow_mut().put(Vec::<LocalCall>::new());

// TODO: receive from main thread, and managed by command line params
state.borrow_mut().put(SceneEnv {
enable_know_env: true,
testing_enable: true,
});

if let Some(scene_main_crdt) = scene_main_crdt {
state.borrow_mut().put(scene_main_crdt);
}
Expand All @@ -212,7 +218,6 @@ pub(crate) fn scene_thread(
.borrow_mut()
.put(SceneStartTime(std::time::SystemTime::now()));


let script = runtime.execute_script("<loader>", ascii_str!("require (\"~scene.js\")"));

let script = match script {
Expand Down
19 changes: 13 additions & 6 deletions rust/decentraland-godot-lib/src/dcl/js/testing.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::{cell::RefCell, rc::Rc};

use deno_core::{
anyhow::{self, anyhow},
error::AnyError,
op, Op, OpDecl, OpState,
};
use godot::builtin::{Vector2, Vector3};

use crate::dcl::{SceneResponse, TakeAndCompareSnapshotResponse};
use crate::dcl::{SceneId, SceneResponse, TakeAndCompareSnapshotResponse};

use super::SceneEnv;

Expand All @@ -14,14 +16,15 @@ pub fn ops() -> Vec<OpDecl> {
}

#[op]
fn op_take_and_compare_snapshot(
state: &mut OpState,
async fn op_take_and_compare_snapshot(
op_state: Rc<RefCell<OpState>>,
id: String,
camera_position: [f32; 3],
camera_target: [f32; 3],
snapshot_frame_size: [f32; 2],
tolerance: f32,
) -> Result<TakeAndCompareSnapshotResponse, AnyError> {
let mut state = op_state.borrow_mut();
let scene_env = state.borrow::<SceneEnv>();
if !scene_env.testing_enable {
return Err(anyhow::anyhow!("Testing mode not available"));
Expand All @@ -30,19 +33,21 @@ fn op_take_and_compare_snapshot(
let (sx, rx) =
tokio::sync::oneshot::channel::<Result<TakeAndCompareSnapshotResponse, String>>();

let scene_id = *state.borrow::<SceneId>();
let sender = state.borrow_mut::<std::sync::mpsc::SyncSender<SceneResponse>>();
sender
.send(SceneResponse::TakeSnapshot {
scene_id,
id,
camera_position: Vector3 {
x: camera_position[0],
y: camera_position[1],
z: camera_position[2],
z: -camera_position[2],
},
camera_target: Vector3 {
x: camera_target[0],
y: camera_target[1],
z: camera_target[2],
z: -camera_target[2],
},
snapshot_frame_size: Vector2 {
x: snapshot_frame_size[0],
Expand All @@ -52,8 +57,10 @@ fn op_take_and_compare_snapshot(
response: sx.into(),
})
.expect("error sending scene response!!");
drop(state);

rx.blocking_recv()
let response = rx.await;
response
.map_err(|e| anyhow::anyhow!(e))?
.map_err(|e| anyhow!(e))
}
11 changes: 6 additions & 5 deletions rust/decentraland-godot-lib/src/dcl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod js;
pub mod scene_apis;
pub mod serialization;

use godot::builtin::{Vector3, Vector2};
use godot::builtin::{Vector2, Vector3};
use serde::Serialize;

use crate::wallet::Wallet;
Expand Down Expand Up @@ -61,6 +61,7 @@ pub enum SceneResponse {
),
RemoveGodotScene(SceneId, Vec<SceneLogMessage>),
TakeSnapshot {
scene_id: SceneId,
id: String,
camera_position: Vector3,
camera_target: Vector3,
Expand All @@ -74,13 +75,13 @@ pub enum SceneResponse {
#[serde(rename_all = "camelCase")]
pub struct TakeAndCompareSnapshotResponse {
// true if the threshold was met, false otherwise or if it wasn't previously exist
is_match: bool,
pub is_match: bool,
// from 0 to 1 how similar the snapshot taken is to the previous one
similarity: f32,
pub similarity: f32,
// true if the snapshot already exists in the snapshot folder, false otherwise
was_exist: bool,
pub was_exist: bool,
// true if the snapshot was created and saved, false otherwise
replaced: bool,
pub replaced: bool,
}

pub type SharedSceneCrdtState = Arc<Mutex<SceneCrdtState>>;
Expand Down
2 changes: 1 addition & 1 deletion rust/decentraland-godot-lib/src/dcl/scene_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct UserData {
pub avatar: Option<AvatarForUserData>,
}

#[derive(Debug, Clone)]
#[derive(Default, Debug, Clone)]
pub struct RpcResultSender<T>(Arc<RwLock<Option<tokio::sync::oneshot::Sender<T>>>>);

impl<T: 'static> RpcResultSender<T> {
Expand Down
4 changes: 4 additions & 0 deletions rust/decentraland-godot-lib/src/godot_classes/dcl_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
avatars::avatar_scene::AvatarScene,
comms::communication_manager::CommunicationManager,
scene_runner::{scene_manager::SceneManager, tokio_runtime::TokioRuntime},
test_runner::testing_tools::DclTestingTools,
};

use super::{dcl_realm::DclRealm, portables::DclPortableExperienceController};
Expand All @@ -28,6 +29,8 @@ pub struct DclGlobal {
pub realm: Gd<DclRealm>,
#[var]
pub portable_experience_controller: Gd<DclPortableExperienceController>,
#[var]
pub testing_tools: Gd<DclTestingTools>,
}

#[godot_api]
Expand Down Expand Up @@ -63,6 +66,7 @@ impl NodeVirtual for DclGlobal {
tokio_runtime,
realm: Gd::new_default(),
portable_experience_controller: Gd::new_default(),
testing_tools: Gd::new_default(),
}
}
}
Expand Down
66 changes: 66 additions & 0 deletions rust/decentraland-godot-lib/src/godot_classes/dcl_rpc_sender.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use godot::engine::RefCounted;
use godot::prelude::*;

use crate::dcl::scene_apis::RpcResultSender;
use crate::dcl::TakeAndCompareSnapshotResponse;

#[derive(GodotClass)]
#[class(init, base=RefCounted)]
pub struct DclRpcSender {
sender: Option<RpcResultSender<Result<TakeAndCompareSnapshotResponse, String>>>,

#[base]
_base: Base<RefCounted>,
}

impl godot::builtin::meta::GodotConvert for TakeAndCompareSnapshotResponse {
type Via = Dictionary;
}

impl FromGodot for TakeAndCompareSnapshotResponse {
fn try_from_godot(via: Dictionary) -> Option<Self> {
let is_match = via.get("is_match")?.to::<bool>();
let similarity = via.get("similarity")?.to::<f32>();
let was_exist = via.get("was_exist")?.to::<bool>();
let replaced = via.get("replaced")?.to::<bool>();
Some(Self {
is_match,
similarity,
was_exist,
replaced,
})
}
}

impl DclRpcSender {
pub fn set_sender(
&mut self,
sender: RpcResultSender<Result<TakeAndCompareSnapshotResponse, String>>,
) {
self.sender = Some(sender);
}
}

#[godot_api]
impl DclRpcSender {
#[func]
fn send(&mut self, response: Variant) {
if let Some(sender) = self.sender.as_ref() {
let response = response.try_to::<Dictionary>().unwrap();
let response = TakeAndCompareSnapshotResponse::from_godot(response);
let sender: tokio::sync::oneshot::Sender<
Result<TakeAndCompareSnapshotResponse, String>,
> = sender.take();
let ret = sender.send(Ok(response));

match ret {
Ok(_) => {
tracing::info!("Response sent");
}
Err(e) => {
tracing::info!("Error sending response {:?}", e);
}
}
}
}
}
1 change: 1 addition & 0 deletions rust/decentraland-godot-lib/src/godot_classes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod dcl_ether;
pub mod dcl_global;
pub mod dcl_gltf_container;
pub mod dcl_realm;
pub mod dcl_rpc_sender;
pub mod dcl_scene_node;
pub mod dcl_ui_background;
pub mod dcl_ui_control;
Expand Down
Loading

0 comments on commit c0b084f

Please sign in to comment.