├── neoforge ├── gradle.properties ├── src │ └── main │ │ ├── resources │ │ ├── icon.png │ │ ├── META-INF │ │ │ ├── accesstransformer.cfg │ │ │ └── neoforge.mods.toml │ │ └── realcamera.mixins.json │ │ └── java │ │ └── com │ │ └── xtracr │ │ └── realcamera │ │ ├── EventHandler.java │ │ └── RealCameraNeoForge.java └── build.gradle ├── common ├── src │ └── main │ │ ├── resources │ │ ├── architectury.common.json │ │ ├── assets │ │ │ └── realcamera │ │ │ │ ├── textures │ │ │ │ └── gui │ │ │ │ │ └── icon.png │ │ │ │ └── lang │ │ │ │ ├── lzh.json │ │ │ │ ├── zh_hk.json │ │ │ │ ├── zh_tw.json │ │ │ │ ├── zh_cn.json │ │ │ │ ├── ja_jp.json │ │ │ │ ├── ko_kr.json │ │ │ │ └── be_by.json │ │ ├── realcamera.accesswidener │ │ └── realcamera-common.mixins.json │ │ └── java │ │ └── com │ │ └── xtracr │ │ └── realcamera │ │ ├── compat │ │ ├── PlatformHelper.java │ │ ├── CompatibilityHelper.java │ │ ├── LegacyBindingMode.java │ │ ├── DisableHelper.java │ │ └── YSMCompat.java │ │ ├── mixin │ │ ├── accessor │ │ │ ├── CameraAccessor.java │ │ │ ├── GameRendererAccessor.java │ │ │ └── PlayerRendererAccessor.java │ │ ├── MixinPlayerRenderer.java │ │ ├── MixinFishingHookRenderer.java │ │ ├── MixinItemInHandRenderer.java │ │ ├── MixinItem.java │ │ ├── MixinGui.java │ │ ├── MixinStuckInBodyLayer.java │ │ ├── MixinLocalPlayer.java │ │ ├── MixinLevelRenderer.java │ │ ├── MixinGameRenderer.java │ │ └── MixinCamera.java │ │ ├── RealCamera.java │ │ ├── util │ │ ├── SmoothUtil.java │ │ ├── CrosshairUtil.java │ │ ├── MathUtil.java │ │ ├── LocUtil.java │ │ ├── RaycastUtil.java │ │ ├── MultiVertexCatcher.java │ │ ├── BuiltIterableBuffer.java │ │ └── VertexData.java │ │ ├── gui │ │ ├── DoubleSlider.java │ │ ├── TexturedButton.java │ │ ├── CyclingTexturedButton.java │ │ ├── GUIHelper.java │ │ └── NumberField.java │ │ ├── api │ │ ├── RealCameraAPI.java │ │ └── BindResult.java │ │ ├── config │ │ └── ConfigFile.java │ │ ├── KeyMappings.java │ │ └── RealCameraCore.java └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── fabric ├── src │ └── main │ │ ├── resources │ │ ├── assets │ │ │ └── realcamera │ │ │ │ └── icon.png │ │ ├── realcamera.mixins.json │ │ └── fabric.mod.json │ │ └── java │ │ └── com │ │ └── xtracr │ │ └── realcamera │ │ ├── config │ │ └── RealCameraMenu.java │ │ ├── EventHandler.java │ │ └── RealCameraFabric.java └── build.gradle ├── settings.gradle ├── .gitignore ├── gradle.properties ├── LICENSE ├── .github ├── workflows │ ├── build.yml │ └── publish.yml └── ISSUE_TEMPLATE │ ├── bug_report_cn.yml │ └── bug_report_en.yml ├── gradlew.bat ├── README_ZH.md ├── README.md └── gradlew /neoforge/gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=neoforge 2 | -------------------------------------------------------------------------------- /common/src/main/resources/architectury.common.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessWidener": "realcamera.accesswidener" 3 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xTracr/RealCamera/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /neoforge/src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xTracr/RealCamera/HEAD/neoforge/src/main/resources/icon.png -------------------------------------------------------------------------------- /fabric/src/main/resources/assets/realcamera/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xTracr/RealCamera/HEAD/fabric/src/main/resources/assets/realcamera/icon.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/textures/gui/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xTracr/RealCamera/HEAD/common/src/main/resources/assets/realcamera/textures/gui/icon.png -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/compat/PlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.compat; 2 | 3 | public interface PlatformHelper { 4 | boolean isModLoaded(String modId); 5 | } 6 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | protected net.minecraft.client.renderer.MultiBufferSource$BufferSource endBatch(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/BufferBuilder;)V -------------------------------------------------------------------------------- /common/src/main/resources/realcamera.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | extendable method net/minecraft/client/renderer/MultiBufferSource$BufferSource endBatch (Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/BufferBuilder;)V -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /fabric/src/main/resources/realcamera.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5", 4 | "package": "com.xtracr.realcamera.mixin", 5 | "compatibilityLevel": "JAVA_21", 6 | "mixins": [ 7 | ], 8 | "client": [ 9 | ], 10 | "server": [ 11 | ], 12 | "injectors": { 13 | "defaultRequire": 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/realcamera.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5", 4 | "package": "com.xtracr.realcamera.mixin", 5 | "compatibilityLevel": "JAVA_21", 6 | "mixins": [ 7 | ], 8 | "client": [ 9 | ], 10 | "server": [ 11 | ], 12 | "injectors": { 13 | "defaultRequire": 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | architectury { 2 | common(rootProject.enabled_platforms.split(",")) 3 | } 4 | 5 | dependencies { 6 | modCompileOnly "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" 7 | 8 | // Cloth Config 9 | modCompileOnly("me.shedaniel.cloth:cloth-config:${rootProject.cloth_config_version}") { 10 | exclude(group: "net.fabricmc.fabric-api") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { url "https://maven.fabricmc.net/" } 5 | maven { url "https://maven.architectury.dev/" } 6 | maven { url "https://maven.neoforged.net/releases" } 7 | } 8 | } 9 | 10 | include("common") 11 | include("fabric") 12 | include("neoforge") 13 | 14 | rootProject.name = "RealCamera" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | libs/ 3 | 4 | # gradle 5 | .gradle/ 6 | build/ 7 | out/ 8 | classes/ 9 | 10 | # eclipse 11 | *.launch 12 | .metadata 13 | 14 | # idea 15 | .idea/ 16 | *.iml 17 | *.ipr 18 | *.iws 19 | 20 | # vscode 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | *.DS_Store 29 | 30 | # run 31 | run/ 32 | 33 | # java 34 | hs_err_*.log 35 | replay_*.log 36 | *.hprof 37 | *.jfr 38 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/xtracr/realcamera/config/RealCameraMenu.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.config; 2 | 3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 4 | import com.terraformersmc.modmenu.api.ModMenuApi; 5 | 6 | public class RealCameraMenu implements ModMenuApi { 7 | @Override 8 | public ConfigScreenFactory getModConfigScreenFactory() { 9 | return ConfigScreen::create; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/accessor/CameraAccessor.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin.accessor; 2 | 3 | import net.minecraft.client.Camera; 4 | import net.minecraft.world.phys.Vec3; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Invoker; 7 | 8 | @Mixin(Camera.class) 9 | public interface CameraAccessor { 10 | @Invoker 11 | void invokeSetPosition(Vec3 position); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/accessor/GameRendererAccessor.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin.accessor; 2 | 3 | import net.minecraft.client.renderer.GameRenderer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(GameRenderer.class) 8 | public interface GameRendererAccessor { 9 | @Accessor 10 | float getFov(); 11 | 12 | @Accessor 13 | float getOldFov(); 14 | } 15 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx4G -XX:+UseG1GC 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | # Loader Properties 6 | minecraft_version=1.21.1 7 | enabled_platforms=fabric,neoforge 8 | fabric_loader_version=0.17.2 9 | neoforge_version=21.1.194 10 | #yarn_mappings=1.21.1+build.3 11 | # Mod Properties 12 | mod_version=0.7.5-beta 13 | maven_group=com.xtracr.realcamera 14 | archives_base_name=realcamera 15 | # Dependencies 16 | fabric_api_version=0.116.5+1.21.1 17 | cloth_config_version=15.0.140 18 | modmenu_version=11.0.3 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/RealCamera.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.xtracr.realcamera.compat.CompatibilityHelper; 4 | import com.xtracr.realcamera.compat.PlatformHelper; 5 | import com.xtracr.realcamera.config.ConfigFile; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public interface RealCamera extends PlatformHelper { 10 | String MODID = "realcamera"; 11 | String FULL_ID = "xtracr_" + MODID; 12 | Logger LOGGER = LoggerFactory.getLogger(MODID); 13 | 14 | default void initialize() { 15 | ConfigFile.load(); 16 | CompatibilityHelper.initialize(this); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/xtracr/realcamera/EventHandler.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.xtracr.realcamera.config.ConfigFile; 4 | import com.xtracr.realcamera.util.CrosshairUtil; 5 | import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; 6 | import net.minecraft.client.Minecraft; 7 | 8 | public class EventHandler { 9 | public static void onWorldRenderStart(WorldRenderContext context) { 10 | if (ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 11 | CrosshairUtil.update(Minecraft.getInstance(), context.camera(), context.positionMatrix(), context.projectionMatrix()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/resources/realcamera-common.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8.5", 4 | "package": "com.xtracr.realcamera.mixin", 5 | "compatibilityLevel": "JAVA_21", 6 | "mixins": [ 7 | ], 8 | "client": [ 9 | "MixinCamera", 10 | "MixinFishingHookRenderer", 11 | "MixinGameRenderer", 12 | "MixinGui", 13 | "MixinItem", 14 | "MixinItemInHandRenderer", 15 | "MixinLevelRenderer", 16 | "MixinLocalPlayer", 17 | "MixinPlayerRenderer", 18 | "MixinStuckInBodyLayer", 19 | "accessor.CameraAccessor", 20 | "accessor.GameRendererAccessor", 21 | "accessor.PlayerRendererAccessor" 22 | ], 23 | "server": [ 24 | ], 25 | "injectors": { 26 | "defaultRequire": 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinPlayerRenderer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 4 | import net.minecraft.util.Mth; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Redirect; 8 | 9 | @Mixin(PlayerRenderer.class) 10 | public abstract class MixinPlayerRenderer { 11 | @Redirect(method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V", at = @At(value = "INVOKE", target = "Ljava/lang/Math;acos(D)D")) 12 | private double realcamera$fixVanillaFallFlying(double n) { 13 | return Math.acos(Mth.clamp(n, -1.0, 1.0)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/accessor/PlayerRendererAccessor.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin.accessor; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.minecraft.client.player.AbstractClientPlayer; 5 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | @Mixin(PlayerRenderer.class) 10 | public interface PlayerRendererAccessor { 11 | @Invoker 12 | void invokeSetModelProperties(AbstractClientPlayer abstractClientPlayer); 13 | 14 | @Invoker 15 | void invokeSetupRotations(AbstractClientPlayer livingEntity, PoseStack poseStack, float f, float g, float deltaTick, float i); 16 | 17 | @Invoker 18 | void invokeScale(AbstractClientPlayer livingEntity, PoseStack poseStack, float deltaTick); 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinFishingHookRenderer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.xtracr.realcamera.compat.DisableHelper; 4 | import net.minecraft.client.CameraType; 5 | import net.minecraft.client.renderer.entity.FishingHookRenderer; 6 | import net.minecraft.world.entity.player.Player; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(FishingHookRenderer.class) 12 | public abstract class MixinFishingHookRenderer { 13 | @Redirect(method = "getPlayerHandPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z")) 14 | private boolean realcamera$redirectIsFirstPerson(CameraType cameraType, Player player) { 15 | if (DisableHelper.RENDER_HANDS.disabled(player)) return false; 16 | return cameraType.isFirstPerson(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/xtracr/realcamera/EventHandler.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.xtracr.realcamera.config.ConfigFile; 4 | import com.xtracr.realcamera.util.CrosshairUtil; 5 | import net.minecraft.client.Minecraft; 6 | import net.neoforged.neoforge.client.event.ClientTickEvent; 7 | import net.neoforged.neoforge.client.event.RenderLevelStageEvent; 8 | 9 | public class EventHandler { 10 | public static void onClientTick(ClientTickEvent.Post event) { 11 | KeyMappings.handle(Minecraft.getInstance()); 12 | } 13 | 14 | public static void onRenderLevelStage(RenderLevelStageEvent event) { 15 | if (RenderLevelStageEvent.Stage.AFTER_SKY.equals(event.getStage())) { 16 | if (ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 17 | CrosshairUtil.update(Minecraft.getInstance(), event.getCamera(), event.getModelViewMatrix(), event.getProjectionMatrix()); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinItemInHandRenderer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.xtracr.realcamera.compat.DisableHelper; 5 | import net.minecraft.client.player.LocalPlayer; 6 | import net.minecraft.client.renderer.ItemInHandRenderer; 7 | import net.minecraft.client.renderer.MultiBufferSource; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(ItemInHandRenderer.class) 14 | public abstract class MixinItemInHandRenderer { 15 | @Inject(method = "renderHandsWithItems", at = @At("HEAD"), cancellable = true) 16 | private void realcamera$cancelRender(float f, PoseStack poseStack, MultiBufferSource.BufferSource bufferSource, LocalPlayer localPlayer, int i, CallbackInfo ci) { 17 | if (DisableHelper.RENDER_HANDS.disabled(localPlayer)) ci.cancel(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/SmoothUtil.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import com.xtracr.realcamera.config.ConfigFile; 4 | import net.minecraft.world.phys.Vec3; 5 | import org.joml.Matrix3f; 6 | import org.joml.Quaterniond; 7 | 8 | public class SmoothUtil { 9 | private static final Quaterniond lastRotation = new Quaterniond(); 10 | private static Vec3 lastPosition = Vec3.ZERO; 11 | 12 | public static Vec3 smoothPosition(Vec3 position) { 13 | lastPosition = position.add(lastPosition.subtract(position).scale(ConfigFile.config().getDisplacementSmoothFactor())); 14 | return lastPosition; 15 | } 16 | 17 | public static Matrix3f smoothRotation(Matrix3f rotation) { 18 | return smoothRotation(new Quaterniond().setFromNormalized(rotation)).get(new Matrix3f()); 19 | } 20 | 21 | public static Quaterniond smoothRotation(Quaterniond rotation) { 22 | lastRotation.slerp(rotation, 1 - ConfigFile.config().getRotationSmoothFactor()); 23 | return lastRotation; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 xTracr 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /fabric/src/main/java/com/xtracr/realcamera/RealCameraFabric.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import net.fabricmc.api.ClientModInitializer; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 7 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; 8 | import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; 9 | import net.fabricmc.loader.api.FabricLoader; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class RealCameraFabric implements ClientModInitializer, RealCamera { 13 | @Override 14 | public void onInitializeClient() { 15 | initialize(); 16 | KeyMappings.register(KeyBindingHelper::registerKeyBinding); 17 | 18 | ClientTickEvents.END_CLIENT_TICK.register(KeyMappings::handle); 19 | WorldRenderEvents.START.register(EventHandler::onWorldRenderStart); 20 | } 21 | 22 | @Override 23 | public boolean isModLoaded(String modId) { 24 | return FabricLoader.getInstance().isModLoaded(modId); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "realcamera", 4 | "version": "${version}", 5 | "name": "Real Camera", 6 | "description": "Make the camera more realistic in the first-person view.", 7 | "authors": [ 8 | "xTracr" 9 | ], 10 | "contact": { 11 | "homepage": "https://modrinth.com/mod/real-camera", 12 | "sources": "https://github.com/xTracr/RealCamera", 13 | "issues": "https://github.com/xTracr/RealCamera/issues" 14 | }, 15 | "license": "MIT license", 16 | "icon": "assets/realcamera/icon.png", 17 | "environment": "client", 18 | "entrypoints": { 19 | "client": [ 20 | "com.xtracr.realcamera.RealCameraFabric" 21 | ], 22 | "modmenu": [ 23 | "com.xtracr.realcamera.config.RealCameraMenu" 24 | ] 25 | }, 26 | "mixins": [ 27 | "realcamera.mixins.json", 28 | "realcamera-common.mixins.json" 29 | ], 30 | "depends": { 31 | "fabricloader": "*", 32 | "fabric-api": "*", 33 | "minecraft": "1.21.1", 34 | "java": ">=21" 35 | }, 36 | "recommends": { 37 | "modmenu": "*", 38 | "cloth-config": "*" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - '**/lang/**' 7 | - '**.md' 8 | - 'LICENSE' 9 | push: 10 | branches: 11 | - '**' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Check Environment Variables 19 | run: env 20 | 21 | - name: Checkout Repository 22 | uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | 26 | - name: Setup JDK 27 | uses: actions/setup-java@v4 28 | with: 29 | distribution: "temurin" 30 | java-version: 21 31 | 32 | - name: Make Gradle Wrapper Executable 33 | if: ${{ runner.os != 'Windows' }} 34 | run: chmod +x ./gradlew 35 | 36 | - name: Build 37 | run: ./gradlew clean build -PcommitSHA=${{ github.sha }} 38 | 39 | - name: Capture Build Artifacts 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: Artifacts 43 | path: | 44 | fabric/build/libs/* 45 | neoforge/build/libs/* -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/gui/DoubleSlider.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.gui; 2 | 3 | import net.minecraft.client.gui.components.AbstractSliderButton; 4 | import net.minecraft.network.chat.Component; 5 | import net.minecraft.util.Mth; 6 | 7 | import java.util.function.Function; 8 | 9 | public class DoubleSlider extends AbstractSliderButton { 10 | private final Function textFactory; 11 | private final double min, max; 12 | 13 | public DoubleSlider(int width, int height, double value, double min, double max, Function textFactory) { 14 | super(0, 0, width, height, textFactory.apply(value), Mth.clamp(0, (value - min) / (max - min), 1)); 15 | this.textFactory = textFactory; 16 | this.min = min; 17 | this.max = max; 18 | } 19 | 20 | public double getValue() { 21 | return min + (max - min) * value; 22 | } 23 | 24 | public void setValue(double value) { 25 | this.value = Mth.clamp(0, (value - min) / (max - min), 1); 26 | applyValue(); 27 | } 28 | 29 | @Override 30 | protected void updateMessage() { 31 | setMessage(textFactory.apply(getValue())); 32 | } 33 | 34 | @Override 35 | protected void applyValue() { 36 | updateMessage(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinItem.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.xtracr.realcamera.RealCameraCore; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import com.xtracr.realcamera.util.RaycastUtil; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.Item; 8 | import net.minecraft.world.level.ClipContext; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.phys.BlockHitResult; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(Item.class) 17 | public abstract class MixinItem { 18 | @Inject(method = "getPlayerPOVHitResult", at = @At("HEAD"), cancellable = true) 19 | private static void realcamera$coverHitResult(Level level, Player player, ClipContext.Fluid fluid, CallbackInfoReturnable cir) { 20 | if (!ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 21 | RaycastUtil.update(player, 25.0d, 1.0f); 22 | cir.setReturnValue(level.clip(RaycastUtil.getClipContext(ClipContext.Block.OUTLINE, fluid, player))); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/CrosshairUtil.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import com.mojang.blaze3d.platform.Window; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import net.minecraft.client.Camera; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.world.phys.EntityHitResult; 8 | import net.minecraft.world.phys.HitResult; 9 | import net.minecraft.world.phys.Vec3; 10 | import org.joml.Matrix4f; 11 | 12 | public class CrosshairUtil { 13 | public static EntityHitResult capturedEntityHitResult; 14 | public static Vec3 offset = Vec3.ZERO; 15 | 16 | public static void translateMatrices(PoseStack poseStack) { 17 | poseStack.translate(offset.x(), -offset.y(), 0.0d); 18 | } 19 | 20 | public static void update(Minecraft client, Camera camera, Matrix4f... projectionMatrices) { 21 | HitResult hitResult = client.hitResult; 22 | offset = Vec3.ZERO; 23 | if (client.crosshairPickEntity != null) hitResult = capturedEntityHitResult; 24 | if (hitResult == null) return; 25 | Window window = client.getWindow(); 26 | offset = MathUtil.projectToVec2(hitResult.getLocation().subtract(camera.getPosition()), projectionMatrices) 27 | .multiply(0.5 * window.getGuiScaledWidth(), 0.5 * window.getGuiScaledHeight(), 0.0d); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/api/RealCameraAPI.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.api; 2 | 3 | import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; 4 | import net.minecraft.client.Minecraft; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.function.BiFunction; 10 | 11 | public class RealCameraAPI { 12 | private static final List> FUNCTIONS = new ArrayList<>(); 13 | private static final Map, Integer> PRIORITIES = new Object2IntOpenHashMap<>(); 14 | 15 | public static void registerFunction(BiFunction function) { 16 | registerFunction(0, function); 17 | } 18 | 19 | public static void registerFunction(int priority, BiFunction function) { 20 | FUNCTIONS.add(function); 21 | PRIORITIES.put(function, priority); 22 | FUNCTIONS.sort((a, b) -> PRIORITIES.get(b) - PRIORITIES.get(a)); 23 | } 24 | 25 | public static BindResult computeBindResult(Minecraft client, float deltaTick) { 26 | for (BiFunction function : FUNCTIONS) { 27 | BindResult result = function.apply(client, deltaTick); 28 | if (result.available()) return result; 29 | } 30 | return BindResult.EMPTY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinGui.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.xtracr.realcamera.RealCameraCore; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import com.xtracr.realcamera.util.CrosshairUtil; 6 | import net.minecraft.client.DeltaTracker; 7 | import net.minecraft.client.gui.Gui; 8 | import net.minecraft.client.gui.GuiGraphics; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(Gui.class) 15 | public abstract class MixinGui { 16 | @Inject(method = "renderCrosshair", at = @At("HEAD")) 17 | private void realcamera$atRenderCrosshairHEAD(GuiGraphics guiGraphics, DeltaTracker deltaTracker, CallbackInfo ci) { 18 | if (ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 19 | guiGraphics.pose().pushPose(); 20 | CrosshairUtil.translateMatrices(guiGraphics.pose()); 21 | } 22 | } 23 | 24 | @Inject(method = "renderCrosshair", at = @At("RETURN")) 25 | private void realcamera$atRenderCrosshairRETURN(GuiGraphics guiGraphics, DeltaTracker deltaTracker, CallbackInfo ci) { 26 | if (ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 27 | guiGraphics.pose().popPose(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinStuckInBodyLayer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.xtracr.realcamera.RealCameraCore; 5 | import com.xtracr.realcamera.config.ConfigFile; 6 | import net.minecraft.client.player.LocalPlayer; 7 | import net.minecraft.client.renderer.MultiBufferSource; 8 | import net.minecraft.client.renderer.entity.layers.StuckInBodyLayer; 9 | import net.minecraft.world.entity.LivingEntity; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(StuckInBodyLayer.class) 16 | public abstract class MixinStuckInBodyLayer { 17 | @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/LivingEntity;FFFFFF)V", at = @At("HEAD"), cancellable = true) 18 | private void realcamera$cancelRender(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { 19 | if (livingEntity instanceof LocalPlayer && RealCameraCore.isRendering() && !ConfigFile.config().isClassic() && !ConfigFile.config().renderStuckObjects()) { 20 | ci.cancel(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/MathUtil.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import net.minecraft.world.phys.Vec3; 4 | import org.joml.Matrix3f; 5 | import org.joml.Matrix4f; 6 | import org.joml.Vector4f; 7 | 8 | public class MathUtil { 9 | public static double round(double d, int digits) { 10 | return Math.round(d * Math.pow(10, digits)) / Math.pow(10, digits); 11 | } 12 | 13 | public static Vec3 getEulerAngleYXZ(Matrix3f normal) { 14 | if (normal.m21 <= -1.0) return new Vec3(Math.PI / 2, Math.atan2(normal.m10, normal.m00), 0.0); 15 | else if (normal.m21 >= 1.0) return new Vec3(-Math.PI / 2, -Math.atan2(normal.m10, normal.m00), 0.0); 16 | double xRot = Math.asin(-normal.m21); 17 | double yRot = Math.atan2(normal.m20, normal.m22); 18 | double zRot = Math.atan2(normal.m01, normal.m11); 19 | return new Vec3(xRot, yRot, zRot); 20 | } 21 | 22 | public static Vec3 getIntersectionPoint(Vec3 planePoint, Vec3 planeNormal, Vec3 linePoint, Vec3 lineNormal) { 23 | double distance = planeNormal.dot(planePoint.subtract(linePoint)) / planeNormal.dot(lineNormal); 24 | return linePoint.add(lineNormal.scale(distance)); 25 | } 26 | 27 | public static Vec3 projectToVec2(Vec3 vec3, Matrix4f... projectionMatrices) { 28 | Vector4f vector4f = new Vector4f((float) vec3.x(), (float) vec3.y(), (float) vec3.z(), 1.0f); 29 | for (Matrix4f matrix4f : projectionMatrices) vector4f.mul(matrix4f); 30 | if (vector4f.w() == 0.0) return Vec3.ZERO; 31 | return new Vec3(vector4f.x(), vector4f.y(), 0).scale(1 / (double) vector4f.w()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/xtracr/realcamera/RealCameraNeoForge.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.xtracr.realcamera.config.ConfigScreen; 4 | import net.neoforged.bus.api.IEventBus; 5 | import net.neoforged.fml.ModContainer; 6 | import net.neoforged.fml.ModList; 7 | import net.neoforged.fml.common.Mod; 8 | import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; 9 | import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; 10 | import net.neoforged.neoforge.client.gui.IConfigScreenFactory; 11 | import net.neoforged.neoforge.common.NeoForge; 12 | 13 | import java.util.Map; 14 | 15 | @Mod(RealCamera.MODID) 16 | public class RealCameraNeoForge implements RealCamera { 17 | private final ModContainer modContainer; 18 | private final Map modIdMap = Map.of("cloth-config", "cloth_config"); 19 | 20 | public RealCameraNeoForge(IEventBus modEventBus, ModContainer modContainer) { 21 | this.modContainer = modContainer; 22 | modEventBus.addListener(this::clientSetup); 23 | modEventBus.addListener(this::onKeyRegister); 24 | } 25 | 26 | public void clientSetup(FMLClientSetupEvent event) { 27 | initialize(); 28 | 29 | NeoForge.EVENT_BUS.addListener(EventHandler::onClientTick); 30 | NeoForge.EVENT_BUS.addListener(EventHandler::onRenderLevelStage); 31 | 32 | if (isModLoaded("cloth-config")) { 33 | modContainer.registerExtensionPoint(IConfigScreenFactory.class, (container, modListScreen) -> ConfigScreen.create(modListScreen)); 34 | } 35 | } 36 | 37 | public void onKeyRegister(RegisterKeyMappingsEvent event) { 38 | KeyMappings.register(event::register); 39 | } 40 | 41 | @Override 42 | public boolean isModLoaded(String modId) { 43 | return ModList.get().isLoaded(modIdMap.getOrDefault(modId, modId)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /neoforge/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" version "8.1.1" 3 | } 4 | 5 | archivesBaseName = rootProject.archives_base_name + "-" + rootProject.minecraft_version + "-neoforge" 6 | 7 | architectury { 8 | platformSetupLoomIde() 9 | neoForge() 10 | } 11 | 12 | repositories { 13 | maven { url "https://maven.neoforged.net/releases" } 14 | } 15 | 16 | loom { 17 | neoForge {} 18 | } 19 | 20 | configurations { 21 | common 22 | shadowCommon 23 | compileClasspath.extendsFrom common 24 | runtimeClasspath.extendsFrom common 25 | developmentNeoForge.extendsFrom common 26 | } 27 | 28 | dependencies { 29 | neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" 30 | 31 | // Cloth Config 32 | modRuntimeOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_config_version}" 33 | 34 | common(project(path: ":common", configuration: "namedElements")) { transitive false } 35 | shadowCommon(project(path: ":common", configuration: "transformProductionNeoForge")) { transitive false } 36 | } 37 | 38 | processResources { 39 | inputs.property "version", project.version 40 | filesMatching("META-INF/neoforge.mods.toml") { 41 | expand "version": project.version 42 | } 43 | } 44 | 45 | shadowJar { 46 | exclude "fabric.mod.json" 47 | exclude "architectury.common.json" 48 | configurations = [project.configurations.shadowCommon] 49 | setArchiveClassifier "dev-shadow" 50 | } 51 | 52 | remapJar { 53 | inputFile.set shadowJar.archiveFile 54 | dependsOn shadowJar 55 | } 56 | 57 | sourcesJar { 58 | def commonSources = project(":common").sourcesJar 59 | dependsOn commonSources 60 | from commonSources.archiveFile.map { zipTree(it) } 61 | } 62 | 63 | components.java { 64 | withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { 65 | skip() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/LocUtil.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import com.xtracr.realcamera.RealCamera; 4 | import net.minecraft.network.chat.Component; 5 | import net.minecraft.network.chat.MutableComponent; 6 | 7 | public class LocUtil { 8 | public static final String KEY_MOD_NAME = "general." + RealCamera.FULL_ID + ".modName"; 9 | 10 | public static MutableComponent MOD_NAME() { 11 | return Component.translatable(KEY_MOD_NAME); 12 | } 13 | 14 | public static MutableComponent CONFIG_CATEGORY(String key) { 15 | return Component.translatable("config.category." + RealCamera.FULL_ID + "." + key); 16 | } 17 | 18 | public static MutableComponent CONFIG_OPTION(String key, Object... args) { 19 | return Component.translatable("config.option." + RealCamera.FULL_ID + "." + key, args); 20 | } 21 | 22 | public static MutableComponent CONFIG_TOOLTIP(String key, Object... args) { 23 | return Component.translatable("config.tooltip." + RealCamera.FULL_ID + "." + key, args); 24 | } 25 | 26 | public static MutableComponent MESSAGE(String key, Object... args) { 27 | return Component.translatable("message." + RealCamera.FULL_ID + "." + key, args); 28 | } 29 | 30 | public static MutableComponent MODEL_VIEW_TITLE() { 31 | return Component.translatable("screen." + RealCamera.FULL_ID + ".modelView_title"); 32 | } 33 | 34 | public static MutableComponent MODEL_VIEW_WIDGET(String key, Object... args) { 35 | return Component.translatable("screen.widget." + RealCamera.FULL_ID + ".modelView_" + key, args); 36 | } 37 | 38 | public static MutableComponent MODEL_VIEW_TOOLTIP(String key, Object... args) { 39 | return Component.translatable("screen.tooltip." + RealCamera.FULL_ID + ".modelView_" + key, args); 40 | } 41 | 42 | public static MutableComponent literal(String string) { 43 | return Component.literal(string); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish on Modrinth & CurseForge 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | env: 8 | CURSEFORGE_ID: 851574 9 | MODRINTH_ID: fYYSAh4R 10 | CURSEFORGE_TOKEN: ${{ secrets.PUBLISH_CURSEFORGE_TOKEN }} 11 | MODRINTH_TOKEN: ${{ secrets.PUBLISH_MODRINTH_TOKEN }} 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Check Environment Variables 18 | run: env 19 | 20 | - name: Checkout Repository 21 | uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | 25 | - name: Setup JDK 26 | uses: actions/setup-java@v4 27 | with: 28 | distribution: "temurin" 29 | java-version: 21 30 | 31 | - name: Make Gradle Wrapper Executable 32 | if: ${{ runner.os != 'Windows' }} 33 | run: chmod +x ./gradlew 34 | 35 | - name: Build 36 | run: ./gradlew clean build 37 | 38 | - name: Publish NeoForge 39 | uses: Kir-Antipov/mc-publish@v3.3 40 | with: 41 | modrinth-id: ${{ env.MODRINTH_ID }} 42 | modrinth-token: ${{ env.MODRINTH_TOKEN }} 43 | curseforge-id: ${{ env.CURSEFORGE_ID }} 44 | curseforge-token: ${{ env.CURSEFORGE_TOKEN }} 45 | loaders: neoforge 46 | name: '[NeoForge] v${{github.ref_name}}' 47 | version-type: beta 48 | files: neoforge/build/libs/!(*-@(shadow|dev|sources|javadoc|all)).jar 49 | 50 | - name: Publish Fabric 51 | uses: Kir-Antipov/mc-publish@v3.3 52 | with: 53 | modrinth-id: ${{ env.MODRINTH_ID }} 54 | modrinth-token: ${{ env.MODRINTH_TOKEN }} 55 | curseforge-id: ${{ env.CURSEFORGE_ID }} 56 | curseforge-token: ${{ env.CURSEFORGE_TOKEN }} 57 | loaders: fabric 58 | name: '[Fabric] v${{github.ref_name}}' 59 | version-type: beta 60 | files: fabric/build/libs/!(*-@(shadow|dev|sources|javadoc|all)).jar 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_cn.yml: -------------------------------------------------------------------------------- 1 | name: Bug反馈 2 | description: 中文Bug反馈模板 3 | title: "[Bug]: " 4 | labels: [ "bug" ] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | 请确保您使用的是最新版的RC,您反馈的bug很可能已经在更新版本中修复 11 | (查看尾缀是否与最新工件相同) 12 | 在反馈bug前,请先尝试搜索是否已有相似的问题,请不要重复打开相同的issue 13 | 14 | 所有有关YSM的问题,请您优先寻求他人的帮助,或者观看配置模型教程 15 | 而不是直接在issue中提问。 16 | 17 | - type: checkboxes 18 | id: preliminary 19 | attributes: 20 | label: 我已确认 ... 21 | options: 22 | - label: 我正在使用最新版的RC 23 | required: true 24 | - label: 无法在关闭主要功能时复现 25 | required: true 26 | 27 | - type: input 28 | id: minecraft-version 29 | attributes: 30 | label: Minecraft版本 31 | description: | 32 | 你所使用的Minecraft的版本. 33 | 34 | 你可以在游戏主界面作下角或者游戏启动时的日志开头等地方找到. 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | id: incompatible-mods 40 | attributes: 41 | label: 不兼容的mod 42 | description: >- 43 | 和bug相关的模组 44 |
请在反馈问题前尝试尽可能移除不相干的模组的情况下复现bug 45 | placeholder: 'Mods list here' 46 | render: 'raw' 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | id: description 52 | attributes: 53 | label: 错误描述 54 | description: >- 55 | 请尽可能详细的描述遇到的问题. 56 |
越清晰和详尽的描述会使该bug的处理优先级更高 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: step 62 | attributes: 63 | label: 复现步骤 64 | placeholder: | 65 | 1. Join the game... 66 | 67 | 2. Change some settings... 68 | 69 | 3. ... 70 | 71 | - type: textarea 72 | id: logs 73 | attributes: 74 | label: 日志和崩溃报告 75 | description: >- 76 | 你可以在/logs/lastest.log 和 /crash-report 中找到错误报告,或者使用启动器的导出功能 77 |
你可以日志上传到类似 https://paste.ubuntu.com/ 或者 https://mclo.gs/ 的网站并在此贴出链接 78 |
**请不要在此此处直接粘贴大段的日志文本!!** 79 | placeholder: '把日志文件往这丢或者使用外部链接' 80 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/RaycastUtil.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import net.minecraft.client.Camera; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.world.entity.Entity; 6 | import net.minecraft.world.level.ClipContext; 7 | import net.minecraft.world.level.ClipContext.Block; 8 | import net.minecraft.world.level.ClipContext.Fluid; 9 | import net.minecraft.world.phys.Vec3; 10 | 11 | public class RaycastUtil { 12 | private static Vec3 startVec = Vec3.ZERO; 13 | private static Vec3 endVec = Vec3.ZERO; 14 | 15 | public static Vec3 getStartVec() { 16 | return startVec; 17 | } 18 | 19 | public static Vec3 getEndVec() { 20 | return endVec; 21 | } 22 | 23 | public static ClipContext getClipContext(Block shapeType, Fluid fluidHandling, Entity entity) { 24 | return new ClipContext(startVec, endVec, shapeType, fluidHandling, entity); 25 | } 26 | 27 | public static void update(Entity entity, double sqDistance, float deltaTick) { 28 | Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); 29 | Vec3 eyePos = entity.getEyePosition(deltaTick); 30 | startVec = camera.getPosition(); 31 | Vec3 direction = Vec3.directionFromRotation(camera.getXRot(), camera.getYRot()); 32 | Vec3 offset = startVec.subtract(eyePos); 33 | Vec3 footPoint = MathUtil.getIntersectionPoint(Vec3.ZERO, direction, offset, direction); 34 | if (footPoint.lengthSqr() > sqDistance) { 35 | startVec = eyePos; 36 | direction = entity.getViewVector(deltaTick); 37 | endVec = startVec.add(direction.scale(Math.sqrt(sqDistance))); 38 | return; 39 | } else if (offset.lengthSqr() > sqDistance) { 40 | startVec = startVec.add(direction.scale(offset.distanceTo(footPoint) - Math.sqrt(sqDistance - footPoint.lengthSqr()))); 41 | } 42 | endVec = eyePos.add(footPoint.add(direction.scale(Math.sqrt(sqDistance - footPoint.lengthSqr())))); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinLocalPlayer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import com.xtracr.realcamera.RealCameraCore; 5 | import com.xtracr.realcamera.compat.DisableHelper; 6 | import com.xtracr.realcamera.config.ConfigFile; 7 | import com.xtracr.realcamera.util.RaycastUtil; 8 | import net.minecraft.client.multiplayer.ClientLevel; 9 | import net.minecraft.client.player.AbstractClientPlayer; 10 | import net.minecraft.client.player.LocalPlayer; 11 | import net.minecraft.world.level.ClipContext; 12 | import net.minecraft.world.phys.HitResult; 13 | import net.minecraft.world.phys.Vec3; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 19 | 20 | @Mixin(LocalPlayer.class) 21 | public abstract class MixinLocalPlayer extends AbstractClientPlayer { 22 | public MixinLocalPlayer(ClientLevel world, GameProfile profile) { 23 | super(world, profile); 24 | } 25 | 26 | @Inject(method = "getRopeHoldPosition", at = @At("HEAD"), cancellable = true) 27 | private void realcamera$atGetRopePosHEAD(float deltaTick, CallbackInfoReturnable cir) { 28 | if (DisableHelper.RENDER_HANDS.disabled(this)) cir.setReturnValue(super.getRopeHoldPosition(deltaTick)); 29 | } 30 | 31 | @Override 32 | public @NotNull HitResult pick(double maxDistance, float deltaTick, boolean includeFluids) { 33 | if (!ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 34 | RaycastUtil.update(this, maxDistance * maxDistance, deltaTick); 35 | return level().clip(RaycastUtil.getClipContext(ClipContext.Block.OUTLINE, 36 | includeFluids ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, this)); 37 | } 38 | return super.pick(maxDistance, deltaTick, includeFluids); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/config/ConfigFile.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.config; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.xtracr.realcamera.RealCamera; 6 | import net.minecraft.client.Minecraft; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.BufferedWriter; 10 | import java.io.File; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | 14 | public class ConfigFile { 15 | private static final ModConfig modConfig = new ModConfig(); 16 | private static final String FILE_NAME = RealCamera.MODID + ".json"; 17 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 18 | private static final Path PATH; 19 | 20 | static { 21 | File configDir = new File(Minecraft.getInstance().gameDirectory, "config"); 22 | if (!configDir.exists()) configDir.mkdirs(); 23 | PATH = configDir.toPath().resolve(FILE_NAME); 24 | } 25 | 26 | public static ModConfig config() { 27 | return modConfig; 28 | } 29 | 30 | public static void load() { 31 | try (BufferedReader reader = Files.newBufferedReader(PATH)) { 32 | modConfig.set(GSON.fromJson(reader, ModConfig.class)); 33 | modConfig.clamp(); 34 | } catch (Exception exception) { 35 | RealCamera.LOGGER.warn("Failed to load " + FILE_NAME); 36 | save(); 37 | } 38 | } 39 | 40 | public static void save() { 41 | try (BufferedWriter writer = Files.newBufferedWriter(PATH)) { 42 | GSON.toJson(modConfig, writer); 43 | } catch (Exception exception) { 44 | RealCamera.LOGGER.warn("Failed to save " + FILE_NAME, exception); 45 | reset(); 46 | } 47 | } 48 | 49 | public static void reset() { 50 | try (BufferedWriter writer = Files.newBufferedWriter(PATH)) { 51 | modConfig.set(new ModConfig()); 52 | GSON.toJson(modConfig, writer); 53 | } catch (Exception exception) { 54 | RealCamera.LOGGER.warn("Failed to reset " + FILE_NAME, exception); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" version "8.1.1" 3 | } 4 | 5 | archivesBaseName = rootProject.archives_base_name + "-" + rootProject.minecraft_version + "-fabric" 6 | 7 | architectury { 8 | platformSetupLoomIde() 9 | fabric() 10 | } 11 | 12 | configurations { 13 | common 14 | shadowCommon 15 | compileClasspath.extendsFrom common 16 | runtimeClasspath.extendsFrom common 17 | developmentFabric.extendsFrom common 18 | } 19 | 20 | dependencies { 21 | modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" 22 | modImplementation "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" 23 | 24 | // Cloth Config 25 | modRuntimeOnly "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_config_version}" 26 | // Modmenu 27 | modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { 28 | exclude(group: "net.fabricmc.fabric-api") 29 | } 30 | 31 | // modImplementation "org.antlr:antlr4-runtime:4.13.1" 32 | // modImplementation "io.github.douira:glsl-transformer:2.0.1" 33 | // modImplementation "org.anarres:jcpp:1.4.14" 34 | // modImplementation "maven.modrinth:iris:1.8.8+1.21.1-fabric" 35 | // modImplementation "maven.modrinth:sodium:mc1.21.1-0.6.13-fabric" 36 | 37 | common(project(path: ":common", configuration: "namedElements")) { transitive false } 38 | shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false } 39 | } 40 | 41 | processResources { 42 | inputs.property "version", project.version 43 | filesMatching("fabric.mod.json") { 44 | expand "version": project.version 45 | } 46 | } 47 | 48 | shadowJar { 49 | exclude "architectury.common.json" 50 | configurations = [project.configurations.shadowCommon] 51 | archiveClassifier = "dev-shadow" 52 | } 53 | 54 | remapJar { 55 | injectAccessWidener = true 56 | inputFile.set shadowJar.archiveFile 57 | dependsOn shadowJar 58 | } 59 | 60 | sourcesJar { 61 | def commonSources = project(":common").sourcesJar 62 | dependsOn commonSources 63 | from commonSources.archiveFile.map { zipTree(it) } 64 | } 65 | 66 | components.java { 67 | withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { 68 | skip() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/gui/TexturedButton.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.gui; 2 | 3 | import com.xtracr.realcamera.RealCamera; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.gui.components.AbstractButton; 6 | import net.minecraft.client.gui.narration.NarrationElementOutput; 7 | import net.minecraft.network.chat.CommonComponents; 8 | import net.minecraft.resources.ResourceLocation; 9 | 10 | import java.util.function.Consumer; 11 | 12 | public class TexturedButton extends AbstractButton { 13 | public static final ResourceLocation ICON_TEXTURE = ResourceLocation.fromNamespaceAndPath(RealCamera.MODID, "textures/gui/icon.png"); 14 | protected final ResourceLocation texture; 15 | protected final int textureWidth, textureHeight, u, v; 16 | private final Consumer onPress; 17 | 18 | public TexturedButton(int u, int v, Consumer onPress) { 19 | this(0, 0, 16, 16, u, v, onPress); 20 | } 21 | 22 | public TexturedButton(int x, int y, int width, int height, int u, int v, Consumer onPress) { 23 | this(x, y, width, height, u, v, ICON_TEXTURE, 256, 256, onPress); 24 | } 25 | 26 | public TexturedButton(int x, int y, int width, int height, int u, int v, ResourceLocation texture, int textureWidth, int textureHeight, Consumer onPress) { 27 | super(x, y, width, height, CommonComponents.EMPTY); 28 | this.textureWidth = textureWidth; 29 | this.textureHeight = textureHeight; 30 | this.onPress = onPress; 31 | this.u = u; 32 | this.v = v; 33 | this.texture = texture; 34 | } 35 | 36 | @Override 37 | public void onPress() { 38 | onPress.accept(this); 39 | } 40 | 41 | @Override 42 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float deltaTick) { 43 | graphics.fill(getX(), getY(), getX() + getWidth(), getY() + getHeight(), 0xFF646464); 44 | graphics.blit(texture, getX(), getY(), u, v, width, height, textureWidth, textureHeight); 45 | if (isHoveredOrFocused()) graphics.renderOutline(getX(), getY(), getWidth(), getHeight(), 0xFFFFFFFF); 46 | } 47 | 48 | @Override 49 | protected void updateWidgetNarration(NarrationElementOutput builder) { 50 | this.defaultButtonNarrationText(builder); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[2,)" 3 | license = "MIT license" 4 | issueTrackerURL = "https://github.com/xTracr/RealCamera/issues" 5 | 6 | [[mods]] 7 | modId = "realcamera" 8 | version = "${version}" 9 | displayName = "Real Camera" 10 | displayURL = "https://modrinth.com/mod/real-camera" 11 | logoFile = "icon.png" 12 | credits = "" 13 | authors = "xTracr" 14 | # Display Test controls the display for your mod in the server connection screen 15 | # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. 16 | # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. 17 | # IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. 18 | # NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. 19 | # IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. 20 | #displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) 21 | description = ''' 22 | Make the camera more realistic in the first-person view. 23 | ''' 24 | 25 | [[dependencies.realcamera]] 26 | modId = "neoforge" 27 | type= "required" 28 | versionRange = "[21,)" 29 | ordering = "NONE" 30 | side = "BOTH" 31 | 32 | [[dependencies.realcamera]] 33 | modId = "minecraft" 34 | type = "required" 35 | versionRange = "[1.21.1,1.21.2)" 36 | ordering = "NONE" 37 | side = "BOTH" 38 | 39 | [[dependencies.realcamera]] 40 | modId = "cloth_config" 41 | type = "optional" 42 | versionRange = "[15.0,)" 43 | ordering = "NONE" 44 | side = "CLIENT" 45 | [dependencies.realcamera.mc-publish] 46 | ignore = false 47 | modrinth = "cloth-config" 48 | curseforge = "cloth-config" 49 | 50 | [[mixins]] 51 | config = "realcamera-common.mixins.json" 52 | 53 | [[mixins]] 54 | config = "realcamera.mixins.json" 55 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinLevelRenderer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.xtracr.realcamera.RealCameraCore; 5 | import com.xtracr.realcamera.config.ConfigFile; 6 | import net.minecraft.client.Camera; 7 | import net.minecraft.client.DeltaTracker; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.renderer.*; 10 | import net.minecraft.world.TickRateManager; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.joml.Matrix4f; 14 | import org.spongepowered.asm.mixin.Final; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | 21 | @Mixin(LevelRenderer.class) 22 | public abstract class MixinLevelRenderer { 23 | @Shadow 24 | @Final private Minecraft minecraft; 25 | @Shadow 26 | @Final private RenderBuffers renderBuffers; 27 | 28 | @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endLastBatch()V", ordinal = 0)) 29 | private void realcamera$renderCameraEntity(DeltaTracker deltaTracker, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f modelView, Matrix4f projection, CallbackInfo ci) { 30 | if (!RealCameraCore.isRendering()) return; 31 | MultiBufferSource.BufferSource bufferSource = renderBuffers.bufferSource(); 32 | Entity entity = camera.getEntity(); 33 | TickRateManager tickManager = minecraft.level.tickRateManager(); 34 | float deltaTick = deltaTracker.getGameTimeDeltaPartialTick(!tickManager.isEntityFrozen(entity)); 35 | if (!ConfigFile.config().isClassic()) RealCameraCore.renderCameraEntity(minecraft, deltaTick, bufferSource, modelView); 36 | else { 37 | Vec3 cameraPos = camera.getPosition(); 38 | renderEntity(entity, cameraPos.x(), cameraPos.y(), cameraPos.z(), deltaTick, new PoseStack(), bufferSource); 39 | } 40 | } 41 | 42 | @Shadow 43 | protected abstract void renderEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float deltaTick, PoseStack poseStack, MultiBufferSource bufferSource); 44 | } 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_en.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: English template for reporting a bug 3 | title: "[Bug]: " 4 | labels: [ "bug" ] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Make sure you're using the latest version of RealCamera, the bug you're reporting has most likely been fixed in the latest update 11 | (Check if the suffix matches the latest artifact) 12 | Before reporting a bug, please search for similar issues first and avoid opening duplicate issues 13 | 14 | For all YSM-related questions, please seek help from others or watch configuration model tutorials first 15 | instead of asking directly in the issue. 16 | 17 | - type: checkboxes 18 | id: preliminary 19 | attributes: 20 | label: I have confirmed that ... 21 | options: 22 | - label: I am using the latest version of RealCamera 23 | required: true 24 | - label: Unable to reproduce when main feature is disabled 25 | required: true 26 | 27 | - type: input 28 | id: minecraft-version 29 | attributes: 30 | label: Minecraft Version 31 | description: | 32 | The Minecraft version you are using. 33 | 34 | You can find them at the corner of main menu or in the log while startup. 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | id: incompatible-mods 40 | attributes: 41 | label: Incompatible Mods 42 | description: >- 43 | The mods related to the bug. 44 |
Try to remove unrelated plugins and mods before reporting 45 | placeholder: 'Mods list here' 46 | render: 'raw' 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | id: description 52 | attributes: 53 | label: Bug Description 54 | description: >- 55 | Please describe the issue you encountered in as much detail as possible. 56 |
Clearer and more detailed descriptions will receive higher processing priority 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: step 62 | attributes: 63 | label: Reproduction Steps 64 | placeholder: | 65 | 1. Join the game... 66 | 67 | 2. Change some settings... 68 | 69 | 3. ... 70 | 71 | - type: textarea 72 | id: logs 73 | attributes: 74 | label: Logs & Crash Reports 75 | description: >- 76 | You can find error reports in /logs/latest.log and /crash-reports, or use the launcher's export function 77 |
You can upload logs to sites like https://paste.ubuntu.com/ or https://mclo.gs/ and post the link here 78 |
**DO NOT PASTE LARGE LOGS DIRECTLY HERE!!** 79 | placeholder: 'Drag log file here or use external link' -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/gui/CyclingTexturedButton.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.gui; 2 | 3 | import net.minecraft.client.gui.GuiGraphics; 4 | import net.minecraft.client.gui.components.AbstractButton; 5 | import net.minecraft.client.gui.narration.NarrationElementOutput; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.network.chat.CommonComponents; 8 | import net.minecraft.resources.ResourceLocation; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.function.IntConsumer; 12 | 13 | public class CyclingTexturedButton extends AbstractButton { 14 | protected final ResourceLocation texture; 15 | protected final int textureWidth, textureHeight, u, v, vOffset, size; 16 | @Nullable 17 | private IntConsumer onValueChange; 18 | private int value; 19 | 20 | public CyclingTexturedButton(int u, int v, int value, int size) { 21 | this(0, 0, 16, 16, u, v, value, size); 22 | } 23 | 24 | public CyclingTexturedButton(int x, int y, int width, int height, int u, int v, int value, int size) { 25 | this(x, y, width, height, u, v, height, value, size, TexturedButton.ICON_TEXTURE, 256, 256); 26 | } 27 | 28 | public CyclingTexturedButton(int x, int y, int width, int height, int u, int v, int vOffset, int value, int size, ResourceLocation texture, int textureWidth, int textureHeight) { 29 | super(x, y, width, height, CommonComponents.EMPTY); 30 | this.u = u; 31 | this.v = v; 32 | this.vOffset = vOffset; 33 | this.size = size; 34 | this.texture = texture; 35 | this.textureWidth = textureWidth; 36 | this.textureHeight = textureHeight; 37 | setValue(value); 38 | } 39 | 40 | public int getValue() { 41 | return value; 42 | } 43 | 44 | public void setValue(int value) { 45 | this.value = (value % size + size) % size; 46 | } 47 | 48 | public CyclingTexturedButton setOnValueChange(IntConsumer onValueChange) { 49 | this.onValueChange = onValueChange; 50 | return this; 51 | } 52 | 53 | @Override 54 | public void onPress() { 55 | if (Screen.hasShiftDown()) setValue(value - 1); 56 | else setValue(value + 1); 57 | if (onValueChange != null) onValueChange.accept(value); 58 | } 59 | 60 | @Override 61 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float deltaTick) { 62 | graphics.fill(getX(), getY(), getX() + getWidth(), getY() + getHeight(), 0xFF646464); 63 | graphics.blit(texture, getX(), getY(), u, v + value * vOffset, width, height, textureWidth, textureHeight); 64 | if (isHoveredOrFocused()) graphics.renderOutline(getX(), getY(), getWidth(), getHeight(), 0xFFFFFFFF); 65 | } 66 | 67 | @Override 68 | protected void updateWidgetNarration(NarrationElementOutput builder) { 69 | defaultButtonNarrationText(builder); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/gui/GUIHelper.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.gui; 2 | 3 | import com.mojang.blaze3d.vertex.VertexConsumer; 4 | import com.xtracr.realcamera.util.VertexData; 5 | import net.minecraft.client.gui.GuiGraphics; 6 | import net.minecraft.client.gui.navigation.ScreenRectangle; 7 | import net.minecraft.client.renderer.RenderType; 8 | import net.minecraft.world.phys.Vec3; 9 | import org.joml.Matrix4f; 10 | 11 | public class GUIHelper { 12 | public static void enableScissor(GuiGraphics graphics, ScreenRectangle rectangle) { 13 | graphics.enableScissor(rectangle.left(), rectangle.top(), rectangle.right(), rectangle.bottom()); 14 | } 15 | 16 | public static void fill(GuiGraphics graphics, float x0, float y0, float x1, float y1, int argb) { 17 | fill(graphics, x0, y0, x1, y1, 0, argb); 18 | } 19 | 20 | public static void fill(GuiGraphics graphics, float x0, float y0, float x1, float y1, float z, int argb) { 21 | Matrix4f matrix4f = graphics.pose().last().pose(); 22 | if (x0 < x1) { 23 | float o = x0; 24 | x0 = x1; 25 | x1 = o; 26 | } 27 | if (y0 < y1) { 28 | float o = y0; 29 | y0 = y1; 30 | y1 = o; 31 | } 32 | VertexConsumer buffer = graphics.bufferSource().getBuffer(RenderType.gui()); 33 | buffer.addVertex(matrix4f, x0, y0, z).setColor(argb); 34 | buffer.addVertex(matrix4f, x0, y1, z).setColor(argb); 35 | buffer.addVertex(matrix4f, x1, y1, z).setColor(argb); 36 | buffer.addVertex(matrix4f, x1, y0, z).setColor(argb); 37 | graphics.flush(); 38 | } 39 | 40 | public static void renderOutline(GuiGraphics graphics, float x, float y, float width, float height, int argb) { 41 | fill(graphics, x, y, x + width, y + 1, argb); 42 | fill(graphics, x, y + height - 1, x + width, y + height, argb); 43 | fill(graphics, x, y + 1, x + 1, y + height - 1, argb); 44 | fill(graphics, x + width - 1, y + 1, x + width, y + height - 1, argb); 45 | } 46 | 47 | public static void renderPolygon(GuiGraphics graphics, VertexData[] polygon, float z, int argb) { 48 | VertexConsumer buffer = graphics.bufferSource().getBuffer(RenderType.gui()); 49 | for (VertexData vertex : polygon) buffer.addVertex(vertex.x(), vertex.y(), z).setColor(argb); 50 | if (polygon.length == 3) buffer.addVertex(polygon[2].x(), polygon[2].y(), z).setColor(argb); 51 | graphics.flush(); 52 | } 53 | 54 | public static void renderVector(GuiGraphics graphics, Vec3 start, Vec3 vector, float z, int argb) { 55 | VertexConsumer buffer = graphics.bufferSource().getBuffer(RenderType.lines()); 56 | buffer.addVertex((float) start.x, (float) start.y, z).setColor(argb).setNormal((float) vector.x, (float) vector.y, 0); 57 | buffer.addVertex((float) (start.x + vector.x), (float) (start.y + vector.y), z).setColor(argb).setNormal((float) vector.x, (float) vector.y, 0); 58 | graphics.flush(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/KeyMappings.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import com.xtracr.realcamera.gui.ModelViewScreen; 6 | import com.xtracr.realcamera.util.LocUtil; 7 | import net.minecraft.client.KeyMapping; 8 | import net.minecraft.client.Minecraft; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.function.Consumer; 13 | 14 | public final class KeyMappings { 15 | private static final Map> KEY_MAPPINGS = new HashMap<>(); 16 | public static final KeyMapping MODEL_VIEW_SCREEN; 17 | 18 | static { 19 | MODEL_VIEW_SCREEN = createKeyMapping("modelViewScreen", client -> client.setScreen(new ModelViewScreen())); 20 | createKeyMapping("togglePerspective", InputConstants.KEY_F6, client -> { 21 | boolean enabled = ConfigFile.config().enabled(); 22 | ConfigFile.load(); 23 | ConfigFile.config().setEnabled(!enabled); 24 | RealCameraCore.reset(); 25 | }); 26 | createKeyMapping("toggleAdjustMode", client -> ConfigFile.config().cycleAdjustMode()); 27 | createKeyMapping("toggleCameraMode", client -> ConfigFile.config().setClassic(!ConfigFile.config().isClassic())); 28 | createKeyMapping("adjustFRONT", client -> ConfigFile.config().adjustOffsetX(1)); 29 | createKeyMapping("adjustBACK", client -> ConfigFile.config().adjustOffsetX(-1)); 30 | createKeyMapping("adjustUP", client -> ConfigFile.config().adjustOffsetY(1)); 31 | createKeyMapping("adjustDOWN", client -> ConfigFile.config().adjustOffsetY(-1)); 32 | createKeyMapping("adjustLEFT", client -> ConfigFile.config().adjustOffsetZ(1)); 33 | createKeyMapping("adjustRIGHT", client -> ConfigFile.config().adjustOffsetZ(-1)); 34 | createKeyMapping("activeConfigIndexNEXT", client -> ConfigFile.config().binding.activeConfigIndex += 1); 35 | createKeyMapping("activeConfigIndexPREV", client -> ConfigFile.config().binding.activeConfigIndex -= 1); 36 | createKeyMapping("activeConfigIndexRESET", client -> ConfigFile.config().binding.activeConfigIndex = 0); 37 | } 38 | 39 | private static KeyMapping createKeyMapping(String id, Consumer whenPressed) { 40 | return createKeyMapping(id, -1, whenPressed); 41 | } 42 | 43 | private static KeyMapping createKeyMapping(String id, int code, Consumer whenPressed) { 44 | KeyMapping keyMapping = new KeyMapping("key." + RealCamera.FULL_ID + "." + id, code, LocUtil.KEY_MOD_NAME); 45 | KEY_MAPPINGS.put(keyMapping, whenPressed); 46 | return keyMapping; 47 | } 48 | 49 | public static void register(Consumer registerer) { 50 | KEY_MAPPINGS.keySet().forEach(registerer); 51 | } 52 | 53 | public static void handle(Minecraft client) { 54 | if (client.player == null) return; 55 | KEY_MAPPINGS.forEach((keyMapping, whenPressed) -> { 56 | while (keyMapping.consumeClick()) { 57 | whenPressed.accept(client); 58 | ConfigFile.save(); 59 | } 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # Real Camera 2 | 3 | ### [English](README.md) 4 | 5 | 使第一人称视角下的摄像头更加真实。 6 | 支持的版本: 1.18.2 - 1.20.4 Forge,1.18.2 - 1.21.8 Fabric,1.21 - 1.21.8 NeoForge 7 | 从[Releases](https://github.com/xTracr/RealCamera/releases)、[Modrinth](https://modrinth.com/mod/real-camera)或[CurseForge](https://curseforge.com/minecraft/mc-mods/real-camera)下载 8 | 快照版在[这里](https://github.com/xTracr/RealCamera/actions/workflows/build.yml) 9 | 10 | ## 特性 11 | 12 | * 将摄像头绑定到身体的特定部位 13 | * 自定义摄像头的位置和旋转角度 14 | * 在第一人称视角下渲染玩家模型 15 | * 按下F6来开关,另外一些键来调整摄像头 16 | * 在配置界面(需要Cloth Config)和模型视图(0.6+)界面,配置以上特性 17 | 18 | ### 配置(0.6+) 19 | 20 | * 理论上支持大多数模组模型,但需要手动进行配置: 21 | * 首先设置`打开模型视图界面`的按键绑定 22 | * ![gui_configs](https://cdn.modrinth.com/data/fYYSAh4R/images/4625282a5683cd38eba2947be1ca82e595e18bad.png) 23 | * 打开模型视图界面,左Alt+左键选择模型的对应的面,左Alt+滚轮可以在模型的不同层间切换 24 | * 通过点击左侧的`选择`按钮,在三者间切换,选好`向前矢量`、`向上矢量`和`目标平面` 25 | * 从右上角的按钮进入`预览`界面,在这里可以看到摄像头与模型的相对关系,并进行一定的调整(也可以通过按键绑定调整) 26 | * ![gui_preview](https://cdn.modrinth.com/data/fYYSAh4R/images/44c87a6f1750f8d1b03422120e6042d1098896cb.png) 27 | * 输入名称并保存 28 | 29 | #### Tips 30 | 31 | * 可以更改配置的优先级,优先级越高的配置,在右侧排序越靠上 32 | * 在`预览`界面可以更改禁用深度来禁用眼前的模型的渲染 33 | * 关于`禁用`界面(当前版本尚不完善,操作较为繁琐): 34 | * 在左侧材质id输入框为空时,可以使用Alt+左键选择材质 35 | * 被蓝框包含的部分不会被渲染。 36 | * 在左侧材质视图中鼠标左键框选,其它键取消选择 37 | * Alt+左键可快速选中被鼠标指针指向的部分 38 | * `全部`模式下,整个材质所对应的模型都不会被渲染(左侧会有一个包含整个材质的蓝框来表示这一点) 39 | * ![gui_disable](https://cdn.modrinth.com/data/fYYSAh4R/images/b49ac4da6bf8a59f13c7e93ca1ef76b73e5d23b4.png) 40 | 41 | ## 依赖项目 42 | 43 | * Fabric: 44 | * [Fabric API](https://modrinth.com/mod/fabric-api) 45 | * 所有平台: 46 | * (可选但建议)[Cloth Config API](https://modrinth.com/mod/cloth-config) 47 | 48 | ## 常见问题 49 | 50 | * 模型的一部分(如头发)始终挡在面前,怎样隐藏它? 51 | * 增加这个值或许有所帮助,或者使用`禁用`功能 52 | ![disable_depth](https://github.com/xTracr/RealCamera/assets/57320980/78c246e8-34aa-4979-89de-780ee907870b) 53 | * 按什么键打开模组视图界面? 54 | * 在按键绑定里自己设置 55 | * 为什么打不开配置界面(模组设置)? 56 | * 没有安装[Cloth Config API](https://modrinth.com/mod/cloth-config) 57 | * 为什么ysm模型有时候会弹出绑定失败? 58 | * 快照0.6.15“尝试”修复了ysm问题,但没有修复透彻 59 | * 如果Minecraft版本为1.20.1,那么需要将向上矢量绑定在头部的左面(或右面),并在预览界面中将翻滚角设置为90度(或-90度) 60 | * 为什么配置ysm时候,模型视图界面中ysm模型头部消失? 61 | * 安装了Better Combat模组,去掉就可以了 62 | 63 | ### 兼容性 64 | 65 | * 不兼容: 66 | * OptiFine 67 | * Armourer's Workshop(时装工坊)(真实相机0.6版本以上) 68 | * 基于GeckoLib的盔甲 69 | * Customizable Player Models(自定义玩家模型) 70 | * Epic Fight(史诗战斗)(真实相机0.6版本以下) 71 | * [TaCZ]永恒枪械工坊:零 1.1.4版本及以上 72 | * 兼容: 73 | * 大多数修改玩家镜头的模组 74 | * 多数模型模组 75 | * Armourer's Workshop(时装工坊)(真实相机0.6版本以下) 76 | * Epic Fight(史诗战斗)(真实相机0.6版本以上) 77 | * First-person Model(更真实的第一人称模型) 78 | * Not Enough Animations(更多动画) 79 | * Player Animation Lib 80 | * Pehkui 81 | * ParCool! 82 | * [TaCZ]永恒枪械工坊:零 1.0.3版本及以下 83 | * Yes Steve Model(是,史蒂夫模型)(不能稳定兼容) 84 | * Superb Warfare(卓越前线) 85 | 86 | * 模型模组与`Real Camera`兼容的必要条件(基于官方映射): 87 | * 顶点数据获取 88 | * `Real Camera`通过替换公共方法`EntityRenderDispatcher.render`的`MultiBufferSource multiBufferSource`参数实现顶点数据获取 89 | * 调用时机为`GameRenderer.renderLevel`流程中的`Camera.setup`阶段之前 90 | * *因此*,模组需要满足以下技术条件: 91 | * 渲染`Minecraft.getCameraEntity`时严格使用**传入的**`multiBufferSource`参数,没有通过其他途径获取或创建`MultiBufferSource`实例 92 | * 在调整后的时机渲染玩家模型,其整体表现不受影响 93 | * 所有顶点数据最终通过`MultiBufferSource.getBuffer`方法获取的`VertexConsumer`发送给GPU 94 | * 渲染次数兼容 95 | * `Real Camera`通过调用`EntityRenderDispatcher.render`公共方法进行`Minecraft.getCameraEntity`的渲染 96 | * 这是在一帧中第二次调用`EntityRenderDispatcher.render`方法(第一次是为了解算摄像机参数) 97 | * *因此*,模组需要满足:同一帧内**多次**渲染玩家模型,其整体表现不受影响 98 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/api/BindResult.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.api; 2 | 3 | import com.xtracr.realcamera.config.BindTarget; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import net.minecraft.world.phys.Vec3; 6 | import org.joml.Matrix3f; 7 | import org.joml.Vector3f; 8 | 9 | import java.util.List; 10 | 11 | public class BindResult { 12 | public static final BindResult EMPTY = new BindResult(BindTarget.EMPTY, false); 13 | public final BindTarget target; 14 | protected final Matrix3f rotation = new Matrix3f(); 15 | protected final boolean mirrored; 16 | private Vec3 position = Vec3.ZERO, forward = Vec3.ZERO, upward = Vec3.ZERO; 17 | 18 | public BindResult(BindTarget target, boolean mirrored) { 19 | this.target = target; 20 | this.mirrored = mirrored; 21 | } 22 | 23 | public static BindResult getOrCreate(String name) { 24 | List fixedTargets = ConfigFile.config().getFixedTargetList(); 25 | BindTarget target = fixedTargets.stream() 26 | .filter(t -> t.name().equals(name)) 27 | .findFirst() 28 | .orElseGet(() -> { 29 | BindTarget blank = BindTarget.blank(name, ""); 30 | fixedTargets.add(blank); 31 | return blank; 32 | }); 33 | return new BindResult(target, false); 34 | } 35 | 36 | public boolean available() { 37 | return !target.isEmpty() && !forward.equals(Vec3.ZERO) && !upward.equals(Vec3.ZERO) && Double.isFinite(position.lengthSqr()) && Math.abs(rotation.determinant() - 1) < 0.01f; 38 | } 39 | 40 | public boolean weakAvailable() { 41 | return !target.isEmpty() && (forward != Vec3.ZERO || upward != Vec3.ZERO || position != Vec3.ZERO) && Double.isFinite(position.lengthSqr()); 42 | } 43 | 44 | public Vec3 getPosition() { 45 | return position; 46 | } 47 | 48 | public void setPosition(Vec3 vec) { 49 | position = vec; 50 | } 51 | 52 | public Vec3 getForward() { 53 | return forward; 54 | } 55 | 56 | public void setForward(Vec3 vec) { 57 | forward = vec.normalize(); 58 | } 59 | 60 | public Vec3 getUpward() { 61 | return upward; 62 | } 63 | 64 | public void setUpward(Vec3 vec) { 65 | upward = vec.normalize(); 66 | } 67 | 68 | public Matrix3f getRotation() { 69 | return rotation; 70 | } 71 | 72 | public BindResult computeCamera() { 73 | if (!available()) return this; 74 | final int orientation = mirrored ? -1 : 1; 75 | upward = forward.cross(upward.cross(forward)).normalize(); 76 | Vec3 left = upward.cross(forward).scale(orientation); 77 | rotation.set(left.toVector3f(), upward.toVector3f(), forward.toVector3f()); 78 | BindTarget.OffsetConfig offsets = target.offsets(); 79 | Vector3f offset = new Vector3f(offsets.getZ(), offsets.getY(), offsets.getX()).mul(offsets.getScale()).mul(rotation); 80 | position = position.add(offset.x(), offset.y(), offset.z()); 81 | rotation.rotateLocal(orientation * (float) Math.toRadians(offsets.getYaw()), rotation.m10, rotation.m11, rotation.m12); 82 | rotation.rotateLocal(orientation * (float) Math.toRadians(offsets.getPitch()), rotation.m00, rotation.m01, rotation.m02); 83 | rotation.rotateLocal(orientation * (float) Math.toRadians(offsets.getRoll()), rotation.m20, rotation.m21, rotation.m22); 84 | return this; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/MultiVertexCatcher.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import com.mojang.blaze3d.vertex.BufferBuilder; 4 | import com.mojang.blaze3d.vertex.ByteBufferBuilder; 5 | import com.mojang.blaze3d.vertex.MeshData; 6 | import com.mojang.blaze3d.vertex.VertexConsumer; 7 | import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; 8 | import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMaps; 9 | import net.minecraft.client.renderer.MultiBufferSource; 10 | import net.minecraft.client.renderer.RenderType; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Arrays; 14 | import java.util.SequencedMap; 15 | import java.util.function.Consumer; 16 | 17 | public interface MultiVertexCatcher extends MultiBufferSource { 18 | static MultiVertexCatcher defaultImpl() { 19 | return MeshCatcher.INSTANCE; 20 | } 21 | 22 | void endCatching(Consumer consumer); 23 | 24 | class MeshCatcher extends MultiBufferSource.BufferSource implements MultiVertexCatcher { 25 | private static final MeshCatcher INSTANCE = new MeshCatcher(); 26 | private final SequencedMap bufferPools = new Object2ObjectLinkedOpenHashMap<>(); 27 | private final SequencedMap caughtMeshes = new Object2ObjectLinkedOpenHashMap<>(); 28 | 29 | protected MeshCatcher() { 30 | super(new ByteBufferBuilder(0), Object2ObjectSortedMaps.emptyMap()); 31 | } 32 | 33 | @Override 34 | public @NotNull VertexConsumer getBuffer(RenderType renderType) { 35 | BufferBuilder bufferBuilder = startedBuilders.get(renderType); 36 | if (bufferBuilder != null) { 37 | endBatch(renderType, bufferBuilder); 38 | } 39 | ByteBufferBuilderPool bufferPool = bufferPools.computeIfAbsent(renderType, type -> new ByteBufferBuilderPool(type.bufferSize())); 40 | bufferBuilder = new BufferBuilder(bufferPool.getBuffer(), renderType.mode(), renderType.format()); 41 | startedBuilders.put(renderType, bufferBuilder); 42 | return bufferBuilder; 43 | } 44 | 45 | @Override 46 | public void endCatching(Consumer consumer) { 47 | endBatch(); 48 | caughtMeshes.forEach((meshData, renderType) -> { 49 | consumer.accept(BuiltIterableBuffer.buildFrom(renderType, meshData)); 50 | meshData.close(); 51 | }); 52 | caughtMeshes.clear(); 53 | bufferPools.values().forEach(ByteBufferBuilderPool::release); 54 | } 55 | 56 | @Override 57 | public void endBatch() { 58 | for(RenderType renderType : bufferPools.keySet()) { 59 | endBatch(renderType); 60 | } 61 | } 62 | 63 | @Override 64 | protected void endBatch(RenderType renderType, BufferBuilder bufferBuilder) { 65 | MeshData meshData = bufferBuilder.build(); 66 | if (meshData != null) { 67 | caughtMeshes.put(meshData, renderType); 68 | } 69 | } 70 | 71 | private static class ByteBufferBuilderPool { 72 | private final int bufferSize; 73 | private ByteBufferBuilder[] pool = new ByteBufferBuilder[0]; 74 | private int next = 0; 75 | 76 | public ByteBufferBuilderPool(int bufferSize) { 77 | this.bufferSize = bufferSize; 78 | } 79 | 80 | public ByteBufferBuilder getBuffer() { 81 | ByteBufferBuilder byteBufferBuilder; 82 | if (next < pool.length) { 83 | byteBufferBuilder = pool[next++]; 84 | } else { 85 | pool = Arrays.copyOf(pool, pool.length + 1); 86 | pool[next++] = byteBufferBuilder = new ByteBufferBuilder(bufferSize); 87 | } 88 | return byteBufferBuilder; 89 | } 90 | 91 | public void release() { 92 | next = 0; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinGameRenderer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import com.xtracr.realcamera.RealCameraCore; 5 | import com.xtracr.realcamera.compat.CompatibilityHelper; 6 | import com.xtracr.realcamera.config.ConfigFile; 7 | import com.xtracr.realcamera.util.CrosshairUtil; 8 | import com.xtracr.realcamera.util.RaycastUtil; 9 | import net.minecraft.client.Camera; 10 | import net.minecraft.client.DeltaTracker; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.renderer.GameRenderer; 13 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 14 | import net.minecraft.world.entity.Entity; 15 | import net.minecraft.world.entity.projectile.ProjectileUtil; 16 | import net.minecraft.world.phys.AABB; 17 | import net.minecraft.world.phys.EntityHitResult; 18 | import net.minecraft.world.phys.Vec3; 19 | import org.joml.Matrix4f; 20 | import org.spongepowered.asm.mixin.Final; 21 | import org.spongepowered.asm.mixin.Mixin; 22 | import org.spongepowered.asm.mixin.Shadow; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 26 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 27 | 28 | @Mixin(GameRenderer.class) 29 | public abstract class MixinGameRenderer { 30 | @Shadow 31 | @Final Minecraft minecraft; 32 | @Shadow 33 | @Final private Camera mainCamera; 34 | 35 | @ModifyVariable(method = "pick(Lnet/minecraft/world/entity/Entity;DDF)Lnet/minecraft/world/phys/HitResult;", at = @At("STORE"), ordinal = 0) 36 | private EntityHitResult realcamera$modifyEntityHitResult(EntityHitResult entityHitResult) { 37 | CrosshairUtil.capturedEntityHitResult = entityHitResult; 38 | if (!ConfigFile.config().dynamicCrosshair() && RealCameraCore.isActive()) { 39 | Vec3 startVec = RaycastUtil.getStartVec(); 40 | Vec3 endVec = RaycastUtil.getEndVec(); 41 | double sqDistance = (minecraft.hitResult != null ? minecraft.hitResult.getLocation().distanceToSqr(startVec) : endVec.distanceToSqr(startVec)); 42 | Entity cameraEntity = minecraft.getCameraEntity(); 43 | double interactionRange = Math.max(minecraft.player.blockInteractionRange(), minecraft.player.entityInteractionRange()); 44 | AABB box = cameraEntity.getBoundingBox().expandTowards(cameraEntity.getViewVector(minecraft.getTimer().getGameTimeDeltaPartialTick(true)).scale(interactionRange)).inflate(1.0, 1.0, 1.0); 45 | CrosshairUtil.capturedEntityHitResult = ProjectileUtil.getEntityHitResult(cameraEntity, startVec, endVec, box, entity -> !entity.isSpectator() && entity.isPickable(), sqDistance); 46 | } 47 | return CrosshairUtil.capturedEntityHitResult; 48 | } 49 | 50 | @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setup(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/world/entity/Entity;ZZF)V")) 51 | private void realcamera$atCameraSetup(DeltaTracker deltaTracker, CallbackInfo ci) { 52 | final float deltaTick = deltaTracker.getGameTimeDeltaPartialTick(true); 53 | CompatibilityHelper.NEA_setDeltaTick(deltaTick); 54 | RealCameraCore.initialize(minecraft); 55 | if (RealCameraCore.isActive() && !ConfigFile.config().isClassic()) { 56 | EntityRenderDispatcher dispatcher = minecraft.getEntityRenderDispatcher(); 57 | dispatcher.prepare(minecraft.level, mainCamera, minecraft.crosshairPickEntity); 58 | RealCameraCore.computeCamera(minecraft, deltaTick); 59 | } 60 | } 61 | 62 | @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;prepareCullFrustum(Lnet/minecraft/world/phys/Vec3;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V")) 63 | private void realcamera$atPrepareCullFrustum(DeltaTracker deltaTracker, CallbackInfo ci, @Local(ordinal = 1) Matrix4f modelView) { 64 | if (RealCameraCore.isActive()) { 65 | modelView.rotateLocalZ(RealCameraCore.getRoll(0) * (float) (Math.PI / 180.0)); 66 | } 67 | CompatibilityHelper.forceSetCameraPos(mainCamera); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/mixin/MixinCamera.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.mixin; 2 | 3 | import com.xtracr.realcamera.RealCameraCore; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import com.xtracr.realcamera.config.ModConfig; 6 | import com.xtracr.realcamera.mixin.accessor.GameRendererAccessor; 7 | import net.minecraft.client.Camera; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.util.Mth; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.entity.LivingEntity; 12 | import net.minecraft.world.level.BlockGetter; 13 | import net.minecraft.world.level.ClipContext; 14 | import net.minecraft.world.phys.AABB; 15 | import net.minecraft.world.phys.HitResult; 16 | import net.minecraft.world.phys.Vec3; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.Unique; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | @Mixin(Camera.class) 25 | public abstract class MixinCamera { 26 | @Shadow 27 | private BlockGetter level; 28 | @Shadow 29 | private Vec3 position; 30 | @Shadow 31 | private float xRot; 32 | @Shadow 33 | private float yRot; 34 | 35 | @Inject(method = "setup", at = @At("RETURN")) 36 | private void realcamera$setupCamera(BlockGetter area, Entity entity, boolean thirdPerson, boolean inverseView, float deltaTick, CallbackInfo ci) { 37 | if (!RealCameraCore.isActive()) return; 38 | ModConfig config = ConfigFile.config(); 39 | Vec3 startVec = position; 40 | AABB box = entity.getBoundingBox(); 41 | if (config.isClassic()) { 42 | double scale = entity instanceof LivingEntity livingEntity ? livingEntity.getScale() : 1; 43 | Vec3 offset = new Vec3(config.getClassicX(), config.getClassicY(), -config.getClassicZ()).scale(scale); 44 | Vec3 center = new Vec3(config.getCenterX(), config.getCenterY(), -config.getCenterZ()).scale(scale); 45 | float newPitch = xRot + config.getClassicPitch(); 46 | float newYaw = yRot - config.getClassicYaw(); 47 | setRotation(yRot, 0.0f); 48 | move((float) center.x(), (float) center.y(), (float) center.z()); 49 | setRotation(newYaw, newPitch); 50 | move((float) offset.x(), (float) offset.y(), (float) offset.z()); 51 | } else { 52 | Vec3 entityPos = entity.position().add(entity.position().subtract(entity.xOld, entity.yOld, entity.zOld).scale(entity.tickCount == 0 ? 0 : deltaTick - 1)); 53 | Vec3 rawPos = RealCameraCore.getRawPos(position, entityPos); 54 | double restrictedY = Mth.clamp(rawPos.y(), box.minY + 0.1D, box.maxY - 0.1D); 55 | startVec = new Vec3(position.x(), restrictedY, position.z()); 56 | setPosition(rawPos); 57 | setRotation(RealCameraCore.getYaw(yRot), RealCameraCore.getPitch(xRot)); 58 | } 59 | realcamera$clipToSpace(startVec, entity, realcamera$getFov(deltaTick)); 60 | RealCameraCore.setCameraPos(position); 61 | } 62 | 63 | @Unique 64 | private void realcamera$clipToSpace(Vec3 startVec, Entity entity, double fov) { 65 | Vec3 offset = position.subtract(startVec); 66 | final float depth = 0.05f + (float) (fov * (0.0001 + 0.000005 * fov)); 67 | for (int i = 0; i < 8; ++i) { 68 | float f = depth * ((i & 1) * 2 - 1); 69 | float g = depth * ((i >> 1 & 1) * 2 - 1); 70 | float h = depth * ((i >> 2 & 1) * 2 - 1); 71 | Vec3 start = startVec.add(f, g, h); 72 | Vec3 end = startVec.add(offset).add(f, g, h); 73 | HitResult hitResult = level.clip(new ClipContext(start, end, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, entity)); 74 | double l = hitResult.getLocation().distanceTo(start); 75 | if (hitResult.getType() == HitResult.Type.MISS || l >= offset.length()) continue; 76 | offset = offset.scale(l / offset.length()); 77 | } 78 | setPosition(startVec.add(offset)); 79 | } 80 | 81 | @Unique 82 | private static float realcamera$getFov(float deltaTick) { 83 | Minecraft client = Minecraft.getInstance(); 84 | float fovModifier = Mth.lerp(deltaTick, ((GameRendererAccessor) client.gameRenderer).getOldFov(), ((GameRendererAccessor) client.gameRenderer).getFov()); 85 | return client.options.fov().get() * fovModifier; 86 | } 87 | 88 | @Shadow 89 | protected abstract void move(float x, float y, float z); 90 | 91 | @Shadow 92 | protected abstract void setRotation(float yaw, float pitch); 93 | 94 | @Shadow 95 | protected abstract void setPosition(Vec3 position); 96 | } 97 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/BuiltIterableBuffer.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | 4 | import com.mojang.blaze3d.vertex.MeshData; 5 | import com.xtracr.realcamera.util.VertexData.UV; 6 | import net.minecraft.client.renderer.RenderType; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.awt.*; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public record BuiltIterableBuffer(RenderType renderType, String textureId, IterableVertexBuffer vertexBuffer) { 16 | private static final Pattern TEXTURE_ID_PATTERN = Pattern.compile("texture\\[Optional\\[(.*?)]"); 17 | private static final Map TEXTURE_ID_CACHE = new HashMap<>(); 18 | private static final Map> FIND_PRIMITIVE_CACHE = new HashMap<>(); 19 | 20 | public static BuiltIterableBuffer buildFrom(RenderType renderType, MeshData meshData) { 21 | String textureId = TEXTURE_ID_CACHE.computeIfAbsent(renderType, rt -> { 22 | String renderTypeName = rt.toString(); 23 | Matcher matcher = TEXTURE_ID_PATTERN.matcher(renderTypeName); 24 | return matcher.find() ? matcher.group(1) : renderTypeName; 25 | }); 26 | return new BuiltIterableBuffer(renderType, textureId, new IterableVertexBuffer(meshData)); 27 | } 28 | 29 | public boolean anyNotCached(UV[] uvs) { 30 | Map cache = FIND_PRIMITIVE_CACHE.get(renderType); 31 | if (cache == null) return true; 32 | for (UV uv : uvs) { 33 | if (!cache.containsKey(uv)) return true; 34 | } 35 | return false; 36 | } 37 | 38 | public VertexData[] @Nullable [] findPrimitivesInCache(UV[] uvs) { 39 | Map cache = FIND_PRIMITIVE_CACHE.get(renderType); 40 | int length = renderType.mode().primitiveLength, uvsLength = uvs.length; 41 | VertexData[][] primitives = new VertexData[uvsLength][]; 42 | if (cache == null) return primitives; 43 | float[][] uvCacheArray = new float[uvsLength][]; 44 | boolean allNull = true; 45 | for (int i = 0; i < uvsLength; i++) { 46 | uvCacheArray[i] = cache.get(uvs[i]); 47 | if (uvCacheArray[i] != null) allNull = false; 48 | } 49 | if (allNull) return primitives; 50 | vertexBuffer.primitiveStream().anyMatch(primitive -> { 51 | float[] uvCache; 52 | boolean allFound = true; 53 | cacheFor: 54 | for (int i = 0; i < uvsLength; i++) { 55 | if (primitives[i] != null) continue; 56 | uvCache = uvCacheArray[i]; 57 | if (uvCache == null) continue; 58 | for (int j = 0; j < length; j++) { 59 | if (uvCache[j * 2] != primitive[j].u() || uvCache[j * 2 + 1] != primitive[j].v()) { 60 | allFound = false; 61 | continue cacheFor; 62 | } 63 | } 64 | primitives[i] = VertexData.asImmutable(primitive); 65 | } 66 | return allFound; 67 | }); 68 | return primitives; 69 | } 70 | 71 | public VertexData[] @Nullable [] findPrimitives(UV[] uvs) { 72 | final int resolution = 1000000; 73 | int length = renderType.mode().primitiveLength, uvsLength = uvs.length; 74 | int[] us = new int[length], vs = new int[length]; 75 | VertexData[][] primitives = new VertexData[uvsLength][]; 76 | vertexBuffer.primitiveStream().anyMatch(primitive -> { 77 | for (int i = 0; i < length; i++) { 78 | us[i] = (int) (resolution * primitive[i].u()); 79 | vs[i] = (int) (resolution * primitive[i].v()); 80 | } 81 | Polygon polygon = new Polygon(us, vs, length); 82 | boolean allFound = true; 83 | for (int i = 0; i < uvsLength; i++) { 84 | if (primitives[i] != null) continue; 85 | UV uv = uvs[i]; 86 | if (uv == null) continue; 87 | if (!polygon.contains(resolution * uv.u(), resolution * uv.v())) { 88 | allFound = false; 89 | continue; 90 | } 91 | float[] uvCache = new float[length * 2]; 92 | for (int j = 0; j < length; j++) { 93 | uvCache[j * 2] = primitive[j].u(); 94 | uvCache[j * 2 + 1] = primitive[j].v(); 95 | } 96 | FIND_PRIMITIVE_CACHE.computeIfAbsent(renderType, k -> new HashMap<>()).put(uv, uvCache); 97 | primitives[i] = VertexData.asImmutable(primitive); 98 | } 99 | return allFound; 100 | }); 101 | return primitives; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/compat/CompatibilityHelper.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.compat; 2 | 3 | import com.xtracr.realcamera.RealCamera; 4 | import com.xtracr.realcamera.RealCameraCore; 5 | import com.xtracr.realcamera.config.ConfigFile; 6 | import com.xtracr.realcamera.mixin.accessor.CameraAccessor; 7 | import net.minecraft.client.Camera; 8 | import net.minecraft.world.entity.Entity; 9 | import net.minecraft.world.entity.player.Player; 10 | 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.Method; 13 | 14 | public class CompatibilityHelper { 15 | private static PlatformHelper platformHelper; 16 | private static Method NEA_playerTransformer_setDeltaTick; 17 | private static Field NEA_NEAnimationsLoader_INSTANCE; 18 | private static Field NEA_NEAnimationsLoader_playerTransformer; 19 | private static Field SBW_ClientEventHandler_zoomTime; 20 | private static Class SBW_VehicleEntity; 21 | 22 | public static void initialize(PlatformHelper platformHelper) { 23 | CompatibilityHelper.platformHelper = platformHelper; 24 | LegacyBindingMode.register(); 25 | if (isModLoaded("yes_steve_model")) YSMCompat.register(); 26 | if (isModLoaded("freecam")) try { 27 | Class FC_Freecam = Class.forName("net.xolt.freecam.Freecam"); 28 | Method FC_Freecam_isEnabled = FC_Freecam.getDeclaredMethod("isEnabled"); 29 | DisableHelper.MAIN_FEATURE.registerOr(player -> { 30 | try { 31 | return (boolean) FC_Freecam_isEnabled.invoke(null); 32 | } catch (Exception ignored) { 33 | return false; 34 | } 35 | }); 36 | } catch (Exception e) { 37 | RealCamera.LOGGER.warn("Compatibility with Freecam is outdated: [{}] {}", e.getClass().getName(), e.getMessage()); 38 | } 39 | if (isModLoaded("notenoughanimations")) try { 40 | Class NEA_NEAnimationsLoader = Class.forName("dev.tr7zw.notenoughanimations.NEAnimationsLoader"); 41 | Class NEA_PlayerTransformer = Class.forName("dev.tr7zw.notenoughanimations.logic.PlayerTransformer"); 42 | NEA_playerTransformer_setDeltaTick = NEA_PlayerTransformer.getDeclaredMethod("setDeltaTick", float.class); 43 | NEA_NEAnimationsLoader_INSTANCE = NEA_NEAnimationsLoader.getDeclaredField("INSTANCE"); 44 | NEA_NEAnimationsLoader_playerTransformer = NEA_NEAnimationsLoader.getDeclaredField("playerTransformer"); 45 | } catch (Exception e) { 46 | RealCamera.LOGGER.warn("Compatibility with Not Enough Animations is outdated: [{}] {}", e.getClass().getName(), e.getMessage()); 47 | } 48 | if (isModLoaded("superbwarfare")) try { 49 | Class SBW_ClientEventHandler = Class.forName("com.atsuishio.superbwarfare.event.ClientEventHandler"); 50 | SBW_ClientEventHandler_zoomTime = SBW_ClientEventHandler.getDeclaredField("zoomTime"); 51 | SBW_VehicleEntity = Class.forName("com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity"); 52 | DisableHelper.MAIN_FEATURE.registerOrInBinding(player -> CompatibilityHelper.SBW_gunsIsZooming()); 53 | DisableHelper.MAIN_FEATURE.registerOrInBinding(CompatibilityHelper::SBW_isDrivingVehicle); 54 | } catch (Exception e) { 55 | RealCamera.LOGGER.warn("Compatibility with SuperbWarfare is outdated: [{}] {}", e.getClass().getName(), e.getMessage()); 56 | } 57 | } 58 | 59 | private static boolean SBW_gunsIsZooming() { 60 | try { 61 | return SBW_ClientEventHandler_zoomTime.getDouble(null) > 0; 62 | } catch (Exception ignored) { 63 | return false; 64 | } 65 | } 66 | 67 | private static boolean SBW_isDrivingVehicle(Player player) { 68 | try { 69 | Entity vehicle = player.getVehicle(); 70 | return vehicle != null && SBW_VehicleEntity.isAssignableFrom(vehicle.getClass()); 71 | } catch (Exception ignored) { 72 | return false; 73 | } 74 | } 75 | 76 | public static void NEA_setDeltaTick(float deltaTick) { 77 | if (NEA_playerTransformer_setDeltaTick != null) try { 78 | Object INSTANCE = NEA_NEAnimationsLoader_INSTANCE.get(null); 79 | Object playerTransformer = NEA_NEAnimationsLoader_playerTransformer.get(INSTANCE); 80 | NEA_playerTransformer_setDeltaTick.invoke(playerTransformer, deltaTick); 81 | } catch (Exception ignored) { 82 | } 83 | } 84 | 85 | public static void forceSetCameraPos(Camera camera) { 86 | if (RealCameraCore.isActive() && !ConfigFile.config().isClassic()) { 87 | ((CameraAccessor) camera).invokeSetPosition(RealCameraCore.getCameraPos(camera.getPosition())); 88 | } 89 | } 90 | 91 | public static boolean isModLoaded(String modId) { 92 | return platformHelper.isModLoaded(modId); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/compat/LegacyBindingMode.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.compat; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.xtracr.realcamera.api.BindResult; 5 | import com.xtracr.realcamera.api.RealCameraAPI; 6 | import com.xtracr.realcamera.config.ConfigFile; 7 | import com.xtracr.realcamera.mixin.accessor.PlayerRendererAccessor; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.model.PlayerModel; 10 | import net.minecraft.client.player.AbstractClientPlayer; 11 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 12 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 13 | import net.minecraft.core.Direction; 14 | import net.minecraft.util.Mth; 15 | import net.minecraft.world.entity.LivingEntity; 16 | import net.minecraft.world.entity.Pose; 17 | import net.minecraft.world.phys.Vec3; 18 | import org.joml.Vector3f; 19 | import org.joml.Vector4f; 20 | 21 | public class LegacyBindingMode { 22 | public static void register() { 23 | RealCameraAPI.registerFunction(100, LegacyBindingMode::computeBindResult); 24 | } 25 | 26 | private static BindResult computeBindResult(Minecraft client, float deltaTick) { 27 | if (!ConfigFile.config().legacyBindingMode()) return BindResult.EMPTY; 28 | PoseStack poseStack = new PoseStack(); 29 | AbstractClientPlayer player = client.player; 30 | // WorldRenderer.render 31 | // EntityRenderDispatcher.render 32 | PlayerRenderer playerRenderer = (PlayerRenderer) client.getEntityRenderDispatcher().getRenderer(player); 33 | Vec3 renderOffset = playerRenderer.getRenderOffset(player, deltaTick); 34 | poseStack.translate(renderOffset.x(), renderOffset.y(), renderOffset.z()); 35 | // PlayerEntityRenderer.render 36 | ((PlayerRendererAccessor) playerRenderer).invokeSetModelProperties(player); 37 | // LivingEntityRenderer.render 38 | PlayerModel playerModel = playerRenderer.getModel(); 39 | playerModel.attackTime = player.getAttackAnim(deltaTick); 40 | playerModel.riding = player.isPassenger(); 41 | playerModel.young = player.isBaby(); 42 | float h = Mth.rotLerp(deltaTick, player.yBodyRotO, player.yBodyRot); 43 | float j = Mth.rotLerp(deltaTick, player.yHeadRotO, player.yHeadRot); 44 | float k = j - h; 45 | if (player.isPassenger() && player.getVehicle() instanceof LivingEntity livingEntity) { 46 | h = Mth.rotLerp(deltaTick, livingEntity.yBodyRotO, livingEntity.yBodyRot); 47 | k = j - h; 48 | float l = Mth.wrapDegrees(k); 49 | if (l < -85.0F) { 50 | l = -85.0F; 51 | } 52 | if (l >= 85.0F) { 53 | l = 85.0F; 54 | } 55 | h = j - l; 56 | if (l * l > 2500.0F) { 57 | h += l * 0.2F; 58 | } 59 | k = j - h; 60 | } 61 | float m = Mth.lerp(deltaTick, player.xRotO, player.getXRot()); 62 | if (LivingEntityRenderer.isEntityUpsideDown(player)) { 63 | m *= -1.0F; 64 | k *= -1.0F; 65 | } 66 | k = Mth.wrapDegrees(k); 67 | if (player.hasPose(Pose.SLEEPING)) { 68 | Direction direction = player.getBedOrientation(); 69 | if (direction != null) { 70 | float n = player.getEyeHeight(Pose.STANDING) - 0.1F; 71 | poseStack.translate((float)(-direction.getStepX()) * n, 0.0F, (float)(-direction.getStepZ()) * n); 72 | } 73 | } 74 | float lx = player.getScale(); 75 | poseStack.scale(lx, lx, lx); 76 | float n = player.tickCount + deltaTick; 77 | ((PlayerRendererAccessor) playerRenderer).invokeSetupRotations(player, poseStack, n, h, deltaTick, lx); 78 | poseStack.scale(-1.0F, -1.0F, 1.0F); 79 | ((PlayerRendererAccessor) playerRenderer).invokeScale(player, poseStack, deltaTick); 80 | poseStack.translate(0.0F, -1.501F, 0.0F); 81 | float o = 0.0F; 82 | float p = 0.0F; 83 | if (!player.isPassenger() && player.isAlive()) { 84 | o = player.walkAnimation.speed(deltaTick); 85 | p = player.walkAnimation.position(deltaTick); 86 | if (player.isBaby()) { 87 | p *= 3.0F; 88 | } 89 | 90 | if (o > 1.0F) { 91 | o = 1.0F; 92 | } 93 | } 94 | playerModel.prepareMobModel(player, p, o, deltaTick); 95 | playerModel.setupAnim(player, p, o, n, k, m); 96 | // AnimalModel.render 97 | // ModelPart.render 98 | playerModel.head.translateAndRotate(poseStack); 99 | 100 | Vector4f offset = poseStack.last().pose().transform(new Vector4f(0, -0.125f, -0.2f, 1.0f)); 101 | BindResult result = BindResult.getOrCreate("LEGACY_MODE"); 102 | result.setPosition(new Vec3(offset.x(), offset.y(), offset.z())); 103 | poseStack.scale(1f, -1f, -1f); 104 | result.setForward(new Vec3(poseStack.last().normal().getColumn(2, new Vector3f()))); 105 | result.setUpward(new Vec3(poseStack.last().normal().getColumn(1, new Vector3f()))); 106 | return result; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/gui/NumberField.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.gui; 2 | 3 | import com.xtracr.realcamera.util.LocUtil; 4 | import net.minecraft.ChatFormatting; 5 | import net.minecraft.client.gui.Font; 6 | import net.minecraft.client.gui.GuiGraphics; 7 | import net.minecraft.client.gui.components.EditBox; 8 | import net.minecraft.client.gui.components.Tooltip; 9 | import net.minecraft.client.gui.navigation.CommonInputs; 10 | import net.minecraft.network.chat.CommonComponents; 11 | import net.minecraft.network.chat.Style; 12 | import net.minecraft.util.FormattedCharSequence; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.function.Consumer; 16 | 17 | public abstract class NumberField> extends EditBox { 18 | private final T defaultValue; 19 | protected T maximum, minimum; 20 | private Tooltip tooltip; 21 | 22 | NumberField(Font font, int width, int height, T defaultValue, T maximum, T minimum, @Nullable NumberField copyFrom) { 23 | super(font, 0, 0, width, height, CommonComponents.EMPTY); 24 | this.defaultValue = defaultValue; 25 | this.maximum = maximum; 26 | this.minimum = minimum; 27 | setNumber(defaultValue); 28 | if (copyFrom != null) setNumber(copyFrom.getNumber()); 29 | } 30 | 31 | public static NumberField ofFloat(Font font, int width, int height, float defaultValue, @Nullable NumberField copyFrom) { 32 | return new FloatField(font, width, height, defaultValue, copyFrom); 33 | } 34 | 35 | public static NumberField ofInt(Font font, int width, int height, int defaultValue, @Nullable NumberField copyFrom) { 36 | return new IntField(font, width, height, defaultValue, copyFrom); 37 | } 38 | 39 | public T getNumber() { 40 | try { 41 | return getNumberInternal(); 42 | } catch (Exception e) { 43 | return defaultValue; 44 | } 45 | } 46 | 47 | public void setNumber(T value) { 48 | try { 49 | if (value.compareTo(minimum) < 0) value = minimum; 50 | else if (value.compareTo(maximum) > 0) value = maximum; 51 | setValue(value.toString()); 52 | } catch (Exception ignored) { 53 | } 54 | } 55 | 56 | public NumberField setMax(T maximum) { 57 | this.maximum = maximum; 58 | return this; 59 | } 60 | 61 | public NumberField setMin(T minimum) { 62 | this.minimum = minimum; 63 | return this; 64 | } 65 | 66 | public NumberField setOnValueChange(Consumer consumer) { 67 | super.setResponder(str -> consumer.accept(getNumber())); 68 | return this; 69 | } 70 | 71 | abstract protected T getNumberInternal(); 72 | 73 | protected void checkText() { 74 | super.setTooltip(tooltip); 75 | setFormatter((string, firstCharacterIndex) -> FormattedCharSequence.forward(string, Style.EMPTY)); 76 | if (getValue().isEmpty()) return; 77 | try { 78 | T value = getNumberInternal(); 79 | if (value.compareTo(minimum) < 0) throw new Exception("< " + minimum); 80 | if (value.compareTo(maximum) > 0) throw new Exception("> " + maximum); 81 | } catch (Exception e) { 82 | super.setTooltip(Tooltip.create(LocUtil.literal("Invalid number: " + e.getMessage()).withStyle(s -> s.withColor(ChatFormatting.RED)))); 83 | setFormatter((string, firstCharacterIndex) -> FormattedCharSequence.forward(string, Style.EMPTY.withColor(ChatFormatting.RED))); 84 | } 85 | } 86 | 87 | @Override 88 | public void setTooltip(Tooltip tooltip) { 89 | this.tooltip = tooltip; 90 | super.setTooltip(tooltip); 91 | } 92 | 93 | @Override 94 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 95 | if (CommonInputs.selected(keyCode)) { 96 | setFocused(false); 97 | return true; 98 | } 99 | return super.keyPressed(keyCode, scanCode, modifiers); 100 | } 101 | 102 | @Override 103 | public boolean charTyped(char chr, int modifiers) { 104 | if (chr != '-' && chr != '.' && (chr < '0' || chr > '9')) return false; 105 | return super.charTyped(chr, modifiers); 106 | } 107 | 108 | @Override 109 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float deltaTick) { 110 | checkText(); 111 | super.renderWidget(graphics, mouseX, mouseY, deltaTick); 112 | } 113 | 114 | private static class FloatField extends NumberField { 115 | FloatField(Font font, int width, int height, float defaultValue, @Nullable NumberField copyFrom) { 116 | super(font, width, height, defaultValue, Float.MAX_VALUE, -Float.MAX_VALUE, copyFrom); 117 | setMaxLength(16); 118 | } 119 | 120 | @Override 121 | protected Float getNumberInternal() { 122 | return Float.parseFloat(getValue()); 123 | } 124 | } 125 | 126 | private static class IntField extends NumberField { 127 | IntField(Font font, int width, int height, int defaultValue, @Nullable NumberField copyFrom) { 128 | super(font, width, height, defaultValue, Integer.MAX_VALUE, Integer.MIN_VALUE, copyFrom); 129 | setMaxLength(8); 130 | } 131 | 132 | @Override 133 | protected Integer getNumberInternal() { 134 | return Integer.parseInt(getValue()); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real Camera 2 | 3 | ### [中文](README_ZH.md) 4 | 5 | Make the camera more realistic in the first-person view. 6 | Supported versions: 1.18.2-1.20.4 Forge, 1.18.2-1.21.8 Fabric, 1.21-1.21.8 NeoForge. 7 | Download the mod from [Releases](https://github.com/xTracr/RealCamera/releases), [Modrinth](https://modrinth.com/mod/real-camera) or [CurseForge](https://curseforge.com/minecraft/mc-mods/real-camera) 8 | Snapshots are [here](https://github.com/xTracr/RealCamera/actions/workflows/build.yml) 9 | 10 | ## Features 11 | 12 | * Bind the camera to a specific part of the body. 13 | * Customize the position and rotation of the camera. 14 | * Render player model in first-person perspective. 15 | * Use F6 to toggle the feature on or off and other hotkeys to adjust the camera. 16 | * Configure these features in the Config Screen (Cloth Config required) and the Model View Screen (0.6+). 17 | 18 | ### Configuration (0.6+) 19 | 20 | * Theoretically, most mod models are supported, but need to be configured manually: 21 | * First, set the key binding for `Open Model View Screen`. 22 | * ![gui_configs](https://cdn.modrinth.com/data/fYYSAh4R/images/4625282a5683cd38eba2947be1ca82e595e18bad.png) 23 | * Open the model view screen and left click with left Alt held to select the corresponding face of the model, scroll with left Alt held to switch between the different layers of the model. 24 | * By clicking the `Selecting` button on the left, switch between the three to select the `Forward Vector`, `Upward Vector`, and `Target Plane`. 25 | * From the top right button, enter the `Preview` section, where you can see the relative relationship between the camera and the model and make certain adjustments (you can also adjust through key bindings). 26 | * ![gui_preview](https://cdn.modrinth.com/data/fYYSAh4R/images/44c87a6f1750f8d1b03422120e6042d1098896cb.png) 27 | * Enter a name and save. 28 | 29 | #### Tips 30 | 31 | * Configs can have their priority adjusted - higher priority configs appear higher in the right-side list 32 | * Disable depth in `Preview` section to hide models blocking the view 33 | * About `Disable` section (current version still rough, complex operations): 34 | * When texture ID field is empty, use Left Alt+Left Click to select texture 35 | * Model parts contained by blue boxes won't be rendered 36 | * Left-click drag to select in texture view, other keys cancel selection 37 | * Left Alt+Left Click quickly selects hovered parts 38 | * In `All` mode, entire texture's model won't render (blue box containing the texture in left panel indicates this) 39 | * ![gui_disable](https://cdn.modrinth.com/data/fYYSAh4R/images/b49ac4da6bf8a59f13c7e93ca1ef76b73e5d23b4.png) 40 | 41 | ## Dependencies 42 | 43 | * Fabric: 44 | * [Fabric API](https://modrinth.com/mod/fabric-api) 45 | * Both: 46 | * (Optional but recommended) [Cloth Config API](https://modrinth.com/mod/cloth-config) 47 | 48 | ## FAQ 49 | 50 | * A part of the model (e.g. hair) is always in the way, how to make it invisible? 51 | * Increasing this value may help, or use `Disable` section 52 | ![disable_depth](https://github.com/xTracr/RealCamera/assets/57320980/78c246e8-34aa-4979-89de-780ee907870b) 53 | * What key to press can open Model View Screen? 54 | * Set the key binding by yourself 55 | * Why can't i open the Config Screen? 56 | * Please downlowd [Cloth Config API](https://modrinth.com/mod/cloth-config) 57 | * Why does it show binding failed when use YSM models? 58 | * Snapshot 0.6.15 "try" to fix problems, but it didn't fix completely 59 | * If Minecraft version is 1.20.1, then bind upward vector to head left (or right) side, and set roll angle to 90 (or -90) in preview section 60 | * Why does the head of YSM model disappear when in the Model View Screen? 61 | * You downloaded Better Combat, just put it away 62 | 63 | ### Compatibility 64 | 65 | * Incompatible: 66 | * OptiFine 67 | * Armourer's Workshop (Mod version 0.6+) 68 | * Armors based on GeckoLib 69 | * Customizable Player Models 70 | * Epic Fight (Mod version 0.6-) 71 | * Timeless and Classics Zero,version 1.1.4+ 72 | 73 | * Compatible: 74 | * most camera mods 75 | * most player model mods 76 | * Armourer's Workshop (Mod version 0.6-) 77 | * Epic Fight (Mod version 0.6+) 78 | * First-person Model 79 | * Not Enough Animations 80 | * ParCool! 81 | * Pehkui 82 | * Player Animation Lib 83 | * Timeless and Classics Zero,version 1.0.3- 84 | * Yes Steve Model (Not stably compatible) 85 | * Superb Warfare 86 | 87 | * Model Mod Compatibility Requirements with `Real Camera` (Based on Official Mappings): 88 | * Vertex Data Acquisition 89 | * `Real Camera` obtains vertex data by overriding the `MultiBufferSource multiBufferSource` parameter of the public method `EntityRenderDispatcher.render` 90 | * The call timing is before the `Camera.setup` phase in the `GameRenderer.renderLevel` method 91 | * *Therefore*, the mod needs to meet the following technical conditions: 92 | * Use only the **provided** `multiBufferSource` parameter when rendering `Minecraft.getCameraEntity`, and avoid alternative approaches for obtaining or creating `MultiBufferSource` instances 93 | * The overall rendering behavior remains unaffected when rendering the player model at the adjusted timing 94 | * Ensure all vertex data ultimately passes through `VertexConsumer` gotten by `MultiBufferSource.getBuffer` 95 | * Render Times Compatibility 96 | * `Real Camera` renders `Minecraft.getCameraEntity` by calling the public method `EntityRenderDispatcher.render` 97 | * This is the second call to the `EntityRenderDispatcher.render` method in a frame (the first call is to solve the camera parameters). 98 | * *Therefore*, the mod needs to meet the condition that the overall rendering behavior remains unaffected when rendering the player model **multiple times** in the same frame 99 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/util/VertexData.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.util; 2 | 3 | import com.mojang.blaze3d.vertex.VertexConsumer; 4 | import net.minecraft.world.phys.Vec3; 5 | import org.joml.Matrix3f; 6 | import org.joml.Matrix4f; 7 | import org.joml.Vector3f; 8 | 9 | public interface VertexData { 10 | static VertexData immutable(float x, float y, float z, int argb, float u, float v, int overlay, int light, float normalX, float normalY, float normalZ) { 11 | return new ImmutableVertex(x, y, z, argb, u, v, overlay, light, normalX, normalY, normalZ); 12 | } 13 | 14 | static MutableVertex mutable() { 15 | return new MutableVertex(); 16 | } 17 | 18 | static VertexData[] asImmutable(VertexData[] vertices) { 19 | VertexData[] immutable = new VertexData[vertices.length]; 20 | for (int i = 0; i < vertices.length; i++) immutable[i] = vertices[i].asImmutable(); 21 | return immutable; 22 | } 23 | 24 | static Vec3 position(VertexData[] vertices, float u, float v) { 25 | if (vertices.length < 3) return vertices[0].position(); 26 | float u0 = vertices[0].u(), v0 = vertices[0].v(), u1 = vertices[1].u(), v1 = vertices[1].v(), u2 = vertices[2].u(), v2 = vertices[2].v(); 27 | float alpha = ((u - u1) * (v1 - v2) - (v - v1) * (u1 - u2)) / ((u0 - u1) * (v1 - v2) - (v0 - v1) * (u1 - u2)), 28 | beta = ((u - u2) * (v2 - v0) - (v - v2) * (u2 - u0)) / ((u1 - u2) * (v2 - v0) - (v1 - v2) * (u2 - u0)); 29 | return vertices[0].position().scale(alpha).add(vertices[1].position().scale(beta)).add(vertices[2].position().scale(1 - alpha - beta)); 30 | } 31 | 32 | static Vec3 normal(VertexData[] vertices) { 33 | return switch (vertices.length) { 34 | case 0 -> Vec3.ZERO; 35 | case 1 -> vertices[0].normal(); 36 | case 2 -> vertices[1].position().subtract(vertices[0].position()).normalize(); 37 | default -> { 38 | Vec3 a = vertices[0].position(), b = vertices[1].position(), c = vertices[2].position(); 39 | yield b.subtract(a).cross(c.subtract(a)).normalize(); 40 | } 41 | }; 42 | } 43 | 44 | float x(); 45 | 46 | float y(); 47 | 48 | float z(); 49 | 50 | default Vec3 position() { 51 | return new Vec3(x(), y(), z()); 52 | } 53 | 54 | int argb(); 55 | 56 | float u(); 57 | 58 | float v(); 59 | 60 | default UV uv() { 61 | return new UV(u(), v()); 62 | } 63 | 64 | int overlay(); 65 | 66 | int light(); 67 | 68 | float normalX(); 69 | 70 | float normalY(); 71 | 72 | float normalZ(); 73 | 74 | default Vec3 normal() { 75 | return new Vec3(normalX(), normalY(), normalZ()); 76 | } 77 | 78 | default VertexData asImmutable() { 79 | return new ImmutableVertex(x(), y(), z(), argb(), u(), v(), overlay(), light(), normalX(), normalY(), normalZ()); 80 | } 81 | 82 | default void render(VertexConsumer buffer, Matrix4f positionMatrix, Matrix3f normalMatrix) { 83 | Vector3f pos = new Vector3f(x(), y(), z()).mulPosition(positionMatrix); 84 | Vector3f normal = new Vector3f(normalX(), normalY(), normalZ()).mul(normalMatrix); 85 | buffer.addVertex(pos.x(), pos.y(), pos.z(), argb(), u(), v(), overlay(), light(), normal.x(), normal.y(), normal.z()); 86 | } 87 | 88 | default void render(VertexConsumer buffer) { 89 | buffer.addVertex(x(), y(), z(), argb(), u(), v(), overlay(), light(), normalX(), normalY(), normalZ()); 90 | } 91 | 92 | record UV(float u, float v) { 93 | @Override 94 | public boolean equals(Object o) { 95 | if (this == o) return true; 96 | if (!(o instanceof UV(float u1, float v1))) return false; 97 | return Float.compare(u, u1) == 0 && Float.compare(v, v1) == 0; 98 | } 99 | 100 | @Override 101 | public int hashCode() { 102 | return Float.hashCode(u) * 31 + Float.hashCode(v); 103 | } 104 | } 105 | 106 | record ImmutableVertex(float x, float y, float z, int argb, float u, float v, int overlay, int light, float normalX, float normalY, float normalZ) implements VertexData { } 107 | 108 | class MutableVertex implements VertexData { 109 | public float x, y, z; 110 | public int argb; 111 | public float u, v; 112 | public int overlay, light; 113 | public float normalX, normalY, normalZ; 114 | 115 | private MutableVertex() { } 116 | 117 | @Override 118 | public float x() { 119 | return x; 120 | } 121 | 122 | @Override 123 | public float y() { 124 | return y; 125 | } 126 | 127 | @Override 128 | public float z() { 129 | return z; 130 | } 131 | 132 | @Override 133 | public int argb() { 134 | return argb; 135 | } 136 | 137 | @Override 138 | public float u() { 139 | return u; 140 | } 141 | 142 | @Override 143 | public float v() { 144 | return v; 145 | } 146 | 147 | @Override 148 | public int overlay() { 149 | return overlay; 150 | } 151 | 152 | @Override 153 | public int light() { 154 | return light; 155 | } 156 | 157 | @Override 158 | public float normalX() { 159 | return normalX; 160 | } 161 | 162 | @Override 163 | public float normalY() { 164 | return normalY; 165 | } 166 | 167 | @Override 168 | public float normalZ() { 169 | return normalZ; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/compat/DisableHelper.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.compat; 2 | 3 | import com.xtracr.realcamera.RealCameraCore; 4 | import com.xtracr.realcamera.config.ConfigFile; 5 | import net.minecraft.core.registries.BuiltInRegistries; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.tags.TagKey; 8 | import net.minecraft.world.entity.Entity; 9 | import net.minecraft.world.entity.LivingEntity; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.minecraft.world.item.Item; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.function.Predicate; 16 | 17 | public class DisableHelper { 18 | private static final Predicate FALSE = player -> false; 19 | private static final Map entries = new HashMap<>(); 20 | public static final Entry MAIN_FEATURE = new Entry("mainFeature", player -> player.isSleeping() || player.isSpectator()); 21 | public static final Entry RENDER_MODEL = new Entry("renderModel", FALSE, Player::isScoping); 22 | public static final Entry RENDER_HANDS = new Entry("renderHands", player -> RealCameraCore.isRendering()); 23 | public static int exitTick = 0; 24 | 25 | static { 26 | MAIN_FEATURE.registerOrInBinding(player -> ConfigFile.config().bindingDisableWhenSneaking() && player.isCrouching()); 27 | MAIN_FEATURE.registerOrInClassic(player -> ConfigFile.config().classicDisableWhenSneaking() && player.isCrouching()); 28 | MAIN_FEATURE.registerOrInBinding(player -> ConfigFile.config().bindingDisableWhenSwimming() && swimmingRecently(player, ConfigFile.config().getBindingSwimOutTick())); 29 | MAIN_FEATURE.registerOrInClassic(player -> ConfigFile.config().classicDisableWhenSwimming() && swimmingRecently(player, ConfigFile.config().getClassicSwimOutTick())); 30 | MAIN_FEATURE.registerOrInBinding(player -> { 31 | Item mainHand = player.getMainHandItem().getItem(); 32 | Item offHand = player.getOffhandItem().getItem(); 33 | for (String pattern : ConfigFile.config().getDisableMainFeatureItems()) 34 | if (matchesItemPattern(mainHand, pattern) || matchesItemPattern(offHand, pattern)) 35 | return true; 36 | return false; 37 | }); 38 | RENDER_MODEL.registerOrInBinding(player -> { 39 | Item mainHand = player.getMainHandItem().getItem(); 40 | Item offHand = player.getOffhandItem().getItem(); 41 | for (String pattern : ConfigFile.config().getDisableRenderItems()) 42 | if (matchesItemPattern(mainHand, pattern) || matchesItemPattern(offHand, pattern)) 43 | return true; 44 | return false; 45 | }); 46 | } 47 | 48 | private static boolean swimmingRecently(Player player, int swimOutTick) { 49 | if (player.isSwimming()) { 50 | exitTick = player.tickCount; 51 | return true; 52 | } 53 | if (exitTick > 0 && !player.isSwimming()) { 54 | int elapsedTicks = player.tickCount - exitTick; 55 | if (elapsedTicks <= swimOutTick) { 56 | return true; 57 | } 58 | exitTick = 0; 59 | } 60 | return false; 61 | } 62 | 63 | @Deprecated 64 | public static void registerOr(String name, Predicate predicate) { 65 | entries.get(name).registerOrInBinding(predicate::test); 66 | } 67 | 68 | public static boolean matchesItemPattern(Item item, String pattern) { 69 | if (pattern.startsWith("#")) { 70 | String tagId = pattern.substring(1); 71 | TagKey itemTag = TagKey.create(BuiltInRegistries.ITEM.key(), ResourceLocation.parse(tagId)); 72 | return BuiltInRegistries.ITEM.getTag(itemTag) 73 | .map(tag -> tag.contains(BuiltInRegistries.ITEM.wrapAsHolder(item))) 74 | .orElse(false); 75 | } 76 | return simpleWildcardMatch(BuiltInRegistries.ITEM.getKey(item).toString(), pattern); 77 | } 78 | 79 | public static boolean simpleWildcardMatch(String text, String pattern) { 80 | if (pattern.isEmpty()) return text.isEmpty(); 81 | if (pattern.equals(text)) return true; 82 | String[] parts = pattern.split("\\*+"); 83 | if (parts.length == 0) return true; 84 | int currentIndex = 0; 85 | if (!pattern.startsWith("*")) { 86 | String firstPart = parts[0]; 87 | if (!text.startsWith(firstPart)) return false; 88 | currentIndex = firstPart.length(); 89 | } 90 | for (int i = 1; i < parts.length; i++) { 91 | String part = parts[i]; 92 | if (part.isEmpty()) continue; 93 | int foundIndex = text.indexOf(part, currentIndex); 94 | if (foundIndex == -1) return false; 95 | currentIndex = foundIndex + part.length(); 96 | } 97 | if (!pattern.endsWith("*")) { 98 | String lastPart = parts[parts.length - 1]; 99 | return text.endsWith(lastPart); 100 | } 101 | return true; 102 | } 103 | 104 | public static class Entry { 105 | protected Predicate predicateInClassic; 106 | protected Predicate predicateInBinding; 107 | 108 | protected Entry(String name, Predicate predicate) { 109 | this(name, predicate, predicate); 110 | } 111 | 112 | protected Entry(String name, Predicate predicateInClassic, Predicate predicateInBinding) { 113 | this.predicateInClassic = predicateInClassic; 114 | this.predicateInBinding = predicateInBinding; 115 | entries.put(name, this); 116 | } 117 | 118 | public void registerOr(Predicate predicate) { 119 | registerOrInClassic(predicate); 120 | registerOrInBinding(predicate); 121 | } 122 | 123 | public void registerOrInClassic(Predicate predicate) { 124 | this.predicateInClassic = this.predicateInClassic.or(predicate); 125 | } 126 | 127 | public void registerOrInBinding(Predicate predicate) { 128 | this.predicateInBinding = this.predicateInBinding.or(predicate); 129 | } 130 | 131 | public boolean disabled(Entity cameraEntity) { 132 | if (!(cameraEntity instanceof Player player)) return false; 133 | return ConfigFile.config().isClassic() ? predicateInClassic.test(player) : predicateInBinding.test(player); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/compat/YSMCompat.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera.compat; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.xtracr.realcamera.api.BindResult; 5 | import com.xtracr.realcamera.api.RealCameraAPI; 6 | import com.xtracr.realcamera.config.BindTarget; 7 | import com.xtracr.realcamera.config.ConfigFile; 8 | import com.xtracr.realcamera.util.BuiltIterableBuffer; 9 | import com.xtracr.realcamera.util.MultiVertexCatcher; 10 | import com.xtracr.realcamera.util.VertexData; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 13 | import net.minecraft.util.Mth; 14 | import net.minecraft.world.entity.Entity; 15 | import net.minecraft.world.phys.Vec3; 16 | import org.joml.Matrix3f; 17 | import org.joml.Matrix4f; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | public class YSMCompat { 23 | private static final Map resultMap = new HashMap<>(); 24 | private static final TransformedVertexRecorder[] transformedRecorders = new TransformedVertexRecorder[4]; 25 | private static BindResult bindResult = BindResult.EMPTY; 26 | private static boolean allCached = false; 27 | 28 | static { 29 | final float pitch = 1.9106332f, yaw = 2.0943951f; 30 | transformedRecorders[0] = new TransformedVertexRecorder(); 31 | transformedRecorders[1] = new TransformedVertexRecorder().setRotation(pitch, 0); 32 | transformedRecorders[2] = new TransformedVertexRecorder().setRotation(pitch, yaw); 33 | transformedRecorders[3] = new TransformedVertexRecorder().setRotation(pitch, 2 * yaw); 34 | } 35 | 36 | public static void register() { 37 | RealCameraAPI.registerFunction(-100, YSMCompat::computeBindResult); 38 | } 39 | 40 | private static BindResult computeBindResult(Minecraft client, float deltaTick) { 41 | resultMap.clear(); 42 | bindResult = BindResult.EMPTY; 43 | allCached = true; 44 | Entity entity = client.getCameraEntity(); 45 | EntityRenderDispatcher dispatcher = client.getEntityRenderDispatcher(); 46 | PoseStack poseStack = new PoseStack(); 47 | for (TransformedVertexRecorder transformedRecorder : transformedRecorders) { 48 | poseStack.pushPose(); 49 | poseStack.mulPose(transformedRecorder.matrix4f.invert(new Matrix4f())); 50 | MultiVertexCatcher catcher = MultiVertexCatcher.defaultImpl(); 51 | dispatcher.render(entity, 0, 0, 0, Mth.lerp(deltaTick, entity.yRotO, entity.getYRot()), deltaTick, poseStack, catcher, dispatcher.getPackedLightCoords(entity, deltaTick)); 52 | catcher.endCatching(transformedRecorder::computeBindResultInCache); 53 | poseStack.popPose(); 54 | if (bindResult.available()) return bindResult; 55 | } 56 | if (allCached) return BindResult.EMPTY; 57 | for (TransformedVertexRecorder transformedRecorder : transformedRecorders) { 58 | poseStack.pushPose(); 59 | poseStack.mulPose(transformedRecorder.matrix4f.invert(new Matrix4f())); 60 | MultiVertexCatcher catcher = MultiVertexCatcher.defaultImpl(); 61 | dispatcher.render(entity, 0, 0, 0, Mth.lerp(deltaTick, entity.yRotO, entity.getYRot()), deltaTick, poseStack, catcher, dispatcher.getPackedLightCoords(entity, deltaTick)); 62 | catcher.endCatching(transformedRecorder::computeBindResult); 63 | poseStack.popPose(); 64 | if (bindResult.available()) return bindResult; 65 | } 66 | return BindResult.EMPTY; 67 | } 68 | 69 | protected static class TransformedVertexRecorder { 70 | protected final Matrix4f matrix4f = new Matrix4f(); 71 | protected final Matrix3f matrix3f = new Matrix3f(); 72 | 73 | public TransformedVertexRecorder setRotation(float pitch, float yaw) { 74 | matrix4f.rotationYXZ(yaw, pitch, 0).invert(); 75 | matrix3f.rotationYXZ(yaw, pitch, 0).invert(); 76 | return this; 77 | } 78 | 79 | public void computeBindResultInCache(BuiltIterableBuffer builtBuffer) { 80 | if (bindResult.available()) return; 81 | for (BindTarget target : ConfigFile.config().getBindTargetList(builtBuffer.textureId())) { 82 | BindResult result = resultMap.computeIfAbsent(target, k -> new BindResult(target, false)); 83 | BindTarget.TargetConfig config = target.targetConfig(); 84 | VertexData.UV posUV = new VertexData.UV(config.posU(), config.posV()); 85 | VertexData.UV forwardUV = new VertexData.UV(config.forwardU(), config.forwardV()); 86 | VertexData.UV upwardUV = new VertexData.UV(config.upwardU(), config.upwardV()); 87 | if (builtBuffer.anyNotCached(new VertexData.UV[]{posUV, forwardUV, upwardUV})) allCached = false; 88 | if (result.getPosition() != Vec3.ZERO) posUV = null; 89 | if (result.getForward() != Vec3.ZERO) forwardUV = null; 90 | if (result.getUpward() != Vec3.ZERO) upwardUV = null; 91 | VertexData[][] primitives = builtBuffer.findPrimitivesInCache(new VertexData.UV[]{posUV, forwardUV, upwardUV}); 92 | if (primitives[0] != null) result.setPosition(new Vec3(VertexData.position(primitives[0], config.posU(), config.posV()).toVector3f().mulPosition(matrix4f))); 93 | if (primitives[1] != null) result.setForward(new Vec3(VertexData.normal(primitives[1]).toVector3f().mul(matrix3f))); 94 | if (primitives[2] != null) result.setUpward(new Vec3(VertexData.normal(primitives[2]).toVector3f().mul(matrix3f))); 95 | if (!result.available()) continue; 96 | bindResult = result; 97 | return; 98 | } 99 | } 100 | 101 | public void computeBindResult(BuiltIterableBuffer builtBuffer) { 102 | if (bindResult.available()) return; 103 | for (BindTarget target : ConfigFile.config().getBindTargetList(builtBuffer.textureId())) { 104 | BindResult result = resultMap.computeIfAbsent(target, k -> new BindResult(target, false)); 105 | BindTarget.TargetConfig config = target.targetConfig(); 106 | VertexData.UV posUV = result.getPosition() == Vec3.ZERO ? new VertexData.UV(config.posU(), config.posV()) : null; 107 | VertexData.UV forwardUV = result.getForward() == Vec3.ZERO ? new VertexData.UV(config.forwardU(), config.forwardV()) : null; 108 | VertexData.UV upwardUV = result.getUpward() == Vec3.ZERO ? new VertexData.UV(config.upwardU(), config.upwardV()) : null; 109 | VertexData[][] primitives = builtBuffer.findPrimitives(new VertexData.UV[]{posUV, forwardUV, upwardUV}); 110 | if (primitives[0] != null) result.setPosition(new Vec3(VertexData.position(primitives[0], config.posU(), config.posV()).toVector3f().mulPosition(matrix4f))); 111 | if (primitives[1] != null) result.setForward(new Vec3(VertexData.normal(primitives[1]).toVector3f().mul(matrix3f))); 112 | if (primitives[2] != null) result.setUpward(new Vec3(VertexData.normal(primitives[2]).toVector3f().mul(matrix3f))); 113 | if (!result.available()) continue; 114 | bindResult = result; 115 | return; 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/lzh.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "啟模型觀象之窗", 4 | "key.xtracr_realcamera.togglePerspective": "開/闔", 5 | "key.xtracr_realcamera.toggleAdjustMode": "易調校之法", 6 | "key.xtracr_realcamera.toggleCameraMode": "易鏡觀之道", 7 | "key.xtracr_realcamera.adjustUP": "上移", 8 | "key.xtracr_realcamera.adjustDOWN": "下移", 9 | "key.xtracr_realcamera.adjustFRONT": "前移", 10 | "key.xtracr_realcamera.adjustBACK": "後移", 11 | "key.xtracr_realcamera.adjustLEFT": "左移", 12 | "key.xtracr_realcamera.adjustRIGHT": "右移", 13 | "modmenu.descriptionTranslation.realcamera": "使第一人稱之鏡觀愈真。", 14 | 15 | "config.category.xtracr_realcamera.general": "總則", 16 | "config.category.xtracr_realcamera.binding": "系定之法", 17 | "config.category.xtracr_realcamera.classic": "古法", 18 | "config.category.xtracr_realcamera.cameraOffset": "鏡觀偏移", 19 | "config.category.xtracr_realcamera.centerOffset": "樞軸偏移", 20 | "config.category.xtracr_realcamera.cameraRotation": "鏡觀旋轉", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "鏡觀相對樞軸之移值", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "註: 戯者之前、上、左分別為X、Y、Z軸", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "樞軸相對首部之移值", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "額外之鏡觀旋轉角度", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "註: 俯仰、偏航、翻滾乃以左、上、前為軸之旋轉", 26 | 27 | "config.option.xtracr_realcamera.enabled": "啟其能", 28 | "config.option.xtracr_realcamera.isClassic": "古法", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "動準星", 30 | "config.option.xtracr_realcamera.renderModel": "顯戯者之形", 31 | "config.option.xtracr_realcamera.adjustStep": "調校之步", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "調校之法", 33 | "config.option.xtracr_realcamera.scale": "縮放之度", 34 | "config.option.xtracr_realcamera.cameraOffset": "鏡觀%s移", 35 | "config.option.xtracr_realcamera.centerOffset": "樞軸%s移", 36 | "config.option.xtracr_realcamera.pitch": "俯仰", 37 | "config.option.xtracr_realcamera.yaw": "偏航", 38 | "config.option.xtracr_realcamera.roll": "翻滾", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "詳設請往%s窗(需設捷鍵以啟之)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "%s窗修飾鍵", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "古版系定之法", 42 | "config.option.xtracr_realcamera.adjustOffset": "調鏡觀移", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "隱系定敗之訊", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "顯附身之物", 45 | "config.option.xtracr_realcamera.rerenderModel": "重顯其形", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "潛伏時禁其能", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "游水時禁其能", 48 | "config.option.xtracr_realcamera.swimOutTick": "出游水之延(單位:tick)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "系定果存之幀數", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "位移平滑之係", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "旋轉平滑之係", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "持...時禁其能", 53 | "config.option.xtracr_realcamera.disableRenderItems": "持...時禁其顯", 54 | "config.tooltip.xtracr_realcamera.isClassic": "古法唯簡調鏡觀之位與旋", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "啟時,準星隨視線而動,保中無誤\n若裝Jade、WTHIT等類模組,宜啟之", 56 | "config.tooltip.xtracr_realcamera.renderModel": "第一人稱顯戯者之形", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "每步調校角度之百分一或長度", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "定當前按調校鍵時調何者", 59 | "config.tooltip.xtracr_realcamera.scale": "控偏移量之大小", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "用古版(v0.5)系定之法,惟原模及原模所出之動可受\n啟之則可於%s窗'LEGACY_MODE'中設其性", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "當前按調校鍵時乃調鏡觀之偏移或旋轉", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "矢,蜂針等附身之物", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "於原顯時重顯其形,免早顯致視誤", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "止游,即復其能所之延時(1 tick = 0.05s)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "系定敗後數幀內,沿用前次成之果", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "鏡觀位移平滑之係,值愈大愈平", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "鏡觀旋轉平滑之係,值愈大愈平", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "三法可配:\n1. 通配符`*`配(如`minecraft:*sword`)\n2. 標籤配以`#`始(如`#minecraft:swords`)\n3. 直物ID(如`minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: 系定敗,請往%s窗手設之(鍵:%s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "模型觀象", 73 | "screen.widget.xtracr_realcamera.modelView_import": "入", 74 | "screen.widget.xtracr_realcamera.modelView_export": "出", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "擇", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "前向矢", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "上向矢", 78 | "screen.widget.xtracr_realcamera.modelView_position": "的平面", 79 | "screen.widget.xtracr_realcamera.modelView_save": "存", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "禁用之法", 81 | "screen.widget.xtracr_realcamera.modelView_all": "全", 82 | "screen.widget.xtracr_realcamera.modelView_part": "塊", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "擇法", 84 | "screen.widget.xtracr_realcamera.modelView_single": "單擇", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "多擇", 86 | "screen.widget.xtracr_realcamera.modelView_range": "域", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "滑塊向框", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "框向滑塊", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "X移 = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Y移 = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Z移 = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "俯仰 = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "偏航 = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "翻滾 = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "禁", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "覽", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "設", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "抄", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "貼", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "自剪貼板入設", 101 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "入%s成!", 102 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "入敗:\n%s", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "出今設至剪貼板", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "出成!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "出敗:\n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+左鍵可取光標處UV座標\n下三組UV座標自上而下為前向矢、上向矢與的平面上系點之UV座標\n註:%s鍵+滾輪可易模型之層,手輸UV座標可精系的位", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "所選模型紋理之id(非全id亦可識),存時可短之以便程序識\n如將minecraft:textures/entity/player/slim/alex.png短為minecraft:textures/entity/player/", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "藍框含之部將禁\n可於左材視以左鍵框擇,他鍵廢擇\n%s+左鍵可速擇光標指之部", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "定%s+左鍵速擇之法\n(%s鍵+滾輪可易域擇之範)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "擇矩之號\n(0示未擇)\n可以空格或回車定位今擇之矩(當輸時,按兩次即可定位)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "去擇之矩\n(或用Delete鍵)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "禁時,鏡觀與模型之相對不變,然鏡觀之對應性不為本模所改", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "縮放之度,控偏移量之大小", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "諸頂點至屏距皆小於此值之面不顯", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "系定之先級,值愈大愈先,且右列愈前", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "設之名", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "禁設\n全禁設在設屏中", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "已存之設", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "於覽式可調鏡觀諸偏移", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "啟全設屏\n(需裝Cloth Config API)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "抄諸禁設", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "貼剪貼板諸禁設至今設\n(不蓋同名設)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "名不可空!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "材id不可空!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "存為\n(當有同名設時,亦可用左存鈕)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "禁設之名" 127 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/zh_hk.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "打開模型視圖界面", 4 | "key.xtracr_realcamera.togglePerspective": "開啟/關閉", 5 | "key.xtracr_realcamera.toggleAdjustMode": "切換調整模式", 6 | "key.xtracr_realcamera.toggleCameraMode": "切換攝像頭模式", 7 | "key.xtracr_realcamera.adjustUP": "向上調整", 8 | "key.xtracr_realcamera.adjustDOWN": "向下調整", 9 | "key.xtracr_realcamera.adjustFRONT": "向前調整", 10 | "key.xtracr_realcamera.adjustBACK": "向後調整", 11 | "key.xtracr_realcamera.adjustLEFT": "向左調整", 12 | "key.xtracr_realcamera.adjustRIGHT": "向右調整", 13 | "modmenu.descriptionTranslation.realcamera": "使第一人稱視角下的攝像頭更加真實。", 14 | 15 | "config.category.xtracr_realcamera.general": "通用", 16 | "config.category.xtracr_realcamera.binding": "綁定模式", 17 | "config.category.xtracr_realcamera.classic": "經典模式", 18 | "config.category.xtracr_realcamera.cameraOffset": "攝像頭偏移", 19 | "config.category.xtracr_realcamera.centerOffset": "旋轉中心偏移", 20 | "config.category.xtracr_realcamera.cameraRotation": "攝像頭旋轉", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "攝像頭相對旋轉中心的偏移值", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "注: 玩家的前方、上方和左方分別為X、Y和Z軸", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "旋轉中心相對頭部的偏移值", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "額外的攝像頭旋轉角度", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "注:俯仰、偏航和翻滾分別是以左方、上方和前方為軸的旋轉", 26 | 27 | "config.option.xtracr_realcamera.enabled": "開啟功能", 28 | "config.option.xtracr_realcamera.isClassic": "經典模式", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "動態十字準心", 30 | "config.option.xtracr_realcamera.renderModel": "渲染玩家模型", 31 | "config.option.xtracr_realcamera.adjustStep": "調整步長", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "調整模式", 33 | "config.option.xtracr_realcamera.scale": "縮放比例", 34 | "config.option.xtracr_realcamera.cameraOffset": "攝像頭%s偏移", 35 | "config.option.xtracr_realcamera.centerOffset": "旋轉中心%s偏移", 36 | "config.option.xtracr_realcamera.pitch": "俯仰角", 37 | "config.option.xtracr_realcamera.yaw": "偏航角", 38 | "config.option.xtracr_realcamera.roll": "翻滾角", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "詳細設置請前往%s界面(需設置快捷鍵來打開它)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "%s界面修飾鍵", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "舊綁定模式", 42 | "config.option.xtracr_realcamera.adjustOffset": "調整攝像頭偏移", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "隱藏綁定失敗消息", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "渲染卡在身上的物體", 45 | "config.option.xtracr_realcamera.rerenderModel": "重新渲染模型", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "潛行時禁用主要功能", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "游泳時禁用主要功能", 48 | "config.option.xtracr_realcamera.swimOutTick": "游泳狀態退出延遲(單位:tick)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "綁定結果保留幀數", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "位移平滑系数", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "旋轉平滑系数", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "在手持...時禁用主要功能", 53 | "config.option.xtracr_realcamera.disableRenderItems": "在手持...時禁用渲染", 54 | "config.tooltip.xtracr_realcamera.isClassic": "經典模式只對攝像頭的位置和旋轉做簡單的修改", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "在開啟狀態下,十字準心會隨著玩家的視線移動,確保命中結果不受影響\n在安裝了Jade、WTHIT或類似模組時建議啟用", 56 | "config.tooltip.xtracr_realcamera.renderModel": "在第一人稱視角下渲染玩家模型", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "每步調整的角度1/100或長度", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "決定當前按下調整鍵時調整哪一個", 59 | "config.tooltip.xtracr_realcamera.scale": "控制偏移量的大小", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "使用舊版本(v0.5)的綁定模式,僅支援原版模型和基於原版模型的動作\n開啟後可在%s界面的'LEGACY_MODE'中設置屬性", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "當前按下調整鍵時是調整攝像頭的偏移還是旋轉", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "箭,蜂刺和其它會卡在玩家身體裡的物體", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "在原本的渲染時機重新渲染模型,以避免一些因提前渲染導致的視覺問題", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "玩家停止游泳狀態後重啟主要功能的時間(1 tick = 0.05秒)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "在綁定失敗的數帧内,沿用上个成功的綁定結果", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "相機位移平滑系数,值越大越平滑", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "相機旋轉平滑系数,值越大越平滑", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "支持三種匹配方式:\n1. 通配符`*`匹配(如`minecraft:*sword`)\n2. 標簽匹配`#`開頭(如`#minecraft:tools`)\n3. 直接物品ID(如`minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: 綁定失敗,請前往%s界面手動設置(鍵位:%s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "模型視圖", 73 | "screen.widget.xtracr_realcamera.modelView_import": "導入", 74 | "screen.widget.xtracr_realcamera.modelView_export": "導出", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "選擇", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "向前向量", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "向上向量", 78 | "screen.widget.xtracr_realcamera.modelView_position": "目標平面", 79 | "screen.widget.xtracr_realcamera.modelView_save": "儲存", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "禁用模式", 81 | "screen.widget.xtracr_realcamera.modelView_all": "全部", 82 | "screen.widget.xtracr_realcamera.modelView_part": "部分", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "選擇模式", 84 | "screen.widget.xtracr_realcamera.modelView_single": "單選", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "多選", 86 | "screen.widget.xtracr_realcamera.modelView_range": "範圍", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "切換滑塊到輸入框", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "切換輸入框到滑塊", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "X偏移量 = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Y偏移量 = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Z偏移量 = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "俯仰角 = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "偏航角 = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "翻滾角 = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "禁用", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "預覽", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "配置", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "複製", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "貼上", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "從剪貼簿導入配置", 101 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "導入失敗:\n%s", 102 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "導入%s成功!", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "導出當前配置到剪貼簿", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "導出成功!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "導出失敗:\n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "左Alt+左鍵可獲取鼠標指針處的UV座標\n下方三組UV座標由上到下依次為向前向量、向上向量和目標平面上綁定點的UV座標\n註:左Alt鍵+滾輪可在模型的不同層間切換,手動輸入UV座標可以更加精確地綁定到目標位置", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "被選中的模型的紋理的id(不是完整的id也可以識別),儲存時可以縮短一部分以便於程序識別\n比如將minecraft:textures/entity/player/slim/alex.png縮短為minecraft:textures/entity/player/", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "被藍框包含的部分會被禁用\n可在左側材質視圖中鼠標左鍵框選,其它鍵取消選擇\n左Alt+左鍵可快速選擇被鼠標指針指向的部分", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "決定左Alt+左鍵快速選擇的模式\n(左Alt鍵+滾輪可改變範圍模式下的選擇範圍)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "選中的矩形編號\n(0表示不選中)\n可通過空格或回車鍵定位到當前選中的矩形(當正在輸入時,按兩次即可定位)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "刪除選中的矩形\n(或使用Delete鍵)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "當禁用時,攝像頭與模型的相對關係不會變化,但攝像頭的對應屬性不會被本模組修改", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "縮放比例,控制偏移量的大小", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "全部頂點到屏幕距離均小於該值的面不會被渲染", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "綁定時的優先級,值越大優先級越高,並且在右側排序越靠上", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "配置的名稱", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "禁用設置\n全局禁用設置在配置屏幕中", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "已儲存的配置", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "在預覽模式可以調整相機的各個偏移量", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "打開全局配置屏幕\n(需要安裝Cloth Config API)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "複製所有禁用設置", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "貼上剪貼簿中的所有禁用設置到當前配置\n(不會覆蓋同名設置)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "名稱不能為空!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "材質id不能為空!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "儲存為\n(當存在同名設置時,也可使用左側儲存按鈕)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "禁用設置的名稱" 127 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "打開模型視圖界面", 4 | "key.xtracr_realcamera.togglePerspective": "開啟/關閉", 5 | "key.xtracr_realcamera.toggleAdjustMode": "切換調整模式", 6 | "key.xtracr_realcamera.toggleCameraMode": "切換攝像頭模式", 7 | "key.xtracr_realcamera.adjustUP": "向上調整", 8 | "key.xtracr_realcamera.adjustDOWN": "向下調整", 9 | "key.xtracr_realcamera.adjustFRONT": "向前調整", 10 | "key.xtracr_realcamera.adjustBACK": "向後調整", 11 | "key.xtracr_realcamera.adjustLEFT": "向左調整", 12 | "key.xtracr_realcamera.adjustRIGHT": "向右調整", 13 | "modmenu.descriptionTranslation.realcamera": "使第一人稱視角下的攝像頭更加真實。", 14 | 15 | "config.category.xtracr_realcamera.general": "通用", 16 | "config.category.xtracr_realcamera.binding": "綁定模式", 17 | "config.category.xtracr_realcamera.classic": "經典模式", 18 | "config.category.xtracr_realcamera.cameraOffset": "攝像頭偏移", 19 | "config.category.xtracr_realcamera.centerOffset": "旋轉中心偏移", 20 | "config.category.xtracr_realcamera.cameraRotation": "攝像頭旋轉", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "攝像頭相對旋轉中心的偏移值", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "注: 玩家的前方、上方和左方分別為X、Y和Z軸", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "旋轉中心相對頭部的偏移值", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "額外的攝像頭旋轉角度", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "注:俯仰、偏航和翻滾分別是以左方、上方和前方為軸的旋轉", 26 | 27 | "config.option.xtracr_realcamera.enabled": "開啟功能", 28 | "config.option.xtracr_realcamera.isClassic": "經典模式", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "動態十字準心", 30 | "config.option.xtracr_realcamera.renderModel": "渲染玩家模型", 31 | "config.option.xtracr_realcamera.adjustStep": "調整步長", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "調整模式", 33 | "config.option.xtracr_realcamera.scale": "縮放比例", 34 | "config.option.xtracr_realcamera.cameraOffset": "攝像頭%s偏移", 35 | "config.option.xtracr_realcamera.centerOffset": "旋轉中心%s偏移", 36 | "config.option.xtracr_realcamera.pitch": "俯仰角", 37 | "config.option.xtracr_realcamera.yaw": "偏航角", 38 | "config.option.xtracr_realcamera.roll": "翻滾角", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "詳細設置請前往%s界面(需設置快捷鍵來打開它)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "%s界面修飾鍵", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "舊綁定模式", 42 | "config.option.xtracr_realcamera.adjustOffset": "調整攝像頭偏移", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "隱藏綁定失敗消息", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "渲染卡在身上的物體", 45 | "config.option.xtracr_realcamera.rerenderModel": "重新渲染模型", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "潛行時禁用主要功能", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "游泳時禁用主要功能", 48 | "config.option.xtracr_realcamera.swimOutTick": "游泳狀態退出延遲(單位:tick)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "綁定結果保留幀數", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "位移平滑系数", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "旋轉平滑系数", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "在手持...時禁用主要功能", 53 | "config.option.xtracr_realcamera.disableRenderItems": "在手持...時禁用渲染", 54 | "config.tooltip.xtracr_realcamera.isClassic": "經典模式只對攝像頭的位置和旋轉做簡單的修改", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "在開啟狀態下,十字準心會隨著玩家的視線移動,確保命中結果不受影響\n在安裝了Jade、WTHIT或類似模組時建議啟用", 56 | "config.tooltip.xtracr_realcamera.renderModel": "在第一人稱視角下渲染玩家模型", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "每步調整的角度1/100或長度", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "決定當前按下調整鍵時調整哪一個", 59 | "config.tooltip.xtracr_realcamera.scale": "控制偏移量的大小", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "使用舊版本(v0.5)的綁定模式,僅支援原版模型和基於原版模型的動作\n開啟後可在%s界面的'LEGACY_MODE'中設置屬性", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "當前按下調整鍵時是調整攝像頭的偏移還是旋轉", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "箭,蜂刺和其它會卡在玩家身體裡的物體", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "在原本的渲染時機重新渲染模型,以避免一些因提前渲染導致的視覺問題", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "玩家停止游泳狀態後重啟主要功能的時間(1 tick = 0.05秒)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "在綁定失敗的數帧内,沿用上个成功的綁定結果", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "相機位移平滑系数,值越大越平滑", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "相機旋轉平滑系数,值越大越平滑", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "支持三種匹配方式:\n1. 通配符`*`匹配(如`minecraft:*sword`)\n2. 標簽匹配`#`開頭(如`#minecraft:tools`)\n3. 直接物品ID(如`minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: 綁定失敗,請前往%s界面手動設置(鍵位:%s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "模型視圖", 73 | "screen.widget.xtracr_realcamera.modelView_import": "導入", 74 | "screen.widget.xtracr_realcamera.modelView_export": "導出", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "選擇", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "向前向量", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "向上向量", 78 | "screen.widget.xtracr_realcamera.modelView_position": "目標平面", 79 | "screen.widget.xtracr_realcamera.modelView_save": "儲存", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "禁用模式", 81 | "screen.widget.xtracr_realcamera.modelView_all": "全部", 82 | "screen.widget.xtracr_realcamera.modelView_part": "部分", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "選擇模式", 84 | "screen.widget.xtracr_realcamera.modelView_single": "單選", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "多選", 86 | "screen.widget.xtracr_realcamera.modelView_range": "範圍", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "切換滑塊到輸入框", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "切換輸入框到滑塊", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "X偏移量 = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Y偏移量 = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Z偏移量 = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "俯仰角 = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "偏航角 = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "翻滾角 = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "禁用", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "預覽", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "配置", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "複製", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "貼上", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "從剪貼簿導入配置", 101 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "導入失敗:\n%s", 102 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "導入%s成功!", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "導出當前配置到剪貼簿", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "導出成功!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "導出失敗:\n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "左Alt+左鍵可獲取鼠標指針處的UV座標\n下方三組UV座標由上到下依次為向前向量、向上向量和目標平面上綁定點的UV座標\n註:左Alt鍵+滾輪可在模型的不同層間切換,手動輸入UV座標可以更加精確地綁定到目標位置", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "被選中的模型的紋理的id(不是完整的id也可以識別),儲存時可以縮短一部分以便於程序識別\n比如將minecraft:textures/entity/player/slim/alex.png縮短為minecraft:textures/entity/player/", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "被藍框包含的部分會被禁用\n可在左側材質視圖中鼠標左鍵框選,其它鍵取消選擇\n左Alt+左鍵可快速選擇被鼠標指針指向的部分", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "決定左Alt+左鍵快速選擇的模式\n(左Alt鍵+滾輪可改變範圍模式下的選擇範圍)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "選中的矩形編號\n(0表示不選中)\n可通過空格或回車鍵定位到當前選中的矩形(當正在輸入時,按兩次即可定位)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "刪除選中的矩形\n(或使用Delete鍵)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "當禁用時,攝像頭與模型的相對關係不會變化,但攝像頭的對應屬性不會被本模組修改", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "縮放比例,控制偏移量的大小", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "全部頂點到屏幕距離均小於該值的面不會被渲染", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "綁定時的優先級,值越大優先級越高,並且在右側排序越靠上", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "配置的名稱", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "禁用設置\n全局禁用設置在配置屏幕中", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "已儲存的配置", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "在預覽模式可以調整相機的各個偏移量", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "打開全局配置屏幕\n(需要安裝Cloth Config API)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "複製所有禁用設置", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "貼上剪貼簿中的所有禁用設置到當前配置\n(不會覆蓋同名設置)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "名稱不能為空!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "材質id不能為空!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "儲存為\n(當存在同名設置時,也可使用左側儲存按鈕)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "禁用設置的名稱" 127 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "打开模型视图界面", 4 | "key.xtracr_realcamera.togglePerspective": "开启/关闭", 5 | "key.xtracr_realcamera.activeConfigIndexPREV": "切换到上一个配置", 6 | "key.xtracr_realcamera.activeConfigIndexNEXT": "切换到下一个配置", 7 | "key.xtracr_realcamera.activeConfigIndexRESET": "重置配置索引", 8 | "key.xtracr_realcamera.toggleAdjustMode": "切换调整模式", 9 | "key.xtracr_realcamera.toggleCameraMode": "切换摄像头模式", 10 | "key.xtracr_realcamera.adjustUP": "向上调整", 11 | "key.xtracr_realcamera.adjustDOWN": "向下调整", 12 | "key.xtracr_realcamera.adjustFRONT": "向前调整", 13 | "key.xtracr_realcamera.adjustBACK": "向后调整", 14 | "key.xtracr_realcamera.adjustLEFT": "向左调整", 15 | "key.xtracr_realcamera.adjustRIGHT": "向右调整", 16 | "modmenu.descriptionTranslation.realcamera": "使第一人称视角下的摄像头更加真实。", 17 | 18 | "config.category.xtracr_realcamera.general": "通用", 19 | "config.category.xtracr_realcamera.binding": "绑定模式", 20 | "config.category.xtracr_realcamera.classic": "经典模式", 21 | "config.category.xtracr_realcamera.cameraOffset": "摄像头偏移", 22 | "config.category.xtracr_realcamera.centerOffset": "旋转中心偏移", 23 | "config.category.xtracr_realcamera.cameraRotation": "摄像头旋转", 24 | "config.tooltip.xtracr_realcamera.classicOffset": "摄像头相对旋转中心的偏移值", 25 | "config.tooltip.xtracr_realcamera.classicOffset_n": "注:玩家的的前方、上方和左方分别为X、Y和Z轴", 26 | "config.tooltip.xtracr_realcamera.centerOffset": "旋转中心相对头部的偏移值", 27 | "config.tooltip.xtracr_realcamera.cameraRotation": "额外的摄像头旋转角度", 28 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "注:俯仰、偏航和翻滚分别是以左方、上方和前方为轴的旋转", 29 | 30 | "config.option.xtracr_realcamera.enabled": "开启功能", 31 | "config.option.xtracr_realcamera.isClassic": "经典模式", 32 | "config.option.xtracr_realcamera.dynamicCrosshair": "动态十字准心", 33 | "config.option.xtracr_realcamera.renderModel": "渲染玩家模型", 34 | "config.option.xtracr_realcamera.adjustStep": "调整步长", 35 | "config.option.xtracr_realcamera.classicAdjustMode": "调整模式", 36 | "config.option.xtracr_realcamera.scale": "缩放比例", 37 | "config.option.xtracr_realcamera.cameraOffset": "摄像头%s偏移", 38 | "config.option.xtracr_realcamera.centerOffset": "旋转中心%s偏移", 39 | "config.option.xtracr_realcamera.pitch": "俯仰角", 40 | "config.option.xtracr_realcamera.yaw": "偏航角", 41 | "config.option.xtracr_realcamera.roll": "翻滚角", 42 | "config.option.xtracr_realcamera.toModelViewScreen": "详细设置请前往%s界面(需设置快捷键来打开它)", 43 | "config.option.xtracr_realcamera.screenModifierKey": "%s界面修饰键", 44 | "config.option.xtracr_realcamera.legacyBindingMode": "旧绑定模式", 45 | "config.option.xtracr_realcamera.adjustOffset": "调整摄像头偏移", 46 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "隐藏绑定失败消息", 47 | "config.option.xtracr_realcamera.renderStuckObjects": "渲染卡在身上的物体", 48 | "config.option.xtracr_realcamera.rerenderModel": "重新渲染模型", 49 | "config.option.xtracr_realcamera.disableWhenSneaking": "潜行时禁用主要功能", 50 | "config.option.xtracr_realcamera.disableWhenSwimming": "游泳时禁用主要功能", 51 | "config.option.xtracr_realcamera.swimOutTick": "游泳状态退出延迟(单位:tick)", 52 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "绑定结果保留帧数", 53 | "config.option.xtracr_realcamera.displacementSmoothFactor": "位移平滑系数", 54 | "config.option.xtracr_realcamera.rotationSmoothFactor": "旋转平滑系数", 55 | "config.option.xtracr_realcamera.activeConfigIndex": "当前配置索引", 56 | "config.option.xtracr_realcamera.disableMainFeatureItems": "在手持...时禁用主要功能", 57 | "config.option.xtracr_realcamera.disableRenderItems": "在手持...时禁用渲染", 58 | "config.tooltip.xtracr_realcamera.isClassic": "经典模式只对摄像头的位置和旋转做简单的修改", 59 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "在开启状态下,十字准心会随着玩家的视线移动,确保命中结果不受影响\n在安装了Jade、WTHIT或类似模组时建议启用", 60 | "config.tooltip.xtracr_realcamera.renderModel": "在第一人称视角下渲染玩家模型", 61 | "config.tooltip.xtracr_realcamera.adjustStep": "每步调整的角度的1/100或长度", 62 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "决定当前按下调整键时调整哪一个", 63 | "config.tooltip.xtracr_realcamera.scale": "控制偏移量的大小", 64 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "使用旧版本(v0.5)的绑定模式,仅支持原版模型和基于原版模型的动作\n开启后可在%s界面的'LEGACY_MODE'中设置属性", 65 | "config.tooltip.xtracr_realcamera.adjustOffset": "当前按下调整键时是调整摄像头的偏移还是旋转", 66 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "箭,蜂刺和其它会卡在玩家身体里的物体", 67 | "config.tooltip.xtracr_realcamera.rerenderModel": "在原本的渲染时机重新渲染模型,以避免一些因提前渲染导致的视觉问题", 68 | "config.tooltip.xtracr_realcamera.swimOutTick": "玩家停止游泳状态后重启主要功能的时间(1 tick = 0.05s)", 69 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "在绑定失败后的数帧内,沿用上个成功的绑定结果", 70 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "相机位移平滑系数,值越大越平滑", 71 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "相机旋转平滑系数,值越大越平滑", 72 | "config.tooltip.xtracr_realcamera.activeConfigIndex": "当前绑定的配置索引,0表示允许绑定任何有效配置", 73 | "config.tooltip.xtracr_realcamera.disableRenderItems": "支持三种匹配方式:\n1. 通配符`*`匹配(如`minecraft:*sword`)\n2. 标签匹配以`#`开头(如`#minecraft:swords`)\n3. 直接物品ID(如`minecraft:diamond_sword`)", 74 | 75 | "message.xtracr_realcamera.bindingFailed": "[%s]: 绑定失败,请前往%s界面手动设置(键位:%s)", 76 | 77 | "screen.xtracr_realcamera.modelView_title": "模型视图", 78 | "screen.widget.xtracr_realcamera.modelView_import": "导入", 79 | "screen.widget.xtracr_realcamera.modelView_export": "导出", 80 | "screen.widget.xtracr_realcamera.modelView_selecting": "选择", 81 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "向前矢量", 82 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "向上矢量", 83 | "screen.widget.xtracr_realcamera.modelView_position": "目标平面", 84 | "screen.widget.xtracr_realcamera.modelView_save": "保存", 85 | "screen.widget.xtracr_realcamera.modelView_disableMode": "禁用模式", 86 | "screen.widget.xtracr_realcamera.modelView_all": "全部", 87 | "screen.widget.xtracr_realcamera.modelView_part": "部分", 88 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "选择模式", 89 | "screen.widget.xtracr_realcamera.modelView_single": "单选", 90 | "screen.widget.xtracr_realcamera.modelView_multiple": "多选", 91 | "screen.widget.xtracr_realcamera.modelView_range": "范围", 92 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "切换滑块到输入框", 93 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "切换输入框到滑块", 94 | "screen.widget.xtracr_realcamera.modelView_offsetX": "X偏移量 = %s", 95 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Y偏移量 = %s", 96 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Z偏移量 = %s", 97 | "screen.widget.xtracr_realcamera.modelView_pitch": "俯仰角 = %s", 98 | "screen.widget.xtracr_realcamera.modelView_yaw": "偏航角 = %s", 99 | "screen.widget.xtracr_realcamera.modelView_roll": "翻滚角 = %s", 100 | "screen.widget.xtracr_realcamera.modelView_disable": "禁用", 101 | "screen.widget.xtracr_realcamera.modelView_preview": "预览", 102 | "screen.widget.xtracr_realcamera.modelView_configs": "配置", 103 | "screen.widget.xtracr_realcamera.modelView_copy": "复制", 104 | "screen.widget.xtracr_realcamera.modelView_paste": "粘贴", 105 | "screen.tooltip.xtracr_realcamera.modelView_import": "从剪贴板导入配置", 106 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "导入失败:\n%s", 107 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "导入%s成功!", 108 | "screen.tooltip.xtracr_realcamera.modelView_export": "导出当前配置到剪贴板", 109 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "导出成功!", 110 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "导出失败:\n%s", 111 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+左键可获取鼠标指针处的UV坐标\n下方三组UV坐标由上到下依次为向前矢量、向上矢量和目标平面上绑定点的UV坐标\n注:%s键+滚轮可在模型的不同层间切换,手动输入UV坐标可以更加精确地绑定到目标位置", 112 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "被选中的模型的纹理的id(不是完整的id也可以识别),保存时可以缩短一部分以便于程序识别\n比如将minecraft:textures/entity/player/slim/alex.png缩短为minecraft:textures/entity/player/", 113 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "被蓝框包含的部分会被禁用\n可在左侧材质视图中鼠标左键框选,其它键取消选择\n%s+左键可快速选择被鼠标指针指向的部分", 114 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "决定%s+左键快速选择的模式\n(%s键+滚轮可改变范围模式下的选择范围)", 115 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "选中的矩形编号\n(0表示不选中)\n可通过空格或回车键定位到当前选中的矩形(当正在输入时,按两次即可定位)", 116 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "删除选中的矩形\n(或使用Delete键)", 117 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "当禁用时,摄像头与模型的相对关系不会变化,但摄像头的对应属性不会被本模组修改", 118 | "screen.tooltip.xtracr_realcamera.modelView_scale": "缩放比例,控制偏移量的大小", 119 | "screen.tooltip.xtracr_realcamera.modelView_depth": "全部顶点到屏幕距离均小于该值的面不会被渲染", 120 | "screen.tooltip.xtracr_realcamera.modelView_priority": "绑定时的优先级,值越大优先级越高,并且在右侧排序越靠上", 121 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "配置的名称", 122 | "screen.tooltip.xtracr_realcamera.modelView_disable": "禁用设置\n全局禁用设置在配置屏幕中", 123 | "screen.tooltip.xtracr_realcamera.modelView_configs": "已保存的配置", 124 | "screen.tooltip.xtracr_realcamera.modelView_preview": "在预览模式可以调整相机的各个偏移量", 125 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "打开全局配置屏幕\n(需要安装Cloth Config API)", 126 | "screen.tooltip.xtracr_realcamera.modelView_copy": "复制所有禁用设置", 127 | "screen.tooltip.xtracr_realcamera.modelView_paste": "粘贴剪贴板中的所有禁用设置到当前配置\n(不会覆盖同名设置)", 128 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "名称不能为空!", 129 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "材质id不能为空!", 130 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "保存为\n(当存在同名设置时,也可使用左侧保存按钮)", 131 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "禁用设置的名称" 132 | } -------------------------------------------------------------------------------- /common/src/main/java/com/xtracr/realcamera/RealCameraCore.java: -------------------------------------------------------------------------------- 1 | package com.xtracr.realcamera; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.blaze3d.vertex.VertexConsumer; 5 | import com.xtracr.realcamera.api.BindResult; 6 | import com.xtracr.realcamera.api.RealCameraAPI; 7 | import com.xtracr.realcamera.compat.DisableHelper; 8 | import com.xtracr.realcamera.config.BindTarget; 9 | import com.xtracr.realcamera.config.BindTarget.DisableConfig; 10 | import com.xtracr.realcamera.config.ConfigFile; 11 | import com.xtracr.realcamera.util.*; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.renderer.MultiBufferSource; 14 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 15 | import net.minecraft.util.Mth; 16 | import net.minecraft.world.entity.Entity; 17 | import net.minecraft.world.phys.Vec3; 18 | import org.joml.Matrix4f; 19 | 20 | public class RealCameraCore { 21 | private static BindResult lastResult = BindResult.EMPTY, newResult = BindResult.EMPTY; 22 | private static Vec3 cameraPos = Vec3.ZERO, eulerAngle = Vec3.ZERO; 23 | private static boolean active = false, rendering = false; 24 | private static int failureFrames = 0; 25 | 26 | public static boolean isActive() { 27 | return active; 28 | } 29 | 30 | public static boolean isRendering() { 31 | return isActive() && rendering; 32 | } 33 | 34 | public static BindTarget currentTarget() { 35 | return lastResult.target; 36 | } 37 | 38 | public static void initialize(Minecraft client) { 39 | Entity entity = client.getCameraEntity(); 40 | active = ConfigFile.config().enabled() && client.options.getCameraType().isFirstPerson() && entity != null && !DisableHelper.MAIN_FEATURE.disabled(entity); 41 | rendering = ConfigFile.config().renderModel() && !DisableHelper.RENDER_MODEL.disabled(entity); 42 | } 43 | 44 | public static void reset() { 45 | cameraPos = eulerAngle = Vec3.ZERO; 46 | failureFrames = 0; 47 | } 48 | 49 | public static float getPitch(float f) { 50 | if (currentTarget().bindConfig().bindRotation()) return (float) eulerAngle.x(); 51 | return f; 52 | } 53 | 54 | public static float getYaw(float f) { 55 | if (currentTarget().bindConfig().bindRotation()) return (float) -eulerAngle.y(); 56 | return f; 57 | } 58 | 59 | public static float getRoll(float f) { 60 | if (ConfigFile.config().isClassic()) return f + ConfigFile.config().getClassicRoll(); 61 | if (currentTarget().bindConfig().bindRotation()) return (float) eulerAngle.z(); 62 | return f; 63 | } 64 | 65 | public static Vec3 getRawPos(Vec3 cameraPos, Vec3 entityPos) { 66 | Vec3 rawPos = SmoothUtil.smoothPosition(lastResult.getPosition()).add(entityPos); 67 | BindTarget.BindConfig bindConfig = currentTarget().bindConfig(); 68 | return new Vec3(bindConfig.bindX() ? rawPos.x() : cameraPos.x(), bindConfig.bindY() ? rawPos.y() : cameraPos.y(), bindConfig.bindZ() ? rawPos.z() : cameraPos.z()); 69 | } 70 | 71 | public static Vec3 getCameraPos(Vec3 vec) { 72 | BindTarget.BindConfig bindConfig = currentTarget().bindConfig(); 73 | return new Vec3(bindConfig.bindX() ? cameraPos.x() : vec.x(), bindConfig.bindY() ? cameraPos.y() : vec.y(), bindConfig.bindZ() ? cameraPos.z() : vec.z()); 74 | } 75 | 76 | public static void setCameraPos(Vec3 vec) { 77 | cameraPos = vec; 78 | } 79 | 80 | public static void computeCamera(Minecraft client, float deltaTick) { 81 | Entity entity = client.getCameraEntity(); 82 | boolean invisible = entity.isInvisible(); 83 | entity.setInvisible(false); 84 | newResult = RealCameraAPI.computeBindResult(client, deltaTick); 85 | if (!newResult.available()) { 86 | EntityRenderDispatcher dispatcher = client.getEntityRenderDispatcher(); 87 | MultiVertexCatcher catcher = MultiVertexCatcher.defaultImpl(); 88 | dispatcher.render(entity, 0, 0, 0, Mth.lerp(deltaTick, entity.yRotO, entity.getYRot()), deltaTick, new PoseStack(), catcher, dispatcher.getPackedLightCoords(entity, deltaTick)); 89 | catcher.endCatching(RealCameraCore::computeBindResult); 90 | } 91 | entity.setInvisible(invisible); 92 | if (newResult.available()) { 93 | failureFrames = 0; 94 | lastResult = newResult.computeCamera(); 95 | } else { 96 | failureFrames++; 97 | Entity player = client.player; 98 | int retentionFrames = ConfigFile.config().getBindResultRetentionFrames(); 99 | if (!ConfigFile.config().hideBindingFailureMessage() && failureFrames == retentionFrames + 1 && player != null) { 100 | player.sendSystemMessage(LocUtil.MESSAGE("bindingFailed", LocUtil.MOD_NAME(), LocUtil.MODEL_VIEW_TITLE(), KeyMappings.MODEL_VIEW_SCREEN.getTranslatedKeyMessage())); 101 | } 102 | if (!lastResult.available() || failureFrames > retentionFrames) { 103 | lastResult = BindResult.EMPTY; 104 | active = false; 105 | return; 106 | } 107 | } 108 | eulerAngle = MathUtil.getEulerAngleYXZ(SmoothUtil.smoothRotation(lastResult.getRotation())).scale(Math.toDegrees(1)); 109 | } 110 | 111 | public static void renderCameraEntity(Minecraft client, float deltaTick, MultiBufferSource bufferSource, Matrix4f modelView) { 112 | Vec3 targetEulerAngle = MathUtil.getEulerAngleYXZ(lastResult.getRotation()); 113 | Matrix4f invertedCameraPose = new Matrix4f() 114 | .rotateZ((float) targetEulerAngle.z()) 115 | .rotateX((float) targetEulerAngle.x()) 116 | .rotateY((float) (Math.PI - targetEulerAngle.y())) 117 | .transpose() 118 | .invert() 119 | .translate(Vec3.ZERO.subtract(lastResult.getPosition()).toVector3f()); 120 | PoseStack poseStack = new PoseStack(); 121 | poseStack.mulPose(new Matrix4f(invertedCameraPose).mulLocal(modelView.invert(new Matrix4f()))); 122 | Entity entity = client.getCameraEntity(); 123 | EntityRenderDispatcher dispatcher = client.getEntityRenderDispatcher(); 124 | MultiVertexCatcher catcher = MultiVertexCatcher.defaultImpl(); 125 | dispatcher.render(entity, 0, 0, 0, Mth.lerp(deltaTick, entity.yRotO, entity.getYRot()), deltaTick, poseStack, catcher, dispatcher.getPackedLightCoords(entity, deltaTick)); 126 | final float m02 = modelView.m02(), m12 = modelView.m12(), m22 = modelView.m22(), m32 = modelView.m32(); 127 | final float depth = currentTarget().disablingDepth(); 128 | catcher.endCatching(builtBuffer -> { 129 | DisableConfig[] disableConfigs = currentTarget().filteredDisableConfigs(config -> builtBuffer.textureId().contains(config.textureId())); 130 | for (DisableConfig config : disableConfigs) { 131 | if (config.disableAll()) return; 132 | } 133 | VertexConsumer buffer = bufferSource.getBuffer(builtBuffer.renderType()); 134 | if (!builtBuffer.renderType().canConsolidateConsecutiveGeometry()) { 135 | for (VertexData vertex : builtBuffer.vertexBuffer()) vertex.render(buffer); 136 | return; 137 | } 138 | builtBuffer.vertexBuffer().primitiveStream().forEach(primitive -> { 139 | primitiveFor: 140 | for (VertexData vertex : primitive) { 141 | if (Math.fma(m02, vertex.x(), Math.fma(m12, vertex.y(), Math.fma(m22, vertex.z(), m32))) > -depth) continue; 142 | for (DisableConfig config : disableConfigs) { 143 | if (config.disable(vertex)) continue primitiveFor; 144 | } 145 | for (VertexData vertexData : primitive) vertexData.render(buffer); 146 | break; 147 | } 148 | }); 149 | }); 150 | } 151 | 152 | private static void computeBindResult(BuiltIterableBuffer builtBuffer) { 153 | if (newResult.available()) return; 154 | targetFor: 155 | for (BindTarget target : ConfigFile.config().getBindTargetList(builtBuffer.textureId())) { 156 | BindResult result = new BindResult(target, false); 157 | BindTarget.TargetConfig config = target.targetConfig(); 158 | VertexData.UV[] uvs = {new VertexData.UV(config.posU(), config.posV()), new VertexData.UV(config.forwardU(), config.forwardV()), new VertexData.UV(config.upwardU(), config.upwardV())}; 159 | VertexData[][] primitives = builtBuffer.findPrimitivesInCache(uvs); 160 | if (builtBuffer.anyNotCached(uvs)) { 161 | for (int i = 0; i < primitives.length; i++) { 162 | if (primitives[i] != null) uvs[i] = null; 163 | } 164 | VertexData[][] newPrimitives = builtBuffer.findPrimitives(uvs); 165 | for (int i = 0; i < primitives.length; i++) { 166 | if (newPrimitives[i] == null && primitives[i] == null) continue targetFor; 167 | else if (newPrimitives[i] != null) primitives[i] = newPrimitives[i]; 168 | } 169 | } 170 | if (primitives[0] != null) result.setPosition(VertexData.position(primitives[0], config.posU(), config.posV())); 171 | if (primitives[1] != null) result.setForward(VertexData.normal(primitives[1])); 172 | if (primitives[2] != null) result.setUpward(VertexData.normal(primitives[2])); 173 | if (!result.available()) continue; 174 | newResult = result; 175 | return; 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "モデルビュー画面を開く", 4 | "key.xtracr_realcamera.togglePerspective": "オン/オフ", 5 | "key.xtracr_realcamera.toggleAdjustMode": "調整モードを切り替え", 6 | "key.xtracr_realcamera.toggleCameraMode": "カメラモードを切り替え", 7 | "key.xtracr_realcamera.adjustUP": "上に調整", 8 | "key.xtracr_realcamera.adjustDOWN": "下に調整", 9 | "key.xtracr_realcamera.adjustFRONT": "前に調整", 10 | "key.xtracr_realcamera.adjustBACK": "後ろに調整", 11 | "key.xtracr_realcamera.adjustLEFT": "左に調整", 12 | "key.xtracr_realcamera.adjustRIGHT": "右に調整", 13 | "modmenu.descriptionTranslation.realcamera": "一人称視点のカメラをよりリアルにします。", 14 | 15 | "config.category.xtracr_realcamera.general": "一般", 16 | "config.category.xtracr_realcamera.binding": "バインディングモード", 17 | "config.category.xtracr_realcamera.classic": "クラシックモード", 18 | "config.category.xtracr_realcamera.cameraOffset": "カメラオフセット", 19 | "config.category.xtracr_realcamera.centerOffset": "回転中心オフセット", 20 | "config.category.xtracr_realcamera.cameraRotation": "カメラ回転", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "カメラの回転中心に対するオフセット値", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "注: プレイヤーの前方、上方、左方がそれぞれX、Y、Z軸", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "回転中心の頭部に対するオフセット値", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "追加のカメラ回転角度", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "注: ピッチ、ヨー、ロールはそれぞれ左方、上方、前方を軸とした回転", 26 | 27 | "config.option.xtracr_realcamera.enabled": "機能を有効にする", 28 | "config.option.xtracr_realcamera.isClassic": "クラシックモード", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "動的クロスヘア", 30 | "config.option.xtracr_realcamera.renderModel": "プレイヤーモデルをレンダリング", 31 | "config.option.xtracr_realcamera.adjustStep": "調整ステップ", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "調整モード", 33 | "config.option.xtracr_realcamera.scale": "スケール", 34 | "config.option.xtracr_realcamera.cameraOffset": "カメラ%sオフセット", 35 | "config.option.xtracr_realcamera.centerOffset": "回転中心%sオフセット", 36 | "config.option.xtracr_realcamera.pitch": "ピッチ角", 37 | "config.option.xtracr_realcamera.yaw": "ヨー角", 38 | "config.option.xtracr_realcamera.roll": "ロール角", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "詳細設定は%s画面で行ってください(開くにはホットキーを設定する必要があります)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "%s画面修飾キー", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "旧バインディングモード", 42 | "config.option.xtracr_realcamera.adjustOffset": "カメラオフセットを調整", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "バインディング失敗メッセージを非表示", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "体に刺さったオブジェクトをレンダリング", 45 | "config.option.xtracr_realcamera.rerenderModel": "モデルを再レンダリング", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "スニーク時にメイン機能を無効化", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "泳いでいる時にメイン機能を無効化", 48 | "config.option.xtracr_realcamera.swimOutTick": "泳ぎ状態退出遅延(単位:TICK)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "バインディング結果保持フレーム数", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "変位平滑化係数", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "回転平滑化係数", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "...を持っている時にメイン機能を無効化", 53 | "config.option.xtracr_realcamera.disableRenderItems": "...を持っている時にレンダリングを無効化", 54 | "config.tooltip.xtracr_realcamera.isClassic": "クラシックモードではカメラの位置と回転に簡単な変更のみを行います", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "有効時、クロスヘアはプレイヤーの視線に合わせて移動し、命中結果に影響を与えません\nJade、WTHITなどの類似MODがインストールされている場合に推奨", 56 | "config.tooltip.xtracr_realcamera.renderModel": "一人称視点でプレイヤーモデルをレンダリング", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "各調整ステップの角度の1/100または長さ", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "現在の調整キーで何を調整するかを決定", 59 | "config.tooltip.xtracr_realcamera.scale": "オフセット量のサイズを制御", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "旧バージョン(v0.5)のバインディングモードを使用します。バニラモデルとバニラモデルベースのアクションのみサポート\n有効にすると、%sインターフェースの'LEGACY_MODE'でプロパティを設定できます", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "現在の調整キーでカメラのオフセットまたは回転を調整", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "矢、蜂の針などプレイヤーの体に刺さるオブジェクト", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "元のレンダリングタイミングでモデルを再レンダリングし、早期レンダリングによる視覚的問題を回避", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "プレイヤーが泳ぎ状態を停止した後、主機能を再開するまでの時間(1 tick = 0.05秒)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "バインディング失敗後の数フレーム間、前回成功したバインディング結果を継続使用", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "カメラ変位平滑化係数、値が大きいほどスムーズ", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "カメラ回転平滑化係数、値が大きいほどスムーズ", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "3つのマッチング方法をサポート:\n1. ワイルドカード`*`マッチング(例: `minecraft:*sword`)\n2. タグマッチング(`#`で始まる、例: `#minecraft:swords`)\n3. 直接アイテムID(例: `minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: バインディングに失敗しました。%s画面で手動設定してください(キー: %s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "モデルビュー", 73 | "screen.widget.xtracr_realcamera.modelView_import": "インポート", 74 | "screen.widget.xtracr_realcamera.modelView_export": "エクスポート", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "選択", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "前方ベクトル", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "上方ベクトル", 78 | "screen.widget.xtracr_realcamera.modelView_position": "ターゲット平面", 79 | "screen.widget.xtracr_realcamera.modelView_save": "保存", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "無効モード", 81 | "screen.widget.xtracr_realcamera.modelView_all": "全て", 82 | "screen.widget.xtracr_realcamera.modelView_part": "一部", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "選択モード", 84 | "screen.widget.xtracr_realcamera.modelView_single": "単一選択", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "複数選択", 86 | "screen.widget.xtracr_realcamera.modelView_range": "範囲", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "スライダーから入力欄へ切り替え", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "入力欄からスライダーへ切り替え", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "Xオフセット = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Yオフセット = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Zオフセット = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "ピッチ = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "ヨー = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "ロール = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "無効", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "プレビュー", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "設定", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "コピー", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "貼り付け", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "クリップボードから設定をインポート", 101 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "%sのインポートに成功しました!", 102 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "インポートに失敗しました: \n%s", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "現在の設定をクリップボードにエクスポート", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "エクスポートに成功しました!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "エクスポートに失敗しました: \n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+左クリックでマウスポインタ位置のUV座標を取得\n下の3組のUV座標は上から順に前方ベクトル、上方ベクトル、ターゲット平面上のバインディング点のUV座標\n注: %s+スクロールでモデルの異なるレイヤー間を切り替えられます。手動でUV座標を入力するとターゲット位置に正確にバインドできます", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "選択されたモデルのテクスチャID(完全なIDでなくても認識可能)、保存時に一部を短縮してプログラム認識を容易にできます\n例: minecraft:textures/entity/player/slim/alex.pngをminecraft:textures/entity/player/に短縮", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "青い枠で囲まれた部分は無効になります\n左側のマテリアルビューでマウスの左ボタンで選択、他のボタンで選択解除\n%s+左クリックでマウスポインタが指す部分を素早く選択できます", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "%s+左クリッククイック選択のモードを決定\n(%s+スクロールで範囲モードの選択範囲を変更)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "選択された矩形の番号\n(0は未選択)\nスペースまたはエンターで現在選択されている矩形を位置特定(入力中は2回押すと位置特定)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "選択された矩形を削除\n(またはDeleteキーを使用)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "無効時、カメラとモデルの相対的な位置関係は変化しませんが、カメラの対応する属性はこのMODによって変更されません", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "スケール、オフセットのサイズを制御", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "全ての頂点から画面までの距離がこの値より小さい面はレンダリングされません", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "バインディング時の優先度、値が大きいほど優先度が高く右側のソートで上位に表示", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "設定名", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "無効設定\nグローバル無効設定は設定画面にあります", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "保存済み設定", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "プレビューモードでカメラの様々なオフセットを調整できます", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "グローバル設定画面を開く\n(Cloth Config APIが必要)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "全ての無効設定をコピー", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "クリップボードの全ての無効設定を現在の設定に貼り付け\n(同名設定は上書きされません)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "名前は空にできません!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "テクスチャIDは空にできません!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "名前を付けて保存\n(同名設定がある場合も左の保存ボタンを使用できます)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "無効設定の名前" 127 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/ko_kr.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "모델 뷰 화면 열기", 4 | "key.xtracr_realcamera.togglePerspective": "활성화/비활성화", 5 | "key.xtracr_realcamera.toggleAdjustMode": "조정 모드 전환", 6 | "key.xtracr_realcamera.toggleCameraMode": "카메라 모드 전환", 7 | "key.xtracr_realcamera.adjustUP": "위로 조정", 8 | "key.xtracr_realcamera.adjustDOWN": "아래로 조정", 9 | "key.xtracr_realcamera.adjustFRONT": "앞으로 조정", 10 | "key.xtracr_realcamera.adjustBACK": "뒤로 조정", 11 | "key.xtracr_realcamera.adjustLEFT": "왼쪽으로 조정", 12 | "key.xtracr_realcamera.adjustRIGHT": "오른쪽으로 조정", 13 | "modmenu.descriptionTranslation.realcamera": "1인칭 시점에서 카메라를 더 현실적으로 만듭니다", 14 | 15 | "config.category.xtracr_realcamera.general": "일반", 16 | "config.category.xtracr_realcamera.binding": "바인딩 모드", 17 | "config.category.xtracr_realcamera.classic": "클래식 모드", 18 | "config.category.xtracr_realcamera.cameraOffset": "카메라 오프셋", 19 | "config.category.xtracr_realcamera.centerOffset": "회전 중심 오프셋", 20 | "config.category.xtracr_realcamera.cameraRotation": "카메라 회전", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "회전 중심으로부터의 카메라 오프셋", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "참고: 플레이어의 전방, 상단, 좌측이 각각 X, Y, Z 축입니다", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "머리로부터의 회전 중심 오프셋", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "추가 카메라 회전 각도", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "참고: 피치, 요, 롤은 각각 좌측, 상단, 전방 축을 기준으로 한 회전입니다", 26 | 27 | "config.option.xtracr_realcamera.enabled": "모드 활성화", 28 | "config.option.xtracr_realcamera.isClassic": "클래식 모드", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "동적 크로스헤어", 30 | "config.option.xtracr_realcamera.renderModel": "플레이어 모델 렌더링", 31 | "config.option.xtracr_realcamera.adjustStep": "조정 단계", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "조정 모드", 33 | "config.option.xtracr_realcamera.scale": "크기 조절", 34 | "config.option.xtracr_realcamera.cameraOffset": "카메라 %s 오프셋", 35 | "config.option.xtracr_realcamera.centerOffset": "회전 중심 %s 오프셋", 36 | "config.option.xtracr_realcamera.pitch": "피치", 37 | "config.option.xtracr_realcamera.yaw": "요", 38 | "config.option.xtracr_realcamera.roll": "롤", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "자세한 설정은 %s 화면에서 확인하세요 (열기 위한 키 설정 필요)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "%s 화면 수정 키", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "레거시 바인딩 모드", 42 | "config.option.xtracr_realcamera.adjustOffset": "카메라 오프셋 조정", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "바인딩 실패 메시지 숨기기", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "박힌 물체 렌더링", 45 | "config.option.xtracr_realcamera.rerenderModel": "모델 재렌더링", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "숨어 있을 때 주요 기능 비활성화", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "수영할 때 주요 기능 비활성화", 48 | "config.option.xtracr_realcamera.swimOutTick": "수영 상태 종료 지연 (틱 단위)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "바인딩 결과 유지 프레임", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "변위 평활화 계수", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "회전 평활화 계수", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "...을(를) 들고 있을 때 주요 기능 비활성화", 53 | "config.option.xtracr_realcamera.disableRenderItems": "...을(를) 들고 있을 때 렌더링 비활성화", 54 | "config.tooltip.xtracr_realcamera.isClassic": "클래식 모드는 카메라의 위치와 회전에 간단한 변경만 적용합니다", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "활성화 시 크로스헤어가 플레이어의 시선을 따라가며 일관된 명중 결과를 보장합니다\nJade, WTHIT 또는 유사 모드 설치 시 활성화 권장", 56 | "config.tooltip.xtracr_realcamera.renderModel": "1인칭 시점에서 플레이어 모델 렌더링", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "단계별 조정 길이 또는 각도의 1/100", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "조정 키 누를 때 조정할 대상을 결정합니다", 59 | "config.tooltip.xtracr_realcamera.scale": "오프셋 크기를 제어합니다", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "이전 버전(v0.5)의 바인딩 모드 사용, 기본 모델 및 기본 모델 기반 작업만 지원합니다\n활성화 시 %s 화면의 'LEGACY_MODE'에서 속성 설정 가능", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "조정 키 누를 때 카메라 오프셋 또는 회전 조정", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "화살, 벌 침 등 플레이어 몸에 박힐 수 있는 물체들", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "원본 렌더링 시간에 모델을 다시 렌더링하여 조기 렌더링으로 인한 시각적 문제 방지", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "수영 상태 종료 후 주요 기능 재활성화까지의 시간 (1 틱 = 0.05초)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "바인딩 실패 후 몇 프레임 동안 이전 성공한 바인딩 결과 사용 유지", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "카메라 변위 평활화 계수, 값이 클수록 이동이 부드러워집니다", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "카메라 회전 평활화 계수, 값이 클수록 이동이 부드러워집니다", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "세 가지 매칭 방식 지원:\n1. 와일드카드 `*` 매칭 (예: `minecraft:*sword`)\n2. 태그 매칭 (`#`으로 시작, 예: `#minecraft:swords`)\n3. 직접 아이템 ID (예: `minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: 바인딩 실패, %s 화면에서 수동으로 설정하세요 (키: %s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "모델 뷰", 73 | "screen.widget.xtracr_realcamera.modelView_import": "가져오기", 74 | "screen.widget.xtracr_realcamera.modelView_export": "내보내기", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "선택", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "전방 벡터", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "상향 벡터", 78 | "screen.widget.xtracr_realcamera.modelView_position": "대상 평면", 79 | "screen.widget.xtracr_realcamera.modelView_save": "저장", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "비활성화 모드", 81 | "screen.widget.xtracr_realcamera.modelView_all": "전체", 82 | "screen.widget.xtracr_realcamera.modelView_part": "부분", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "선택 모드", 84 | "screen.widget.xtracr_realcamera.modelView_single": "단일", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "다중", 86 | "screen.widget.xtracr_realcamera.modelView_range": "범위", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "슬라이더에서 필드로 전환", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "필드에서 슬라이더로 전환", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "X 오프셋 = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Y 오프셋 = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Z 오프셋 = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "피치 = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "요 = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "롤 = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "비활성화", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "미리보기", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "설정", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "복사", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "붙여넣기", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "클립보드에서 설정 가져오기", 101 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "%s 가져오기 성공!", 102 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "가져오기 실패: \n%s", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "현재 설정을 클립보드로 내보내기", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "내보내기 성공!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "내보내기 실패: \n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+왼쪽 클릭으로 마우스 포인터 위치의 UV 좌표 얻기\n아래 세 가지 UV 좌표는 위에서부터 전방 벡터, 상향 벡터, 대상 평면의 바인딩 지점 UV 좌표입니다\n참고: %s+스크롤로 모델의 다른 레이어 간 전환, 수동 UV 좌표 입력으로 대상 위치에 더 정확하게 바인딩 가능", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "선택된 모델의 텍스처 ID (전체 ID일 필요 없음), 저장 시 일부 생략하여 프로그램 인식 용이하게 함\n예: minecraft:textures/entity/player/slim/alex.png → minecraft:textures/entity/player/", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "파란색 상자 안의 부분이 비활성화됩니다\n왼쪽 텍스처 뷰에서 왼쪽 클릭으로 선택, 다른 키로 선택 취소\n%s+왼쪽 클릭으로 마우스 포인터가 가리키는 부분 빠르게 선택", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "%s+왼쪽 클릭 빠른 선택 모드 결정\n(%s+스크롤로 범위 모드에서 선택 범위 변경)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "선택된 사각형 번호\n(0은 미선택)\n스페이스 또는 엔터로 현재 선택된 사각형 위치 확인 (입력 중일 때 두 번 누르면 위치 확인)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "선택된 사각형 삭제\n(또는 Delete 키 사용)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "비활성화 시 카메라와 모델의 상대적 관계는 변하지 않지만, 카메라의 해당 속성은 이 모드에서 수정되지 않습니다", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "크기 조절, 오프셋 크기 제어", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "모든 정점-화면 거리가 이 값보다 작은 면은 렌더링되지 않습니다", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "바인딩 시 우선순위, 값이 클수록 우선순위가 높으며 오른쪽 정렬에서 위쪽에 표시", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "설정 이름", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "비활성화 설정\n전역 비활성화 설정은 설정 화면에 있습니다", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "저장된 설정", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "미리보기 모드에서 카메라의 다양한 오프셋 조정 가능", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "전역 설정 화면 열기\n(Cloth Config API 필요)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "모든 비활성화 설정 복사", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "클립보드의 모든 비활성화 설정을 현재 설정에 붙여넣기\n(동일 이름 설정 덮어쓰지 않음)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "이름은 비울 수 없습니다!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "텍스처 ID는 비울 수 없습니다!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "다른 이름으로 저장\n(동일 이름 설정이 있을 때 왼쪽 저장 버튼도 사용 가능)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "비활성화 설정 이름" 127 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | org.gradle.wrapper.GradleWrapperMain \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/realcamera/lang/be_by.json: -------------------------------------------------------------------------------- 1 | { 2 | "general.xtracr_realcamera.modName": "Real Camera", 3 | "key.xtracr_realcamera.modelViewScreen": "Адкрыць экран прагляду мадэлі", 4 | "key.xtracr_realcamera.togglePerspective": "Уключыць/Выключыць", 5 | "key.xtracr_realcamera.toggleAdjustMode": "Пераключыць рэжым налады", 6 | "key.xtracr_realcamera.toggleCameraMode": "Пераключыць рэжым камеры", 7 | "key.xtracr_realcamera.adjustUP": "Наладзіць УВЕРХ", 8 | "key.xtracr_realcamera.adjustDOWN": "Наладзіць УНІЗ", 9 | "key.xtracr_realcamera.adjustFRONT": "Наладзіць УПЕРАД", 10 | "key.xtracr_realcamera.adjustBACK": "Наладзіць НАЗАД", 11 | "key.xtracr_realcamera.adjustLEFT": "Наладзіць УЛЕВА", 12 | "key.xtracr_realcamera.adjustRIGHT": "Наладзіць УПРАВА", 13 | "modmenu.descriptionTranslation.realcamera": "Зрабіць камеру больш рэалістычнай у рэжыме ад першай асобы.", 14 | 15 | "config.category.xtracr_realcamera.general": "Асноўныя", 16 | "config.category.xtracr_realcamera.binding": "Рэжым прывязкі", 17 | "config.category.xtracr_realcamera.classic": "Класічны рэжым", 18 | "config.category.xtracr_realcamera.cameraOffset": "Зрух камеры", 19 | "config.category.xtracr_realcamera.centerOffset": "Зрух цэнтра кручэння", 20 | "config.category.xtracr_realcamera.cameraRotation": "Паварот камеры", 21 | "config.tooltip.xtracr_realcamera.classicOffset": "Зрух камеры ад цэнтра кручэння", 22 | "config.tooltip.xtracr_realcamera.classicOffset_n": "Заўвага: пярэдняя, верхняя і левая бакі гульца адпавядаюць восям X, Y і Z", 23 | "config.tooltip.xtracr_realcamera.centerOffset": "Зрух цэнтра кручэння ад галавы", 24 | "config.tooltip.xtracr_realcamera.cameraRotation": "Дадатковы паварот камеры ў градусах", 25 | "config.tooltip.xtracr_realcamera.cameraRotation_n": "Заўвага: тангаж, рысканне і крэн - гэта павароты вакол левай, верхняй і пярэдняй восей адпаведна", 26 | 27 | "config.option.xtracr_realcamera.enabled": "Мод уключаны", 28 | "config.option.xtracr_realcamera.isClassic": "Класічны рэжым", 29 | "config.option.xtracr_realcamera.dynamicCrosshair": "Дынамічны прыцэл", 30 | "config.option.xtracr_realcamera.renderModel": "Адлюстроўваць мадэль гульца", 31 | "config.option.xtracr_realcamera.adjustStep": "Крок налады", 32 | "config.option.xtracr_realcamera.classicAdjustMode": "Рэжым налады", 33 | "config.option.xtracr_realcamera.scale": "Маштаб", 34 | "config.option.xtracr_realcamera.cameraOffset": "Зрух камеры %s", 35 | "config.option.xtracr_realcamera.centerOffset": "Зрух цэнтра кручэння %s", 36 | "config.option.xtracr_realcamera.pitch": "Тангаж", 37 | "config.option.xtracr_realcamera.yaw": "Рысканне", 38 | "config.option.xtracr_realcamera.roll": "Крэн", 39 | "config.option.xtracr_realcamera.toModelViewScreen": "Для падрабязных налад перайдзіце на экран %s (неабходна прызначыць клавішу для адкрыцця)", 40 | "config.option.xtracr_realcamera.screenModifierKey": "Клавіша-мадыфікатар для экрана %s", 41 | "config.option.xtracr_realcamera.legacyBindingMode": "Рэжым састарэлай прывязкі", 42 | "config.option.xtracr_realcamera.adjustOffset": "Наладзіць зрух камеры", 43 | "config.option.xtracr_realcamera.hideBindingFailureMessage": "Хаваць паведамленні пра няўдалую прывязку", 44 | "config.option.xtracr_realcamera.renderStuckObjects": "Адлюстроўваць застряўшыя аб'екты", 45 | "config.option.xtracr_realcamera.rerenderModel": "Перамаляваць мадэль", 46 | "config.option.xtracr_realcamera.disableWhenSneaking": "Адключыць асноўную функцыю пры крадзяжы", 47 | "config.option.xtracr_realcamera.disableWhenSwimming": "Адключыць асноўную функцыю пры плаванні", 48 | "config.option.xtracr_realcamera.swimOutTick": "Затрымка выхаду з стану плавання (у тіках)", 49 | "config.option.xtracr_realcamera.bindResultRetentionFrames": "Кадры захавання выніку прывязкі", 50 | "config.option.xtracr_realcamera.displacementSmoothFactor": "Каэфіцыент згладжвання зруху", 51 | "config.option.xtracr_realcamera.rotationSmoothFactor": "Каэфіцыент згладжвання павароту", 52 | "config.option.xtracr_realcamera.disableMainFeatureItems": "Адключыць асноўную функцыю пры трыманні...", 53 | "config.option.xtracr_realcamera.disableRenderItems": "Адключыць адлюстраванне пры трыманні...", 54 | "config.tooltip.xtracr_realcamera.isClassic": "Класічны рэжым уносіць толькі простыя змены ў становішча і паварот камеры", 55 | "config.tooltip.xtracr_realcamera.dynamicCrosshair": "Пры ўключэнні прыцэл будзе сачыць за поглядам гульца, забяспечваючы дакладнае трапленне\nРэкамендуецца ўключыць пры ўсталёўцы Jade, WTHIT ці падобных модаў", 56 | "config.tooltip.xtracr_realcamera.renderModel": "Адлюстроўваць мадэль гульца ў рэжыме ад першай асобы", 57 | "config.tooltip.xtracr_realcamera.adjustStep": "Даўжыня або 1/100 вугла налады за крок", 58 | "config.tooltip.xtracr_realcamera.classicAdjustMode": "Вызначае, што наладжваць пры націсканні клавішы налады", 59 | "config.tooltip.xtracr_realcamera.scale": "Кантраляваць памер зрухаў", 60 | "config.tooltip.xtracr_realcamera.legacyBindingMode": "Выкарыстоўвае рэжым прывязкі састарэлай версіі (v0.5), падтрымліваюцца толькі стандартныя мадэлі і дзеянні на іх аснове\nПасля ўключэння ўласцівасці можна наладзіць у раздзеле 'LEGACY_MODE' экрана %s", 61 | "config.tooltip.xtracr_realcamera.adjustOffset": "Пры націсканні клавішы налады, рэгуляваць зрух або паварот камеры", 62 | "config.tooltip.xtracr_realcamera.renderStuckObjects": "Стрэлы, джалы пчол і іншыя аб'екты, якія могуць застряць у целе гульца", 63 | "config.tooltip.xtracr_realcamera.rerenderModel": "Перамалёўваць мадэль у зыходны час рэндэрынгу, каб пазбегнуць візуальных праблем", 64 | "config.tooltip.xtracr_realcamera.swimOutTick": "Затрымка ўключэння асноўнай функцыі пасля выхаду з стану плавання (1 tick = 0.05s)", 65 | "config.tooltip.xtracr_realcamera.bindResultRetentionFrames": "На працягу некалькіх кадраў пасля няўдалай прывязкі працягваць выкарыстоўваць папярэдні паспяховы вынік прывязкі", 66 | "config.tooltip.xtracr_realcamera.displacementSmoothFactor": "Каэфіцыент згладжвання зруху камеры, чым вышэй значэнне, тым больш плаўным будзе рух", 67 | "config.tooltip.xtracr_realcamera.rotationSmoothFactor": "Каэфіцыент згладжвання павароту камеры, чым вышэй значэнне, тым больш плаўным будзе рух", 68 | "config.tooltip.xtracr_realcamera.disableRenderItems": "Падтрымліваюцца тры метады супадзення:\n1. Сімвал падстаноўкі `*` (напрыклад, `minecraft:*sword`)\n2. Супадзенне па тэгах (пачынаецца з `#`, напрыклад, `#minecraft:swords`)\n3. Прамы ID прадмета (напрыклад, `minecraft:diamond_sword`)", 69 | 70 | "message.xtracr_realcamera.bindingFailed": "[%s]: Памылка прывязкі, калі ласка, перайдзіце на экран %s і наладзьце ўручную (ключ: %s)", 71 | 72 | "screen.xtracr_realcamera.modelView_title": "Прагляд мадэлі", 73 | "screen.widget.xtracr_realcamera.modelView_import": "Імпартаваць", 74 | "screen.widget.xtracr_realcamera.modelView_export": "Экспартаваць", 75 | "screen.widget.xtracr_realcamera.modelView_selecting": "Выбар", 76 | "screen.widget.xtracr_realcamera.modelView_forwardVector": "Вектар напрамку", 77 | "screen.widget.xtracr_realcamera.modelView_upwardVector": "Вектар уверх", 78 | "screen.widget.xtracr_realcamera.modelView_position": "Мэтавая плоскасць", 79 | "screen.widget.xtracr_realcamera.modelView_save": "Захаваць", 80 | "screen.widget.xtracr_realcamera.modelView_disableMode": "Рэжым адключэння", 81 | "screen.widget.xtracr_realcamera.modelView_all": "Усё", 82 | "screen.widget.xtracr_realcamera.modelView_part": "Частка", 83 | "screen.widget.xtracr_realcamera.modelView_selectionMode": "Рэжым выбару", 84 | "screen.widget.xtracr_realcamera.modelView_single": "Адзіночны", 85 | "screen.widget.xtracr_realcamera.modelView_multiple": "Множны", 86 | "screen.widget.xtracr_realcamera.modelView_range": "Дыяпазон", 87 | "screen.widget.xtracr_realcamera.modelView_toggleSliderToField": "Пераключыць паўзунок у поле", 88 | "screen.widget.xtracr_realcamera.modelView_toggleFieldToSlider": "Пераключыць поле ў паўзунок", 89 | "screen.widget.xtracr_realcamera.modelView_offsetX": "Зрух X = %s", 90 | "screen.widget.xtracr_realcamera.modelView_offsetY": "Зрух Y = %s", 91 | "screen.widget.xtracr_realcamera.modelView_offsetZ": "Зрух Z = %s", 92 | "screen.widget.xtracr_realcamera.modelView_pitch": "Тангаж = %s", 93 | "screen.widget.xtracr_realcamera.modelView_yaw": "Рысканне = %s", 94 | "screen.widget.xtracr_realcamera.modelView_roll": "Крэн = %s", 95 | "screen.widget.xtracr_realcamera.modelView_disable": "Адключыць", 96 | "screen.widget.xtracr_realcamera.modelView_preview": "Прадпрагляд", 97 | "screen.widget.xtracr_realcamera.modelView_configs": "Канфігурацыі", 98 | "screen.widget.xtracr_realcamera.modelView_copy": "Капіяваць", 99 | "screen.widget.xtracr_realcamera.modelView_paste": "Уставіць", 100 | "screen.tooltip.xtracr_realcamera.modelView_import": "Імпартаваць канфіг з буфера абмену", 101 | "screen.tooltip.xtracr_realcamera.modelView_importSucceeded": "Імпарт %s паспяховы!", 102 | "screen.tooltip.xtracr_realcamera.modelView_importFailed": "Імпарт не ўдаўся: \n%s", 103 | "screen.tooltip.xtracr_realcamera.modelView_export": "Экспартаваць бягучы канфіг у буфер абмену", 104 | "screen.tooltip.xtracr_realcamera.modelView_exportSucceeded": "Экспарт паспяховы!", 105 | "screen.tooltip.xtracr_realcamera.modelView_exportFailed": "Экспарт не ўдаўся: \n%s", 106 | "screen.tooltip.xtracr_realcamera.modelView_selecting": "%s+Левы клік каб атрымаць UV-каардынаты пад паказальнікам мышы\nТры наборы UV-каардынат ніжэй, зверху ўніз, гэта UV-каардынаты вектара наперад, вектара уверх і пункту прывязкі на мэтавай плоскасці\nЗаўвага: %s+Пракрутка каб пераключацца паміж рознымі слаямі мадэлі. Увод UV-каардынат уручную дазваляе дакладней прывязацца да мэтавай пазіцыі", 107 | "screen.tooltip.xtracr_realcamera.modelView_textureId": "ID тэкстуры выбранай мадэлі (не абавязковы поўны ID), можа быць скарочаны пры захаванні для лягчэйшага распазнання.\nНапрыклад, скараціць minecraft:textures/entity/player/slim/alex.png да minecraft:textures/entity/player/.", 108 | "screen.tooltip.xtracr_realcamera.modelView_disableMode": "Часткі, уключаныя ў сінія рамкі, будуць адключаны\nЛевы клік для выбару, іншыя клавішы для скасавання выбару\n%s+Левы клік каб хутка выбраць частку, на якую наведзены паказальнік мышы", 109 | "screen.tooltip.xtracr_realcamera.modelView_selectionMode": "Вызначае рэжым хуткага выбару %s+Левы клік\n(%s+Пракрутка каб змяніць дыяпазон выбару ў рэжыме дыяпазону)", 110 | "screen.tooltip.xtracr_realcamera.modelView_focusedRectangleNumber": "Нумар абранай прастакутнай вобласці\n(0 азначае адсутнасць выбару)\nНацісніце Прабел ці Enter каб знайсці бягучую абраную вобласць (Калі друкуеце, націсніце двойчы каб знайсці)", 111 | "screen.tooltip.xtracr_realcamera.modelView_deleteSelectedRectangle": "Выдаліць абраную прастакутную вобласць\n(Ці выкарыстайце клавішу Delete)", 112 | "screen.tooltip.xtracr_realcamera.modelView_bindButtons": "Калі адключана, адноснае становішча камеры да мадэлі не мяняецца, але адпаведныя атрыбуты камеры не будуць зменены гэтым модам", 113 | "screen.tooltip.xtracr_realcamera.modelView_scale": "Маштаб, які кіруе памерам зрухаў", 114 | "screen.tooltip.xtracr_realcamera.modelView_depth": "Плоскасці, адлегласць ад усіх вяршынь да экрана ў якіх меншая за гэта значэнне, не будуць адлюстроўвацца", 115 | "screen.tooltip.xtracr_realcamera.modelView_priority": "Прыярытэт пры прывязцы, чым больш значэнне, тым вышэй прыярытэт і размяшчэнне справей у спісе", 116 | "screen.tooltip.xtracr_realcamera.modelView_targetName": "Назва канфігурацыі", 117 | "screen.tooltip.xtracr_realcamera.modelView_disable": "Налады адключэння\nГлабальныя налады адключэння знаходзяцца на экране канфігурацыі", 118 | "screen.tooltip.xtracr_realcamera.modelView_configs": "Захаваныя канфігурацыі", 119 | "screen.tooltip.xtracr_realcamera.modelView_preview": "У рэжыме прадпрагляду вы можаце наладжваць зрух і паварот камеры", 120 | "screen.tooltip.xtracr_realcamera.modelView_toConfigScreen": "Адкрыць глабальны экран канфігурацыі\n(Патрабуецца Cloth Config API)", 121 | "screen.tooltip.xtracr_realcamera.modelView_copy": "Капіяваць усе налады адключэння", 122 | "screen.tooltip.xtracr_realcamera.modelView_paste": "Уставіць усе налады адключэння з буфера абмену ў бягучы канфіг\n(Не перазапіша існуючыя налады з такой жа назвай)", 123 | "screen.tooltip.xtracr_realcamera.modelView_emptyName": "Назва не можа быць пустой!", 124 | "screen.tooltip.xtracr_realcamera.modelView_emptyTextureId": "ID тэкстуры не можа быць пустым!", 125 | "screen.tooltip.xtracr_realcamera.modelView_saveAs": "Захаваць як\n(Калі існуе канфігурацыя з такой жа назвай, вы таксама можаце націснуць кнопку захавання злева)", 126 | "screen.tooltip.xtracr_realcamera.modelView_disabledName": "Назва налады адключэння" 127 | } --------------------------------------------------------------------------------