375 lines
12 KiB
C++
375 lines
12 KiB
C++
#include <jni.h>
|
|
|
|
#include <game-activity/native_app_glue/android_native_app_glue.h>
|
|
#include <game-activity/GameActivity.h>
|
|
#include "AndroidOut.h"
|
|
#include "FaceApp.h"
|
|
#include <android/asset_manager.h>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
using namespace std;
|
|
extern "C" {
|
|
|
|
android_app* g_android_app = nullptr;
|
|
FaceApp* g_Application = nullptr;
|
|
AAssetManager* g_assetManager = nullptr;
|
|
|
|
jobject g_callback = nullptr;
|
|
jobject g_callbackAnimationFinished = nullptr;
|
|
|
|
void handle_cmd(android_app *pApp, int32_t cmd) {
|
|
switch (cmd) {
|
|
case APP_CMD_INIT_WINDOW:
|
|
aout << "APP_CMD_INIT_WINDOW" << std::endl;
|
|
g_Application->initVulkan();
|
|
break;
|
|
case APP_CMD_TERM_WINDOW:
|
|
aout << "APP_CMD_TERM_WINDOW" << std::endl;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const int ArgLen = 128*1024;
|
|
char g_InitArgString[ArgLen] = {0};
|
|
|
|
void android_main(struct android_app *pApp) {
|
|
aout << "Welcome to android_main" << std::endl;
|
|
g_android_app = pApp;
|
|
g_assetManager = pApp->activity->assetManager;
|
|
//FaceApp application;
|
|
//g_Application = &application;
|
|
if(g_Application == nullptr) {
|
|
g_Application = new FaceApp();
|
|
}
|
|
g_Application->SetInitArg(g_InitArgString);
|
|
pApp->onAppCmd = handle_cmd;
|
|
long long last_update_time = 0;
|
|
android_app_set_motion_event_filter(pApp, nullptr);
|
|
long long _lastDrawFrameTime = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
|
do {
|
|
auto start_time = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
|
// Process all pending events before running game logic.
|
|
bool done = false;
|
|
while (!done) {
|
|
int timeout = 0;
|
|
int events;
|
|
android_poll_source *pSource;
|
|
int result = ALooper_pollOnce(timeout, nullptr, &events, reinterpret_cast<void**>(&pSource));
|
|
switch (result) {
|
|
case ALOOPER_POLL_TIMEOUT:
|
|
[[clang::fallthrough]];
|
|
case ALOOPER_POLL_WAKE:
|
|
// No events occurred before the timeout or explicit wake. Stop checking for events.
|
|
done = true;
|
|
break;
|
|
case ALOOPER_EVENT_ERROR:
|
|
aout << "ALooper_pollOnce returned an error" << std::endl;
|
|
break;
|
|
case ALOOPER_POLL_CALLBACK:
|
|
break;
|
|
default:
|
|
if (pSource) {
|
|
pSource->process(pApp, pSource);
|
|
}
|
|
}
|
|
}
|
|
if(g_Application->isInited())
|
|
{
|
|
auto frameTime = (start_time - _lastDrawFrameTime);
|
|
_lastDrawFrameTime = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
|
g_Application->drawFrame(frameTime);
|
|
}
|
|
auto end_time = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
|
auto frameTime = end_time - last_update_time;
|
|
last_update_time = end_time;
|
|
auto function_time = (end_time - start_time);
|
|
//aout << "function_time:" << function_time << " fps:" << (1000/frameTime) << std::endl;
|
|
if(function_time < 35)
|
|
{
|
|
this_thread::sleep_for(chrono::milliseconds((35-function_time)));
|
|
}
|
|
} while (!pApp->destroyRequested);
|
|
//application.cleanup();
|
|
}
|
|
}
|
|
|
|
extern void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize);
|
|
extern void ReceiveFacePoint(float* pos, int count, int width, int height);
|
|
|
|
void processWithVulkan(uint8_t* data, int width, int height, int format,
|
|
int rowStride, size_t dataSize) {
|
|
TextureLoadProcessWithVulkan(data, width, height, rowStride, dataSize);
|
|
}
|
|
|
|
|
|
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_processImageNative(JNIEnv *env, jobject thiz, jobject buffer,
|
|
jint width, jint height, jint format,
|
|
jint row_stride, jint pixel_stride,
|
|
jint rotation) {
|
|
// TODO: implement processImageNative()
|
|
uint8_t* imageData = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
|
|
jlong capacity = env->GetDirectBufferCapacity(buffer);
|
|
|
|
if (imageData == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// 在这里将图像数据传递给 Vulkan
|
|
// 创建 Vulkan 图像或更新现有图像
|
|
processWithVulkan(imageData, width, height, format, row_stride, capacity);
|
|
}
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_passDataToNative(JNIEnv *env, jobject thiz, jobject buffer,
|
|
jint point_count, jint width, jint height) {
|
|
// TODO: implement passDataToNative()
|
|
float* pos = static_cast<float*>(env->GetDirectBufferAddress(buffer));
|
|
|
|
if (pos == nullptr) {
|
|
// 处理错误
|
|
return;
|
|
}
|
|
|
|
// 或者直接将指针传递给Vulkan
|
|
ReceiveFacePoint(pos, point_count, width, height);
|
|
}
|
|
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_SetCppInitArg(JNIEnv *env, jobject thiz, jstring json) {
|
|
// TODO: implement SetCppInitArg()
|
|
const char *nativeString = env->GetStringUTFChars(json, nullptr);
|
|
jsize len = env->GetStringUTFLength(json);
|
|
if(len > ArgLen)
|
|
{
|
|
aout << "ArgLen to long:" << len << std::endl;
|
|
return;
|
|
}
|
|
memcpy(g_InitArgString, nativeString, len);
|
|
}
|
|
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_StopRunning(JNIEnv *env, jobject thiz) {
|
|
g_Application->Stop();
|
|
}
|
|
|
|
JavaVM* g_jvm = nullptr;
|
|
|
|
// 添加 JNI_OnLoad 函数
|
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
g_jvm = vm; // 保存 JavaVM 指针,供其他线程使用
|
|
return JNI_VERSION_1_6;
|
|
}
|
|
|
|
void CppCallback()
|
|
{
|
|
JNIEnv* env = nullptr;
|
|
bool needDetach = false;
|
|
|
|
// 获取当前线程的 JNIEnv
|
|
JavaVM* vm = g_jvm; // 你需要提前保存 JavaVM* 到全局变量 g_jvm
|
|
int status = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
|
|
if (status == JNI_EDETACHED) {
|
|
// 当前线程未 attach,需 attach
|
|
if (vm->AttachCurrentThread(&env, nullptr) != 0) {
|
|
return;
|
|
}
|
|
needDetach = true;
|
|
} else if (status != JNI_OK) {
|
|
return;
|
|
}
|
|
|
|
if (g_callback == nullptr) {
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 1. 获取 CallbackInterface 的 class
|
|
jclass callbackClass = env->GetObjectClass(g_callback);
|
|
if (callbackClass == nullptr) {
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 2. 获取 OnLoadActionFinished 方法 ID(注意方法名大小写必须一致!)
|
|
jmethodID methodId = env->GetMethodID(callbackClass, "OnLoadMotionListFinished", "(Ljava/lang/String;)V");
|
|
if (methodId == nullptr) {
|
|
env->ExceptionDescribe(); // 打印异常
|
|
env->ExceptionClear();
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 3. 构造 jstring 参数
|
|
jstring jResult = env->NewStringUTF("ok");
|
|
|
|
// 4. 调用 Java 回调
|
|
env->CallVoidMethod(g_callback, methodId, jResult);
|
|
|
|
// 5. 检查是否抛出异常
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
env->ExceptionClear();
|
|
}
|
|
|
|
// 6. 释放局部引用(可选,但推荐)
|
|
env->DeleteLocalRef(jResult);
|
|
env->DeleteLocalRef(callbackClass);
|
|
|
|
// 7. 如果是临时 attach 的线程,要 detach
|
|
if (needDetach) {
|
|
vm->DetachCurrentThread();
|
|
}
|
|
}
|
|
|
|
|
|
void CppAnimationFinishedCallback()
|
|
{
|
|
JNIEnv* env = nullptr;
|
|
bool needDetach = false;
|
|
|
|
// 获取当前线程的 JNIEnv
|
|
JavaVM* vm = g_jvm; // 你需要提前保存 JavaVM* 到全局变量 g_jvm
|
|
int status = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
|
|
if (status == JNI_EDETACHED) {
|
|
// 当前线程未 attach,需 attach
|
|
if (vm->AttachCurrentThread(&env, nullptr) != 0) {
|
|
return;
|
|
}
|
|
needDetach = true;
|
|
} else if (status != JNI_OK) {
|
|
return;
|
|
}
|
|
|
|
if (g_callbackAnimationFinished == nullptr) {
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 1. 获取 CallbackInterface 的 class
|
|
jclass callbackClass = env->GetObjectClass(g_callbackAnimationFinished);
|
|
if (callbackClass == nullptr) {
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 2. 获取 OnLoadActionFinished 方法 ID(注意方法名大小写必须一致!)
|
|
jmethodID methodId = env->GetMethodID(callbackClass, "OnPlayMotionListFinished", "()V");
|
|
if (methodId == nullptr) {
|
|
env->ExceptionDescribe(); // 打印异常
|
|
env->ExceptionClear();
|
|
if (needDetach) vm->DetachCurrentThread();
|
|
return;
|
|
}
|
|
|
|
// 4. 调用 Java 回调
|
|
env->CallVoidMethod(g_callbackAnimationFinished, methodId);
|
|
|
|
// 5. 检查是否抛出异常
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
env->ExceptionClear();
|
|
}
|
|
|
|
env->DeleteLocalRef(callbackClass);
|
|
|
|
// 7. 如果是临时 attach 的线程,要 detach
|
|
if (needDetach) {
|
|
vm->DetachCurrentThread();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
extern "C"
|
|
JNIEXPORT jstring JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_PreReadAction(JNIEnv *env, jobject thiz, jstring motion,
|
|
jobject callback) {
|
|
// 清理旧的全局引用
|
|
if (g_callback != nullptr) {
|
|
env->DeleteGlobalRef(g_callback);
|
|
g_callback = nullptr;
|
|
}
|
|
|
|
// 保存新的回调
|
|
if (callback != nullptr) {
|
|
g_callback = env->NewGlobalRef(callback);
|
|
} else {
|
|
g_callback = nullptr;
|
|
}
|
|
|
|
const char *nativeString = env->GetStringUTFChars(motion, nullptr);
|
|
jsize len = env->GetStringUTFLength(motion);
|
|
if(len > ArgLen)
|
|
{
|
|
aout << "ArgLen to long:" << len << std::endl;
|
|
}
|
|
|
|
std::string ret = g_Application->preLoadMotionList(nativeString, CppCallback);
|
|
return env->NewStringUTF(ret.c_str());
|
|
}
|
|
|
|
const char DELIMITER = 0x1F; // same as \u001F
|
|
|
|
std::vector<std::string> splitMotionString(const std::string& input) {
|
|
std::vector<std::string> result;
|
|
if (input.empty()) return result;
|
|
|
|
size_t start = 0;
|
|
size_t pos = input.find(DELIMITER);
|
|
|
|
while (pos != std::string::npos) {
|
|
result.push_back(input.substr(start, pos - start));
|
|
start = pos + 1;
|
|
pos = input.find(DELIMITER, start);
|
|
}
|
|
// Add last part
|
|
result.push_back(input.substr(start));
|
|
|
|
return result;
|
|
}
|
|
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_ChangeMotionCpp(JNIEnv *env, jobject thiz, jstring json,
|
|
jobject callback, jboolean loop) {
|
|
// TODO: implement ChangeMotionCpp()
|
|
const char *nativeString = env->GetStringUTFChars(json, nullptr);
|
|
jsize len = env->GetStringUTFLength(json);
|
|
if(len > ArgLen)
|
|
{
|
|
aout << "ArgLen to long:" << len << std::endl;
|
|
return;
|
|
}
|
|
|
|
// 保存新的回调
|
|
if (callback != nullptr) {
|
|
g_callbackAnimationFinished = env->NewGlobalRef(callback);
|
|
} else {
|
|
g_callbackAnimationFinished = nullptr;
|
|
}
|
|
|
|
if(g_Application->isInited())
|
|
{
|
|
std::vector<std::string> motions = splitMotionString(nativeString);
|
|
g_Application->changeMotionList(motions, CppAnimationFinishedCallback, loop);
|
|
}
|
|
}
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_StopMotionNative(JNIEnv *env, jobject thiz) {
|
|
g_Application->StopMotion();
|
|
}
|
|
extern "C"
|
|
JNIEXPORT void JNICALL
|
|
Java_com_hmwl_face_1sdk_FaceActivity_ResumeMotionNative(JNIEnv *env, jobject thiz) {
|
|
// TODO: implement ResumeMotionNative()
|
|
g_Application->ResumeMotion();
|
|
} |