Skip to content

Commit 79d2b59

Browse files
committed
UI 쌈뽕하게 만들었슨.
1 parent a0782ab commit 79d2b59

File tree

1 file changed

+138
-57
lines changed

1 file changed

+138
-57
lines changed

src/index.html

Lines changed: 138 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!DOCTYPE html>
22
<html lang="en-us">
33
<head>
4-
<title>Websockets Doom</title>
4+
<title>Websockets Doom - TERMINAL</title>
55
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
66
<style>
77
@font-face {
@@ -18,63 +18,170 @@
1818

1919
html {
2020
font-family: "VT323", monospace;
21-
font-size: 12px;
21+
font-size: 16px;
22+
height: 100%;
23+
overflow: hidden;
2224
}
2325

2426
body {
25-
background-color: #111;
27+
background-color: #050505;
28+
background-image:
29+
linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
30+
linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(255, 0, 0, 0.02));
31+
background-size: 100% 2px, 3px 100%;
2632
margin: 0;
27-
padding: 2ch;
33+
padding: 0;
2834
display: flex;
2935
justify-content: center;
3036
align-items: center;
3137
min-height: 100vh;
38+
color: #d32f2f;
3239
}
3340

41+
/* 모니터 전체 프레임 */
3442
#container {
35-
max-width: 100vw;
36-
max-height: 95vh;
37-
margin: auto;
43+
position: relative;
44+
width: 95vw;
45+
max-width: 1600px;
46+
height: 90vh; /* 높이를 화면의 90%로 고정 */
47+
aspect-ratio: 4/3;
48+
border: 4px solid #444;
49+
border-radius: 20px;
50+
background: #000;
51+
box-shadow:
52+
0 0 0 4px #1a1a1a,
53+
0 0 50px rgba(220, 20, 60, 0.4),
54+
inset 0 0 100px rgba(0,0,0,0.9);
55+
padding: 30px; /* 패딩을 조금 줄임 */
3856
display: flex;
39-
justify-content: center;
57+
flex-direction: column; /* 세로 정렬 */
58+
justify-content: space-between; /* 위아래 요소 분배 */
59+
align-items: center;
60+
overflow: hidden; /* 밖으로 나가는 것 방지 */
4061
}
4162

42-
.frame {
43-
padding-right: 0;
44-
margin-left: auto;
45-
margin-right: auto;
46-
display: block;
63+
/* 상단 장식용 헤더 */
64+
.monitor-header {
65+
width: 100%;
66+
display: flex;
67+
justify-content: space-between;
68+
padding: 0 10px 10px 10px;
69+
border-bottom: 2px solid #333;
70+
margin-bottom: 10px;
71+
color: #ff3333;
72+
text-shadow: 0 0 5px #ff3333;
73+
font-size: 1.2rem;
74+
letter-spacing: 2px;
75+
flex-shrink: 0; /* 크기 줄어들지 않게 고정 */
76+
}
77+
78+
.blink {
79+
animation: blinker 1s linear infinite;
80+
}
81+
@keyframes blinker {
82+
50% { opacity: 0; }
83+
}
84+
85+
/* 캔버스 래퍼 (화면 안쪽) - 여기가 핵심 수정 */
86+
.screen-wrapper {
87+
position: relative;
88+
width: 100%;
89+
flex: 1; /* 남은 공간을 모두 차지하도록 설정 */
90+
min-height: 0; /* Flex item이 내용물보다 작아질 수 있게 허용 */
91+
overflow: hidden;
92+
border: 2px solid #222;
93+
border-radius: 4px;
94+
box-shadow: inset 0 0 20px rgba(0,0,0,1);
95+
background-color: #000;
4796
}
4897

4998
canvas.frame {
50-
background-color: black;
99+
background-color: #000;
51100
width: 100%;
52-
height: auto;
53-
object-fit: contain;
54-
image-rendering: -moz-crisp-edges;
55-
image-rendering: -webkit-crisp-edges;
101+
height: 100%;
102+
object-fit: contain; /* 비율 유지하며 짤리지 않게 */
56103
image-rendering: pixelated;
57-
image-rendering: crisp-edges;
104+
display: block;
105+
opacity: 0.9;
106+
}
107+
108+
/* CRT 스캔라인 효과 */
109+
.crt-overlay {
110+
position: absolute;
111+
top: 0;
112+
left: 0;
113+
width: 100%;
114+
height: 100%;
115+
pointer-events: none;
116+
background: linear-gradient(
117+
to bottom,
118+
rgba(255,255,255,0),
119+
rgba(255,255,255,0) 50%,
120+
rgba(0,0,0,0.2) 50%,
121+
rgba(0,0,0,0.2)
122+
);
123+
background-size: 100% 4px;
124+
z-index: 10;
125+
box-shadow: inset 0 0 80px rgba(0,0,0,0.7);
126+
}
127+
128+
/* 화면 반사광 효과 */
129+
.crt-glare {
130+
position: absolute;
131+
top: 0;
132+
left: 0;
133+
width: 100%;
134+
height: 100%;
135+
pointer-events: none;
136+
background: linear-gradient(135deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0) 40%);
137+
z-index: 11;
138+
border-radius: 4px;
58139
}
59140

