Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b7fd7f320 |
@@ -3,7 +3,7 @@
|
|||||||
마인크래프트 안에서 임의의 동영상 URL을 벽·바닥·천장에 평면으로 재생하는 Fabric 모드.
|
마인크래프트 안에서 임의의 동영상 URL을 벽·바닥·천장에 평면으로 재생하는 Fabric 모드.
|
||||||
|
|
||||||
- 모드 ID: `video_player`
|
- 모드 ID: `video_player`
|
||||||
- 현재 버전: **0.4.6**
|
- 현재 버전: **0.4.7**
|
||||||
- 마인크래프트 버전: **26.1.2**
|
- 마인크래프트 버전: **26.1.2**
|
||||||
- 필요 Java: **25** (마인크래프트 26.x 가 요구함)
|
- 필요 Java: **25** (마인크래프트 26.x 가 요구함)
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ Fabric은 마인크래프트에 모드 기능을 추가해 주는 로더입니
|
|||||||
- 받은 `fabric-api-0.149.0+26.1.2.jar` 를 `mods` 폴더에 넣습니다.
|
- 받은 `fabric-api-0.149.0+26.1.2.jar` 를 `mods` 폴더에 넣습니다.
|
||||||
2. **video_player** (이 모드)
|
2. **video_player** (이 모드)
|
||||||
- 다운로드: https://git.tkrmagid.kr/tkrmagid/mc_video_player_mod/releases
|
- 다운로드: https://git.tkrmagid.kr/tkrmagid/mc_video_player_mod/releases
|
||||||
- `video_player-0.4.6.jar` 를 다운로드해서 같은 `mods` 폴더에 넣습니다.
|
- `video_player-0.4.7.jar` 를 다운로드해서 같은 `mods` 폴더에 넣습니다.
|
||||||
|
|
||||||
이전 버전(`video_player-0.4.0.jar`, `0.4.2.jar`, `0.4.3.jar`, `0.3.x.jar` 등)이 mods 폴더에 남아있다면 **반드시 삭제**하세요. 두 개가 같이 있으면 마인크래프트가 충돌로 켜지지 않습니다.
|
이전 버전(`video_player-0.4.0.jar`, `0.4.2.jar`, `0.4.3.jar`, `0.3.x.jar` 등)이 mods 폴더에 남아있다면 **반드시 삭제**하세요. 두 개가 같이 있으면 마인크래프트가 충돌로 켜지지 않습니다.
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ Fabric은 마인크래프트에 모드 기능을 추가해 주는 로더입니
|
|||||||
게임 안에서 채팅창에 `/videostick` 을 입력하세요. 정상이라면:
|
게임 안에서 채팅창에 `/videostick` 을 입력하세요. 정상이라면:
|
||||||
|
|
||||||
- 인벤토리에 **비디오 스틱** 아이템이 들어옵니다 (보라/검정 missing-texture 가 아니라 작대기 모양 아이콘).
|
- 인벤토리에 **비디오 스틱** 아이템이 들어옵니다 (보라/검정 missing-texture 가 아니라 작대기 모양 아이콘).
|
||||||
- 보라/검정 missing texture 가 나오면 **STEP 4** 에서 이전 버전 jar(`video_player-0.4.0.jar` / `0.4.1.jar` 등)가 mods 폴더에 같이 남아있는 경우입니다. 다 지우고 `0.4.6` 만 남기고 다시 시작하세요. (0.4.1 이하는 Fabric 26.1.2 model 로더가 unprefixed `item/generated` parent 를 거부해서 스틱 아이콘이 missing-model 큐브로 보입니다 — 0.4.2 에서 수정됨.)
|
- 보라/검정 missing texture 가 나오면 **STEP 4** 에서 이전 버전 jar(`video_player-0.4.0.jar` / `0.4.1.jar` 등)가 mods 폴더에 같이 남아있는 경우입니다. 다 지우고 `0.4.7` 만 남기고 다시 시작하세요. (0.4.1 이하는 Fabric 26.1.2 model 로더가 unprefixed `item/generated` parent 를 거부해서 스틱 아이콘이 missing-model 큐브로 보입니다 — 0.4.2 에서 수정됨.)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ Fabric은 마인크래프트에 모드 기능을 추가해 주는 로더입니
|
|||||||
JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64 ./gradlew build
|
JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64 ./gradlew build
|
||||||
```
|
```
|
||||||
|
|
||||||
산출물: `build/libs/video_player-0.4.6.jar`
|
산출물: `build/libs/video_player-0.4.7.jar`
|
||||||
|
|
||||||
JavaCV를 직접 의존성으로 가져오는 경우의 Maven 좌표:
|
JavaCV를 직접 의존성으로 가져오는 경우의 Maven 좌표:
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ org.gradle.configuration-cache=false
|
|||||||
|
|
||||||
# Mod
|
# Mod
|
||||||
mod_id=video_player
|
mod_id=video_player
|
||||||
mod_version=0.4.6
|
mod_version=0.4.7
|
||||||
maven_group=com.ejclaw.videoplayer
|
maven_group=com.ejclaw.videoplayer
|
||||||
archives_base_name=video_player
|
archives_base_name=video_player
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents
|
|||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||||
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
|
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents;
|
||||||
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
|
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
@@ -54,10 +55,15 @@ public class VideoPlayerClient implements ClientModInitializer {
|
|||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
// Pump frame uploads on every render frame (60+Hz) rather than every client tick
|
||||||
VideoPlayback.tick();
|
// (20Hz). At 24fps source, a 20Hz pump can skip a frame whenever a tick window happens
|
||||||
updateDistanceGains(client);
|
// to miss the decode boundary, producing visible micro-stutter even when frames are
|
||||||
});
|
// ready. Polling at render rate lets the texture latch as soon as a frame is decoded.
|
||||||
|
LevelRenderEvents.START_MAIN.register(ctx -> VideoPlayback.tick());
|
||||||
|
|
||||||
|
// Distance-gain math is cheap and only audible state — 20Hz is plenty and avoids
|
||||||
|
// recomputing it on every render frame.
|
||||||
|
ClientTickEvents.END_CLIENT_TICK.register(VideoPlayerClient::updateDistanceGains);
|
||||||
|
|
||||||
ClientTickEvents.END_LEVEL_TICK.register(world -> {
|
ClientTickEvents.END_LEVEL_TICK.register(world -> {
|
||||||
// no-op for now
|
// no-op for now
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.resources.Identifier;
|
import net.minecraft.resources.Identifier;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -196,15 +197,13 @@ public final class VideoPlayback {
|
|||||||
NativeImage img = texture.getPixels();
|
NativeImage img = texture.getPixels();
|
||||||
if (img == null) return;
|
if (img == null) return;
|
||||||
|
|
||||||
int pixels = w * h;
|
// RGBA bytes from the backend already match NativeImage's ABGR-int layout when
|
||||||
for (int i = 0; i < pixels; i++) {
|
// viewed as little-endian bytes: byte 0 = R (low byte of ABGR int), byte 1 = G,
|
||||||
int r = rgba.get() & 0xFF;
|
// byte 2 = B, byte 3 = A. So a flat memcpy works — no per-pixel swap needed.
|
||||||
int g = rgba.get() & 0xFF;
|
// This replaces a 2M-iteration Java loop with one native memcpy for 1080p frames,
|
||||||
int b = rgba.get() & 0xFF;
|
// cutting upload time from ~20ms to <1ms and removing the main stutter source.
|
||||||
int a = rgba.get() & 0xFF;
|
long bytes = (long) w * h * 4L;
|
||||||
int abgr = (a << 24) | (b << 16) | (g << 8) | r;
|
MemoryUtil.memCopy(MemoryUtil.memAddress(rgba), img.getPointer(), bytes);
|
||||||
img.setPixelABGR(i % w, i / w, abgr);
|
|
||||||
}
|
|
||||||
texture.upload();
|
texture.upload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user