### Setup Model and View Matrices for Rendering Source: https://context7.com/live2d/cubismnativeframework/llms.txt Configures model and view matrices for positioning, scaling, and camera transformations. Includes projection matrix setup and combining matrices for rendering. Transforms screen coordinates to model coordinates. ```cpp #include "Math/CubismMatrix44.hpp" #include "Math/CubismViewMatrix.hpp" #include "Math/CubismModelMatrix.hpp" using namespace Live2D::Cubism::Framework; void SetupMatrices(CubismRenderer* renderer, CubismModel* model) { // Model matrix - positions the model in world space CubismModelMatrix modelMatrix(model->GetCanvasWidth(), model->GetCanvasHeight()); modelMatrix.SetPosition(0.0f, 0.0f); // Center position modelMatrix.SetWidth(2.0f); // Fit to normalized coordinates // View matrix - camera/viewport transformations CubismViewMatrix viewMatrix; viewMatrix.SetScreenRect(-1.0f, 1.0f, -1.0f, 1.0f); // Screen bounds viewMatrix.SetMaxScreenRect(-2.0f, 2.0f, -2.0f, 2.0f); // Max scroll bounds viewMatrix.SetMaxScale(2.0f); // Max zoom viewMatrix.SetMinScale(0.5f); // Min zoom // Apply zoom viewMatrix.Scale(1.5f, 1.5f); // Apply pan viewMatrix.Translate(0.1f, -0.1f); // Projection matrix (typically orthographic for 2D) CubismMatrix44 projectionMatrix; projectionMatrix.LoadIdentity(); // For aspect ratio correction (example: 16:9) float aspectRatio = 16.0f / 9.0f; projectionMatrix.Scale(1.0f, aspectRatio); // Combine matrices: projection * view * model CubismMatrix44 mvpMatrix; mvpMatrix.LoadIdentity(); // Multiply matrices CubismMatrix44::Multiply( projectionMatrix.GetArray(), viewMatrix.GetArray(), mvpMatrix.GetArray() ); CubismMatrix44::Multiply( mvpMatrix.GetArray(), modelMatrix.GetArray(), mvpMatrix.GetArray() ); // Set MVP matrix on renderer renderer->SetMvpMatrix(&mvpMatrix); // Transform point from screen to model coordinates float screenX = 0.5f; float screenY = 0.3f; float modelX = viewMatrix.InvertTransformX(screenX); float modelY = viewMatrix.InvertTransformY(screenY); printf("Screen (%.2f, %.2f) -> Model (%.2f, %.2f)\n", screenX, screenY, modelX, modelY); } ``` -------------------------------- ### Example Usage of MotionController Source: https://context7.com/live2d/cubismnativeframework/llms.txt Demonstrates loading and playing animations with different priorities. Includes a basic update loop for animation playback. ```cpp // Usage example void PlayAnimations(CubismModel* model) { MotionController controller; // Load motions csmSizeInt size; csmByte* idleBuffer = LoadFile("motions/idle.motion3.json", &size); controller.LoadMotion("idle", idleBuffer, size); CSM_FREE(idleBuffer); csmByte* waveBuffer = LoadFile("motions/wave.motion3.json", &size); controller.LoadMotion("wave", waveBuffer, size); CSM_FREE(waveBuffer); // Play idle motion with low priority controller.PlayMotion("idle", 1); // Play wave motion with higher priority (will interrupt idle) controller.PlayMotion("wave", 2); // Update loop csmFloat32 deltaTime = 1.0f / 60.0f; // 60 FPS while (true) { controller.Update(model, deltaTime); model->Update(); // Render... } } ``` -------------------------------- ### Setup and Configure Cubism Pose Source: https://context7.com/live2d/cubismnativeframework/llms.txt Initializes the CubismPose controller from pose settings in a JSON file. Resets the pose to its initial state and updates parameters for smooth transitions. ```cpp #include "Effect/CubismPose.hpp" using namespace Live2D::Cubism::Framework; void SetupPose(CubismModel* model) { csmSizeInt size; csmByte* poseBuffer = LoadFile("model.pose3.json", &size); // Create pose controller CubismPose* pose = CubismPose::Create(poseBuffer, size); CSM_FREE(poseBuffer); if (!pose) { printf("Failed to create pose\n"); return; } // Reset pose to initial state pose->Reset(model); // In your update loop - pose handles smooth transitions csmFloat32 deltaTime = 1.0f / 60.0f; // Update pose (handles fade transitions between part groups) pose->UpdateParameters(model, deltaTime); // Apply to model model->Update(); // Cleanup CubismPose::Delete(pose); } ``` -------------------------------- ### Setup and Configure Cubism Physics Source: https://context7.com/live2d/cubismnativeframework/llms.txt Initializes and configures the CubismPhysics controller using physics settings from a JSON file. Includes setting gravity, wind, and stabilizing the initial state. ```cpp #include "Physics/CubismPhysics.hpp" using namespace Live2D::Cubism::Framework; void SetupPhysics(CubismModel* model) { csmSizeInt size; csmByte* physicsBuffer = LoadFile("model.physics3.json", &size); // Create physics controller from JSON CubismPhysics* physics = CubismPhysics::Create(physicsBuffer, size); CSM_FREE(physicsBuffer); if (!physics) { printf("Failed to create physics\n"); return; } // Configure physics options CubismPhysics::Options options; options.Gravity = CubismVector2(0.0f, -1.0f); // Gravity pointing down options.Wind = CubismVector2(0.0f, 0.0f); // No wind physics->SetOptions(options); // Stabilize physics at current parameter state // (prevents initial jitter) physics->Stabilization(model); // Simulate wind effect CubismPhysics::Options windOptions; windOptions.Gravity = CubismVector2(0.0f, -1.0f); windOptions.Wind = CubismVector2(0.5f, 0.0f); // Wind from left physics->SetOptions(windOptions); // In your update loop csmFloat32 deltaTime = 1.0f / 60.0f; // Evaluate physics simulation physics->Evaluate(model, deltaTime); // Apply to model model->Update(); // Reset physics state if needed physics->Reset(); // Cleanup CubismPhysics::Delete(physics); } ``` -------------------------------- ### Setup Breathing Animation Source: https://context7.com/live2d/cubismnativeframework/llms.txt Create subtle breathing animations by configuring CubismBreath with sine wave oscillations for various model parameters. Requires a model instance. Call UpdateParameters in the main loop. ```cpp #include "Effect/CubismBreath.hpp" using namespace Live2D::Cubism::Framework; void SetupBreathing(CubismModel* model) { // Create breath controller CubismBreath* breath = CubismBreath::Create(); CubismIdManager* idManager = CubismFramework::GetIdManager(); // Define breathing parameters with sine wave settings csmVector breathParams; // Body expansion/contraction for breathing breathParams.PushBack(CubismBreath::BreathParameterData( idManager->GetId("ParamBreath"), // Parameter ID 0.0f, // Offset (center value) 0.5f, // Peak (amplitude) 3.5f, // Cycle (seconds for one breath) 0.5f // Weight )); // Subtle body angle movement during breathing breathParams.PushBack(CubismBreath::BreathParameterData( idManager->GetId("ParamAngleX"), 0.0f, // Center at 0 1.0f, // Small angle variation 5.0f, // Slower cycle 0.3f // Lower weight for subtle effect )); // Chest movement breathParams.PushBack(CubismBreath::BreathParameterData( idManager->GetId("ParamBodyAngleZ"), 0.0f, 0.8f, 3.5f, // Same cycle as main breath 0.4f )); // Set parameters to breath controller breath->SetParameters(breathParams); // In your update loop csmFloat32 deltaTime = 1.0f / 60.0f; // Update breathing animation breath->UpdateParameters(model, deltaTime); // Apply to model model->Update(); // Get current parameters for debugging const csmVector& params = breath->GetParameters(); printf("Breathing with %d parameters\n", static_cast(params.GetSize())); // Cleanup CubismBreath::Delete(breath); } ``` -------------------------------- ### Setup Eye Blink Animation Source: https://context7.com/live2d/cubismnativeframework/llms.txt Implement automatic eye blinking by creating and configuring CubismEyeBlink. Requires model settings and a model instance. Call UpdateParameters in the main loop. ```cpp #include "Effect/CubismEyeBlink.hpp" #include "ICubismModelSetting.hpp" using namespace Live2D::Cubism::Framework; void SetupEyeBlink(CubismModel* model, ICubismModelSetting* modelSetting) { // Create eye blink controller from model settings CubismEyeBlink* eyeBlink = CubismEyeBlink::Create(modelSetting); // Configure blink timing eyeBlink->SetBlinkingInterval(4.0f); // Average 4 seconds between blinks // Configure blink animation speed eyeBlink->SetBlinkingSettings( 0.1f, // Closing time (seconds) 0.05f, // Closed time (seconds) 0.15f // Opening time (seconds) ); // Or manually set which parameters to use for blinking csmVector blinkParamIds; CubismIdManager* idManager = CubismFramework::GetIdManager(); blinkParamIds.PushBack(idManager->GetId("ParamEyeLOpen")); blinkParamIds.PushBack(idManager->GetId("ParamEyeROpen")); eyeBlink->SetParameterIds(blinkParamIds); // In your update loop csmFloat32 deltaTime = 1.0f / 60.0f; // Update blink state and apply to model eyeBlink->UpdateParameters(model, deltaTime); // Apply model updates model->Update(); // Cleanup CubismEyeBlink::Delete(eyeBlink); } ``` -------------------------------- ### Initialize Cubism Native Framework Source: https://context7.com/live2d/cubismnativeframework/llms.txt Demonstrates the initialization process for the Cubism Native Framework, including setting up a custom memory allocator and a logging callback. This must be called once at application startup. ```cpp #include "CubismFramework.hpp" #include "ICubismAllocator.hpp" // Custom memory allocator implementation class MyAllocator : public Live2D::Cubism::Framework::ICubismAllocator { public: void* Allocate(const Csm::csmSizeType size) override { return malloc(size); } void Deallocate(void* memory) override { free(memory); } void* AllocateAligned(const Csm::csmSizeType size, const Csm::csmUint32 alignment) override { size_t offset, shift, alignedAddress; void* allocation = malloc(size + alignment); if (!allocation) return nullptr; alignedAddress = reinterpret_cast(allocation) + alignment; shift = alignedAddress % alignment; alignedAddress -= shift; return reinterpret_cast(alignedAddress); } void DeallocateAligned(void* alignedMemory) override { free(alignedMemory); } }; // Logging callback function void LogFunction(const Csm::csmChar* message) { printf("[Live2D] %s\n", message); } int main() { // Create allocator instance MyAllocator allocator; // Configure framework options Csm::CubismFramework::Option option; option.LogFunction = LogFunction; option.LoggingLevel = Csm::CubismFramework::Option::LogLevel_Verbose; // Start up the framework (call once at application start) if (!Csm::CubismFramework::StartUp(&allocator, &option)) { printf("Failed to start Cubism Framework\n"); return -1; } // Initialize framework resources Csm::CubismFramework::Initialize(); // Check if framework is ready if (Csm::CubismFramework::IsInitialized()) { printf("Cubism Framework initialized successfully\n"); } // ... application logic ... // Cleanup on exit Csm::CubismFramework::Dispose(); Csm::CubismFramework::CleanUp(); return 0; } ``` -------------------------------- ### Create and Initialize Cubism Model Source: https://context7.com/live2d/cubismnativeframework/llms.txt Loads a MOC3 model from a buffer, checks its version and consistency, creates a CubismModel instance, and prints basic model information. Ensure MOC data is valid before proceeding. Cleanup is crucial. ```cpp void LoadModel() { csmSizeInt mocSize; csmByte* mocBuffer = LoadFileToBuffer("model.moc3", &mocSize); // Check MOC version before loading Core::csmMocVersion version = CubismMoc::GetMocVersionFromBuffer(mocBuffer, mocSize); printf("MOC Version: %d\n", version); // Check MOC consistency (recommended for security) bool shouldCheckConsistency = true; // Create MOC instance with optional consistency check CubismMoc* moc = CubismMoc::Create(mocBuffer, mocSize, shouldCheckConsistency); if (!moc) { printf("Failed to create MOC\n"); CSM_FREE(mocBuffer); return; } // Create model instance from MOC CubismModel* model = moc->CreateModel(); if (model) { printf("Model loaded successfully\n"); printf("Parameter count: %d\n", model->GetParameterCount()); printf("Part count: %d\n", model->GetPartCount()); printf("Drawable count: %d\n", model->GetDrawableCount()); printf("Canvas size: %.0f x %.0f pixels\n", model->GetCanvasWidthPixel(), model->GetCanvasHeightPixel()); } // Cleanup moc->DeleteModel(model); CubismMoc::Delete(moc); CSM_FREE(mocBuffer); } ``` -------------------------------- ### Initialize CubismUserModel Source: https://context7.com/live2d/cubismnativeframework/llms.txt Extend CubismUserModel to load model assets, including settings, MOC3, physics, and pose data. Requires platform-specific file loading and renderer creation. Call IsInitialized(true) upon successful completion. ```cpp #include "Model/CubismUserModel.hpp" #include "CubismModelSettingJson.hpp" using namespace Live2D::Cubism::Framework; class MyLive2DModel : public CubismUserModel { public: MyLive2DModel() : CubismUserModel() {} virtual ~MyLive2DModel() {} bool Initialize(const csmChar* modelDirectory) { csmSizeInt size; // Load model settings JSON csmString settingPath = csmString(modelDirectory) + "model.model3.json"; csmByte* settingBuffer = LoadFile(settingPath.GetRawString(), &size); CubismModelSettingJson* setting = CSM_NEW CubismModelSettingJson(settingBuffer, size); CSM_FREE(settingBuffer); // Load MOC3 file csmString mocPath = csmString(modelDirectory) + setting->GetModelFileName(); csmByte* mocBuffer = LoadFile(mocPath.GetRawString(), &size); LoadModel(mocBuffer, size, true); // Enable MOC consistency check CSM_FREE(mocBuffer); // Load physics settings if (setting->GetPhysicsFileName() && strlen(setting->GetPhysicsFileName()) > 0) { csmString physicsPath = csmString(modelDirectory) + setting->GetPhysicsFileName(); csmByte* physicsBuffer = LoadFile(physicsPath.GetRawString(), &size); LoadPhysics(physicsBuffer, size); CSM_FREE(physicsBuffer); } // Load pose settings if (setting->GetPoseFileName() && strlen(setting->GetPoseFileName()) > 0) { csmString posePath = csmString(modelDirectory) + setting->GetPoseFileName(); csmByte* poseBuffer = LoadFile(posePath.GetRawString(), &size); LoadPose(poseBuffer, size); CSM_FREE(poseBuffer); } // Create renderer (platform-specific) CreateRenderer(1920, 1080, 1); CSM_DELETE(setting); IsInitialized(true); return true; } void Update(csmFloat32 deltaTime) { // Update motion playback if (_motionManager) { _motionManager->UpdateMotion(GetModel(), deltaTime); } // Update physics simulation if (_physics) { _physics->Evaluate(GetModel(), deltaTime); } // Update eye blink effect if (_eyeBlink) { _eyeBlink->UpdateParameters(GetModel(), deltaTime); } // Update breathing effect if (_breath) { _breath->UpdateParameters(GetModel(), deltaTime); } // Update pose (arm switching, etc.) if (_pose) { _pose->UpdateParameters(GetModel(), deltaTime); } // Apply model updates GetModel()->Update(); } private: csmByte* LoadFile(const csmChar* path, csmSizeInt* outSize) { // Platform-specific file loading implementation FILE* file = fopen(path, "rb"); if (!file) return nullptr; fseek(file, 0, SEEK_END); *outSize = ftell(file); fseek(file, 0, SEEK_SET); csmByte* buffer = static_cast(CSM_MALLOC(*outSize)); fread(buffer, 1, *outSize, file); fclose(file); return buffer; } }; ``` -------------------------------- ### Initialize and Configure Cubism Renderer Source: https://context7.com/live2d/cubismnativeframework/llms.txt Initializes the CubismRenderer with specified dimensions and model. Configures rendering options like alpha, culling, anisotropy, and color. Sets MVP matrix and render target size. ```cpp #include "Rendering/CubismRenderer.hpp" #include "Rendering/OpenGL/CubismRenderer_OpenGLES2.hpp" using namespace Live2D::Cubism::Framework; using namespace Live2D::Cubism::Framework::Rendering; void SetupRenderer(CubismModel* model) { // Create renderer with target dimensions csmUint32 width = 1920; csmUint32 height = 1080; csmInt32 maskBufferCount = 1; // Number of mask buffers CubismRenderer* renderer = CubismRenderer::Create(width, height); renderer->Initialize(model, maskBufferCount); // Configure rendering options renderer->IsPremultipliedAlpha(true); // Enable premultiplied alpha renderer->IsCulling(false); // Disable backface culling renderer->SetAnisotropy(4.0f); // Anisotropic filtering level // Set model color (RGBA, 1.0 = normal) renderer->SetModelColor(1.0f, 1.0f, 1.0f, 1.0f); // Full color // Configure mask precision renderer->UseHighPrecisionMask(true); // Higher quality masks // Set MVP matrix CubismMatrix44 mvpMatrix; mvpMatrix.LoadIdentity(); mvpMatrix.Scale(1.0f, static_cast(width) / height); renderer->SetMvpMatrix(&mvpMatrix); // Change render target size if window resizes renderer->SetRenderTargetSize(1280, 720); // In your render loop // ... setup OpenGL/DirectX state ... // Draw the model renderer->DrawModel(); // Cleanup CubismRenderer::Delete(renderer); } ``` -------------------------------- ### Manually Switch Cubism Pose Parts (e.g., Arms) Source: https://context7.com/live2d/cubismnativeframework/llms.txt Demonstrates how to manually control part visibility and opacity for effects like switching between different arm poses. Requires a CubismModel and IdManager. ```cpp // Example: Manually control arm switching void SwitchArms(CubismModel* model, bool useLeftArm) { CubismIdManager* idManager = CubismFramework::GetIdManager(); // Part groups for arm switching CubismIdHandle armAId = idManager->GetId("PartArmA"); CubismIdHandle armBId = idManager->GetId("PartArmB"); if (useLeftArm) { model->SetPartOpacity(armAId, 1.0f); model->SetPartOpacity(armBId, 0.0f); } else { model->SetPartOpacity(armAId, 0.0f); model->SetPartOpacity(armBId, 1.0f); } model->Update(); } ``` -------------------------------- ### Set Output Directories Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Defines the root, intermediate, and library directories for compiled shaders. These paths are used to organize the build artifacts. ```cmake set(output_root_dir ${CMAKE_CURRENT_BINARY_DIR}) set(output_intermediate_dir ${output_root_dir}/Intermediates) set(output_lib_dir ${output_root_dir}/FrameworkMetallibs) ``` -------------------------------- ### Configure Live2D Cubism Native Framework Build Source: https://github.com/live2d/cubismnativeframework/blob/develop/CMakeLists.txt This CMake script sets up the build environment for the Live2D Cubism Native Framework. It defines the library name, adds source directories, and configures include paths and compile definitions for a static library build. ```cmake cmake_minimum_required(VERSION 3.10) set(LIB_NAME Framework) # Force static library. add_library(${LIB_NAME} STATIC) add_subdirectory(src) # Add include path. target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src PRIVATE ${RENDER_INCLUDE_PATH} ) # Deprecated functions # The following expressions are written for compatibility # and will be removed in a future release. # Add core include. target_include_directories(${LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../Core/include ) # Add definitions. target_compile_definitions(${LIB_NAME} PRIVATE ${FRAMEWORK_DEFINITIOINS} ) ``` -------------------------------- ### Update Cubism Physics with Dragging Input Source: https://context7.com/live2d/cubismnativeframework/llms.txt Applies physics simulation based on user input like dragging, influencing model parameters such as head angle. Requires an existing CubismPhysics instance. ```cpp // Physics with interactive input void UpdatePhysicsWithDragging(CubismPhysics* physics, CubismModel* model, csmFloat32 dragX, csmFloat32 dragY, csmFloat32 deltaTime) { // Apply drag influence through model parameters first CubismIdManager* idManager = CubismFramework::GetIdManager(); // Set head angle based on drag model->SetParameterValue(idManager->GetId("ParamAngleX"), dragX * 30.0f); model->SetParameterValue(idManager->GetId("ParamAngleY"), dragY * 30.0f); // Physics will react to parameter changes physics->Evaluate(model, deltaTime); model->Update(); } ``` -------------------------------- ### Define Normal Shader Files (CMake) Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Vulkan/Shaders/CMakeLists.txt Lists the GLSL and GLSL shader files for normal rendering operations. Ensure these files are correctly referenced in your CMake build. ```cmake set(normal_shader_files ${CMAKE_CURRENT_SOURCE_DIR}/src/common.glsl ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrc.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcCopy.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcMask.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcMaskInverted.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcMaskInvertedPremultipliedAlpha.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcMaskPremultipliedAlpha.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcPremultipliedAlpha.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/FragShaderSrcSetupMask.frag ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrc.vert ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrcCopy.vert ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrcMasked.vert ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrcSetupMask.vert ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrcBlend.vert ${CMAKE_CURRENT_SOURCE_DIR}/src/VertShaderSrcMaskedBlend.vert ) ``` -------------------------------- ### MotionController Class for Animation Playback Source: https://context7.com/live2d/cubismnativeframework/llms.txt Manages loading, playing, and updating animations. Handles motion queuing, priority, and callbacks for motion events. Ensure motions are loaded before playing and managed for cleanup. ```cpp #include "Motion/CubismMotion.hpp" #include "Motion/CubismMotionManager.hpp" using namespace Live2D::Cubism::Framework; class MotionController { public: CubismMotionManager* _motionManager; csmMap _motions; MotionController() { _motionManager = CSM_NEW CubismMotionManager(); } ~MotionController() { // Release all loaded motions for (auto iter = _motions.Begin(); iter != _motions.End(); ++iter) { ACubismMotion::Delete(iter->Second); } CSM_DELETE(_motionManager); } void LoadMotion(const csmChar* name, const csmByte* buffer, csmSizeInt size) { // Create motion with callbacks CubismMotion* motion = CubismMotion::Create( buffer, size, OnMotionFinished, // Finished callback OnMotionBegan, // Began callback true // Check motion consistency ); if (motion) { // Configure motion settings motion->SetFadeInTime(0.5f); // 0.5 second fade in motion->SetFadeOutTime(0.5f); // 0.5 second fade out motion->SetLoop(false); // Don't loop by default motion->SetWeight(1.0f); // Full weight _motions[name] = motion; printf("Motion '%s' loaded. Duration: %.2f seconds\n", name, motion->GetDuration()); } } CubismMotionQueueEntryHandle PlayMotion(const csmChar* name, csmInt32 priority) { ACubismMotion* motion = _motions[name]; if (!motion) { printf("Motion '%s' not found\n", name); return InvalidMotionQueueEntryHandleValue; } // Check if we can play this motion based on priority if (_motionManager->GetCurrentPriority() > priority) { printf("Cannot play motion: current priority is higher\n"); return InvalidMotionQueueEntryHandleValue; } // Reserve motion for playback if (_motionManager->ReserveMotion(priority)) { // Start motion with priority // autoDelete = false since we manage motion lifetime ourselves CubismMotionQueueEntryHandle handle = _motionManager->StartMotionPriority(motion, false, priority); printf("Playing motion '%s' with priority %d\n", name, priority); return handle; } return InvalidMotionQueueEntryHandleValue; } void Update(CubismModel* model, csmFloat32 deltaTime) { // Update all playing motions and apply to model _motionManager->UpdateMotion(model, deltaTime); } bool IsMotionFinished(CubismMotionQueueEntryHandle handle) { return _motionManager->IsFinished(handle); } void StopAllMotions() { _motionManager->StopAllMotions(); } private: static void OnMotionBegan(ACubismMotion* self) { printf("Motion began playing\n"); } static void OnMotionFinished(ACubismMotion* self) { printf("Motion finished playing\n"); } }; ``` -------------------------------- ### Compile Normal Shaders Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Iterates through normal shader files, compiling each into AIR and metallib formats. It uses `xcrun` with the determined SDK name and sets up custom commands for the build process. ```cmake foreach(shader ${normal_shader_files}) get_filename_component(file_name ${shader} NAME) get_filename_component(full_path ${shader} ABSOLUTE) get_filename_component(output_file ${shader} NAME_WE) set(output_air_file ${output_intermediate_dir}/${output_file}.air) set(output_metallib_file ${output_lib_dir}/${output_file}.metallib) set(compiled_shaders_framework ${compiled_shaders_framework} ${output_air_file}) set(compiled_shaders_framework ${compiled_shaders_framework} ${output_metallib_file}) set(compiled_shaders_framework ${compiled_shaders_framework} PARENT_SCOPE) add_custom_command( OUTPUT ${output_metallib_file} COMMAND xcrun -sdk ${sdk_name} metal -o ${output_air_file} -c ${shader} COMMAND xcrun -sdk ${sdk_name} metallib -o ${output_metallib_file} ${output_air_file} DEPENDS "${full_path};${shader_include_files}" ) set(air_files "${air_files};${output_air_file}") set(metallib_files "${metallib_files};${output_metallib_file}" PARENT_SCOPE) endforeach() ``` -------------------------------- ### Define Blend Shader Include Files Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Lists the header files required for blend shaders. These files contain definitions specific to color and alpha blending operations. ```cmake set(blend_shader_include_files ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcColorBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcAlphaBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/BlendShaderFuncs.h ${CMAKE_CURRENT_SOURCE_DIR}/BlendShaderStructs.h ) ``` -------------------------------- ### Define Blend Shader Source Files Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Lists the source files for blend shaders. These files implement various blending techniques, including masked and premultiplied alpha blending. ```cmake set(blend_shader_files ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcMaskBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcMaskInvertedBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcMaskInvertedPremultipliedAlphaBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcMaskPremultipliedAlphaBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/FragShaderSrcPremultipliedAlphaBlend.metal ) ``` -------------------------------- ### Compile Shaders to Metallib Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Custom command to compile shader files into a metallib archive. This is used for Metal shader compilation on macOS. ```cmake COMMAND xcrun -sdk ${sdk_name} metallib -o ${output_metallib_file} ${output_air_file} DEPENDS "${full_path};${shader_include_files};${blend_shader_include_files}" ``` -------------------------------- ### Define Normal Shader Files Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Lists the source files for normal shaders, which are used for standard rendering operations. This includes Metal shaders and vertex shaders for blending. ```cmake set(normal_shader_files ${CMAKE_CURRENT_SOURCE_DIR}/MetalShaders.metal ${CMAKE_CURRENT_SOURCE_DIR}/VertShaderSrcBlend.metal ${CMAKE_CURRENT_SOURCE_DIR}/VertShaderSrcMaskedBlend.metal ) ``` -------------------------------- ### Combine Shader Lists (CMake) Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Vulkan/Shaders/CMakeLists.txt Combines the normal and blend shader file lists into a single variable for easier management in CMake. ```cmake set (shader_files "${normal_shader_files};${blend_shader_files}") ``` -------------------------------- ### Define Shader Include Files Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Specifies the header files that need to be included during shader compilation. This ensures that necessary definitions and types are available. ```cmake set(shader_include_files ${CMAKE_CURRENT_SOURCE_DIR}/MetalShaderTypes.h ) ``` -------------------------------- ### Implement Hit Testing for Interactive Models Source: https://context7.com/live2d/cubismnativeframework/llms.txt Extend CubismUserModel to add hit testing functionality. This involves transforming screen coordinates to model space and checking against defined hit areas. Ensure model matrix is available for coordinate transformation. ```cpp #include "Model/CubismUserModel.hpp" using namespace Live2D::Cubism::Framework; class InteractiveModel : public CubismUserModel { public: // Check if a point hits a named hit area const csmChar* HitTest(csmFloat32 x, csmFloat32 y) { CubismModel* model = GetModel(); if (!model) return nullptr; // Transform screen coordinates to model space if needed CubismModelMatrix* modelMatrix = GetModelMatrix(); // ... coordinate transformation ... // Check hit areas defined in model settings // Common hit area names: "Head", "Body", "TouchArea" CubismIdManager* idManager = CubismFramework::GetIdManager(); // Test specific drawable areas CubismIdHandle headAreaId = idManager->GetId("HitAreaHead"); CubismIdHandle bodyAreaId = idManager->GetId("HitAreaBody"); if (IsHit(headAreaId, x, y)) { return "Head"; } if (IsHit(bodyAreaId, x, y)) { return "Body"; } return nullptr; } // Custom hit test using drawable bounds bool IsPointInDrawable(csmInt32 drawableIndex, csmFloat32 x, csmFloat32 y) { CubismModel* model = GetModel(); // Get drawable vertices csmInt32 vertexCount = model->GetDrawableVertexCount(drawableIndex); const Core::csmVector2* vertices = model->GetDrawableVertexPositions(drawableIndex); // Simple bounding box check csmFloat32 minX = vertices[0].X, maxX = vertices[0].X; csmFloat32 minY = vertices[0].Y, maxY = vertices[0].Y; for (csmInt32 i = 1; i < vertexCount; i++) { if (vertices[i].X < minX) minX = vertices[i].X; if (vertices[i].X > maxX) maxX = vertices[i].X; if (vertices[i].Y < minY) minY = vertices[i].Y; if (vertices[i].Y > maxY) maxY = vertices[i].Y; } return (x >= minX && x <= maxX && y >= minY && y <= maxY); } }; // Usage void HandleClick(InteractiveModel* model, float screenX, float screenY) { const csmChar* hitArea = model->HitTest(screenX, screenY); if (hitArea) { printf("Clicked on: %s\n", hitArea); if (strcmp(hitArea, "Head") == 0) { // Play head touch reaction // model->PlayMotion("head_touch"); } else if (strcmp(hitArea, "Body") == 0) { // Play body touch reaction // model->PlayMotion("body_touch"); } } } ``` -------------------------------- ### Inspect Cubism Model Drawables Source: https://context7.com/live2d/cubismnativeframework/llms.txt Iterates through all drawables in a Cubism model, printing detailed information for each. Requires a pointer to a CubismModel object. Includes checks for masking and retrieves mask details if applicable. ```cpp #include "Model/CubismModel.hpp" #include "Rendering/csmBlendMode.hpp" using namespace Live2D::Cubism::Framework; void InspectDrawables(CubismModel* model) { csmInt32 drawableCount = model->GetDrawableCount(); printf("Total drawables: %d\n", drawableCount); // Get render order list const csmInt32* renderOrders = model->GetRenderOrders(); for (csmInt32 i = 0; i < drawableCount; i++) { CubismIdHandle drawableId = model->GetDrawableId(i); // Basic properties csmInt32 textureIndex = model->GetDrawableTextureIndex(i); csmInt32 vertexCount = model->GetDrawableVertexCount(i); csmInt32 indexCount = model->GetDrawableVertexIndexCount(i); csmFloat32 opacity = model->GetDrawableOpacity(i); csmInt32 renderOrder = renderOrders[i]; printf("\nDrawable %d: %s\n", i, drawableId->GetString().GetRawString()); printf(" Texture: %d, Vertices: %d, Indices: %d\n", textureIndex, vertexCount, indexCount); printf(" Opacity: %.2f, Render order: %d\n", opacity, renderOrder); // Blend mode csmBlendMode blendMode = model->GetDrawableBlendModeType(i); const char* blendModeStr = "Unknown"; switch (blendMode) { case csmBlendMode_Normal: blendModeStr = "Normal"; break; case csmBlendMode_Additive: blendModeStr = "Additive"; break; case csmBlendMode_Multiplicative: blendModeStr = "Multiplicative"; break; } printf(" Blend mode: %s\n", blendModeStr); // Multiply and screen colors Core::csmVector4 multiplyColor = model->GetDrawableMultiplyColor(i); Core::csmVector4 screenColor = model->GetDrawableScreenColor(i); printf(" Multiply color: (%.2f, %.2f, %.2f, %.2f)\n", multiplyColor.X, multiplyColor.Y, multiplyColor.Z, multiplyColor.W); printf(" Screen color: (%.2f, %.2f, %.2f, %.2f)\n", screenColor.X, screenColor.Y, screenColor.Z, screenColor.W); // Visibility and dynamic flags bool isVisible = model->GetDrawableDynamicFlagIsVisible(i); bool visibilityChanged = model->GetDrawableDynamicFlagVisibilityDidChange(i); bool opacityChanged = model->GetDrawableDynamicFlagOpacityDidChange(i); bool verticesChanged = model->GetDrawableDynamicFlagVertexPositionsDidChange(i); printf(" Visible: %s, Changed: vis=%s opacity=%s verts=%s\n", isVisible ? "yes" : "no", visibilityChanged ? "yes" : "no", opacityChanged ? "yes" : "no", verticesChanged ? "yes" : "no"); // Mask information bool invertedMask = model->GetDrawableInvertedMask(i); printf(" Inverted mask: %s\n", invertedMask ? "yes" : "no"); // Parent part csmInt32 parentPartIndex = model->GetDrawableParentPartIndex(i); if (parentPartIndex >= 0) { CubismIdHandle parentId = model->GetPartId(parentPartIndex); printf(" Parent part: %s\n", parentId->GetString().GetRawString()); } // Culling csmInt32 culling = model->GetDrawableCulling(i); printf(" Culling: %d\n", culling); } // Check if model uses masking if (model->IsUsingMasking()) { printf("\nModel uses clipping masks\n"); // Get mask information const csmInt32** masks = model->GetDrawableMasks(); const csmInt32* maskCounts = model->GetDrawableMaskCounts(); for (csmInt32 i = 0; i < drawableCount; i++) { if (maskCounts[i] > 0) { printf("Drawable %d masked by: ", i); for (csmInt32 m = 0; m < maskCounts[i]; m++) { printf("%d ", masks[i][m]); } printf("\n"); } } } } ``` -------------------------------- ### Compile Normal Shaders Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Vulkan/Shaders/CMakeLists.txt Compiles normal GLSL shaders into SPIR-V format. Skips 'common.glsl' and sets HEADER_FILE_ONLY property. Uses glslc for compilation, with platform-specific paths for the executable. ```cmake foreach(shader ${normal_shader_files}) get_filename_component(file_name ${shader} NAME) if(file_name MATCHES "common.glsl") continue() endif() get_filename_component(full_path ${shader} ABSOLUTE) set(output_dir ${CMAKE_CURRENT_BINARY_DIR}/compiledShaders) string(REGEX REPLACE \.frag|\.vert "" output_file ${file_name}) set(output_file ${output_dir}/${output_file}.spv) set(compiled_shaders_framework ${compiled_shaders_framework} ${output_file}) set(compiled_shaders_framework ${compiled_shaders_framework} PARENT_SCOPE) set_source_files_properties(${shader} PROPERTIES HEADER_FILE_ONLY TRUE) if(WIN32) add_custom_command( OUTPUT ${output_file} COMMAND ${CMAKE_COMMAND} -E make_directory ${output_dir} COMMAND $ENV{VK_SDK_PATH}/Bin/glslc.exe ${full_path} -o ${output_file} DEPENDS ${full_path} ) endif() if(UNIX AND NOT APPLE) add_custom_command( OUTPUT ${output_file} COMMAND ${CMAKE_COMMAND} -E make_directory ${output_dir} COMMAND glslc ${full_path} -o ${output_file} DEPENDS ${full_path} ) endif() endforeach() ``` -------------------------------- ### Compile Blend Shaders with Blend Modes Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Metal/Shaders/CMakeLists.txt Compiles blend shaders by iterating through each shader file and combining it with all possible color and alpha blend modes. This generates a unique metallib for each combination, excluding the 'Normal' color and 'Over' alpha blend. ```cmake foreach(shader ${blend_shader_files}) get_filename_component(file_name ${shader} NAME) get_filename_component(full_path ${shader} ABSOLUTE) foreach(color_blend_mode ${color_blend_modes}) foreach(alpha_blend_mode ${alpha_blend_modes}) if((color_blend_mode MATCHES "^Normal$") AND (alpha_blend_mode MATCHES "^Over$")) continue() endif() get_filename_component(output_file ${shader} NAME_WE) set(output_file ${output_file}${color_blend_mode}${alpha_blend_mode}) set(output_air_file ${output_intermediate_dir}/${output_file}.air) set(output_metallib_file ${output_lib_dir}/${output_file}.metallib) set(compiled_shaders_framework ${compiled_shaders_framework} ${output_air_file}) set(compiled_shaders_framework ${compiled_shaders_framework} ${output_metallib_file}) set(compiled_shaders_framework ${compiled_shaders_framework} PARENT_SCOPE) set(define_color_blend_mode "CSM_COLOR_BLEND_MODE=${Color_Blend_Mode_${color_blend_mode}}") set(define_alpha_blend_mode "CSM_ALPHA_BLEND_MODE=${Alpha_Blend_Mode_${alpha_blend_mode}}") add_custom_command( OUTPUT ${output_metallib_file} COMMAND ${CMAKE_COMMAND} -E make_directory ${output_intermediate_dir} COMMAND ${CMAKE_COMMAND} -E make_directory ${output_lib_dir} COMMAND xcrun -sdk ${sdk_name} metal -D ${define_alpha_blend_mode} -D ${define_color_blend_mode} -o ${output_air_file} -c ${shader} ``` -------------------------------- ### Define Color Blend Modes (CMake) Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Vulkan/Shaders/CMakeLists.txt Lists the available color blend modes supported by the framework. These are typically used in conjunction with shader programs. ```cmake set(color_blend_modes Normal Add AddGlow Darken Multiply ColorBurn LinearBurn Lighten Screen ColorDodge Overlay SoftLight HardLight LinearLight Hue Color ) ``` -------------------------------- ### Create Framework Shaders Target Source: https://github.com/live2d/cubismnativeframework/blob/develop/src/Rendering/Vulkan/Shaders/CMakeLists.txt Defines a custom target 'FrameworkShaders' that depends on all compiled shaders. This ensures shaders are compiled before they are used. ```cmake source_group("shaders" FILES ${shader_files}) add_custom_target( FrameworkShaders ALL DEPENDS ${compiled_shaders_framework} SOURCES ${shader_files} ) ```