60141
#ascii-output {
61142
display: none;
62143
}
144+
145+
/* 하단 장식 텍스트 */
146+
.monitor-footer {
147+
margin-top: 10px;
148+
width: 100%;
149+
text-align: right;
150+
color: #555;
151+
font-size: 0.9rem;
152+
flex-shrink: 0; /* 크기 줄어들지 않게 고정 */
153+
}
154+
155+
.noselect {
156+
user-select: none;
157+
}
63158
</style>
64159
</head>
65160
<body>
66161
<div id="container" class="noselect">
67-
<!-- Hidden canvas for SDL (required for initialization) -->
68-
<canvas id="sdl-canvas" style="display: none; width: 320px; height: 200px;"></canvas>
69-
<!-- Visible canvas for ASCII rendering -->
70-
<canvas class="frame" id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
162+
<div class="monitor-header">
163+
<span>DOOM_OS // VER 6.6.6</span>
164+
<span><span class="blink"></span> REC</span>
165+
</div>
166+
167+
<div class="screen-wrapper">
168+
<canvas id="sdl-canvas" style="display: none; width: 320px; height: 200px;"></canvas>
169+
170+
<canvas class="frame" id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
171+
172+
<div class="crt-overlay"></div>
173+
<div class="crt-glare"></div>
174+
</div>
175+
71176
<pre id="ascii-output"></pre>
177+
178+
<div class="monitor-footer">
179+
SYSTEM: ONLINE | RENDERER: ASCII_WASM
180+
</div>
72181
</div>
182+
73183
<script>
74184
// 공통적으로 Doom 실행 시에 사용할 인자들
75-
// - doom1.wad: 기본 WAD 파일
76-
// - -window, -nogui, -nomusic 등: 웹 환경에 맞게 최소 옵션으로 실행
77-
// - -force_software_renderer 1: WebGL 대신 소프트웨어 렌더러 사용 강제
78185
var commonArgs = ["-iwad", "doom1.wad", "-window", "-nogui", "-nomusic", "-config", "default.cfg", "-servername", "doomflare", "-force_software_renderer", "1"];
79186

80187
var Module = {
@@ -107,7 +214,6 @@
107214
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(" ");
108215
console.error(text);
109216
},
110-
// SDL이 사용할 실제 캔버스 (숨겨져 있음)
111217
canvas: document.getElementById("sdl-canvas"),
112218
print: function (text) {
113219
console.log(text);
@@ -118,9 +224,6 @@
118224
};
119225

