feat: updated engine

This commit is contained in:
Sara Gerretsen 2026-07-10 17:04:34 +02:00
parent cbe99774ff
commit f4cf6b3999
6607 changed files with 910135 additions and 430025 deletions

View file

@ -6,19 +6,25 @@ Import("env_modules")
env_camera = env_modules.Clone()
if env["platform"] in ["windows", "macos", "linuxbsd", "android"]:
if env["platform"] in ["windows", "macos", "linuxbsd", "android", "ios", "visionos"]:
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
elif env["platform"] == "macos":
env_camera.add_source_files(env.modules_sources, "camera_macos.mm")
env_camera.add_source_files(env.modules_sources, "camera_apple.mm")
elif env["platform"] == "android":
env_camera.add_source_files(env.modules_sources, "camera_android.cpp")
env.Append(LIBS=["camera2ndk", "mediandk"])
elif env["platform"] in ["ios", "visionos"]:
ext_module_source = ["camera_apple.mm"]
ext_camera_lib = env_camera.add_library("#bin/libgodot_camera", ext_module_source)
env.Append(LIBS_EXTERNAL=[ext_camera_lib])
env.Append(MODULES_EXTERNAL=["_camera"])
elif env["platform"] == "linuxbsd":
env_camera.add_source_files(env.modules_sources, "camera_linux.cpp")
env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp")

View file

