📄 cmodel.pas
字号:
frac := 0;
if (frac > 1) then
frac := 1;
midf := p1f + (p2f - p1f) * frac;
for i := 0 to 2 do
mid[i] := p1[i] + frac * (p2[i] - p1[i]);
CM_RecursiveHullCheck(node^.children[side], p1f, midf, p1, mid);
// go past the node
if (frac2 < 0) then
frac2 := 0;
if (frac2 > 1) then
frac2 := 1;
midf := p1f + (p2f - p1f) * frac2;
for i := 0 to 2 do
mid[i] := p1[i] + frac2 * (p2[i] - p1[i]);
CM_RecursiveHullCheck(node^.children[side xor 1], midf, p2f, mid, p2);
end;
//======================================================================
(*
==================
CM_BoxTrace
==================
*)
function CM_BoxTrace(const start, _end, mins, maxs: vec3_t;
headnode, brushmask: Integer): trace_t;
var
i: Integer;
leafs: array[0..1023] of Integer;
numleafs: Integer;
c1, c2: vec3_t;
topnode: Integer;
begin
Inc(checkcount); // for multi-check avoidance
Inc(c_traces); // for statistics, may be zeroed
// fill in a default trace
FillChar(trace_trace, SizeOf(trace_trace), 0);
trace_trace.fraction := 1;
trace_trace.surface := @nullsurface.c;
if (numnodes = 0) then
begin // map not loaded
Result := trace_trace;
exit;
end;
trace_contents := brushmask;
VectorCopy(start, trace_start);
VectorCopy(_end, trace_end);
VectorCopy(mins, trace_mins);
VectorCopy(maxs, trace_maxs);
//
// check for position test special case
//
if (start[0] = _end[0]) and (start[1] = _end[1]) and (start[2] = _end[2]) then
begin
VectorAdd(start, mins, c1);
VectorAdd(start, maxs, c2);
for i := 0 to 2 do
begin
c1[i] := c1[i] - 1;
c2[i] := c2[i] + 1;
end;
numleafs := CM_BoxLeafnums_headnode(c1, c2, @leafs, 1024, headnode, @topnode);
for i := 0 to numleafs - 1 do
begin
CM_TestInLeaf(leafs[i]);
if (trace_trace.allsolid) then
Break;
end;
VectorCopy(start, trace_trace.endpos);
Result := trace_trace;
end;
//
// check for point special case
//
if (mins[0] = 0) and (mins[1] = 0) and (mins[2] = 0) and
(maxs[0] = 0) and (maxs[1] = 0) and (maxs[2] = 0) then
begin
trace_ispoint := True;
VectorClear(trace_extents);
end
else
begin
trace_ispoint := False;
if (-mins[0] > maxs[0]) then
trace_extents[0] := -mins[0]
else
trace_extents[0] := maxs[0];
if (-mins[1] > maxs[1]) then
trace_extents[1] := -mins[1]
else
trace_extents[1] := maxs[1];
if (-mins[2] > maxs[2]) then
trace_extents[2] := -mins[2]
else
trace_extents[2] := maxs[2];
end;
//
// general sweeping through world
//
CM_RecursiveHullCheck(headnode, 0, 1, start, _end);
if (trace_trace.fraction = 1) then
begin
VectorCopy(_end, trace_trace.endpos);
end
else
begin
for i := 0 to 2 do
trace_trace.endpos[i] := start[i] + trace_trace.fraction * (_end[i] - start[i]);
end;
Result := trace_trace;
end;
(*
==================
CM_TransformedBoxTrace
Handles offseting and rotation of the end points for moving and
rotating entities
==================
*)
function CM_TransformedBoxTrace(const start, _end, mins, maxs: vec3_t;
headnode, brushmask: Integer; const origin, angles: vec3_t): trace_t;
var
trace: trace_t;
start_l, end_l: vec3_t;
a: vec3_t;
forward_, right, up: vec3_t;
temp: vec3_t;
rotated: qboolean;
begin
// subtract origin offset
VectorSubtract(start, origin, start_l);
VectorSubtract(_end, origin, end_l);
// rotate start and end into the models frame of reference
if (headnode <> box_headnode) and
((angles[0] <> 0) or (angles[1] <> 0) or (angles[2] <> 0)) then
rotated := True
else
rotated := False;
if (rotated) then
begin
AngleVectors(angles, @forward_, @right, @up);
VectorCopy(start_l, temp);
start_l[0] := DotProduct(temp, forward_);
start_l[1] := -DotProduct(temp, right);
start_l[2] := DotProduct(temp, up);
VectorCopy(end_l, temp);
end_l[0] := DotProduct(temp, forward_);
end_l[1] := -DotProduct(temp, right);
end_l[2] := DotProduct(temp, up);
end;
// sweep the box through the model
trace := CM_BoxTrace(start_l, end_l, mins, maxs, headnode, brushmask);
if rotated and (trace.fraction <> 1.0) then
begin
// FIXME: figure out how to do this with existing angles
VectorNegate(angles, a);
AngleVectors(a, @forward_, @right, @up);
VectorCopy(trace.plane.normal, temp);
trace.plane.normal[0] := DotProduct(temp, forward_);
trace.plane.normal[1] := -DotProduct(temp, right);
trace.plane.normal[2] := DotProduct(temp, up);
end;
trace.endpos[0] := start[0] + trace.fraction * (_end[0] - start[0]);
trace.endpos[1] := start[1] + trace.fraction * (_end[1] - start[1]);
trace.endpos[2] := start[2] + trace.fraction * (_end[2] - start[2]);
Result := trace;
end;
(*
===============================================================================
PVS / PHS
===============================================================================
*)
(*
===================
CM_DecompressVis
===================
*)
procedure CM_DecompressVis(in_, out_: PByte);
var
c: Integer;
out_p: PByte;
row: Integer;
begin
row := (numclusters + 7) shr 3;
out_p := out_;
if (in_ = nil) or (numvisibility = 0) then
begin // no vis info, so make all visible
while (row <> 0) do
begin
out_p^ := $FF;
Inc(out_p);
Dec(row);
end;
Exit;
end;
repeat
if (in_^ <> 0) then
begin
out_p^ := in_^;
Inc(out_p);
Inc(in_);
Continue;
end;
Inc(in_);
c := in_^;
Inc(in_);
if ((Integer(out_p) - Integer(out_)) + c > row) then
begin
c := row - (Integer(out_p) - Integer(out_));
Com_DPrintf('warning: Vis decompression overrun'#10, []);
end;
while (c <> 0) do
begin
out_p^ := 0;
Inc(out_p);
Dec(c);
end;
until (Integer(out_p) - Integer(out_) >= row);
end;
var
pvsrow: array[0..MAX_MAP_LEAFS div 8 - 1] of Byte;
phsrow: array[0..MAX_MAP_LEAFS div 8 - 1] of Byte;
function CM_ClusterPVS(cluster: Integer): PByte;
begin
if (cluster = -1) then
FillChar(pvsrow, (numclusters + 7) shr 3, 0)
else
CM_DecompressVis(@map_visibility[map_vis.bitofs[cluster][DVIS_PVS]], @pvsrow);
Result := @pvsrow;
end;
function CM_ClusterPHS(cluster: Integer): PByte;
begin
if (cluster = -1) then
FillChar(phsrow, (numclusters + 7) shr 3, 0)
else
CM_DecompressVis(@map_visibility[map_vis.bitofs[cluster][DVIS_PHS]], @phsrow);
Result := @phsrow;
end;
(*
===============================================================================
AREAPORTALS
===============================================================================
*)
procedure FloodArea_r(area: carea_p; floodnum: Integer);
var
i: Integer;
p: dareaportal_p;
begin
if (area^.floodvalid = floodvalid) then
begin
if (area^.floodnum = floodnum) then
Exit;
Com_Error(ERR_DROP, 'FloodArea_r: reflooded', []);
end;
area^.floodnum := floodnum;
area^.floodvalid := floodvalid;
p := @map_areaportals[area^.firstareaportal];
for i := 0 to area^.numareaportals - 1 do // i++, p++)
begin
if (portalopen[p^.portalnum]) then
FloodArea_r(@map_areas[p^.otherarea], floodnum);
Inc(p);
end;
end;
(*
====================
FloodAreaConnections
====================
*)
procedure FloodAreaConnections;
var
i: Integer;
area: carea_p;
floodnum: Integer;
begin
// all current floods are now invalid
Inc(floodvalid);
floodnum := 0;
// area 0 is not used
for i := 1 to numareas - 1 do
begin
area := @map_areas[i];
if (area^.floodvalid = floodvalid) then
Continue; // already flooded into
Inc(floodnum);
FloodArea_r(area, floodnum);
end;
end;
procedure CM_SetAreaPortalState(portalnum: Integer; open: qboolean);
begin
if (portalnum > numareaportals) then
Com_Error(ERR_DROP, 'areaportal > numareaportals', []);
portalopen[portalnum] := open;
FloodAreaConnections;
end;
function CM_AreasConnected(area1, area2: Integer): qboolean;
begin
if (map_noareas.value <> 0) then
begin
Result := True;
Exit;
end;
if (area1 > numareas) or (area2 > numareas) then
Com_Error(ERR_DROP, 'area > numareas', []);
if (map_areas[area1].floodnum = map_areas[area2].floodnum) then
begin
Result := True;
Exit;
end;
Result := False;
end;
(*
=================
CM_WriteAreaBits
Writes a length byte followed by a bit vector of all the areas
that area in the same flood as the area parameter
This is used by the client refreshes to cull visibility
=================
*)
function CM_WriteAreaBits(buffer: PByte; area: Integer): Integer;
var
i: Integer;
floodnum: Integer;
bytes: Integer;
b: PByte;
begin
bytes := (numareas + 7) shr 3;
if (map_noareas^.value <> 0) then
begin // for debugging, send everything
FillChar(buffer^, bytes, 255);
end
else
begin
FillChar(buffer^, bytes, 0);
floodnum := map_areas[area].floodnum;
for i := 0 to numareas - 1 do
begin
if (map_areas[i].floodnum = floodnum) or (area = 0) then
begin
// buffer[i>>3] |= 1<<(i&7);
b := @PByteArray(buffer)^[i shr 3];
b^ := b^ or (1 shl (i and 7));
end;
end;
end;
Result := bytes;
end;
(*
===================
CM_WritePortalState
Writes the portal state to a savegame file
===================
*)
procedure CM_WritePortalState(var file_: integer);
begin
FileWrite(file_, portalopen, sizeof(portalopen));
end;
(*
===================
CM_ReadPortalState
Reads the portal state from a savegame file
and recalculates the area connections
===================
*)
procedure CM_ReadPortalState(var file_: integer);
begin
FS_Read(@portalopen, SizeOf(portalopen), file_);
FloodAreaConnections;
end;
(*
=============
CM_HeadnodeVisible
Returns true if any leaf under headnode has a cluster that
is potentially visible
=============
*)
function CM_HeadnodeVisible(nodenum: Integer; visbits: PByteArray): qboolean;
var
leafnum: Integer;
cluster: Integer;
node: cnode_p;
begin
if (nodenum < 0) then
begin
leafnum := -1 - nodenum;
cluster := map_leafs[leafnum].cluster;
if (cluster = -1) then
begin
Result := False;
Exit;
end;
if (visbits[cluster shr 3] and (1 shl (cluster and 7))) <> 0 then
begin
Result := True;
Exit;
end;
Result := false;
Exit;
end;
node := @map_nodes[nodenum];
if CM_HeadnodeVisible(node^.children[0], visbits) then
begin
Result := true;
Exit;
end;
Result := CM_HeadnodeVisible(node^.children[1], visbits);
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -