diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 3ddc415..25b1bb7 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -15,6 +15,8 @@ android_app* g_android_app = nullptr; FaceApp* g_Application = nullptr; AAssetManager* g_assetManager = nullptr; +jobject g_callback = nullptr; + void handle_cmd(android_app *pApp, int32_t cmd) { switch (cmd) { case APP_CMD_INIT_WINDOW: @@ -167,4 +169,97 @@ extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_StopRunning(JNIEnv *env, jobject thiz) { g_Application->_running = false; +} + +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(const string& motion_type) +{ + 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, "OnLoadActionFinished", "(Ljava/lang/String;)V"); + if (methodId == nullptr) { + env->ExceptionDescribe(); // 打印异常 + env->ExceptionClear(); + if (needDetach) vm->DetachCurrentThread(); + return; + } + + // 3. 构造 jstring 参数 + jstring jResult = env->NewStringUTF(motion_type.c_str()); + + // 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(); + } +} + + + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_hmwl_face_1sdk_FaceActivity_PreReadAction(JNIEnv *env, jobject thiz, jstring motion, + jobject callback) { + g_callback = env->NewGlobalRef(callback); + + const char *nativeString = env->GetStringUTFChars(motion, nullptr); + jsize len = env->GetStringUTFLength(motion); + if(len > ArgLen) + { + aout << "ArgLen to long:" << len << std::endl; + } + if(g_Application->isInited()) + { + g_Application->changeMotion(nativeString); + } + + // TODO: implement PreReadAction() + std::string ret = g_Application->preReadyMotion(nativeString, CppCallback); + return env->NewStringUTF(ret.c_str()); } \ No newline at end of file diff --git a/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java b/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java index 45ac1da..813d0d0 100644 --- a/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java +++ b/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java @@ -272,6 +272,15 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L SetCppInitArg(json); } + public interface CallbackInterface { + void OnLoadActionFinished(String result); + } + + protected native String PreReadAction(String motion, CallbackInterface callback); + protected String PreLoadAction(String json, CallbackInterface callback){ + return PreReadAction(json, callback); + } + protected native void ChangeStateCpp(String json); protected void ChangeState(String json){ Log.i("FaceActivity", "ChangeState:" + json); diff --git a/app/src/main/java/com/hmwl/face_sdk/InitArg.java b/app/src/main/java/com/hmwl/face_sdk/InitArg.java index 3c311f5..6194e73 100644 --- a/app/src/main/java/com/hmwl/face_sdk/InitArg.java +++ b/app/src/main/java/com/hmwl/face_sdk/InitArg.java @@ -4,13 +4,12 @@ import org.json.JSONObject; public class InitArg { public int action_fps; public float zoom; - public Motion motion; + public String toJson() { try { JSONObject jsonObject = new JSONObject(); jsonObject.put("action_fps", action_fps); jsonObject.put("zoom", zoom); - jsonObject.put("motion", motion.toJsonObject()); return jsonObject.toString(); } catch (Exception e) { e.printStackTrace(); diff --git a/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt b/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt index d804269..e932058 100644 --- a/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt +++ b/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt @@ -15,28 +15,43 @@ import java.io.BufferedReader import java.io.InputStreamReader -class MakeupActivity : FaceActivity() { +class MakeupActivity : FaceActivity() +{ private var curIndex = 0 private var pngFilesByFolder: List? = null override fun onCreate(savedInstanceState: Bundle?) { - val motion = Motion().apply { - type = "4" - png_names.add("4.png") - } + FaceInit(this) val initArg = InitArg().apply { action_fps = 10 zoom = 1.5f - this.motion = motion } SetInitArg(initArg.toJson()) super.onCreate(savedInstanceState) - FaceInit(this) - Toast.makeText(this, "4.png", Toast.LENGTH_LONG).show() + + var motion = getMotionByIndex(0); + var jsonString = motion.toJson() + val result = PreLoadAction(jsonString) { motion_type -> + // 在这里处理回调结果 + println("OnLoadActionFinished: $motion_type") + ChangeState(motion_type); + } + //Toast.makeText(this, "4.png", Toast.LENGTH_LONG).show() + } + + private fun getMotionByIndex(index: Int): Motion { + pngFilesByFolder?.let { folders -> + val motion = Motion().apply { + type = folders[index].folderName + png_names = folders[index].fileList + } + return motion + } + return Motion() } private fun getCurMotionState(index: Int): String { diff --git a/vulkan/Application.h b/vulkan/Application.h index 09a5dc6..60f667a 100644 --- a/vulkan/Application.h +++ b/vulkan/Application.h @@ -8,7 +8,6 @@ struct Texture { - std::string name; VkSampler sampler; VkImage image; VkImageLayout image_layout; @@ -19,6 +18,14 @@ struct Texture VkBuffer stagingBuffer = VK_NULL_HANDLE; VkDeviceMemory stagingBufferMemory = VK_NULL_HANDLE; VkDevice device; + void reset() { + sampler = VK_NULL_HANDLE; + image = VK_NULL_HANDLE; + device_memory = VK_NULL_HANDLE; + view = VK_NULL_HANDLE; + stagingBuffer = VK_NULL_HANDLE; + stagingBufferMemory = VK_NULL_HANDLE; + } }; class Application : public AppBase diff --git a/vulkan/FaceApp.cpp b/vulkan/FaceApp.cpp index a175dbe..d68062c 100644 --- a/vulkan/FaceApp.cpp +++ b/vulkan/FaceApp.cpp @@ -567,7 +567,11 @@ void FaceApp::setup_descriptor_set() alloc_info.pSetLayouts = layouts.data(); alloc_info.descriptorSetCount = m_descriptor_sets.size(); VK_CHECK(vkAllocateDescriptorSets(device, &alloc_info, m_descriptor_sets.data())); + update_descriptor_set(m_texs); +} +void FaceApp::update_descriptor_set(vector& texs) +{ for (int i = 0; i < kMaxTexture; ++i) { VkDescriptorBufferInfo buffer_descriptor{}; @@ -577,9 +581,9 @@ void FaceApp::setup_descriptor_set() VkDescriptorImageInfo image_descriptor; - image_descriptor.imageView = m_texs[i].view; - image_descriptor.sampler = m_texs[i].sampler; - image_descriptor.imageLayout = m_texs[i].image_layout; + image_descriptor.imageView = texs[i].view; + image_descriptor.sampler = texs[i].sampler; + image_descriptor.imageLayout = texs[i].image_layout; VkWriteDescriptorSet write_descriptor_set_uniform{}; write_descriptor_set_uniform.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -635,17 +639,7 @@ void FaceApp::setup_descriptor_set() vkUpdateDescriptorSets(device, static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); } - - - - - - - - } - - } void FaceApp::render(VkCommandBuffer commandBuffer, long long frameTime) @@ -718,6 +712,7 @@ void FaceApp::initVulkan() createVmaAllocator(); LoadOBJ("face_picture_3dmax.obj", obj_vertices, obj_indices); m_texs.resize(kMaxTexture); + m_texs_next.resize(kMaxTexture); if (kThick) { m_texs_ex.resize(kMaxTexture); @@ -731,6 +726,7 @@ void FaceApp::initVulkan() for (int i = 0; i < kMaxTexture; ++i) { loadTexture(image, image.size(), w, h, m_texs[i], true); + loadTexture(image, image.size(), w, h, m_texs_next[i], true); if (kThick) { loadTexture(image, image.size(), w, h, m_texs_ex[i], true); @@ -753,7 +749,7 @@ void FaceApp::initVulkan() uploadVertexData(); last_update_time = getCurrentTimeMillis(); - changeMotion(_initArg.motion); + //changeMotion(_initArg.motion); _running = true; faceAppInited = true; #if _WIN32 @@ -1153,11 +1149,13 @@ void FaceApp::destroyTexture(VkDevice device, Texture& texture) { if (texture.stagingBuffer != VK_NULL_HANDLE) { vkDestroyBuffer(device, texture.stagingBuffer, nullptr); + texture.stagingBuffer = VK_NULL_HANDLE; } if (texture.stagingBufferMemory != VK_NULL_HANDLE) { vkFreeMemory(device, texture.stagingBufferMemory, nullptr); + texture.stagingBufferMemory = VK_NULL_HANDLE; } } @@ -1261,6 +1259,11 @@ void FaceApp::cleanup() destroyTexture(device, m_texs[i]); } + for (int i = 0; i < this->m_texs_next.size(); ++i) + { + destroyTexture(device, m_texs_next[i]); + } + destroyTexture(device, tex_bg); vkDestroyCommandPool(device, commandPool, nullptr); Application::cleanup(); @@ -1284,6 +1287,32 @@ void FaceApp::drawFrame(long long frameTime) return; } + if (_motionState == loading_next_motion) + { + int i = _nextMotionIndex; + //for (int i = 0; i < _curMotion.png_names.size(); ++i) + { + +#ifdef _WIN32 + string path = "pic"; + string path_name = path + "/" + _nextMotion.type + "/" + _nextMotion.png_names[i];// +std::to_string(i) + ".png"; + loadTextureExample(UTF8ToWideString(path_name), m_texs_next[i], true); +#else + //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type; + string path = "pic"; + string path_name = path + "/" + _nextMotion.type + "/" + _nextMotion.png_names[i];// +std::to_string(i) + ".png"; + loadTexture(path_name, m_texs_next[i], true); +#endif // _WIN32 + + _nextMotionIndex++; + if (_nextMotionIndex >= _nextMotion.png_names.size()) + { + _motionState = load_next_motion_finished; + _callback(_nextMotion.type); + } + } + } + std::unique_lock lock(changeMotionMtx); static long long game_time = 0; game_time += frameTime; @@ -1291,7 +1320,7 @@ void FaceApp::drawFrame(long long frameTime) if (game_time > actionTime) { _curTexIndex++; - if (_curTexIndex >= _curMotion.png_names.size()) + if (_curTexIndex >= _curMotion_png_size) { _curTexIndex = 0; } @@ -1300,44 +1329,88 @@ void FaceApp::drawFrame(long long frameTime) Application::drawFrame(frameTime); } -void FaceApp::changeMotion(const char* json) +//void FaceApp::changeMotion(const char* motion_type) +//{ +// //Motion motion = json::parse(json); +// changeMotion(motion); +//} + +string FaceApp::preReadyMotion(const std::string& json, Callback callback) { - Motion motion = json::parse(json); - changeMotion(motion); + if (_motionState == loading_next_motion) + { + return "busy on loading:" + _nextMotion.type; + } + else if (_motionState == load_next_motion_finished) + { + return "waitting change next action:" + _nextMotion.type; + } + else if (_motionState == ready) + { + Motion motion = json::parse(json); + _nextMotion = motion; + _motionState = loading_next_motion; + _nextMotionIndex = 0; + _callback = callback; + return "success"; + } + return "failure"; } -void FaceApp::changeMotion(Motion& motion) +void FaceApp::changeMotion(const string motion_type) { - if (_curMotion.getMotionId() == motion.getMotionId()) + if (_curMotion_type == motion_type) { return; } - std::unique_lock lock_changeMotion(changeMotionMtx); - _curMotion = motion; - for (int i = 0; i < _curMotion.png_names.size(); ++i) + + if (_motionState != load_next_motion_finished) { - -#ifdef _WIN32 - //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type; - string path = "pic"; - string path_name = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i];// +std::to_string(i) + ".png"; - loadTextureExample(UTF8ToWideString(path_name), m_texs[i], true); - if (kThick) - { - string path_name_ex = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i] + std::to_string(i) + "_thick.png"; - loadTextureExample(UTF8ToWideString(path_name_ex), m_texs_ex[i], true); - } -#else - //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type; - string path = "pic"; - string path_name = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i];// +std::to_string(i) + ".png"; - loadTexture(path_name, m_texs[i], true); - if (kThick) - { - loadTexture(path_name, m_texs_ex[i], true); - } -#endif // _WIN32 - - + return; } + + std::unique_lock lock_changeMotion(changeMotionMtx); + + for (int i = 0; i < this->m_texs.size(); ++i) + { + destroyTexture(device, m_texs[i]); + } + + for (int i = 0; i < m_texs_next.size(); ++i) + { + m_texs[i] = m_texs_next[i]; + m_texs_next[i].reset(); + } + + update_descriptor_set(m_texs); + _curMotion_type = motion_type; + _curMotion_png_size = _nextMotion.png_names.size(); + + +// for (int i = 0; i < _curMotion.png_names.size(); ++i) +// { +// +//#ifdef _WIN32 +// //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type; +// string path = "pic"; +// string path_name = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i];// +std::to_string(i) + ".png"; +// loadTextureExample(UTF8ToWideString(path_name), m_texs[i], true); +// if (kThick) +// { +// string path_name_ex = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i] + std::to_string(i) + "_thick.png"; +// loadTextureExample(UTF8ToWideString(path_name_ex), m_texs_ex[i], true); +// } +//#else +// //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type; +// string path = "pic"; +// string path_name = path + "/" + _curMotion.type + "/" + _curMotion.png_names[i];// +std::to_string(i) + ".png"; +// loadTexture(path_name, m_texs[i], true); +// if (kThick) +// { +// loadTexture(path_name, m_texs_ex[i], true); +// } +//#endif // _WIN32 +// +// +// } } \ No newline at end of file diff --git a/vulkan/FaceApp.h b/vulkan/FaceApp.h index 34cc965..ef4a007 100644 --- a/vulkan/FaceApp.h +++ b/vulkan/FaceApp.h @@ -2,6 +2,7 @@ #define __FaceApp_H__ #include "Application.h" +#include #include "../third_party/vma/include/vk_mem_alloc.h" #include #include @@ -24,8 +25,8 @@ struct InitArg { int action_fps; float zoom; - Motion motion; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(InitArg, action_fps, zoom, motion); + //Motion motion; + NLOHMANN_DEFINE_TYPE_INTRUSIVE(InitArg, action_fps, zoom); }; @@ -33,7 +34,7 @@ struct PushConstants { float myValue; }; - +using Callback = std::function; class FaceApp :public Application { @@ -79,6 +80,7 @@ private: void setup_descriptor_pool(); void setup_descriptor_set_layout(); void setup_descriptor_set(); + void update_descriptor_set(vector& texs); const bool kThick = false; void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); @@ -102,6 +104,7 @@ private: vector m_descriptor_sets; const uint32_t kMaxTexture = 30; vector m_texs; + vector m_texs_next; vector m_texs_ex; @@ -135,10 +138,25 @@ private: public: - void changeMotion(const char* json); - void changeMotion(Motion& motion); + Callback _callback; + string preReadyMotion(const std::string& json, Callback callback); + void changeMotion(const string motion_type); + //void changeMotion(Motion& motion); InitArg _initArg; - Motion _curMotion; + //Motion _curMotion; + string _curMotion_type; + int _curMotion_png_size; + Motion _nextMotion; + int _nextMotionIndex; + + enum MotionState { + ready, + loading_next_motion, + load_next_motion_finished, + }; + + MotionState _motionState = ready; + int _curTexIndex = 0; void SetInitArg(const char* arg); diff --git a/vulkan/main.cpp b/vulkan/main.cpp index a4f275d..65d7ca7 100644 --- a/vulkan/main.cpp +++ b/vulkan/main.cpp @@ -39,6 +39,12 @@ std::wstring ReadFileWithChinesePath(const std::wstring& filePath) { return AppBase::UTF8ToWideString(context); } +FaceApp* g_app; + +void CppCallback(const string& motion_type) +{ + g_app->changeMotion(motion_type.c_str()); +} int main() { @@ -51,23 +57,23 @@ int main() { Motion motion; motion.type = "4"; motion.png_names.push_back("4.png"); - //for (int i = 0; i < 25; ++i) - //{ - // if (i < 10) - // { - // motion.png_names.push_back("00000" + std::to_string(i) + ".png"); - // } - // else - // { - // motion.png_names.push_back("0000" + std::to_string(i) + ".png"); + for (int i = 0; i < 25; ++i) + { + if (i < 10) + { + motion.png_names.push_back("00000" + std::to_string(i) + ".png"); + } + else + { + motion.png_names.push_back("0000" + std::to_string(i) + ".png"); - // } - // - //} + } + + } # - init_arg.motion = motion; + //init_arg.motion = motion; init_arg.action_fps = 10; @@ -76,9 +82,22 @@ int main() { string json_str = json(init_arg).dump(); FaceApp app; + g_app = &app; app.SetInitArg(json_str.c_str()); app.initVulkan(); app._running = true; + auto motion_json = json(motion); + if (!motion_json.is_object()) { + std::cout << "Invalid JSON structure" << std::endl; + } + std::string motion_str = motion_json.dump(); + // 添加完整性检查 + if (motion_str.empty()) { + std::cout << "Warning: Empty motion string" << std::endl; + } + + + app.preReadyMotion(motion_str, CppCallback); app.mainLoop(); //try { // app.mainLoop();