diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 99f6760e..def1a40b 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -13,6 +13,7 @@ diff --git a/app/build.gradle b/app/build.gradle index 539f7fcb..31b27a54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,7 @@ android { applicationIdSuffix '.leia' minSdkVersion 30 dependencies { - implementation files("../leia-cnsdk/cnsdk.aar") + implementation project(":leia-cnsdk") } } } diff --git a/app/src/leia/java/com/simongellis/vvb/game/LeiaSurfaceViewAdapter.kt b/app/src/leia/java/com/simongellis/vvb/game/LeiaSurfaceViewAdapter.kt index 8c46268a..65ba9b55 100644 --- a/app/src/leia/java/com/simongellis/vvb/game/LeiaSurfaceViewAdapter.kt +++ b/app/src/leia/java/com/simongellis/vvb/game/LeiaSurfaceViewAdapter.kt @@ -23,7 +23,6 @@ class LeiaSurfaceViewAdapter : InterlacedSurfaceView, SurfaceViewAdapter, LeiaSD initArgs.platform.context = context.applicationContext initArgs.platform.activity = getActivity(context) initArgs.enableFaceTracking = true - initArgs.requiresFaceTrackingPermissionCheck = false LeiaSDK.createSDK(initArgs) } @@ -82,7 +81,7 @@ class LeiaSurfaceViewAdapter : InterlacedSurfaceView, SurfaceViewAdapter, LeiaSD Log.i("LeiaSurfaceViewAdapter", "setting up $textureId (error 0x${GLES20.glGetError().toString(16)})") GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId) Log.i("LeiaSurfaceViewAdapter", "bound $textureId (error 0x${GLES20.glGetError().toString(16)})") - GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width * 2, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null) + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null) Log.i("LeiaSurfaceViewAdapter", "initialized $textureId (error 0x${GLES20.glGetError().toString(16)})") val framebuffers = IntArray(1) @@ -103,6 +102,7 @@ class LeiaSurfaceViewAdapter : InterlacedSurfaceView, SurfaceViewAdapter, LeiaSD override fun didInitialize(sdk: LeiaSDK) { Log.i("LeiaSurfaceViewAdapter", "didInitialize") + sdk.enableBacklight(true) } override fun onFaceTrackingFatalError(sdk: LeiaSDK) { diff --git a/app/src/main/java/com/simongellis/vvb/emulator/CNSDKRenderer.kt b/app/src/main/java/com/simongellis/vvb/emulator/CNSDKRenderer.kt new file mode 100644 index 00000000..65cadd3c --- /dev/null +++ b/app/src/main/java/com/simongellis/vvb/emulator/CNSDKRenderer.kt @@ -0,0 +1,45 @@ +package com.simongellis.vvb.emulator + +import androidx.annotation.ColorInt + +class CNSDKRenderer(emulator: Emulator, settings: Settings): Renderer { + private var _pointer = 0L + + init { + nativeConstructor(emulator, settings) + } + + fun finalize() { + destroy() + } + + override fun destroy() { + if (_pointer != 0L) { + nativeDestructor() + } + } + + override fun onSurfaceCreated() { + nativeOnSurfaceCreated() + } + + override fun onSurfaceChanged(width: Int, height: Int) { + nativeOnSurfaceChanged(width, height) + } + + override fun onDrawFrame() { + nativeOnDrawFrame() + } + + class Settings( + val screenZoom: Float, + val aspectRatio: Int, + val verticalOffset: Float, + @ColorInt val color: Int) + + private external fun nativeConstructor(emulator: Emulator, settings: Settings) + private external fun nativeDestructor() + private external fun nativeOnSurfaceCreated() + private external fun nativeOnSurfaceChanged(width: Int, height: Int) + private external fun nativeOnDrawFrame() +} \ No newline at end of file diff --git a/app/src/main/java/com/simongellis/vvb/game/GameActivity.kt b/app/src/main/java/com/simongellis/vvb/game/GameActivity.kt index da6015a1..bcb4356f 100644 --- a/app/src/main/java/com/simongellis/vvb/game/GameActivity.kt +++ b/app/src/main/java/com/simongellis/vvb/game/GameActivity.kt @@ -35,7 +35,7 @@ class GameActivity : AppCompatActivity() { _audio = Audio(emulator, _preferences.audioSettings) _controller = Controller(emulator) - _view = GameView(baseContext) + _view = GameView(this) requestedOrientation = _view.requestedOrientation _view.controller = _controller setContentView(_view) diff --git a/app/src/main/java/com/simongellis/vvb/game/GamePreferences.kt b/app/src/main/java/com/simongellis/vvb/game/GamePreferences.kt index 61b65303..1b25bfd6 100644 --- a/app/src/main/java/com/simongellis/vvb/game/GamePreferences.kt +++ b/app/src/main/java/com/simongellis/vvb/game/GamePreferences.kt @@ -74,6 +74,9 @@ class GamePreferences(context: Context) { val stereoSettings get() = StereoRenderer.Settings(screenZoom, aspectRatio.ordinal, verticalOffset, color) + val cnsdkSettings + get() = CNSDKRenderer.Settings(screenZoom, aspectRatio.ordinal, verticalOffset, color) + val leiaSettings get() = LeiaRenderer.Settings(screenZoom, aspectRatio.ordinal, verticalOffset, color, colorBG) @@ -83,7 +86,7 @@ class GamePreferences(context: Context) { val prefs = PreferenceManager.getDefaultSharedPreferences(context) - val displayManager = LeiaSDK.getDisplayManager(context) + val displayManager = true//LeiaSDK.getDisplayManager(context) var defaultMode = VideoMode.ANAGLYPH.name var defaultScreenZoom = 100 if(displayManager !== null){ diff --git a/app/src/main/java/com/simongellis/vvb/game/GameView.kt b/app/src/main/java/com/simongellis/vvb/game/GameView.kt index f9811342..a1cfb533 100644 --- a/app/src/main/java/com/simongellis/vvb/game/GameView.kt +++ b/app/src/main/java/com/simongellis/vvb/game/GameView.kt @@ -1,9 +1,9 @@ package com.simongellis.vvb.game +import android.app.Activity import android.content.Context import android.content.pm.ActivityInfo import android.graphics.Color -import android.util.AttributeSet import android.view.LayoutInflater import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible @@ -19,7 +19,7 @@ import com.leia.android.lights.BacklightModeListener import com.leia.android.lights.LeiaDisplayManager.BacklightMode.MODE_2D import com.leia.android.lights.LeiaDisplayManager.BacklightMode.MODE_3D -class GameView : ConstraintLayout, BacklightModeListener { +class GameView(context: Context) : ConstraintLayout(context), BacklightModeListener { private val _binding: GameViewBinding private val _renderer: Renderer private val _preferences: GamePreferences @@ -38,12 +38,6 @@ class GameView : ConstraintLayout, BacklightModeListener { private val _surfaceView: SurfaceViewAdapter get() = _binding.surfaceView as SurfaceViewAdapter - constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - @Suppress("unused") - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) - init { val emulator = Emulator.instance _preferences = GamePreferences(context) @@ -53,7 +47,7 @@ class GameView : ConstraintLayout, BacklightModeListener { VideoMode.MONO_LEFT -> MonoRenderer(emulator, _preferences.monoSettings(Eye.LEFT)) VideoMode.MONO_RIGHT -> MonoRenderer(emulator, _preferences.monoSettings(Eye.RIGHT)) VideoMode.STEREO -> StereoRenderer(emulator, _preferences.stereoSettings) - VideoMode.LEIA -> LeiaRenderer(emulator, _preferences.leiaSettings) + VideoMode.LEIA -> CNSDKRenderer(emulator, _preferences.cnsdkSettings) } val layoutInflater = LayoutInflater.from(context) @@ -77,11 +71,14 @@ class GameView : ConstraintLayout, BacklightModeListener { setBackgroundColor(Color.BLACK) + /* mDisplayManager = LeiaSDK.getDisplayManager(context) mDisplayManager?.apply { registerBacklightModeListener(this@GameView) checkShouldToggle3D(true) } + + */ } fun onPause() { @@ -124,10 +121,10 @@ class GameView : ConstraintLayout, BacklightModeListener { } private fun enable3D() { - mDisplayManager?.requestBacklightMode(MODE_3D) + //mDisplayManager?.requestBacklightMode(MODE_3D) } private fun disable3D() { - mDisplayManager?.requestBacklightMode(MODE_2D) + //mDisplayManager?.requestBacklightMode(MODE_2D) } } \ No newline at end of file diff --git a/app/src/main/java/com/simongellis/vvb/game/PreviewActivity.kt b/app/src/main/java/com/simongellis/vvb/game/PreviewActivity.kt index c7f203d8..75f48dd5 100644 --- a/app/src/main/java/com/simongellis/vvb/game/PreviewActivity.kt +++ b/app/src/main/java/com/simongellis/vvb/game/PreviewActivity.kt @@ -18,7 +18,7 @@ class PreviewActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) VvbLibrary.instance.initialize(this) - _view = GameView(baseContext) + _view = GameView(this) _preferences = GamePreferences(baseContext) requestedOrientation = _view.requestedOrientation setContentView(_view) diff --git a/app/src/main/java/com/simongellis/vvb/menu/VideoMenuFragment.kt b/app/src/main/java/com/simongellis/vvb/menu/VideoMenuFragment.kt index 78fa0da2..1f36e994 100644 --- a/app/src/main/java/com/simongellis/vvb/menu/VideoMenuFragment.kt +++ b/app/src/main/java/com/simongellis/vvb/menu/VideoMenuFragment.kt @@ -60,7 +60,7 @@ class VideoMenuFragment: PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences_video, rootKey) var defaultModeName = VideoMode.ANAGLYPH.name - val leiaDisplayManager = LeiaSDK.getDisplayManager(context) + val leiaDisplayManager = true//LeiaSDK.getDisplayManager(context) var defaultBGColor = ContextCompat.getColor(requireContext(), R.color.black) var defaultScreenZoom = 100 if(leiaDisplayManager !== null){ diff --git a/leia-cnsdk/.gitignore b/leia-cnsdk/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/leia-cnsdk/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/leia-cnsdk/build.gradle b/leia-cnsdk/build.gradle new file mode 100644 index 00000000..810b7f62 --- /dev/null +++ b/leia-cnsdk/build.gradle @@ -0,0 +1,2 @@ +configurations.maybeCreate("default") +artifacts.add("default", file('cnsdk.aar')) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6e2ceea3..0010ed6f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ include ':cardboard-sdk' +include ':leia-cnsdk' include ':app' rootProject.name = "Virtual Virtual Boy" \ No newline at end of file diff --git a/src/video/renderers.rs b/src/video/renderers.rs index 95095f45..2a663b9b 100644 --- a/src/video/renderers.rs +++ b/src/video/renderers.rs @@ -1,5 +1,6 @@ mod anaglyph; mod cardboard; +mod cnsdk; mod common; mod gl; mod leia; @@ -9,6 +10,7 @@ mod stereo; pub mod jni { pub use super::anaglyph::jni::*; pub use super::cardboard::jni::*; + pub use super::cnsdk::jni::*; pub use super::leia::jni::*; pub use super::mono::jni::*; pub use super::stereo::jni::*; diff --git a/src/video/renderers/cnsdk.rs b/src/video/renderers/cnsdk.rs new file mode 100644 index 00000000..b8978e23 --- /dev/null +++ b/src/video/renderers/cnsdk.rs @@ -0,0 +1,218 @@ +use super::common::RenderLogic; +use super::gl::{ + utils::{self, VB_HEIGHT, VB_WIDTH}, + AspectRatio, Program, Textures, +}; +use crate::emulator::video::Eye; +use crate::video::gl::types::{GLfloat, GLint, GLuint}; + +use anyhow::Result; +use cgmath::{self, vec3, Matrix4}; + +const VERTEX_SHADER: &str = "\ +attribute vec4 a_Pos; +attribute vec2 a_TexCoord; +uniform mat4 u_MV; +varying vec2 v_TexCoord; +void main() { + gl_Position = u_MV * a_Pos; + v_TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); +} +"; + +const FRAGMENT_SHADER: &str = "\ +precision mediump float; +varying vec2 v_TexCoord; +uniform sampler2D u_Texture; +uniform vec4 u_Color; +void main() { + gl_FragColor = u_Color * texture2D(u_Texture, v_TexCoord).r; +} +"; + +pub struct CNSDKRenderLogic { + program: Program, + textures: Textures, + + position_location: GLuint, + tex_coord_location: GLuint, + modelview_location: GLint, + texture_location: GLint, + color_location: GLint, + + texture_color: [GLfloat; 4], + aspect_ratio: AspectRatio, + transforms: [Matrix4; 2], + model_views: [[GLfloat; 16]; 2], +} +impl CNSDKRenderLogic { + pub fn new(settings: &Settings) -> Self { + let zoom = settings.screen_zoom; + let offset = -settings.vertical_offset; + Self { + program: Program::new(VERTEX_SHADER, FRAGMENT_SHADER), + textures: Textures::new(2, (VB_WIDTH, VB_HEIGHT)), + + position_location: 0, + tex_coord_location: 0, + modelview_location: -1, + texture_location: -1, + color_location: -1, + + texture_color: utils::color_as_vector(settings.color), + aspect_ratio: settings.aspect_ratio, + transforms: [ + Matrix4::from_translation(vec3(-0.5, offset, 0.0)) * Matrix4::from_scale(zoom), + Matrix4::from_translation(vec3(0.5, offset, 0.0)) * Matrix4::from_scale(zoom), + ], + model_views: [utils::identity_matrix(), utils::identity_matrix()], + } + } +} + +impl RenderLogic for CNSDKRenderLogic { + fn init(&mut self) -> Result<()> { + self.program.init()?; + self.textures.init()?; + + self.position_location = self.program.get_attribute_location("a_Pos"); + self.tex_coord_location = self.program.get_attribute_location("a_TexCoord"); + self.modelview_location = self.program.get_uniform_location("u_MV"); + self.texture_location = self.program.get_uniform_location("u_Texture"); + self.color_location = self.program.get_uniform_location("u_Color"); + + // Set color here, because it's the same for the entire life of the program + self.program + .set_uniform_vector(self.color_location, &self.texture_color); + + Ok(()) + } + + fn resize(&mut self, screen_size: (i32, i32)) -> Result<()> { + self.program.set_viewport(screen_size)?; + + let base_mv = self + .aspect_ratio + .compute_mvp_matrix(screen_size, (VB_WIDTH * 2, VB_HEIGHT)); + self.model_views = [ + utils::to_matrix(base_mv * self.transforms[0]), + utils::to_matrix(base_mv * self.transforms[1]), + ]; + + Ok(()) + } + + fn update(&mut self, eye: Eye, buffer: &[u8]) -> Result<()> { + self.textures.update(eye as usize, buffer) + } + + fn draw(&self) -> Result<()> { + self.program.start_render()?; + for i in 0..self.textures.ids.len() { + let texture_id = self.textures.ids[i]; + let model_view = &self.model_views[i]; + + self.program + .set_uniform_texture(self.texture_location, texture_id); + self.program + .set_uniform_matrix(self.modelview_location, model_view); + + self.program + .draw_square(self.position_location, self.tex_coord_location)?; + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct Settings { + pub screen_zoom: f32, + pub aspect_ratio: AspectRatio, + pub vertical_offset: f32, + pub color: (u8, u8, u8), +} + +#[rustfmt::skip::macros(jni_func)] +pub mod jni { + use super::{CNSDKRenderLogic, Settings}; + use crate::emulator::jni::get_emulator; + use crate::jni_helpers::{JavaBinding, JavaGetResult}; + use crate::video::renderers::common::Renderer; + use crate::{jni_func, EnvExtensions}; + use anyhow::Result; + use jni::objects::JObject; + use jni::sys::jint; + use jni::JNIEnv; + use std::convert::TryInto; + + type CNSDKRenderer = Renderer; + + static CNSDK_BINDING: JavaBinding = JavaBinding::new(); + + pub fn get_settings<'a>(env: &mut JNIEnv<'a>, this: JObject<'a>) -> Result { + let screen_zoom = env.get_percent(&this, "screenZoom")?; + let aspect_ratio = env.get_int(&this, "aspectRatio")?.try_into()?; + let vertical_offset = env.get_percent(&this, "verticalOffset")?; + let color = env.get_color(&this, "color")?; + + Ok(Settings { + screen_zoom, + aspect_ratio, + vertical_offset, + color, + }) + } + + fn get_renderer<'a>( + env: &'a mut JNIEnv, + this: JObject<'a>, + ) -> JavaGetResult<'a, CNSDKRenderer> { + CNSDK_BINDING.get_value(env, this) + } + + jni_func!(CNSDKRenderer_nativeConstructor, constructor, JObject<'a>, JObject<'a>); + fn constructor<'a>( + env: &mut JNIEnv<'a>, + this: JObject<'a>, + emulator: JObject<'a>, + settings: JObject<'a>, + ) -> Result<()> { + let settings = get_settings(env, settings)?; + let renderer = { + let mut emulator = get_emulator(env, emulator)?; + Renderer::new( + emulator.claim_frame_buffer_consumers(), + CNSDKRenderLogic::new(&settings), + ) + }; + CNSDK_BINDING.init_value(env, this, renderer) + } + + jni_func!(CNSDKRenderer_nativeDestructor, destructor); + fn destructor(env: &mut JNIEnv, this: JObject) -> Result<()> { + CNSDK_BINDING.drop_value(env, this) + } + + jni_func!(CNSDKRenderer_nativeOnSurfaceCreated, on_surface_created); + fn on_surface_created(env: &mut JNIEnv, this: JObject) -> Result<()> { + let mut this = get_renderer(env, this)?; + this.on_surface_created() + } + + jni_func!(CNSDKRenderer_nativeOnSurfaceChanged, on_surface_changed, jint, jint); + fn on_surface_changed( + env: &mut JNIEnv, + this: JObject, + width: jint, + height: jint, + ) -> Result<()> { + let mut this = get_renderer(env, this)?; + this.on_surface_changed(width, height) + } + + jni_func!(CNSDKRenderer_nativeOnDrawFrame, on_draw_frame); + fn on_draw_frame(env: &mut JNIEnv, this: JObject) -> Result<()> { + let mut this = get_renderer(env, this)?; + this.on_draw_frame() + } +}