120226
function setupRenderer() {
121-
// WASM에서 export 한 함수들을 JS에서 호출하기 위해 cwrap 사용
122-
// ascii_get_buffer : AsciiCell 버퍼의 포인터를 반환
123-
// ascii_get_buffer_size: AsciiCell 버퍼의 총 바이트 크기 반환
124227
const getBufferPtr = Module.cwrap('ascii_get_buffer', 'number', []);
125228
const getBufferSize = Module.cwrap('ascii_get_buffer_size', 'number', []);
126229

@@ -132,32 +235,26 @@
132235
const canvas = document.getElementById('canvas');
133236
const ctx = canvas.getContext('2d');
134237

135-
// C 쪽에서 정의된 ASCII 해상도 (i_ascii.h 의 ASCII_WIDTH/HEIGHT)
136238
const ASCII_WIDTH = 480;
137239
const ASCII_HEIGHT = 160;
138240

139-
// 사용자 요청에 따라 최종 캔버스 해상도 설정
140241
const TARGET_CANVAS_WIDTH = 1600;
141242
const TARGET_CANVAS_HEIGHT = 1200;
142243

143-
// 각 아스키 문자 셀이 차지할 픽셀 크기 계산
144244
const charWidth = TARGET_CANVAS_WIDTH / ASCII_WIDTH;
145245
const charHeight = TARGET_CANVAS_HEIGHT / ASCII_HEIGHT;
146246

147-
// 폰트 크기는 charHeight에 맞춰 설정 (소수점 폰트 크기는 일부 브라우저에서 렌더링 문제가 있을 수 있으므로 반올림)
148247
const FONT_SIZE = Math.round(charHeight);
149248
const FONT = `${FONT_SIZE}px "Courier New", monospace`;
150249

151-
ctx.font = FONT; // 폰트 적용 (measureText는 이제 사용하지 않음)
250+
ctx.font = FONT;
152251

153252
canvas.width = TARGET_CANVAS_WIDTH;
154253
canvas.height = TARGET_CANVAS_HEIGHT;
155254

156255
console.log(`Canvas initialized: ${canvas.width}x${canvas.height}`);
157256
console.log(`Character cell size: ${charWidth}x${charHeight}`);
158257

159-
// 현재 WASM 메모리 버퍼를 얻는 함수
160-
// Emscripten 설정에 따라 HEAPU8 이나 wasmMemory 등 접근 방법이 달라질 수 있음
161258
function getMemoryBuffer() {
162259
if (typeof HEAPU8 !== 'undefined' && HEAPU8 && HEAPU8.buffer) {
163260
return HEAPU8.buffer;
@@ -171,15 +268,13 @@
171268
return null;
172269
}
173270

174-
// Test if we can access memory
175271
const testBuffer = getMemoryBuffer();
176272
if (!testBuffer) {
177-
console.error("Cannot access WASM memory buffer. Available properties:", Object.keys(Module));
273+
console.error("Cannot access WASM memory buffer.");
178274
return;
179275
}
180276
console.log("Memory buffer access confirmed");
181277

182-
// 렌더링 프레임 수 및 디버깅용 타임스탬프
183278
let frameCount = 0;
184279
let lastLogTime = Date.now();
185280

@@ -195,18 +290,14 @@
195290
return;
196291
}
197292

198-
// Get fresh memory buffer each frame (may have been reallocated)
199293
const heapBuffer = getMemoryBuffer();
200294
if (!heapBuffer) {
201295
console.error("Cannot access memory buffer");
202296
return;
203297
}
204298

205-
// C++에서 만든 AsciiCell 배열(연속된 구조체 메모리)을 JS에서
206-
// 그대로 Uint8Array 로 바라본 뒤, 4바이트 단위로 파싱한다.
207299
const buffer = new Uint8Array(heapBuffer, bufferPtr, bufferSize);
208300

209-
// Debug: Check if buffer has data
210301
if (frameCount === 0 || Date.now() - lastLogTime > 2000) {
211302
const sampleData = Array.from(buffer.slice(0, 20));
212303
console.log("Buffer sample:", sampleData, "Size:", bufferSize, "Expected:", ASCII_WIDTH * ASCII_HEIGHT * 4);
@@ -217,41 +308,31 @@
217308
ctx.fillRect(0, 0, canvas.width, canvas.height);
218309
ctx.font = FONT;
219310

220-
// AsciiCell 구조체 레이아웃:
221-
// char character (1 byte)
222-
// uint8_t r (1 byte)
223-
// uint8_t g (1 byte)
224-
// uint8_t b (1 byte)
225-
// → 총 4 바이트가 한 글자 셀을 표현
226-
// 따라서 전체 버퍼 크기는 ASCII_WIDTH * ASCII_HEIGHT * 4 바이트가 되어야 함
227311
const expectedSize = ASCII_WIDTH * ASCII_HEIGHT * 4;
228312
if (buffer.length < expectedSize) {
229313
if (frameCount % 60 === 0) {
230314
console.warn(`Buffer size mismatch: got ${buffer.length}, expected ${expectedSize}`);
231315
}
232316
}
233317

234-
let i = 0;
318+
let i = 0;
235319
let hasData = false;
236320
let nonZeroCount = 0;
237321

238322
for (let y = 0; y < ASCII_HEIGHT; y++) {
239323
for (let x = 0; x < ASCII_WIDTH; x++) {
240324
if (i + 3 >= buffer.length) break;
241325

242-
// Read AsciiCell: character, r, g, b
243326
const charCode = buffer[i];
244327
const r = buffer[i + 1];
245328
const g = buffer[i + 2];
246329
const b = buffer[i + 3];
247330

248-
// Check if this cell has any data
249331
if (charCode !== 0 || r !== 0 || g !== 0 || b !== 0) {
250332
hasData = true;
251333
nonZeroCount++;
252334
}
253335

254-
// Only render non-empty cells for better performance
255336
if (charCode !== 0 || r !== 0 || g !== 0 || b !== 0) {
256337
const char = charCode > 0 && charCode < 256 ? String.fromCharCode(charCode) : ' ';
257338

@@ -278,7 +359,7 @@
278359
}
279360
}
280361

281-
const renderInterval = setInterval(renderFrame, 16); // ~60 FPS
362+
const renderInterval = setInterval(renderFrame, 16);
282363

283364
callMain(commonArgs);
284365
}
@@ -289,4 +370,4 @@
289370
</script>
290371
<script async type="text/javascript" src="chocolate-doom.js"></script>
291372
</body>
292-
</html>
373+
</html>

0 commit comments

Comments
 (0)