mirror of https://github.com/mgba-emu/mgba.git
3DS: Support camera
This commit is contained in:
parent
8a3d54c769
commit
22400e336f
|
@ -27,6 +27,9 @@
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <3ds/gpu/gx.h>
|
#include <3ds/gpu/gx.h>
|
||||||
|
|
||||||
|
mLOG_DECLARE_CATEGORY(GUI_3DS);
|
||||||
|
mLOG_DEFINE_CATEGORY(GUI_3DS, "3DS", "gui.3ds");
|
||||||
|
|
||||||
static enum ScreenMode {
|
static enum ScreenMode {
|
||||||
SM_PA_BOTTOM,
|
SM_PA_BOTTOM,
|
||||||
SM_AF_BOTTOM,
|
SM_AF_BOTTOM,
|
||||||
|
@ -58,12 +61,21 @@ static enum DarkenMode {
|
||||||
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16)
|
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16)
|
||||||
#define DSP_BUFFERS 4
|
#define DSP_BUFFERS 4
|
||||||
|
|
||||||
static struct GBA3DSRotationSource {
|
static struct m3DSRotationSource {
|
||||||
struct mRotationSource d;
|
struct mRotationSource d;
|
||||||
accelVector accel;
|
accelVector accel;
|
||||||
angularRate gyro;
|
angularRate gyro;
|
||||||
} rotation;
|
} rotation;
|
||||||
|
|
||||||
|
static struct m3DSImageSource {
|
||||||
|
struct mImageSource d;
|
||||||
|
Handle handles[2];
|
||||||
|
u32 bufferSize;
|
||||||
|
u32 transferSize;
|
||||||
|
void* buffer;
|
||||||
|
unsigned cam;
|
||||||
|
} camera;
|
||||||
|
|
||||||
static enum {
|
static enum {
|
||||||
NO_SOUND,
|
NO_SOUND,
|
||||||
DSP_SUPPORTED,
|
DSP_SUPPORTED,
|
||||||
|
@ -128,6 +140,7 @@ static void _cleanup(void) {
|
||||||
ndspExit();
|
ndspExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
camExit();
|
||||||
csndExit();
|
csndExit();
|
||||||
ptmuExit();
|
ptmuExit();
|
||||||
}
|
}
|
||||||
|
@ -234,6 +247,19 @@ static void _guiFinish(void) {
|
||||||
ctrFlushBatch();
|
ctrFlushBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _resetCamera(struct m3DSImageSource* imageSource) {
|
||||||
|
if (!imageSource->cam) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CAMU_SetSize(imageSource->cam, SIZE_QCIF, CONTEXT_A);
|
||||||
|
CAMU_SetOutputFormat(imageSource->cam, OUTPUT_RGB_565, CONTEXT_A);
|
||||||
|
CAMU_SetFrameRate(imageSource->cam, FRAME_RATE_30);
|
||||||
|
|
||||||
|
CAMU_SetNoiseFilter(imageSource->cam, true);
|
||||||
|
CAMU_SetAutoExposure(imageSource->cam, false);
|
||||||
|
CAMU_SetAutoWhiteBalance(imageSource->cam, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void _setup(struct mGUIRunner* runner) {
|
static void _setup(struct mGUIRunner* runner) {
|
||||||
bool isNew3DS = false;
|
bool isNew3DS = false;
|
||||||
APT_CheckNew3DS(&isNew3DS);
|
APT_CheckNew3DS(&isNew3DS);
|
||||||
|
@ -243,6 +269,7 @@ static void _setup(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
|
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
|
||||||
|
runner->core->setPeripheral(runner->core, mPERIPH_IMAGE_SOURCE, &camera.d);
|
||||||
if (hasSound != NO_SOUND) {
|
if (hasSound != NO_SOUND) {
|
||||||
runner->core->setAVStream(runner->core, &stream);
|
runner->core->setAVStream(runner->core, &stream);
|
||||||
}
|
}
|
||||||
|
@ -284,6 +311,7 @@ static void _setup(struct mGUIRunner* runner) {
|
||||||
static void _gameLoaded(struct mGUIRunner* runner) {
|
static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
switch (runner->core->platform(runner->core)) {
|
switch (runner->core->platform(runner->core)) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
|
// TODO: Move these to callbacks
|
||||||
case PLATFORM_GBA:
|
case PLATFORM_GBA:
|
||||||
if (((struct GBA*) runner->core->board)->memory.hw.devices & HW_TILT) {
|
if (((struct GBA*) runner->core->board)->memory.hw.devices & HW_TILT) {
|
||||||
HIDUSER_EnableAccelerometer();
|
HIDUSER_EnableAccelerometer();
|
||||||
|
@ -334,6 +362,27 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
if (mCoreConfigGetUIntValue(&runner->config, "darkenMode", &mode) && mode < DM_MAX) {
|
if (mCoreConfigGetUIntValue(&runner->config, "darkenMode", &mode) && mode < DM_MAX) {
|
||||||
darkenMode = mode;
|
darkenMode = mode;
|
||||||
}
|
}
|
||||||
|
if (mCoreConfigGetUIntValue(&runner->config, "camera", &mode)) {
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
mode = SELECT_NONE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
mode = SELECT_IN1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mode = SELECT_OUT1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mode != camera.cam) {
|
||||||
|
camera.cam = mode;
|
||||||
|
if (camera.buffer) {
|
||||||
|
_resetCamera(&camera);
|
||||||
|
CAMU_Activate(camera.cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gameUnloaded(struct mGUIRunner* runner) {
|
static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||||
|
@ -347,6 +396,7 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||||
|
|
||||||
switch (runner->core->platform(runner->core)) {
|
switch (runner->core->platform(runner->core)) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
|
// TODO: Move these to callbacks
|
||||||
case PLATFORM_GBA:
|
case PLATFORM_GBA:
|
||||||
if (((struct GBA*) runner->core->board)->memory.hw.devices & HW_TILT) {
|
if (((struct GBA*) runner->core->board)->memory.hw.devices & HW_TILT) {
|
||||||
HIDUSER_DisableAccelerometer();
|
HIDUSER_DisableAccelerometer();
|
||||||
|
@ -600,27 +650,97 @@ static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _sampleRotation(struct mRotationSource* source) {
|
static void _sampleRotation(struct mRotationSource* source) {
|
||||||
struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
|
struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source;
|
||||||
// Work around ctrulib getting the entries wrong
|
// Work around ctrulib getting the entries wrong
|
||||||
rotation->accel = *(accelVector*) &hidSharedMem[0x48];
|
rotation->accel = *(accelVector*) &hidSharedMem[0x48];
|
||||||
rotation->gyro = *(angularRate*) &hidSharedMem[0x5C];
|
rotation->gyro = *(angularRate*) &hidSharedMem[0x5C];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t _readTiltX(struct mRotationSource* source) {
|
static int32_t _readTiltX(struct mRotationSource* source) {
|
||||||
struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
|
struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source;
|
||||||
return rotation->accel.x << 18L;
|
return rotation->accel.x << 18L;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t _readTiltY(struct mRotationSource* source) {
|
static int32_t _readTiltY(struct mRotationSource* source) {
|
||||||
struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
|
struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source;
|
||||||
return rotation->accel.y << 18L;
|
return rotation->accel.y << 18L;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t _readGyroZ(struct mRotationSource* source) {
|
static int32_t _readGyroZ(struct mRotationSource* source) {
|
||||||
struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
|
struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source;
|
||||||
return rotation->gyro.y << 18L; // Yes, y
|
return rotation->gyro.y << 18L; // Yes, y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _startRequestImage(struct mImageSource* source, unsigned w, unsigned h, int colorFormats) {
|
||||||
|
UNUSED(colorFormats);
|
||||||
|
struct m3DSImageSource* imageSource = (struct m3DSImageSource*) source;
|
||||||
|
|
||||||
|
_resetCamera(imageSource);
|
||||||
|
|
||||||
|
CAMU_SetTrimming(PORT_CAM1, true);
|
||||||
|
CAMU_SetTrimmingParamsCenter(PORT_CAM1, w, h, 176, 144);
|
||||||
|
CAMU_GetBufferErrorInterruptEvent(&imageSource->handles[1], PORT_CAM1);
|
||||||
|
|
||||||
|
if (imageSource->bufferSize != w * h * 2 && imageSource->buffer) {
|
||||||
|
free(imageSource->buffer);
|
||||||
|
imageSource->buffer = NULL;
|
||||||
|
}
|
||||||
|
imageSource->bufferSize = w * h * 2;
|
||||||
|
if (!imageSource->buffer) {
|
||||||
|
imageSource->buffer = malloc(imageSource->bufferSize);
|
||||||
|
}
|
||||||
|
CAMU_GetMaxBytes(&imageSource->transferSize, w, h);
|
||||||
|
CAMU_SetTransferBytes(PORT_CAM1, imageSource->transferSize, w, h);
|
||||||
|
CAMU_Activate(imageSource->cam);
|
||||||
|
CAMU_ClearBuffer(PORT_CAM1);
|
||||||
|
CAMU_StartCapture(PORT_CAM1);
|
||||||
|
CAMU_PlayShutterSound(SHUTTER_SOUND_TYPE_MOVIE);
|
||||||
|
|
||||||
|
if (imageSource->cam) {
|
||||||
|
CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _stopRequestImage(struct mImageSource* source) {
|
||||||
|
struct m3DSImageSource* imageSource = (struct m3DSImageSource*) source;
|
||||||
|
|
||||||
|
free(imageSource->buffer);
|
||||||
|
imageSource->buffer = NULL;
|
||||||
|
svcCloseHandle(imageSource->handles[0]);
|
||||||
|
svcCloseHandle(imageSource->handles[1]);
|
||||||
|
|
||||||
|
CAMU_StopCapture(PORT_CAM1);
|
||||||
|
CAMU_Activate(SELECT_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _requestImage(struct mImageSource* source, const void** buffer, size_t* stride, enum mColorFormat* colorFormat) {
|
||||||
|
struct m3DSImageSource* imageSource = (struct m3DSImageSource*) source;
|
||||||
|
|
||||||
|
if (!imageSource->cam) {
|
||||||
|
memset(imageSource->buffer, 0, imageSource->bufferSize);
|
||||||
|
*buffer = imageSource->buffer;
|
||||||
|
*stride = 128;
|
||||||
|
*colorFormat = mCOLOR_RGB565;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 i;
|
||||||
|
svcWaitSynchronizationN(&i, imageSource->handles, 2, false, U64_MAX);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
*buffer = imageSource->buffer;
|
||||||
|
*stride = 128;
|
||||||
|
*colorFormat = mCOLOR_RGB565;
|
||||||
|
} else {
|
||||||
|
CAMU_ClearBuffer(PORT_CAM1);
|
||||||
|
CAMU_StartCapture(PORT_CAM1);
|
||||||
|
}
|
||||||
|
|
||||||
|
svcCloseHandle(imageSource->handles[0]);
|
||||||
|
CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize);
|
||||||
|
}
|
||||||
|
|
||||||
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
|
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
|
||||||
UNUSED(stream);
|
UNUSED(stream);
|
||||||
if (hasSound == CSND_SUPPORTED) {
|
if (hasSound == CSND_SUPPORTED) {
|
||||||
|
@ -670,6 +790,13 @@ int main() {
|
||||||
stream.postAudioFrame = 0;
|
stream.postAudioFrame = 0;
|
||||||
stream.postAudioBuffer = _postAudioBuffer;
|
stream.postAudioBuffer = _postAudioBuffer;
|
||||||
|
|
||||||
|
camera.d.startRequestImage = _startRequestImage;
|
||||||
|
camera.d.stopRequestImage = _stopRequestImage;
|
||||||
|
camera.d.requestImage = _requestImage;
|
||||||
|
camera.buffer = NULL;
|
||||||
|
camera.bufferSize = 0;
|
||||||
|
camera.cam = SELECT_IN1;
|
||||||
|
|
||||||
if (!allocateRomBuffer()) {
|
if (!allocateRomBuffer()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -677,6 +804,8 @@ int main() {
|
||||||
aptHook(&cookie, _aptHook, 0);
|
aptHook(&cookie, _aptHook, 0);
|
||||||
|
|
||||||
ptmuInit();
|
ptmuInit();
|
||||||
|
camInit();
|
||||||
|
|
||||||
hasSound = NO_SOUND;
|
hasSound = NO_SOUND;
|
||||||
if (!ndspInit()) {
|
if (!ndspInit()) {
|
||||||
hasSound = DSP_SUPPORTED;
|
hasSound = DSP_SUPPORTED;
|
||||||
|
@ -819,9 +948,21 @@ int main() {
|
||||||
"Grayed",
|
"Grayed",
|
||||||
},
|
},
|
||||||
.nStates = 4
|
.nStates = 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Camera",
|
||||||
|
.data = "camera",
|
||||||
|
.submenu = 0,
|
||||||
|
.state = 1,
|
||||||
|
.validStates = (const char*[]) {
|
||||||
|
"None",
|
||||||
|
"Inner",
|
||||||
|
"Outer",
|
||||||
|
},
|
||||||
|
.nStates = 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.nConfigExtra = 3,
|
.nConfigExtra = 4,
|
||||||
.setup = _setup,
|
.setup = _setup,
|
||||||
.teardown = 0,
|
.teardown = 0,
|
||||||
.gameLoaded = _gameLoaded,
|
.gameLoaded = _gameLoaded,
|
||||||
|
|
Loading…
Reference in New Issue