mirror of
https://git.chinosk6.cn/chinosk/gkms-local.git
synced 2026-10-07 23:21:48 +07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76037b26f1 | ||
|
|
c07adf7a20 | ||
|
|
ebfede9093 | ||
|
|
047b584677 | ||
|
|
3c5c9ac9dd | ||
|
|
fcdf249b40 | ||
|
|
1af809c11e | ||
|
|
b39dc37801 |
No files matched your search
+2
-2
@@ -15,8 +15,8 @@ android {
|
|||||||
applicationId "io.github.chinosk.gakumas.localify"
|
applicationId "io.github.chinosk.gakumas.localify"
|
||||||
minSdk 29
|
minSdk 29
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 12
|
versionCode 13
|
||||||
versionName "v3.3.1"
|
versionName "v3.5.0"
|
||||||
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include "GyroEmu.hpp"
|
||||||
|
#include "config/Config.hpp"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace GakumasLocal::GyroEmu {
|
||||||
|
using Quaternion = UnityResolve::UnityType::Quaternion;
|
||||||
|
using Vector3 = UnityResolve::UnityType::Vector3;
|
||||||
|
|
||||||
|
static float pitch = 0, yaw = 0, roll = 0; // 度,Unity 相机欧拉角
|
||||||
|
static POINT lastCursor{};
|
||||||
|
static bool dragging = false;
|
||||||
|
static auto lastTick = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
static bool Down(int vk) { return (GetAsyncKeyState(vk) & 0x8000) != 0; }
|
||||||
|
|
||||||
|
void Reset() { pitch = yaw = roll = 0; }
|
||||||
|
|
||||||
|
static void PollInput() {
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const float dt = (std::min)(std::chrono::duration<float>(now - lastTick).count(), 0.1f);
|
||||||
|
lastTick = now;
|
||||||
|
|
||||||
|
const float sx = Config::gyroSensitivity * (Config::gyroInvertX ? -1.f : 1.f);
|
||||||
|
const float sy = Config::gyroSensitivity * (Config::gyroInvertY ? -1.f : 1.f);
|
||||||
|
|
||||||
|
// 右键拖动:鼠标位移 -> yaw/pitch
|
||||||
|
if (Down(VK_RBUTTON)) {
|
||||||
|
POINT cur; GetCursorPos(&cur);
|
||||||
|
if (dragging) {
|
||||||
|
yaw += (cur.x - lastCursor.x) * 0.1f * sx;
|
||||||
|
pitch += (cur.y - lastCursor.y) * 0.1f * sy;
|
||||||
|
}
|
||||||
|
lastCursor = cur;
|
||||||
|
dragging = true;
|
||||||
|
} else {
|
||||||
|
dragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小键盘:7/9 翻滚, 5 复位。4/6/8/2 是游戏自己拍摄页的镜头键,不占用
|
||||||
|
const float step = 60.f * dt * Config::gyroSensitivity;
|
||||||
|
if (Down(VK_NUMPAD7)) roll += step;
|
||||||
|
if (Down(VK_NUMPAD9)) roll -= step;
|
||||||
|
if (Down(VK_NUMPAD5)) Reset();
|
||||||
|
|
||||||
|
pitch = std::clamp(pitch, -89.f, 89.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion Attitude() {
|
||||||
|
PollInput();
|
||||||
|
Quaternion q; q.Euler(pitch, yaw, roll);
|
||||||
|
// 反汇编 VLGyro.get_gyroRotationRaw:raw = gyroBaseRotation * Quaternion(-a.x, -a.y, a.z, a.w);
|
||||||
|
// CampusGyroController.Rotation = Inverse(raw@reset) * raw,base 被消掉,所以直接把相机欧拉角编码进 a 即可。
|
||||||
|
return Quaternion(-q.x, -q.y, q.z, q.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Gravity() {
|
||||||
|
// ponytail: 竖持手机时重力恒指向设备 -Y,VLGyro 没用到重力,够用
|
||||||
|
return Vector3(0.f, -1.f, 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../deps/UnityResolve/UnityResolve.hpp"
|
||||||
|
|
||||||
|
// 用鼠标右键拖动 / 小键盘模拟手机陀螺仪姿态(PC 版无陀螺仪)
|
||||||
|
namespace GakumasLocal::GyroEmu {
|
||||||
|
// 读一次输入并返回当前 Input.gyro.attitude
|
||||||
|
UnityResolve::UnityType::Quaternion Attitude();
|
||||||
|
UnityResolve::UnityType::Vector3 Gravity();
|
||||||
|
void Reset();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -20,6 +20,7 @@
|
|||||||
namespace GakumasLocal::HookMain
|
namespace GakumasLocal::HookMain
|
||||||
{
|
{
|
||||||
using Il2cppString = UnityResolve::UnityType::String;
|
using Il2cppString = UnityResolve::UnityType::String;
|
||||||
|
using Il2CppGCHandle = void*;
|
||||||
|
|
||||||
extern void* (*Sprite_get_texture_Orig)(void* self);
|
extern void* (*Sprite_get_texture_Orig)(void* self);
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ namespace GakumasLocal::HookMain
|
|||||||
|
|
||||||
Il2cppUtils::Il2CppClassHead* Texture2DClass = nullptr;
|
Il2cppUtils::Il2CppClassHead* Texture2DClass = nullptr;
|
||||||
Il2cppUtils::Il2CppClassHead* SpriteClass = nullptr;
|
Il2cppUtils::Il2CppClassHead* SpriteClass = nullptr;
|
||||||
std::unordered_map<std::string, uint32_t> LoadedLocalTextureHandles{};
|
std::unordered_map<std::string, Il2CppGCHandle> LoadedLocalTextureHandles{};
|
||||||
std::unordered_set<std::string> AppliedLocalTextureKeys{};
|
std::unordered_set<std::string> AppliedLocalTextureKeys{};
|
||||||
|
|
||||||
Il2cppUtils::Il2CppClassHead* GetTexture2DClass() {
|
Il2cppUtils::Il2CppClassHead* GetTexture2DClass() {
|
||||||
@@ -73,9 +74,35 @@ namespace GakumasLocal::HookMain
|
|||||||
Il2cppString* GetObjectName(void* obj) {
|
Il2cppString* GetObjectName(void* obj) {
|
||||||
if (!obj) return nullptr;
|
if (!obj) return nullptr;
|
||||||
|
|
||||||
static auto Object_GetName = reinterpret_cast<Il2cppString * (*)(void*)>(
|
static const auto Object_get_name =
|
||||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Object::GetName(UnityEngine.Object)"));
|
Il2cppUtils::GetMethod(
|
||||||
return Object_GetName ? Object_GetName(obj) : nullptr;
|
"UnityEngine.CoreModule.dll",
|
||||||
|
"UnityEngine",
|
||||||
|
"Object",
|
||||||
|
"get_name"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!Object_get_name ||
|
||||||
|
!Object_get_name->function) {
|
||||||
|
Log::Error("Cannot find UnityEngine.Object.get_name");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
using ObjectGetNameFn =
|
||||||
|
Il2cppString* (*)(
|
||||||
|
void* self,
|
||||||
|
void* method
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto getName =
|
||||||
|
reinterpret_cast<ObjectGetNameFn>(
|
||||||
|
Object_get_name->function
|
||||||
|
);
|
||||||
|
|
||||||
|
return getName(
|
||||||
|
obj,
|
||||||
|
Object_get_name->address
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDontUnloadUnusedAsset(void* obj) {
|
void SetDontUnloadUnusedAsset(void* obj) {
|
||||||
@@ -494,6 +521,57 @@ namespace GakumasLocal::HookMain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LoadImageFromByteArray(
|
||||||
|
void* texture,
|
||||||
|
void* byteArray,
|
||||||
|
bool markNonReadable
|
||||||
|
) {
|
||||||
|
if (!texture || !byteArray) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto LoadImageMethod =
|
||||||
|
Il2cppUtils::GetMethod(
|
||||||
|
"UnityEngine.ImageConversionModule.dll",
|
||||||
|
"UnityEngine",
|
||||||
|
"ImageConversion",
|
||||||
|
"LoadImage",
|
||||||
|
{
|
||||||
|
"UnityEngine.Texture2D",
|
||||||
|
"System.Byte[]",
|
||||||
|
"System.Boolean"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!LoadImageMethod ||
|
||||||
|
!LoadImageMethod->function) {
|
||||||
|
Log::Error(
|
||||||
|
"Cannot find ImageConversion.LoadImage(Texture2D, Byte[], Boolean)"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using LoadImageFn =
|
||||||
|
bool (*)(
|
||||||
|
void* texture,
|
||||||
|
void* byteArray,
|
||||||
|
bool markNonReadable,
|
||||||
|
void* method
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto loadImage =
|
||||||
|
reinterpret_cast<LoadImageFn>(
|
||||||
|
LoadImageMethod->function
|
||||||
|
);
|
||||||
|
|
||||||
|
return loadImage(
|
||||||
|
texture,
|
||||||
|
byteArray,
|
||||||
|
markNonReadable,
|
||||||
|
LoadImageMethod->address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void* LoadLocalTexture2D(const std::filesystem::path& path) {
|
void* LoadLocalTexture2D(const std::filesystem::path& path) {
|
||||||
if (!std::filesystem::is_regular_file(path)) return nullptr;
|
if (!std::filesystem::is_regular_file(path)) return nullptr;
|
||||||
|
|
||||||
@@ -516,33 +594,14 @@ namespace GakumasLocal::HookMain
|
|||||||
const auto ctor = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, ".ctor", 2) : nullptr;
|
const auto ctor = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, ".ctor", 2) : nullptr;
|
||||||
return ctor ? reinterpret_cast<void (*)(void*, int, int)>(ctor->methodPointer) : nullptr;
|
return ctor ? reinterpret_cast<void (*)(void*, int, int)>(ctor->methodPointer) : nullptr;
|
||||||
}();
|
}();
|
||||||
static auto ImageConversion_LoadImage = [] {
|
|
||||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
|
||||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
|
||||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
|
||||||
return reinterpret_cast<LoadImageFn>(icall);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
|
||||||
const auto assembly = UnityResolve::Get(assemblyName);
|
|
||||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
|
||||||
const auto method = imageConversionClass
|
|
||||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
|
||||||
: nullptr;
|
|
||||||
if (method) {
|
|
||||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return static_cast<LoadImageFn>(nullptr);
|
|
||||||
}();
|
|
||||||
static auto File_ReadAllBytes = [] {
|
static auto File_ReadAllBytes = [] {
|
||||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (!Texture2D_ctor || !ImageConversion_LoadImage || !File_ReadAllBytes) {
|
if (!Texture2D_ctor || !File_ReadAllBytes) {
|
||||||
Log::Error("LoadLocalTexture2D failed: Unity Texture2D/ImageConversion/File API not found.");
|
Log::Error("LoadLocalTexture2D failed: Unity Texture2D/File API not found.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,19 +610,28 @@ namespace GakumasLocal::HookMain
|
|||||||
|
|
||||||
const auto texture = UnityResolve::Invoke<void*>("il2cpp_object_new", textureClass);
|
const auto texture = UnityResolve::Invoke<void*>("il2cpp_object_new", textureClass);
|
||||||
Texture2D_ctor(texture, 2, 2);
|
Texture2D_ctor(texture, 2, 2);
|
||||||
if (!ImageConversion_LoadImage(texture, fileBytes, false)) {
|
if (!LoadImageFromByteArray(texture, fileBytes, false)) {
|
||||||
Log::ErrorFmt("LoadLocalTexture2D failed: %s", path.string().c_str());
|
Log::ErrorFmt("LoadLocalTexture2D failed: %s", path.string().c_str());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDontUnloadUnusedAsset(texture);
|
SetDontUnloadUnusedAsset(texture);
|
||||||
LoadedLocalTextureHandles.emplace(cacheKey, UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", texture, false));
|
const auto textureHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", texture, false);
|
||||||
|
if (!textureHandle) {
|
||||||
|
Log::ErrorFmt("Cannot create texture GCHandle: %s", path.string().c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
LoadedLocalTextureHandles.emplace(cacheKey, textureHandle);
|
||||||
Log::InfoFmt("Texture replaced from local file: %s", path.string().c_str());
|
Log::InfoFmt("Texture replaced from local file: %s", path.string().c_str());
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* LoadLocalTexture2DFromCandidates(const std::vector<std::filesystem::path>& candidates) {
|
void* LoadLocalTexture2DFromCandidates(const std::vector<std::filesystem::path>& candidates) {
|
||||||
for (const auto& candidate : candidates) {
|
for (const auto& candidate : candidates) {
|
||||||
|
if (!std::filesystem::is_regular_file(candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto texture = LoadLocalTexture2D(candidate)) {
|
if (auto texture = LoadLocalTexture2D(candidate)) {
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
@@ -578,40 +646,21 @@ namespace GakumasLocal::HookMain
|
|||||||
+ "|" + std::to_string(reinterpret_cast<std::uintptr_t>(texture2D));
|
+ "|" + std::to_string(reinterpret_cast<std::uintptr_t>(texture2D));
|
||||||
if (AppliedLocalTextureKeys.contains(cacheKey)) return true;
|
if (AppliedLocalTextureKeys.contains(cacheKey)) return true;
|
||||||
|
|
||||||
static auto ImageConversion_LoadImage = [] {
|
|
||||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
|
||||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
|
||||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
|
||||||
return reinterpret_cast<LoadImageFn>(icall);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
|
||||||
const auto assembly = UnityResolve::Get(assemblyName);
|
|
||||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
|
||||||
const auto method = imageConversionClass
|
|
||||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
|
||||||
: nullptr;
|
|
||||||
if (method) {
|
|
||||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return static_cast<LoadImageFn>(nullptr);
|
|
||||||
}();
|
|
||||||
static auto File_ReadAllBytes = [] {
|
static auto File_ReadAllBytes = [] {
|
||||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (!ImageConversion_LoadImage || !File_ReadAllBytes) {
|
if (!File_ReadAllBytes) {
|
||||||
Log::Error("ApplyLocalImageToTexture2D failed: Unity ImageConversion/File API not found.");
|
Log::Error("ApplyLocalImageToTexture2D failed: Unity File API not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fileBytes = File_ReadAllBytes(Il2cppString::New(path.string()));
|
const auto fileBytes = File_ReadAllBytes(Il2cppString::New(path.string()));
|
||||||
if (!fileBytes) return false;
|
if (!fileBytes) return false;
|
||||||
|
|
||||||
if (!ImageConversion_LoadImage(texture2D, fileBytes, false)) {
|
if (!LoadImageFromByteArray(texture2D, fileBytes, false)) {
|
||||||
Log::ErrorFmt("ApplyLocalImageToTexture2D failed: %s", path.string().c_str());
|
Log::ErrorFmt("ApplyLocalImageToTexture2D failed: %s", path.string().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -736,6 +785,16 @@ namespace GakumasLocal::HookMain
|
|||||||
return texture2D;
|
return texture2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unity 6 compatibility warning:
|
||||||
|
// The resolver functions below currently mix two possible target ABIs:
|
||||||
|
// - il2cpp_resolve_icall(): native icall signature
|
||||||
|
// - GetMethodPointer(): managed IL2CPP methodPointer, normally with a
|
||||||
|
// trailing MethodInfo* argument
|
||||||
|
//
|
||||||
|
// The corresponding AssetBundle / Resources / Sprite hooks in Hook.cpp are
|
||||||
|
// intentionally disabled. They must not be restored until the resolver and
|
||||||
|
// hook declarations distinguish these ABI forms, or until the managed
|
||||||
|
// fallback is removed and a verified icall is required.
|
||||||
void* ResolveSpriteGetTextureHookAddress() {
|
void* ResolveSpriteGetTextureHookAddress() {
|
||||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Sprite::get_texture(UnityEngine.Sprite)")) {
|
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Sprite::get_texture(UnityEngine.Sprite)")) {
|
||||||
return addr;
|
return addr;
|
||||||
|
|||||||
@@ -598,7 +598,7 @@ namespace GakumasLocal::Local {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unTransResultRet.empty() || (splitTransStat == SplitTagsTranslationStat::NO_SPLIT)) {
|
if (unTransResultRet.empty() || (splitTransStat == SplitTagsTranslationStat::NO_SPLIT)) {
|
||||||
DumpGenericText(origText);
|
DumpGenericText(origText, DumpStrStat::SPLITTABLE_ORIG);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (const auto& i : unTransResultRet) {
|
for (const auto& i : unTransResultRet) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "baseCamera.hpp"
|
#include "baseCamera.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <cmath>
|
||||||
#include "../Misc.hpp"
|
#include "../Misc.hpp"
|
||||||
#include "../BaseDefine.h"
|
#include "../BaseDefine.h"
|
||||||
#include "../../platformDefine.hpp"
|
#include "../../platformDefine.hpp"
|
||||||
@@ -411,6 +412,14 @@ namespace GKCamera {
|
|||||||
const auto currentY = targetPos.y;
|
const auto currentY = targetPos.y;
|
||||||
static auto lastRetY = currentY;
|
static auto lastRetY = currentY;
|
||||||
|
|
||||||
|
// NaN/Inf 防护:目标 Y 非法时保持上一次有效值,避免污染平滑队列后相机飞走
|
||||||
|
if (!std::isfinite(currentY)) {
|
||||||
|
return std::isfinite(lastRetY) ? lastRetY : 0.0f;
|
||||||
|
}
|
||||||
|
if (!std::isfinite(lastRetY)) {
|
||||||
|
lastRetY = currentY;
|
||||||
|
}
|
||||||
|
|
||||||
if (followModeY == FollowModeY::APPLY_Y) {
|
if (followModeY == FollowModeY::APPLY_Y) {
|
||||||
lastRetY = currentY;
|
lastRetY = currentY;
|
||||||
return currentY;
|
return currentY;
|
||||||
@@ -423,6 +432,10 @@ namespace GKCamera {
|
|||||||
recordsY.Push(currentY);
|
recordsY.Push(currentY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!std::isfinite(currentAvg)) {
|
||||||
|
return lastRetY;
|
||||||
|
}
|
||||||
|
|
||||||
if (abs(currentY - currentAvg) < 0.02) {
|
if (abs(currentY - currentAvg) < 0.02) {
|
||||||
return lastRetY;
|
return lastRetY;
|
||||||
}
|
}
|
||||||
@@ -460,7 +473,17 @@ namespace GKCamera {
|
|||||||
|
|
||||||
// 计算角色的右方向
|
// 计算角色的右方向
|
||||||
Vector3 up(0, 1, 0); // Y轴方向
|
Vector3 up(0, 1, 0); // Y轴方向
|
||||||
Vector3 right = forward.cross(up).Normalize();
|
Vector3 right = forward.cross(up);
|
||||||
|
|
||||||
|
// 修正归一化:原 UnityResolve::Vector3::Normalize 除的是平方长度,且零向量会产出 NaN
|
||||||
|
const auto rightLenSq = right.x * right.x + right.y * right.y + right.z * right.z;
|
||||||
|
if (rightLenSq < 1e-8f) {
|
||||||
|
// forward 与 up 平行/接近或为零向量,无法求右向,退化为仅高度偏移
|
||||||
|
return Vector3(position.x, position.y + offset.y, position.z);
|
||||||
|
}
|
||||||
|
const auto invRightLen = 1.0f / std::sqrt(rightLenSq);
|
||||||
|
right = Vector3(right.x * invRightLen, right.y * invRightLen, right.z * invRightLen);
|
||||||
|
|
||||||
Vector3 fwd = forward;
|
Vector3 fwd = forward;
|
||||||
Vector3 pos = position;
|
Vector3 pos = position;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ namespace GakumasLocal::Config {
|
|||||||
bool dumpText = false;
|
bool dumpText = false;
|
||||||
bool dumpRuntimeTexture = false;
|
bool dumpRuntimeTexture = false;
|
||||||
bool enableFreeCamera = false;
|
bool enableFreeCamera = false;
|
||||||
|
bool enableGyroEmu = false;
|
||||||
|
float gyroSensitivity = 1.0f;
|
||||||
|
bool gyroInvertX = false;
|
||||||
|
bool gyroInvertY = false;
|
||||||
int targetFrameRate = 0;
|
int targetFrameRate = 0;
|
||||||
bool unlockAllLive = false;
|
bool unlockAllLive = false;
|
||||||
bool unlockAllLiveCostume = false;
|
bool unlockAllLiveCostume = false;
|
||||||
@@ -77,6 +81,10 @@ namespace GakumasLocal::Config {
|
|||||||
GetConfigItem(dumpRuntimeTexture);
|
GetConfigItem(dumpRuntimeTexture);
|
||||||
GetConfigItem(targetFrameRate);
|
GetConfigItem(targetFrameRate);
|
||||||
GetConfigItem(enableFreeCamera);
|
GetConfigItem(enableFreeCamera);
|
||||||
|
GetConfigItem(enableGyroEmu);
|
||||||
|
GetConfigItem(gyroSensitivity);
|
||||||
|
GetConfigItem(gyroInvertX);
|
||||||
|
GetConfigItem(gyroInvertY);
|
||||||
GetConfigItem(unlockAllLive);
|
GetConfigItem(unlockAllLive);
|
||||||
GetConfigItem(unlockAllLiveCostume);
|
GetConfigItem(unlockAllLiveCostume);
|
||||||
GetConfigItem(enableLiveCustomeDress);
|
GetConfigItem(enableLiveCustomeDress);
|
||||||
@@ -135,6 +143,10 @@ namespace GakumasLocal::Config {
|
|||||||
SetConfigItem(dumpRuntimeTexture);
|
SetConfigItem(dumpRuntimeTexture);
|
||||||
SetConfigItem(targetFrameRate);
|
SetConfigItem(targetFrameRate);
|
||||||
SetConfigItem(enableFreeCamera);
|
SetConfigItem(enableFreeCamera);
|
||||||
|
SetConfigItem(enableGyroEmu);
|
||||||
|
SetConfigItem(gyroSensitivity);
|
||||||
|
SetConfigItem(gyroInvertX);
|
||||||
|
SetConfigItem(gyroInvertY);
|
||||||
SetConfigItem(unlockAllLive);
|
SetConfigItem(unlockAllLive);
|
||||||
SetConfigItem(unlockAllLiveCostume);
|
SetConfigItem(unlockAllLiveCostume);
|
||||||
SetConfigItem(enableLiveCustomeDress);
|
SetConfigItem(enableLiveCustomeDress);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ namespace GakumasLocal::Config {
|
|||||||
extern bool dumpText;
|
extern bool dumpText;
|
||||||
extern bool dumpRuntimeTexture;
|
extern bool dumpRuntimeTexture;
|
||||||
extern bool enableFreeCamera;
|
extern bool enableFreeCamera;
|
||||||
|
extern bool enableGyroEmu;
|
||||||
|
extern float gyroSensitivity;
|
||||||
|
extern bool gyroInvertX;
|
||||||
|
extern bool gyroInvertY;
|
||||||
extern int targetFrameRate;
|
extern int targetFrameRate;
|
||||||
extern bool unlockAllLive;
|
extern bool unlockAllLive;
|
||||||
extern bool unlockAllLiveCostume;
|
extern bool unlockAllLiveCostume;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<string name="gakumas_localify">Gakumas Localify</string>
|
<string name="gakumas_localify">Gakumas Localify</string>
|
||||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||||
<string name="replace_font">替換字體</string>
|
<string name="replace_font">替換字體</string>
|
||||||
|
<string name="replace_texture">替換貼圖</string>
|
||||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<string name="gakumas_localify">Gakumas Localify</string>
|
<string name="gakumas_localify">Gakumas Localify</string>
|
||||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||||
<string name="replace_font">替換字體</string>
|
<string name="replace_font">替換字體</string>
|
||||||
|
<string name="replace_texture">替換貼圖</string>
|
||||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<string name="gakumas_localify">Gakumas Localify</string>
|
<string name="gakumas_localify">Gakumas Localify</string>
|
||||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||||
<string name="replace_font">替換字體</string>
|
<string name="replace_font">替換字體</string>
|
||||||
|
<string name="replace_texture">替換貼圖</string>
|
||||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||||
|
|||||||
Reference in new issue
Block a user