diff --git a/include/bind/FrameBuffer.hxx b/include/bind/FrameBuffer.hxx index 6f178793..889982f8 100644 --- a/include/bind/FrameBuffer.hxx +++ b/include/bind/FrameBuffer.hxx @@ -47,6 +47,7 @@ class FrameBuffer : public Reference { int get_height() noexcept; int get_freq() noexcept; bool write_rgb888(Ref img); + PoolByteArray read_rgb888(); }; }; // namespace godot diff --git a/project/Global.gd b/project/Global.gd index 6fb82a1e..a14dcacf 100644 --- a/project/Global.gd +++ b/project/Global.gd @@ -39,7 +39,8 @@ var _classes: Array = [ preload("res://src/attachments/Gyroscope.gd"), preload("res://src/utilities/sensors/odometer/Odometer.gd"), RayCar, RayWheel, UltraSonic, - preload("res://src/attachments/Odometer.gd")] + preload("res://src/attachments/Odometer.gd"), + ScreenBuffer] var classes: Dictionary = {} diff --git a/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.cpp b/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.cpp new file mode 100644 index 00000000..9b898484 --- /dev/null +++ b/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.cpp @@ -0,0 +1,37 @@ +// +// Created by danie on 2021-11-02. +// + +#include "MKRRGBMatrix.h" + +RGBMatrixClass MATRIX; + +RGBMatrixClass::RGBMatrixClass() + : ArduinoGraphics(RGB_MATRIX_WIDTH, RGB_MATRIX_HEIGHT), framebufferAccessor() {} + +RGBMatrixClass::~RGBMatrixClass() {} + +int RGBMatrixClass::begin() { + return framebufferAccessor.begin(RGB_MATRIX_WIDTH, RGB_MATRIX_HEIGHT, PIXEL_FORMAT, FPS); +} + +void RGBMatrixClass::end() { framebufferAccessor.end(); } + +void RGBMatrixClass::brightness(uint8_t brightness) {} + +void RGBMatrixClass::beginDraw() { + ArduinoGraphics::beginDraw(); + framebufferAccessor.read(buf); +} + +void RGBMatrixClass::endDraw() { + ArduinoGraphics::endDraw(); + framebufferAccessor.write(buf); +} + +void RGBMatrixClass::set(int x, int y, uint8_t r, uint8_t g, uint8_t b) { + int index = (y * RGB_MATRIX_WIDTH + x) * (BITS_PER_PIXEL / CHAR_BIT); + buf[index] = static_cast(r); + buf[index + 1] = static_cast(g); + buf[index + 2] = static_cast(b); +} \ No newline at end of file diff --git a/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.h b/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.h new file mode 100644 index 00000000..5fbb226a --- /dev/null +++ b/project/share/library_patches/arduino_mkrrgb/src/MKRRGBMatrix.h @@ -0,0 +1,41 @@ +// +// Created by danie on 2021-11-02. +// + +#ifndef MKRRGBMATRIX_H +#define MKRRGBMATRIX_H + +#include +#include +#include +#include +#include "SMCE_dll.hpp" + +#define RGB_MATRIX_WIDTH 640 +#define RGB_MATRIX_HEIGHT 480 +#define PIXEL_FORMAT SMCE_Pixel_Format::RGB888 +#define BITS_PER_PIXEL 24 +#define FPS 60 + +class RGBMatrixClass : public ArduinoGraphics { + private: + FramebufferAccess framebufferAccessor; + std::byte buf[(BITS_PER_PIXEL * RGB_MATRIX_HEIGHT * RGB_MATRIX_WIDTH) / CHAR_BIT]; + + public: + RGBMatrixClass(); + virtual ~RGBMatrixClass(); + + int begin(); + void end(); + + void brightness(uint8_t brightness); + + virtual void beginDraw(); + virtual void endDraw(); + + virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b); +}; + +extern RGBMatrixClass MATRIX; +#endif // MKRRGBMATRIX_H \ No newline at end of file diff --git a/project/src/attachments/ScreenBuffer.gd b/project/src/attachments/ScreenBuffer.gd new file mode 100644 index 00000000..dc9522e2 --- /dev/null +++ b/project/src/attachments/ScreenBuffer.gd @@ -0,0 +1,96 @@ +class_name ScreenBuffer +extends Spatial + +onready var mesh: MeshInstance = MeshInstance.new() +onready var screen: MeshInstance = MeshInstance.new() +var img: Image = Image.new() +var texture: ImageTexture = ImageTexture.new() + +func extern_class_name(): + return "ScreenBuffer" + +export var pin = 0 +export var display_width = 0.34 +export var display_height = 0.2 +var resolution = Vector2(0, 0) # Base resolution (for MKRRGBMAtrix) +var image_buf = null +var fps = 1 +var aspect_ratio = 1 +var view = null +var timer: Timer = Timer.new() + +func _ready(): + timer.connect("timeout", self, "_on_frame") + timer.autostart = true + add_child(timer) + + img.create(resolution.x, resolution.y, false, Image.FORMAT_RGB8) + img.fill(Color.black) + _create_texture() + + mesh.mesh = CubeMesh.new() + _update_mesh_size() + mesh.rotate_y(PI) + + screen.mesh = PlaneMesh.new() + screen.scale_object_local(Vector3(0.9, 0.9, 0.9)) + screen.rotate_x(PI/2) + screen.translate(Vector3(0, 1.5, 0)) + screen.material_override = SpatialMaterial.new() + screen.material_override.albedo_texture = texture + + mesh.add_child(screen) + add_child(mesh) + +func _on_frame() -> void: + if ! view || ! view.is_valid(): + return + + var new_image_buf = view.framebuffers(pin).read_rgb888(img) + if new_image_buf != image_buf: + img.create_from_data(resolution.x, resolution.y, + false, Image.FORMAT_RGB8, new_image_buf) + _create_texture() + image_buf = new_image_buf + +func _physics_process(delta): + var buffer = view.framebuffers(pin) + var new_res = Vector2(buffer.get_width(), buffer.get_height()) + var new_fps = buffer.get_freq() + + if new_fps != fps and new_fps != 0: + timer.wait_time = 1.0/new_fps + fps = new_fps + + if new_res != resolution and new_res.x != 0 and new_res.y != 0: + resolution = new_res + aspect_ratio = float(resolution.x) / resolution.y + _update_mesh_size() + + +func _create_texture(): + if texture.get_size() != resolution: + texture.create_from_image(img, 0) + else: + texture.set_data(img) + +func _update_mesh_size(): + if aspect_ratio >= 1: + mesh.scale = Vector3(display_width, display_width / aspect_ratio, 0.025) + else: + mesh.scale = Vector3(display_height * aspect_ratio, display_height, 0.025) + +func set_view(_view: Node) -> void: + if ! _view: + return + view = _view + +func visualize() -> Control: + var visualizer = ScreenBufferVisualizer.new() + visualizer.display_node(self, "visualize_content") + return visualizer + + +func visualize_content() -> Array: + var text = " Resolution: %dx%d" % [resolution.x, resolution.y] + return [texture, aspect_ratio, text] diff --git a/project/src/objects/ray_car/RayCar.tscn b/project/src/objects/ray_car/RayCar.tscn index 35678e90..77c201fc 100644 --- a/project/src/objects/ray_car/RayCar.tscn +++ b/project/src/objects/ray_car/RayCar.tscn @@ -123,6 +123,9 @@ transform = Transform( -4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.974, [node name="Back" type="Position3D" parent="AttachmentSlots"] transform = Transform( -1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.4, 1.8 ) +[node name="Logo" type="Position3D" parent="AttachmentSlots"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00163135, 0.0371331, -2.20274 ) + [node name="BuiltinAttachments" type="Spatial" parent="."] [node name="Left BrushedMotor" type="Node" parent="BuiltinAttachments"] diff --git a/project/src/ui/node_visualizer/ScreenBufferVisualizer.gd b/project/src/ui/node_visualizer/ScreenBufferVisualizer.gd new file mode 100644 index 00000000..d8501bf1 --- /dev/null +++ b/project/src/ui/node_visualizer/ScreenBufferVisualizer.gd @@ -0,0 +1,71 @@ +# +# ScreenBufferVisualizer.gd +# Copyright 2021 ItJustWorksTM +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class_name ScreenBufferVisualizer +extends VBoxContainer + +var _node: Node = null +var _visual_func: String = "" + +var text_display: Label = Label.new() +var image_display: TextureRect = TextureRect.new() +var viewport: Viewport = Viewport.new() +var viewport_container: ViewportContainer = ViewportContainer.new() + +func display_node(node: Node, visual_func: String) -> bool: + if ! node || ! node.has_method(visual_func): + return false + _node = node + _visual_func = visual_func + + add_child(viewport_container) + viewport_container.add_child(viewport) + viewport.add_child(image_display) + + # Needed to make sure that the viewport does not swallow clicks meant for the collapsable element + viewport.set_disable_input(true) + + add_child(text_display) + + return true + +func _process(_delta: float) -> void: + if ! _node: + return + + # values[0] = texture + # values[1] = aspect ratio of image + # values[2] = text + var values = _node.call(_visual_func) + + image_display.texture = values[0] + + # Set max width of viewport and use aspect ration to determine height + var aspect_ratio = values[1] + if aspect_ratio == 0: + viewport.size = Vector2(0, 0) + else: + viewport.size = Vector2(290, 290 * (1.0/aspect_ratio)) + + # Expanding the display to fit the viewport + image_display.set_anchors_and_margins_preset(Control.PRESET_TOP_LEFT, Control.PRESET_MODE_MINSIZE, 0) + image_display.rect_min_size = viewport.size + image_display.expand = true + #image_display.stretch_mode = TextureRect.STRETCH_SCALE + + text_display.text = values[2] + diff --git a/src/bind/FrameBuffer.cxx b/src/bind/FrameBuffer.cxx index 05fd99dc..dc0ac53b 100644 --- a/src/bind/FrameBuffer.cxx +++ b/src/bind/FrameBuffer.cxx @@ -29,6 +29,7 @@ void FrameBuffer::_register_methods() { register_method("get_height", &FrameBuffer::get_height); register_method("get_freq", &FrameBuffer::get_freq); register_method("write_rgb888", &FrameBuffer::write_rgb888); + register_method("read_rgb888", &FrameBuffer::read_rgb888); } bool FrameBuffer::exists() { return frame_buf.exists(); } @@ -50,3 +51,17 @@ bool FrameBuffer::write_rgb888(Ref img) { return frame_buf.write_rgb888(byte_span); } + +PoolByteArray FrameBuffer::read_rgb888() { + auto bytes = PoolByteArray{}; + + bytes.resize(get_height() * get_width() * 3); + + const auto byte_span = + std::span{reinterpret_cast(bytes.write().ptr()), static_cast(bytes.size())}; + + // TODO: maybe check success :| + frame_buf.read_rgb888(byte_span); + + return bytes; // pray for some kind of return value optimization +} diff --git a/src/bind/Sketch.cxx b/src/bind/Sketch.cxx index 6f436d70..eb62f85d 100644 --- a/src/bind/Sketch.cxx +++ b/src/bind/Sketch.cxx @@ -34,21 +34,37 @@ void Sketch::_register_methods() { #undef U void Sketch::init(String src, String home_dir) { - sketch = - smce::Sketch{std_str(src), - {.fqbn = "arduino:sam:arduino_due_x", - .legacy_preproc_libs = {smce::SketchConfig::ArduinoLibrary{"MQTT@2.5.0"}, - smce::SketchConfig::ArduinoLibrary{"WiFi@1.2.7"}, - smce::SketchConfig::ArduinoLibrary{"Arduino_OV767X@0.0.2"}, - smce::SketchConfig::ArduinoLibrary{"SD@1.2.4"}}, - .plugins = {smce::PluginManifest{ - .name = "Smartcar_shield", - .version = "7.0.1", - .uri = "https://github.com/platisd/smartcar_shield/archive/refs/tags/7.0.1.tar.gz", - .patch_uri = "file://" + (std::filesystem::absolute(std_str(home_dir)) / - "library_patches" / "smartcar_shield") - .generic_string(), - .defaults = smce::PluginManifest::Defaults::arduino}}}}; + std::vector dep = {"ArduinoGraphics"}; + sketch = smce::Sketch{ + std_str(src), + {.fqbn = "arduino:sam:arduino_due_x", + .legacy_preproc_libs = {smce::SketchConfig::ArduinoLibrary{"MQTT@2.5.0"}, + smce::SketchConfig::ArduinoLibrary{"WiFi@1.2.7"}, + smce::SketchConfig::ArduinoLibrary{"Arduino_OV767X@0.0.2"}, + smce::SketchConfig::ArduinoLibrary{"SD@1.2.4"}}, + .plugins = { + smce::PluginManifest{ + .name = "Smartcar_shield", + .version = "7.0.1", + .uri = "https://github.com/platisd/smartcar_shield/archive/refs/tags/7.0.1.tar.gz", + .patch_uri = "file://" + (std::filesystem::absolute(std_str(home_dir)) / "library_patches" / + "smartcar_shield") + .generic_string(), + .defaults = smce::PluginManifest::Defaults::arduino}, + smce::PluginManifest{ + .name = "ArduinoGraphics", + .version = "1.0.0", + .uri = "https://github.com/arduino-libraries/ArduinoGraphics/archive/refs/tags/1.0.0.zip", + .defaults = smce::PluginManifest::Defaults::arduino}, + smce::PluginManifest{ + .name = "Arduino_MKRRGB", + .version = "1.0.0", + .depends = dep, + .uri = "https://github.com/arduino-libraries/Arduino_MKRRGB/archive/refs/tags/1.0.0.zip", + .patch_uri = "file://" + (std::filesystem::absolute(std_str(home_dir)) / "library_patches" / + "arduino_mkrrgb") + .generic_string(), + .defaults = smce::PluginManifest::Defaults::arduino}}}}; } String Sketch::get_source() { return sketch.get_source().c_str(); }