Skip to content

Commit e4eff61

Browse files
Port over recent primitive shapes
Hollow domes and parallelograms were unhandled previously.
1 parent b67b6b1 commit e4eff61

1 file changed

Lines changed: 180 additions & 4 deletions

File tree

lua/prop2mesh/compat/construct.lua

Lines changed: 180 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
prop2mesh.primitive = {}
99
local addon = prop2mesh.primitive
1010

11+
-- Primitive.minSize, copied rather than read, so this library doesn't need that addon installed
12+
local MinSize = 0.4
13+
1114

1215
-------------------------------
1316
local bit, math, util, table, isvector, WorldToLocal, LocalToWorld, Vector, Angle =
@@ -233,12 +236,13 @@ do
233236
@RETURN:
234237
either a function or a coroutine that will build the mesh
235238
--]]
236-
function addon.construct.generate( construct, param, threaded, physics )
239+
function addon.construct.generate( construct, param, threaded, physics, missingname )
237240
if SERVER then threaded = nil end
238241

239-
-- Non-existant construct, error model CODE 1
242+
-- Non-existant construct, error model CODE 1. Named by the caller, since a construct
243+
-- that doesn't exist has no data table to read a name out of.
240244
if construct == nil then
241-
return true, errorModel( 1, name )
245+
return true, errorModel( 1, missingname or "NO_NAME" )
242246
end
243247

244248
construct.data.name = construct.data.name or "NO_NAME"
@@ -271,7 +275,7 @@ do
271275
either a function or a coroutine that will build the mesh
272276
--]]
273277
function addon.construct.get( name, param, threaded, physics )
274-
return addon.construct.generate( construct_types[name], param, threaded, physics )
278+
return addon.construct.generate( construct_types[name], param, threaded, physics, name )
275279
end
276280
end
277281

