Skip to content

Commit a3051df

Browse files
committed
chore: stage 6f differenct sectors ceil and floor height
1 parent f888dc0 commit a3051df

4 files changed

Lines changed: 388 additions & 114 deletions

File tree

src/stages/Stage6f/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import KeyboardControls from '@app/components/Controls';
1111
const Stage5: Component = () => {
1212
const [settings, setSettings] = createSignal<Settings>(defaultSettings);
1313

14-
useCameraControls<Settings>({ settings, setSettings });
14+
useCameraControls<Settings>({ settings, setSettings, withVertical: true });
1515

1616
return (
1717
<section class="flex flex-col gap-4">
@@ -41,14 +41,12 @@ const Stage5: Component = () => {
4141
height={320}
4242
settings={settings}
4343
render={create2dRenderStage1({ scale: 0.5 })} />
44-
<KeyboardControls />
45-
44+
<KeyboardControls withVertical />
4645
<Canvas
4746
width={400}
4847
height={320}
4948
settings={settings}
5049
render={create2dRenderStage6({ scale: 0.5 })} />
51-
<KeyboardControls />
5250
</div>
5351
</div>
5452
</section>

src/stages/Stage6f/projection.ts

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import { Angle, normalizeAngle } from "@app/lib/Angle";
2+
3+
function toAngle(a: Vertex, b: Vertex): Angle {
4+
const dx = a.x - b.x;
5+
const dy = a.y - b.y;
6+
7+
return new Angle((Math.atan2(dy, dx) * 180) / Math.PI);
8+
}
9+
10+
export function toDistance(a: Vertex, b: Vertex): number {
11+
const dx = a.x - b.x;
12+
const dy = a.y - b.y;
13+
14+
return Math.sqrt(dx * dx + dy * dy);
15+
}
16+
17+
function distanceToSeg(linedef: Seg, camera: Camera): number {
18+
const len = toDistance(linedef.start, linedef.end);
19+
const dx = linedef.end.x - linedef.start.x;
20+
const dy = linedef.end.y - linedef.start.y;
21+
22+
return Math.abs(
23+
dy * camera.x - dx * camera.y
24+
+ linedef.end.x * linedef.start.y - linedef.end.y * linedef.start.x
25+
) / len;
26+
}
27+
28+
function angleFromScreenX(screenX: number, camera: Camera): Angle {
29+
const fov = camera.fov.degrees;
30+
const centerX = camera.screen.width / 2;
31+
const angleOffset = ((screenX - centerX) / camera.screen.width) * fov;
32+
33+
return new Angle(angleOffset);
34+
}
35+
36+
function caclulateScaleFactor(
37+
screenX: number,
38+
linedef: Seg,
39+
camera: Camera
40+
): number {
41+
const screenXAngle = angleFromScreenX(screenX, camera);
42+
43+
const wallDir = toAngle(linedef.end, linedef.start);
44+
const wallNormal = new Angle(wallDir.degrees + 90);
45+
46+
const viewAngle = camera.angle.degrees + screenXAngle.degrees;
47+
const skewAngle = new Angle(viewAngle - wallNormal.degrees);
48+
const skewAngleCos = Math.abs(skewAngle.cos);
49+
50+
const screenXAngleCos = Math.abs(screenXAngle.cos);
51+
52+
return skewAngleCos / screenXAngleCos;
53+
}
54+
55+
function toScreenX(angle: number, angles: IntersectionAngles, camera: Camera): number {
56+
const fov = camera.fov.degrees
57+
58+
return ((angle - angles.cameraFrom) / fov) * camera.screen.width;
59+
}
60+
61+
function intersects(aFrom: number, aTo: number, bFrom: number, bTo: number) {
62+
return (aFrom <= bFrom && bFrom <= aTo) || (aFrom <= bTo && bTo <= aTo);
63+
}
64+
65+
function includes(aFrom: number, aTo: number, bFrom: number, bTo: number) {
66+
return (aFrom <= bFrom && bFrom <= aTo) && (aFrom <= bTo && bTo <= aTo);
67+
}
68+
69+
export function projectionToPoints(projection: SegProjection): Vertex[] {
70+
return [
71+
{ x: projection.start.screenX, y: projection.start.bottomY },
72+
{ x: projection.start.screenX, y: projection.start.topY },
73+
{ x: projection.end.screenX, y: projection.end.topY },
74+
{ x: projection.end.screenX, y: projection.end.bottomY },
75+
]
76+
}
77+
78+
interface IntersectionAngles {
79+
linedefFrom: number;
80+
linedefTo: number;
81+
cameraFrom: number;
82+
cameraTo: number;
83+
}
84+
85+
function calculateIntersectionAngles(linedef: Seg, camera: Camera): null | IntersectionAngles {
86+
let linedefFrom = toAngle(linedef.start, camera).degrees;
87+
let linedefTo = toAngle(linedef.end, camera).degrees;
88+
let cameraFrom = normalizeAngle(camera.angle.degrees - camera.fov.degrees / 2);
89+
let cameraTo = normalizeAngle(camera.angle.degrees + camera.fov.degrees / 2);
90+
91+
const clockwiseDiff = normalizeAngle(linedefTo - linedefFrom);
92+
93+
if (clockwiseDiff > 180) {
94+
let tmpDegrees = linedef.start;
95+
linedef.start = linedef.end;
96+
linedef.end = tmpDegrees;
97+
98+
let tmpVertex = linedefTo;
99+
linedefTo = linedefFrom;
100+
linedefFrom = tmpVertex;
101+
}
102+
103+
if (cameraTo < cameraFrom) {
104+
cameraTo += 360;
105+
}
106+
107+
if (linedefFrom > linedefTo) {
108+
linedefTo += 360;
109+
}
110+
111+
if (!includes(cameraFrom, cameraTo, linedefFrom, linedefTo)) {
112+
if (!intersects(linedefFrom, linedefTo, cameraFrom, cameraTo)) {
113+
if (cameraTo < 360 && linedefTo > 360) {
114+
cameraTo += 360;
115+
cameraFrom += 360;
116+
}
117+
if (linedefTo < 360 && cameraTo > 360) {
118+
linedefTo += 360;
119+
linedefFrom += 360;
120+
}
121+
if (
122+
!includes(cameraFrom, cameraTo, linedefFrom, linedefTo) &&
123+
!intersects(linedefFrom, linedefTo, cameraFrom, cameraTo)
124+
) {
125+
return null;
126+
}
127+
}
128+
}
129+
130+
return {
131+
linedefFrom,
132+
linedefTo,
133+
cameraFrom,
134+
cameraTo
135+
};
136+
}
137+
138+
interface VertexProjection {
139+
screenX: number;
140+
topY: number;
141+
bottomY: number;
142+
}
143+
144+
export interface SegProjection {
145+
start: VertexProjection;
146+
end: VertexProjection;
147+
sector: Sector;
148+
distance: number;
149+
}
150+
151+
export function projectSeg(camera: Camera, sector: Sector, linedef: Seg): SegProjection | null {
152+
const angles = calculateIntersectionAngles(linedef, camera);
153+
154+
if (angles === null) {
155+
return null;
156+
}
157+
158+
const distance = distanceToSeg(linedef, camera);
159+
160+
const ceilRelativeHeight = (sector.ceilHeight ?? 0) - camera.z!;
161+
const floorRelativeHeight = camera.z! - (sector.floorHeight ?? 0);
162+
163+
const startScreenX = angles.linedefFrom < angles.cameraFrom
164+
? 0
165+
: toScreenX(angles.linedefFrom, angles, camera);
166+
167+
const endScreenX = angles.linedefTo > angles.cameraTo
168+
? camera.screen.width
169+
: toScreenX(angles.linedefTo, angles, camera);
170+
171+
const startScale = caclulateScaleFactor(startScreenX, linedef, camera);
172+
const endScale = caclulateScaleFactor(endScreenX, linedef, camera);
173+
174+
const horizontalCenter = camera.screen.height / 2;
175+
176+
const startTopY = horizontalCenter - (ceilRelativeHeight * startScale / distance);
177+
const startBottomY = horizontalCenter + (floorRelativeHeight * startScale / distance);
178+
const endTopY = horizontalCenter - (ceilRelativeHeight * endScale / distance);
179+
const endBottomY = horizontalCenter + (floorRelativeHeight * endScale / distance);
180+
181+
return {
182+
start: {
183+
screenX: startScreenX,
184+
topY: startTopY,
185+
bottomY: startBottomY
186+
},
187+
end: {
188+
screenX: endScreenX,
189+
topY: endTopY,
190+
bottomY: endBottomY
191+
},
192+
sector: sector,
193+
distance: distance
194+
};
195+
}
196+
197+
export interface ProjectionScreenX {
198+
start: number;
199+
end: number;
200+
}
201+
202+
export function projectSegX(camera: Camera, linedef: Seg): ProjectionScreenX | null {
203+
const angles = calculateIntersectionAngles(linedef, camera);
204+
205+
if (angles === null) {
206+
return null;
207+
}
208+
209+
const startScreenX = angles.linedefFrom < angles.cameraFrom
210+
? 0
211+
: toScreenX(angles.linedefFrom, angles, camera);
212+
213+
const endScreenX = angles.linedefTo > angles.cameraTo
214+
? camera.screen.width
215+
: toScreenX(angles.linedefTo, angles, camera);
216+
217+
return {
218+
start: startScreenX,
219+
end: endScreenX
220+
};
221+
}
222+
223+
interface ProjectionScreenY {
224+
start: {
225+
top: number;
226+
bottom: number;
227+
}
228+
end: {
229+
top: number;
230+
bottom: number;
231+
}
232+
}
233+
234+
export function projectSegY(camera: Camera, projectionX: ProjectionScreenX, sector: Sector, linedef: Seg): ProjectionScreenY {
235+
const ceilRelativeHeight = (sector.ceilHeight ?? 0) - camera.z!;
236+
const floorRelativeHeight = camera.z! - (sector.floorHeight ?? 0);
237+
238+
const horizontalCenter = camera.screen.height / 2;
239+
240+
const startScale = caclulateScaleFactor(projectionX.start, linedef, camera);
241+
const endScale = caclulateScaleFactor(projectionX.end, linedef, camera);
242+
const distance = distanceToSeg(linedef, camera);
243+
244+
const startTopY = horizontalCenter - (ceilRelativeHeight * startScale / distance);
245+
const startBottomY = horizontalCenter + (floorRelativeHeight * startScale / distance);
246+
const endTopY = horizontalCenter - (ceilRelativeHeight * endScale / distance);
247+
const endBottomY = horizontalCenter + (floorRelativeHeight * endScale / distance);
248+
249+
return {
250+
start: {
251+
top: startTopY,
252+
bottom: startBottomY
253+
},
254+
end: {
255+
top: endTopY,
256+
bottom: endBottomY
257+
}
258+
};
259+
}

0 commit comments

Comments
 (0)