@ -31,6 +31,7 @@
#include "camera_android.h"
#include "core/os/os.h"
#include "platform/android/display_server_android.h"
#include "platform/android/java_godot_io_wrapper.h"
#include "platform/android/os_android.h"
@ -72,7 +73,7 @@ public:
#ifndef IF_EQUAL_RETURN
#define MAKE_FORMAT_CONST(suffix) AIMAGE_FORMAT_##suffix
#define IF_EQUAL_RETURN(param, val) \
#define IF_EQUAL_RETURN(param, val) \
if (MAKE_FORMAT_CONST(val) == param) \
return #val
#endif
@ -89,8 +90,7 @@ String GetFormatName(const int32_t &format) {
// CameraFeedAndroid - Subclass for our camera feed on Android
CameraFeedAndroid::CameraFeedAndroid(ACameraManager *manager, ACameraMetadata *metadata, const char *id,
CameraFeed::FeedPosition position, int32_t orientation) :
CameraFeed() {
CameraFeed::FeedPosition position, int32_t orientation) {
this->manager = manager;
this->metadata = metadata;
this->orientation = orientation;

View file

@ -40,6 +40,7 @@
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraMetadataTags.h>
#include <media/NdkImageReader.h>
#include <optional>
enum class CameraFacing {

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_macos.h */
/* camera_apple.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -35,12 +35,15 @@
#include "servers/camera/camera_server.h"
class CameraMacOS : public CameraServer {
GDSOFTCLASS(CameraMacOS, CameraServer);
class CameraApple : public CameraServer {
GDSOFTCLASS(CameraApple, CameraServer);
public:
CameraMacOS() = default;
CameraApple() = default;
void update_feeds();
void set_monitoring_feeds(bool p_monitoring_feeds) override;
void handle_display_rotation_change(int p_orientation) override;
void handle_application_pause() override;
void handle_application_resume() override;
};

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_macos.mm */
/* camera_apple.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -31,11 +31,14 @@
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
#import "camera_macos.h"
#import "camera_apple.h"
#include "servers/camera/camera_feed.h"
#import <AVFoundation/AVFoundation.h>
#ifdef IOS_ENABLED
#import <UIKit/UIKit.h>
#endif
//////////////////////////////////////////////////////////////////////////
// MyCaptureSession - This is a little helper class so we can capture our frames
@ -63,33 +66,56 @@
width[1] = 0;
height[1] = 0;
#ifdef APPLE_EMBEDDED_ENABLED
[p_device lockForConfiguration:&error];
#ifdef IOS_ENABLED
if ([p_device lockForConfiguration:&error]) {
if ([p_device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
[p_device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if ([p_device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
[p_device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if ([p_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
}
[p_device setFocusMode:AVCaptureFocusModeLocked];
[p_device setExposureMode:AVCaptureExposureModeLocked];
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
[p_device unlockForConfiguration];
#endif // APPLE_EMBEDDED_ENABLED
[p_device unlockForConfiguration];
}
#endif // IOS_ENABLED
[self beginConfiguration];
#ifdef APPLE_EMBEDDED_ENABLED
#ifdef IOS_ENABLED
self.sessionPreset = AVCaptureSessionPreset1280x720;
#endif // APPLE_EMBEDDED_ENABLED
#endif // IOS_ENABLED
input = [[AVCaptureDeviceInput alloc] initWithDevice:p_device error:&error];
input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
if (!input) {
print_line("Couldn't get input device for camera");
[self commitConfiguration];
return nil;
}
if (![self canAddInput:input]) {
print_line("Couldn't add input to capture session");
input = nullptr;
[self commitConfiguration];
return nil;
}
[self addInput:input];
output = [AVCaptureVideoDataOutput new];
if (!output) {
print_line("Couldn't get output device for camera");
[self removeInput:input];
input = nullptr;
[self commitConfiguration];
return nil;
}
if (![self canAddOutput:output]) {
print_line("Couldn't add output to capture session");
[self removeInput:input];
input = nullptr;
output = nullptr;
[self commitConfiguration];
return nil;
}
@ -183,6 +209,7 @@
// do Y
size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
size_t row_stride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
if ((width[0] != new_width) || (height[0] != new_height)) {
width[0] = new_width;
@ -191,7 +218,15 @@
}
uint8_t *w = img_data[0].ptrw();
memcpy(w, dataY, new_width * new_height);
if (new_width == row_stride) {
memcpy(w, dataY, new_width * new_height);
} else {
for (size_t i = 0; i < new_height; i++) {
memcpy(w, dataY, new_width);
w += new_width;
dataY += row_stride;
}
}
img[0].instantiate();
img[0]->set_data(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
@ -201,6 +236,7 @@
// do CbCr
size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
size_t row_stride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
if ((width[1] != new_width) || (height[1] != new_height)) {
width[1] = new_width;
@ -209,7 +245,15 @@
}
uint8_t *w = img_data[1].ptrw();
memcpy(w, dataCbCr, 2 * new_width * new_height);
if (new_width * 2 == row_stride) {
memcpy(w, dataCbCr, 2 * new_width * new_height);
} else {
for (size_t i = 0; i < new_height; i++) {
memcpy(w, dataCbCr, new_width * 2);
w += new_width * 2;
dataCbCr += row_stride;
}
}
///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion
img[1].instantiate();
@ -226,43 +270,51 @@
@end
//////////////////////////////////////////////////////////////////////////
// CameraFeedMacOS - Subclass for camera feeds in macOS
// CameraFeedApple - Subclass for camera feeds in macOS
class CameraFeedMacOS : public CameraFeed {
GDSOFTCLASS(CameraFeedMacOS, CameraFeed);
class CameraFeedApple : public CameraFeed {
GDSOFTCLASS(CameraFeedApple, CameraFeed);
private:
AVCaptureDevice *device;
MyCaptureSession *capture_session;
bool device_locked;
bool was_active_before_pause = false;
public:
AVCaptureDevice *get_device() const;
CameraFeedMacOS();
~CameraFeedMacOS();
CameraFeedApple();
~CameraFeedApple();
void set_device(AVCaptureDevice *p_device);
void handle_rotation_change(int p_orientation);
void handle_pause();
void handle_resume();
bool activate_feed() override;
void deactivate_feed() override;
};
AVCaptureDevice *CameraFeedMacOS::get_device() const {
AVCaptureDevice *CameraFeedApple::get_device() const {
return device;
}
CameraFeedMacOS::CameraFeedMacOS() {
CameraFeedApple::CameraFeedApple() {
device = nullptr;
capture_session = nullptr;
device_locked = false;
transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
}
CameraFeedMacOS::~CameraFeedMacOS() {
CameraFeedApple::~CameraFeedApple() {
if (is_active()) {
deactivate_feed();
}
}
void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
void CameraFeedApple::set_device(AVCaptureDevice *p_device) {
device = p_device;
// get some info
@ -276,47 +328,96 @@ void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
};
}
bool CameraFeedMacOS::activate_feed() {
void CameraFeedApple::handle_rotation_change(int p_orientation) {
// UIInterfaceOrientation values:
// 1 = UIInterfaceOrientationPortrait
// 2 = UIInterfaceOrientationPortraitUpsideDown
// 3 = UIInterfaceOrientationLandscapeLeft
// 4 = UIInterfaceOrientationLandscapeRight
int display_rotation = 0;
switch (p_orientation) {
case 1:
display_rotation = 0;
break;
case 2:
display_rotation = 180;
break;
case 3:
display_rotation = 270;
break;
case 4:
display_rotation = 90;
break;
default:
display_rotation = 0;
break;
}
// iOS camera sensor orientation is 90 degrees.
int sensor_orientation = 90;
int sign = position == CameraFeed::FEED_FRONT ? 1 : -1;
int image_rotation = (sensor_orientation - display_rotation * sign + 360) % 360;
transform = Transform2D();
transform = transform.rotated(Math::deg_to_rad((float)image_rotation));
}
void CameraFeedApple::handle_pause() {
if (capture_session) {
was_active_before_pause = true;
deactivate_feed();
} else {
was_active_before_pause = false;
}
}
void CameraFeedApple::handle_resume() {
if (was_active_before_pause) {
activate_feed();
was_active_before_pause = false;
}
}
bool CameraFeedApple::activate_feed() {
if (capture_session) {
// Already recording.
return true;
}
// Start camera capture, check permission.
if (@available(macOS 10.14, *)) {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusAuthorized) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
return capture_session != nullptr;
} else if (status == AVAuthorizationStatusNotDetermined) {
// Request permission asynchronously.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted) {
if (granted) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
}
}];
return false;
} else if (status == AVAuthorizationStatusDenied) {
print_line("Camera permission denied by user.");
return false;
} else if (status == AVAuthorizationStatusRestricted) {
print_line("Camera access restricted.");
return false;
}
return false;
} else {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusAuthorized) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
return capture_session != nullptr;
} else if (status == AVAuthorizationStatusNotDetermined) {
// Request permission asynchronously.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted) {
if (granted) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
}
}];
return false;
} else if (status == AVAuthorizationStatusDenied) {
print_line("Camera permission denied by user.");
return false;
} else if (status == AVAuthorizationStatusRestricted) {
print_line("Camera access restricted.");
return false;
}
return false;
}
void CameraFeedMacOS::deactivate_feed() {
void CameraFeedApple::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
capture_session = nullptr;
};
if (device_locked) {
[device unlockForConfiguration];
device_locked = false;
}
}
//////////////////////////////////////////////////////////////////////////
@ -324,7 +425,7 @@ void CameraFeedMacOS::deactivate_feed() {
// when devices are connected/disconnected
@interface MyDeviceNotifications : NSObject {
CameraMacOS *camera_server;
CameraApple *camera_server;
}
@end
@ -335,7 +436,7 @@ void CameraFeedMacOS::deactivate_feed() {
camera_server->update_feeds();
}
- (id)initForServer:(CameraMacOS *)p_server {
- (id)initForServer:(CameraApple *)p_server {
if (self = [super init]) {
camera_server = p_server;
@ -356,29 +457,40 @@ void CameraFeedMacOS::deactivate_feed() {
MyDeviceNotifications *device_notifications = nil;
//////////////////////////////////////////////////////////////////////////
// CameraMacOS - Subclass for our camera server on macOS
// CameraApple - Subclass for our camera server on macOS
void CameraMacOS::update_feeds() {
void CameraApple::update_feeds() {
NSArray<AVCaptureDevice *> *devices = nullptr;
#if defined(__x86_64__)
if (@available(macOS 10.15, *)) {
#endif
AVCaptureDeviceDiscoverySession *session;
if (@available(macOS 14.0, *)) {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeContinuityCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
} else {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternalUnknown, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
#ifdef APPLE_EMBEDDED_ENABLED
{
NSMutableArray *deviceTypes = [NSMutableArray array];
if (@available(iOS 14.0, visionOS 2.1, *)) {
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
}
#ifdef IOS_ENABLED
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTripleCamera];
#endif // IOS_ENABLED
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
devices = session.devices;
#if defined(__x86_64__)
} else {
devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
}
#endif
#else // APPLE_EMBEDDED_ENABLED
AVCaptureDeviceDiscoverySession *session;
if (@available(macOS 14.0, *)) {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeContinuityCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
} else {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternalUnknown, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
}
devices = session.devices;
#endif // APPLE_EMBEDDED_ENABLED
// Deactivate feeds that are gone before removing them.
for (int i = feeds.size() - 1; i >= 0; i--) {
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_null()) {
continue;
}
@ -394,7 +506,7 @@ void CameraMacOS::update_feeds() {
for (AVCaptureDevice *device in devices) {
bool found = false;
for (int i = 0; i < feeds.size() && !found; i++) {
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_null()) {
continue;
}
@ -404,17 +516,29 @@ void CameraMacOS::update_feeds() {
};
if (!found) {
Ref<CameraFeedMacOS> newfeed;
Ref<CameraFeedApple> newfeed;
newfeed.instantiate();
newfeed->set_device(device);
add_feed(newfeed);
};
};
#ifdef IOS_ENABLED
// Update rotation for all feeds.
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
UIWindowScene *windowScene = window.windowScene;
if (windowScene) {
orientation = windowScene.interfaceOrientation;
}
handle_display_rotation_change((int)orientation);
#endif // IOS_ENABLED
emit_signal(SNAME(CameraServer::feeds_updated_signal_name));
}
void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
void CameraApple::set_monitoring_feeds(bool p_monitoring_feeds) {
if (p_monitoring_feeds == monitoring_feeds) {
return;
}
@ -431,3 +555,41 @@ void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
device_notifications = nil;
}
}
void CameraApple::handle_display_rotation_change(int p_orientation) {
for (int i = 0; i < feeds.size(); i++) {
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_valid()) {
feed->handle_rotation_change(p_orientation);
}
}
}
void CameraApple::handle_application_pause() {
for (int i = 0; i < feeds.size(); i++) {
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_valid()) {
feed->handle_pause();
}
}
}
void CameraApple::handle_application_resume() {
for (int i = 0; i < feeds.size(); i++) {
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_valid()) {
feed->handle_resume();
}
}
}
#ifdef APPLE_EMBEDDED_ENABLED
void register_camera_external_module() {
CameraServer::make_default<CameraApple>();
}
void unregister_camera_external_module() {
}
#endif // APPLE_EMBEDDED_ENABLED

View file

@ -350,8 +350,7 @@ bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
return true;
}
CameraFeedLinux::CameraFeedLinux(const String &p_device_name) :
CameraFeed() {
CameraFeedLinux::CameraFeedLinux(const String &p_device_name) {
device_name = p_device_name;
_query_device(device_name);
}

View file

@ -32,6 +32,7 @@
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "servers/camera/camera_server.h"
class CameraLinux : public CameraServer {

View file

@ -30,6 +30,8 @@
#include "camera_win.h"
#include "servers/camera/camera_feed.h"
///@TODO sorry guys, I got about 80% through implementing this using DirectShow only
// to find out Microsoft deprecated half the API and its replacement is as confusing
// as they could make it. Joey suggested looking into libuvc which offers a more direct

View file

@ -30,7 +30,6 @@
#pragma once
#include "servers/camera/camera_feed.h"
#include "servers/camera/camera_server.h"
class CameraWindows : public CameraServer {

View file

@ -3,7 +3,14 @@ def can_build(env, platform):
if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
return False
return platform == "macos" or platform == "windows" or platform == "linuxbsd" or platform == "android"
return (
platform == "macos"
or platform == "windows"
or platform == "linuxbsd"
or platform == "android"
or platform == "ios"
or platform == "visionos"
)
def configure(env):

View file

@ -37,7 +37,7 @@
#include "camera_win.h"
#endif
#if defined(MACOS_ENABLED)
#include "camera_macos.h"
#include "camera_apple.h"
#endif
#if defined(ANDROID_ENABLED)
#include "camera_android.h"
@ -55,7 +55,7 @@ void initialize_camera_module(ModuleInitializationLevel p_level) {
CameraServer::make_default<CameraWindows>();
#endif
#if defined(MACOS_ENABLED)
CameraServer::make_default<CameraMacOS>();
CameraServer::make_default<CameraApple>();
#endif
#if defined(ANDROID_ENABLED)
CameraServer::make_default<CameraAndroid>();