@@ -2870,3 +2874,175 @@ registerType( "ladder", function( param, data, threaded, physics )
28702874

28712875
return model
28722876
end )
2877+
2878+
2879+
-- PARALLELOGRAM
2880+
-- Ported from the primitive addon. This library is a fork, so a type only the addon knows
2881+
-- about falls through to the error model, and its clips then cut up a 24 unit cube instead.
2882+
registerType( "parallelogram", function( param, data, threaded, physics )
2883+
local dx = ( isvector( param.PrimSIZE ) and param.PrimSIZE[1] or 1 ) * 0.5
2884+
local dy = ( isvector( param.PrimSIZE ) and param.PrimSIZE[2] or 1 ) * 0.5
2885+
local dz = ( isvector( param.PrimSIZE ) and param.PrimSIZE[3] or 1 ) * 0.5
2886+
2887+
local shift = tonumber( param.PrimSLANT ) or 0
2888+
2889+
local model = simpleton.New()
2890+
local verts = model.verts
2891+
2892+
-- Bottom face at z=-dz is unshifted; all horizontal offset goes to the top face.
2893+
-- The entity origin sits at the center of the bottom face.
2894+
model:PushXYZ( dx, dy, -dz ) -- 1 bottom y+ x+
2895+
model:PushXYZ( dx, -dy, -dz ) -- 2 bottom y- x+
2896+
model:PushXYZ( -dx, -dy, -dz ) -- 3 bottom y- x-
2897+
model:PushXYZ( -dx, dy, -dz ) -- 4 bottom y+ x-
2898+
model:PushXYZ( dx + shift, dy, dz ) -- 5 top y+ x+
2899+
model:PushXYZ( dx + shift, -dy, dz ) -- 6 top y- x+
2900+
model:PushXYZ( -dx + shift, -dy, dz ) -- 7 top y- x-
2901+
model:PushXYZ( -dx + shift, dy, dz ) -- 8 top y+ x-
2902+
2903+
if CLIENT then
2904+
model:PushFace( 1, 2, 3, 4 ) -- bottom (-z)
2905+
model:PushFace( 5, 8, 7, 6 ) -- top (+z)
2906+
model:PushFace( 1, 4, 8, 5 ) -- y+ side
2907+
model:PushFace( 2, 6, 7, 3 ) -- y- side
2908+
model:PushFace( 2, 1, 5, 6 ) -- x+ side (slanted)
2909+
model:PushFace( 4, 3, 7, 8 ) -- x- side (slanted)
2910+
end
2911+
2912+
if physics then
2913+
model.convexes = { verts }
2914+
end
2915+
2916+
util_Transform( verts, param.PrimMESHROT, param.PrimMESHPOS, threaded )
2917+
2918+
return model
2919+
end )
2920+
2921+
2922+
-- DOME_HOLLOW
2923+
-- Ported from the primitive addon, see the note on parallelogram above
2924+
local function pushHollowRings( model, maxseg2, numseg2, maxseg, numseg, dx, dy, dz, idx, idy, idz )
2925+
for r = 0, numseg2 do
2926+
local t = math_pi * 0.5 * ( 1 - r / maxseg2 )
2927+
local sinT, cosT = math_sin( t ), math_cos( t )
2928+
for v = 0, numseg do
2929+
local p = math_tau * v / maxseg
2930+
model:PushXYZ( -dx * math_cos( p ) * sinT, dy * math_sin( p ) * sinT, dz * cosT ) -- outer
2931+
model:PushXYZ( -idx * math_cos( p ) * sinT, idy * math_sin( p ) * sinT, idz * cosT ) -- inner
2932+
end
2933+
end
2934+
end
2935+
2936+
registerType( "dome_hollow", function( param, data, threaded, physics )
2937+
local maxseg = param.PrimMAXSEGH or 1
2938+
local numseg = param.PrimNUMSEGH or 1
2939+
local maxseg2 = param.PrimMAXSEGV or 1
2940+
local numseg2 = param.PrimNUMSEGV or 1
2941+
2942+
-- Legacy subdiv param will always come with these set to 1. Backwards compatibility measure.
2943+
-- Normally you wouldn't set these anyways (degenerate cases)
2944+
if maxseg == 1 and numseg == 1 and maxseg2 == 1 and numseg2 == 1 then
2945+
local subdiv = math_clamp( 2 * math_round( ( param.PrimSUBDIV or 8 ) / 2 ), 4, 32 )
2946+
maxseg, numseg = subdiv, subdiv
2947+
maxseg2, numseg2 = subdiv / 2, subdiv / 2
2948+
end
2949+
2950+
-- Horizontal (longitude) arc: numseg of maxseg segments around the full 360 degrees.
2951+
maxseg = math_clamp( maxseg, 1, 16 )
2952+
numseg = math_clamp( numseg, 1, maxseg )
2953+
2954+
-- Vertical (latitude) arc: numseg2 of maxseg2 segments over the 90 degrees from equator to apex.
2955+
maxseg2 = math_clamp( maxseg2, 1, 8 )
2956+
numseg2 = math_clamp( numseg2, 1, maxseg2 )
2957+
2958+
local dx = ( isvector( param.PrimSIZE ) and param.PrimSIZE[1] or 1 ) * 0.5
2959+
local dy = ( isvector( param.PrimSIZE ) and param.PrimSIZE[2] or 1 ) * 0.5
2960+
local dz = ( isvector( param.PrimSIZE ) and param.PrimSIZE[3] or 1 ) * 0.5
2961+
local dt = math_max( math_min( param.PrimDT or 1, dx - MinSize, dy - MinSize, dz - MinSize ), MinSize )
2962+
2963+
local idx = dx - dt
2964+
local idy = dy - dt
2965+
local idz = dz - dt
2966+
2967+
local ringSize = numseg + 1 -- verts per ring: numseg segments need numseg+1 verts to span the arc
2968+
local twoRing = ringSize * 2 -- stride per ring in interleaved layout
2969+
2970+
local model = simpleton.New()
2971+
2972+
if CLIENT then
2973+
pushHollowRings( model, maxseg2, numseg2, maxseg, numseg, dx, dy, dz, idx, idy, idz )
2974+
2975+
-- Emit quad strips between adjacent rings. Each ring occupies twoRing slots,
2976+
-- so adding twoRing to an index moves to the same position one ring toward the apex.
2977+
for r = 1, numseg2 do
2978+
for v = 0, numseg - 1 do
2979+
local a = 1 + ( r - 1 ) * twoRing + v * 2 -- bottom-left outer
2980+
local d = a + twoRing -- top-left outer
2981+
model:PushFace( d, d + 2, a + 2, a ) -- outer, outward normals
2982+
model:PushFace( a + 1, a + 3, d + 3, d + 1 ) -- inner, inward normals
2983+
end
2984+
end
2985+
2986+
-- Close the open bottom edge with a ring of rim quads connecting outer equator to inner equator.
2987+
for v = 0, numseg - 1 do
2988+
local ao = 1 + v * 2
2989+
model:PushFace( ao, ao + 2, ao + 3, ao + 1 ) -- rim, downward normals
2990+
end
2991+
2992+
-- A partial vertical arc stops short of the apex, so close that ring too.
2993+
if numseg2 ~= maxseg2 then
2994+
local top = 1 + numseg2 * twoRing
2995+
for v = 0, numseg - 1 do
2996+
local ao = top + v * 2
2997+
model:PushFace( ao + 1, ao + 3, ao + 2, ao ) -- cap, upward normals
2998+
end
2999+
end
3000+
3001+
-- When the arc is partial, cap the two cut edges with wall quads spanning outer to inner.
3002+
if numseg ~= maxseg then
3003+
for r = 1, numseg2 do
3004+
local aStart = 1 + ( r - 1 ) * twoRing
3005+
local dStart = aStart + twoRing
3006+
model:PushFace( aStart, aStart + 1, dStart + 1, dStart )
3007+
3008+
local aEnd = aStart + numseg * 2
3009+
local dEnd = aEnd + twoRing
3010+
model:PushFace( aEnd, dEnd, dEnd + 1, aEnd + 1 )
3011+
end
3012+
end
3013+
3014+
util_Transform( model.verts, param.PrimMESHROT, param.PrimMESHPOS, threaded )
3015+
end
3016+
3017+
if physics then
3018+
-- Limit physics to 8 * 4 = 32 convexes at most.
3019+
local physMaxseg = math_min( maxseg, 8 )
3020+
local physNumseg = math_max( math_round( numseg * physMaxseg / maxseg ), 1 )
3021+
local physMaxseg2 = math_min( maxseg2, 4 )
3022+
local physNumseg2 = math_max( math_round( numseg2 * physMaxseg2 / maxseg2 ), 1 )
3023+
local physTwoRing = ( physNumseg + 1 ) * 2
3024+
local physModel = simpleton.New()
3025+
local physVerts = physModel.verts
3026+
3027+
pushHollowRings( physModel, physMaxseg2, physNumseg2, physMaxseg, physNumseg, dx, dy, dz, idx, idy, idz )
3028+
3029+
-- One convex per patch: four outer corners + four inner corners of each wall quad.
3030+
-- This approximates the hollow shell as a set of thin wedge-shaped convex hulls.
3031+
local convexes = {}
3032+
for r = 0, physNumseg2 - 1 do
3033+
for v = 0, physNumseg - 1 do
3034+
local oa = 1 + r * physTwoRing + v * 2 -- bottom-left outer
3035+
local od = oa + physTwoRing -- top-left outer
3036+
convexes[#convexes + 1] = {
3037+
physVerts[oa], physVerts[oa + 2], physVerts[od], physVerts[od + 2], -- outer quad corners
3038+
physVerts[oa + 1], physVerts[oa + 3], physVerts[od + 1], physVerts[od + 3], -- inner quad corners
3039+
}
3040+
end
3041+
end
3042+
3043+
model.convexes = convexes
3044+
util_Transform( physVerts, param.PrimMESHROT, param.PrimMESHPOS, threaded )
3045+
end
3046+
3047+
return model
3048+
end )

0 commit comments

Comments
 (0)