@@ -46,6 +46,26 @@ namespace Babylon::Plugins
4646 return oesTexture;
4747 }
4848
49+ int GetCurrentSensorRotationDiff () {
50+ // Get the phone's current rotation so we can determine if the camera image needs to be rotated based on the sensor's natural orientation
51+ int32_t phoneRotation{GetAppContext ().getSystemService <android::view::WindowManager>().getDefaultDisplay ().getRotation () * 90 };
52+
53+ // The sensor rotation dictates the orientation of the camera when the phone is in it's default orientation
54+ // Subtracting the phone's rotation from the camera's rotation will give us the current orientation
55+ // of the sensor. Then add 360 and modulus 360 to ensure we're always talking about positive degrees.
56+ int currentSensorRotationDiff{(sensorRotation - phoneRotation + 360 ) % 360 };
57+ bool sensorIsPortrait{currentSensorRotationDiff == 90 || currentSensorRotationDiff == 270 };
58+ if (facingUser && !sensorIsPortrait)
59+ {
60+ // Compensate for the front facing camera being naturally mirrored. In the portrait orientation
61+ // the mirrored behavior matches the browser, but in landscape it would result in the image rendering
62+ // upside down. Rotate the image by 180 to compensate.
63+ currentSensorRotationDiff = (currentSensorRotationDiff + 180 ) % 360 ;
64+ }
65+
66+ return currentSensorRotationDiff;
67+ }
68+
4969 Napi::Env env;
5070
5171 arcana::affinity threadAffinity{};
@@ -56,6 +76,8 @@ namespace Babylon::Plugins
5676 int32_t sensorRotation{};
5777 bool facingUser{};
5878 CameraDimensions cameraDimensions{};
79+ int32_t sensorRotationDiff{};
80+ bool updateTextureDimensions{true };
5981
6082 Graphics::DeviceContext* deviceContext{};
6183
@@ -73,7 +95,6 @@ namespace Babylon::Plugins
7395 GLuint cameraRGBATextureId{};
7496 GLuint cameraShaderProgramId{};
7597 GLuint frameBufferId{};
76- const GLfloat* cameraUVs{};
7798
7899 EGLContext context{EGL_NO_CONTEXT};
79100 EGLDisplay display{};
@@ -186,25 +207,10 @@ namespace Babylon::Plugins
186207 return android::Permissions::CheckCameraPermissionAsync ().then (arcana::inline_scheduler, arcana::cancellation::none (), [this , &track]()
187208 {
188209 // Get the phone's current rotation so we can determine if the camera image needs to be rotated based on the sensor's natural orientation
189- int phoneRotation{GetAppContext ().getSystemService <android::view::WindowManager>().getDefaultDisplay ().getRotation () * 90 };
190-
191- // The sensor rotation dictates the orientation of the camera when the phone is in it's default orientation
192- // Subtracting the phone's rotation from the camera's rotation will give us the current orientation
193- // of the sensor. Then add 360 and modulus 360 to ensure we're always talking about positive degrees.
194- int sensorRotationDiff{(m_impl->sensorRotation - phoneRotation + 360 ) % 360 };
195- bool sensorIsPortrait{sensorRotationDiff == 90 || sensorRotationDiff == 270 };
196- if (m_impl->facingUser && !sensorIsPortrait)
197- {
198- // Compensate for the front facing camera being naturally mirrored. In the portrait orientation
199- // the mirrored behavior matches the browser, but in landscape it would result in the image rendering
200- // upside down. Rotate the image by 180 to compensate.
201- sensorRotationDiff = (sensorRotationDiff + 180 ) % 360 ;
202- }
210+ m_impl->sensorRotationDiff = m_impl->GetCurrentSensorRotationDiff ();
203211
204- // To match the web implementation if the sensor is rotated into a portrait orientation then the width and height
205- // of the video should be swapped
206- m_impl->cameraDimensions .width = !sensorIsPortrait ? track.Width () : track.Height ();
207- m_impl->cameraDimensions .height = !sensorIsPortrait ? track.Height () : track.Width ();
212+ m_impl->cameraDimensions .width = track.Width ();
213+ m_impl->cameraDimensions .height = track.Height ();
208214
209215 // Check if there is an already available context for this thread
210216 EGLContext currentContext = eglGetCurrentContext ();
@@ -243,26 +249,6 @@ namespace Babylon::Plugins
243249 }
244250 }
245251
246- glGenTextures (1 , &m_impl->cameraRGBATextureId );
247- glBindTexture (GL_TEXTURE_2D, m_impl->cameraRGBATextureId );
248- glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, m_impl->cameraDimensions .width , m_impl->cameraDimensions .height , 0 , GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
249- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
250- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
251- glGenerateMipmap (GL_TEXTURE_2D);
252-
253- glBindTexture (GL_TEXTURE_2D, 0 );
254-
255- glGenFramebuffers (1 , &m_impl->frameBufferId );
256- glBindFramebuffer (GL_FRAMEBUFFER, m_impl->frameBufferId );
257- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_impl->cameraRGBATextureId , 0 );
258-
259- glBindFramebuffer (GL_FRAMEBUFFER, 0 );
260-
261- m_impl->cameraUVs = sensorRotationDiff == 90 ? CAMERA_UVS_ROTATION_90 :
262- sensorRotationDiff == 180 ? CAMERA_UVS_ROTATION_180 :
263- sensorRotationDiff == 270 ? CAMERA_UVS_ROTATION_270 :
264- CAMERA_UVS_ROTATION_0;
265-
266252 m_impl->cameraShaderProgramId = android::OpenGLHelpers::CreateShaderProgram (CAMERA_VERT_SHADER, CAMERA_FRAG_SHADER);
267253
268254 m_impl->cameraOESTextureId = m_impl->GenerateOESTexture ();
@@ -315,7 +301,12 @@ namespace Babylon::Plugins
315301 throw std::runtime_error{" Unable to restore GL context for camera texture init." };
316302 }
317303
318- return m_impl->cameraDimensions ;
304+ // To match the web implementation if the sensor is rotated into a portrait orientation then the width and height
305+ // of the video should be swapped
306+ bool sensorIsPortrait{m_impl->sensorRotationDiff == 90 || m_impl->sensorRotationDiff == 270 };
307+ return !sensorIsPortrait
308+ ? CameraDimensions{m_impl->cameraDimensions .width , m_impl->cameraDimensions .height }
309+ : CameraDimensions{m_impl->cameraDimensions .height , m_impl->cameraDimensions .width };
319310 });
320311 }
321312
@@ -446,7 +437,7 @@ namespace Babylon::Plugins
446437 return cameraDevices;
447438 }
448439
449- void CameraDevice::UpdateCameraTexture (bgfx::TextureHandle textureHandle)
440+ CameraDevice::CameraDimensions CameraDevice::UpdateCameraTexture (bgfx::TextureHandle textureHandle)
450441 {
451442 EGLContext currentContext = eglGetCurrentContext ();
452443 if (m_impl->context != EGL_NO_CONTEXT)
@@ -458,17 +449,52 @@ namespace Babylon::Plugins
458449 }
459450 }
460451
452+ int currentSensorRotationDiff = m_impl->GetCurrentSensorRotationDiff ();
453+
454+ // The UI Orientation has changed. Update our internal texture
455+ if (currentSensorRotationDiff != m_impl->sensorRotationDiff )
456+ {
457+ m_impl->sensorRotationDiff = currentSensorRotationDiff;
458+ m_impl->updateTextureDimensions = true ;
459+ }
460+
461+ bool sensorIsPortrait{m_impl->sensorRotationDiff == 90 || m_impl->sensorRotationDiff == 270 };
462+
463+ if (m_impl->updateTextureDimensions )
464+ {
465+ glGenTextures (1 , &m_impl->cameraRGBATextureId );
466+ glBindTexture (GL_TEXTURE_2D, m_impl->cameraRGBATextureId );
467+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, !sensorIsPortrait ? m_impl->cameraDimensions .width : m_impl->cameraDimensions .height , !sensorIsPortrait ? m_impl->cameraDimensions .height : m_impl->cameraDimensions .width , 0 , GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
468+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
469+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
470+ glGenerateMipmap (GL_TEXTURE_2D);
471+
472+ glBindTexture (GL_TEXTURE_2D, 0 );
473+
474+ glGenFramebuffers (1 , &m_impl->frameBufferId );
475+ glBindFramebuffer (GL_FRAMEBUFFER, m_impl->frameBufferId );
476+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_impl->cameraRGBATextureId , 0 );
477+
478+ glBindFramebuffer (GL_FRAMEBUFFER, 0 );
479+
480+ m_impl->updateTextureDimensions = false ;
481+ }
482+
461483 m_impl->surfaceTexture .updateTexImage ();
462484
463485 glBindFramebuffer (GL_FRAMEBUFFER, m_impl->frameBufferId );
464- glViewport (0 , 0 , m_impl->cameraDimensions .width , m_impl->cameraDimensions .height );
486+ glViewport (0 , 0 , !sensorIsPortrait ? m_impl->cameraDimensions .width : m_impl-> cameraDimensions . height , !sensorIsPortrait ? m_impl->cameraDimensions .height : m_impl-> cameraDimensions . width );
465487 glUseProgram (m_impl->cameraShaderProgramId );
466488
467489 auto vertexPositionsUniformLocation{glGetUniformLocation (m_impl->cameraShaderProgramId , " positions" )};
468490 glUniform2fv (vertexPositionsUniformLocation, CAMERA_VERTEX_COUNT, CAMERA_VERTEX_POSITIONS);
469491
470492 auto uvsUniformLocation{glGetUniformLocation (m_impl->cameraShaderProgramId , " uvs" )};
471- glUniform2fv (uvsUniformLocation, CAMERA_UVS_COUNT, m_impl->cameraUVs );
493+ glUniform2fv (uvsUniformLocation, CAMERA_UVS_COUNT,
494+ m_impl->sensorRotationDiff == 90 ? CAMERA_UVS_ROTATION_90 :
495+ m_impl->sensorRotationDiff == 180 ? CAMERA_UVS_ROTATION_180 :
496+ m_impl->sensorRotationDiff == 270 ? CAMERA_UVS_ROTATION_270 :
497+ CAMERA_UVS_ROTATION_0);
472498
473499 // Configure the camera texture
474500 auto cameraTextureUniformLocation{glGetUniformLocation (m_impl->cameraShaderProgramId , " cameraTexture" )};
@@ -492,6 +518,10 @@ namespace Babylon::Plugins
492518 arcana::make_task (m_impl->deviceContext ->BeforeRenderScheduler (), arcana::cancellation::none (), [rgbaTextureId = m_impl->cameraRGBATextureId , textureHandle] {
493519 bgfx::overrideInternal (textureHandle, rgbaTextureId);
494520 });
521+
522+ return !sensorIsPortrait
523+ ? CameraDimensions{m_impl->cameraDimensions .width , m_impl->cameraDimensions .height }
524+ : CameraDimensions{m_impl->cameraDimensions .height , m_impl->cameraDimensions .width };
495525 }
496526
497527 void CameraDevice::Close ()
0 commit comments