1. 模型设置新增功能:自定义Live2D模型显示比例

This commit is contained in:
Misaki
2025-12-25 03:27:43 +08:00
parent c1d90c73b8
commit e9a9d483db
9 changed files with 143 additions and 22 deletions
+23 -8
View File
@@ -39,23 +39,22 @@ public:
/**
* @brief Resources フォルダにあるモデルフォルダ名をセットする
*
*
* 无需多言,这个也宣布弃用了,这个函数功能是自动扫描路径下所有模型
*/
[[deprecated("Do not use")]]
void SetUpModel();
/**
* @brief Resources フォルダにあるモデルフォルダ名を取得する
*
*/
Csm::csmVector<Csm::csmString> GetModelDir() const;
[[nodiscard]] Csm::csmVector<Csm::csmString> GetModelDir() const;
/**
* @brief Resources フォルダにあるモデルフォルダのサイズを取得する
*
*/
Csm::csmInt32 GetModelDirSize() const;
[[nodiscard]] Csm::csmInt32 GetModelDirSize() const;
/**
* @brief 現在のシーンで保持しているモデルを返す
@@ -63,7 +62,7 @@ public:
* @param[in] no モデルリストのインデックス値
* @return モデルのインスタンスを返す。インデックス値が範囲外の場合はNULLを返す。
*/
LAppModel* GetModel(Csm::csmUint32 no) const;
[[nodiscard]] LAppModel* GetModel(Csm::csmUint32 no) const;
/**
* @brief 現在のシーンで保持しているすべてのモデルを解放する
@@ -110,16 +109,31 @@ public:
[[deprecated("如需使用,请在单线程中使用本函数")]]
void LoadModelFromPath(const std::string& modelPath, const std::string& fileName);
// 仅在内存中加载模型,不干扰当前运行状态(供子线程调用)
/**
* 模型显示大小比例实时缩放接口
* @param Sacle 缩放比例
*/
void ModelSizeChange(int Sacle);
/**
* @brief 从指定路径加载模型 仅在内存中加载模型,不干扰当前运行状态(供子线程调用)
* @param modelPath 模型文件路径
* @param fileName 模型文件名称
* @return 模型实例
*/
LAppModel* LoadModelInstance(const std::string& modelPath, const std::string& fileName);
// 将已经加载好的模型应用到系统中(供主线程调用)
/**
* @brief 模型加载完成后,将模型挂载到系统中
* @param model 模型实例
*/
void MountLoadedModel(LAppModel* model);
/**
* @brief 次のシーンに切り替える<br>
* サンプルアプリケーションではモデルセットの切り替えを行う。
*/
[[deprecated("Do not use")]]
void NextScene();
/**
@@ -132,6 +146,7 @@ public:
* 至于为什么修改,原生的SDK是自动查找指定目录的模型文件,自动加载的
* 但这样就与实际使用的需求大不相同,我们需要能够手动选择
*/
[[deprecated("Use LoadModelFromPath()")]]
void ChangeScene(Csm::csmInt32 index);
/**
@@ -151,7 +166,7 @@ public:
* @brief モデル個数を得る
* @return 所持モデル個数
*/
Csm::csmUint32 GetModelNum() const;
[[nodiscard]] Csm::csmUint32 GetModelNum() const;
/**
* @brief viewMatrixをセットする
+2 -2
View File
@@ -52,7 +52,7 @@ public:
* @brief 获取 Live2D 模型的 Canvas 宽度像素 (在 Live2D 坐标系下)
* @return Canvas 宽度
*/
Live2D::Cubism::Framework::csmFloat32 GetModelCanvasWidthPixel() const
[[nodiscard]] Live2D::Cubism::Framework::csmFloat32 GetModelCanvasWidthPixel() const
{
// _model 是 CubismModel 的基类指针
return _model ? _model->GetCanvasWidthPixel() : 0.0f;
@@ -62,7 +62,7 @@ public:
* @brief 获取 Live2D 模型的 Canvas 高度像素 (在 Live2D 坐标系下)
* @return Canvas 高度
*/
Live2D::Cubism::Framework::csmFloat32 GetModelCanvasHeightPixel() const
[[nodiscard]] Live2D::Cubism::Framework::csmFloat32 GetModelCanvasHeightPixel() const
{
return _model ? _model->GetCanvasHeightPixel() : 0.0f;
}
+13 -5
View File
@@ -94,28 +94,36 @@ public:
*
* @param[in] deviceX デバイスX座標
*/
float TransformViewX(float deviceX) const;
[[nodiscard]] float TransformViewX(float deviceX) const;
/**
* @brief Y座標をView座標に変換する。
*
* @param[in] deviceY デバイスY座標
*/
float TransformViewY(float deviceY) const;
[[nodiscard]] float TransformViewY(float deviceY) const;
/**
* @brief X座標をScreen座標に変換する。
*
* @param[in] deviceX デバイスX座標
*/
float TransformScreenX(float deviceX) const;
[[nodiscard]] float TransformScreenX(float deviceX) const;
/**
* @brief Y座標をScreen座標に変換する。
*
* @param[in] deviceY デバイスY座標
*/
float TransformScreenY(float deviceY) const;
[[nodiscard]] float TransformScreenY(float deviceY) const;
/**
* @brief 检测坐标是否在模型上(跨平台穿透功能核心)
* @param deviceX 设备坐标X(鼠标位置)
* @param deviceY 设备坐标Y
* @return 是否击中模型
*/
[[nodiscard]] bool IsModelHit(float deviceX, float deviceY) const;
/**
* @brief モデル1体を描画する直前にコールされる
@@ -131,7 +139,7 @@ public:
* @brief 別レンダリングターゲットにモデルを描画するサンプルで
* 描画時のαを決定する
*/
float GetSpriteAlpha(int assign) const;
[[nodiscard]] float GetSpriteAlpha(int assign) const;
/**
* @brief レンダリング先を切り替える
@@ -263,6 +263,19 @@ void LAppLive2DManager::OnUpdate() const
}
}
#include <AppContext.h>
void LAppLive2DManager::ModelSizeChange(const int Sacle = 15)
{
// 加载完后根据模型大小来重新设置当前窗口大小
const int width = static_cast<int>(_models[0]->GetModel()->GetCanvasWidthPixel() / Sacle);
const int height = static_cast<int>(_models[0]->GetModel()->GetCanvasHeightPixel() / Sacle);
// 确保在主线程调用 UI 相关操作
if(AppContext::GetGLCore()) {
AppContext::GetGLCore()->setWindowSize(width, height);
}
LAppPal::PrintLogLn("[APP]窗口尺寸重新设置为: W: %d H: %d", width, height);
}
void LAppLive2DManager::LoadModelFromPath(const std::string& modelPath, const std::string& fileName)
{
const csmString modelPathStr(modelPath.c_str());
+35
View File
@@ -17,6 +17,7 @@
#include "LAppSprite.hpp"
#include "LAppModel.hpp"
using namespace std;
using namespace LAppDefine;
@@ -265,6 +266,40 @@ float LAppView::TransformScreenY(float deviceY) const
return _deviceToScreen->TransformY(deviceY);
}
#include "Id/CubismIdManager.hpp"
bool LAppView::IsModelHit(const float deviceX, const float deviceY) const {
const LAppLive2DManager* live2DManager = LAppLive2DManager::GetInstance();
if (!live2DManager || live2DManager->GetModelNum() == 0) {
return false;
}
// 坐标转换(设备坐标 -> Live2D View 坐标)
const csmFloat32 viewX = _deviceToScreen->TransformX(deviceX);
const csmFloat32 viewY = _deviceToScreen->TransformY(deviceY);
// 正确获取 ID Handle(在 SDK 初始化后调用)
static const Csm::CubismId* bodyId = nullptr;
static const Csm::CubismId* headId = nullptr;
if (!bodyId) {
bodyId = CubismFramework::GetIdManager()->GetId("Body");
headId = CubismFramework::GetIdManager()->GetId("Head");
}
// 遍历所有模型进行碰撞检测
const csmUint32 modelCount = live2DManager->GetModelNum();
for (csmUint32 i = 0; i < modelCount; ++i) {
LAppModel* model = live2DManager->GetModel(i);
if (!model || !model->GetModel()) continue;
// 检查命中区域(修正:使用 SDK 提供的 ID)
if (model->IsHit(bodyId, viewX, viewY) || model->IsHit(headId, viewX, viewY)) {
return true; // 击中模型
}
}
return false; // 未击中模型
}
void LAppView::PreModelDraw(LAppModel& refModel)
{
// 別のレンダリングターゲットへ向けて描画する場合の使用するフレームバッファ
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Misakiotoha
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -61,7 +61,7 @@ GLCore::GLCore(int w, int h, QWidget *parent)
TextRenderer::getInstance()->setGravity(600.0f); // 更快的下坠速度
TextRenderer::getInstance()->setDampFactor(0.85f); // 更强的弹性效果
// this->setWindowFlag(Qt::FramelessWindowHint); // 设置无边框窗口
this->setWindowFlag(Qt::FramelessWindowHint); // 设置无边框窗口
this->setWindowFlag(Qt::WindowStaysOnTopHint); // 设置窗口始终在顶部
this->setWindowFlag(Qt::Tool); // 隐藏应用程序图标
this->setAttribute(Qt::WA_TranslucentBackground); // 设置窗口背景透明
+6 -5
View File
@@ -12,12 +12,11 @@
#include "ElaPushButton.h"
#include "ElaLineEdit.h"
#include "ElaComboBox.h"
#include "ElaSlider.h"
#include <QUrl>
#include <utility>
class ElaLineEdit;
class ElaPushButton;
class ModelPage final : public BasePage
{
Q_OBJECT
@@ -31,9 +30,11 @@ public:
private:
// 设置当前模型
ElaLineEdit* modelUrlEdit = nullptr;
ElaPushButton* modelChoosePushButton = nullptr;
ElaPushButton* modelUsePushButton = nullptr;
ElaLineEdit* modelUrlEdit = nullptr; /// 模型Url 编辑框
ElaPushButton* modelChoosePushButton = nullptr; /// 选择模型按钮
ElaPushButton* modelUsePushButton = nullptr; /// 使用模型按钮
ElaSlider* modelSlider = nullptr; /// 滑块(用于设置模型实时大小)
QUrl modelFileUrl;
QString modelFilePathFirst;
QString modelFilePathSecond;
+29 -1
View File
@@ -20,13 +20,16 @@ ModelPage::ModelPage(QWidget *parent)
setWindowTitle("ModelPage");
modelUrlEdit = new ElaLineEdit(this);
modelUrlEdit = new ElaLineEdit(this); // 模型Url Edit
modelUrlEdit->setFixedWidth(300);
modelUrlEdit->setPlaceholderText("用于显示当前的模型Url");
modelChoosePushButton = new ElaPushButton("选择模型", this);
modelChoosePushButton->setToolTip("选择.model3.json结尾的文件");
modelUsePushButton = new ElaPushButton("使用模型", this);
modelUsePushButton->setToolTip("使用选择的模型或Url对应的模型");
ElaScrollPageArea *modelSetArea = new ElaScrollPageArea(this);
QHBoxLayout *modelSetLayout = new QHBoxLayout(modelSetArea);
ElaText *modelSetText = new ElaText("模型设置", this);
@@ -37,6 +40,30 @@ ModelPage::ModelPage(QWidget *parent)
modelSetLayout->addWidget(modelChoosePushButton);
modelSetLayout->addWidget(modelUsePushButton);
modelSetLayout->addSpacing(10);
// 创建滑块控件和标签
ElaText *modelSizeText = new ElaText("模型大小比例设置", this);
modelSizeText->setTextPixelSize(15);
modelSlider = new ElaSlider(this); // 滑块(用于设置模型实时大小)
modelSlider->setRange(0, 99); // 设置范围
modelSlider->setValue(85); // 设置默认值
modelSlider->setOrientation(Qt::Horizontal); // 水平方向
// 创建独立的区域容器
ElaScrollPageArea *modelSliderArea = new ElaScrollPageArea(this);
QHBoxLayout *modelSliderLayout = new QHBoxLayout(modelSliderArea);
// 添加到布局(加一个标签显示数值)
ElaText *modelSliderValueText = new ElaText("85%", this);
modelSliderValueText->setTextPixelSize(14);
connect(modelSlider, &ElaSlider::valueChanged, this, [modelSliderValueText](const int value){
modelSliderValueText->setText(QString("%1%").arg(value + 1));
LAppLive2DManager::GetInstance()->ModelSizeChange(100 - value); // 实时更新模型大小
});
modelSliderLayout->addWidget(modelSizeText);
modelSliderLayout->addWidget(modelSlider);
modelSliderLayout->addWidget(modelSliderValueText);
modelSliderLayout->addStretch(); // 让内容靠左
connect(modelChoosePushButton, &ElaPushButton::clicked, this, [this]() {
// 创建对话框对象(使用 heap 分配,由 Qt 对象树管理内存)
auto *fileDialog = new QFileDialog(this);
@@ -140,6 +167,7 @@ ModelPage::ModelPage(QWidget *parent)
centralWidget->setWindowTitle("模型设置");
QVBoxLayout *centerLayout = new QVBoxLayout(centralWidget);
centerLayout->addWidget(modelSetArea);
centerLayout->addWidget(modelSliderArea);
centerLayout->addStretch();
centerLayout->setContentsMargins(0, 0, 0, 0);
addCentralWidget(centralWidget, true, true, 0);