Advertisement
yal_f

the minecraf

Aug 15th, 2024
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.40 KB | None | 0 0
  1. --!optimize 2
  2. --!native
  3.  
  4. local RepStorage = game:FindService("ReplicatedStorage")
  5.  
  6. local chunkReference = script.Chunk
  7. local chunkFolder = workspace.ChunkFolder
  8.  
  9. local faceDrawFunctions = require(script.FaceDrawFunctions)
  10. local blockData = require(RepStorage.BlockData)
  11. local structureData = require(RepStorage.StructureData)
  12. local DrawFront, DrawBack, DrawTop, DrawBottom, DrawRight, DrawLeft = unpack(faceDrawFunctions)
  13.  
  14. local NOISE_SEED = os.clock()
  15. local VECTOR3HALF = Vector3.one/2
  16.  
  17. local VERT_OFFS_1 = Vector3.new(-0.5, -0.5, -0.5)
  18. local VERT_OFFS_2 = Vector3.new( 0.5, -0.5, -0.5)
  19. local VERT_OFFS_3 = Vector3.new(-0.5, -0.5,  0.5)
  20. local VERT_OFFS_4 = Vector3.new( 0.5, -0.5,  0.5)
  21.  
  22. local chunks = {}
  23.  
  24. --config
  25.  
  26. local CHUNK_WIDTH = 16
  27.  
  28. local OCTAVES = 5 -- default 4
  29. local PERSISTANCE = 0.325 -- default 0.33
  30. local LUCANARITY = 3 -- default 2.74
  31.  
  32. local NOISE_WIDTH = 780 --default 300
  33. local NOISE_HEIGHT = 450 -- default 155
  34.  
  35. local OCEAN_HEIGHT = 2
  36. local TREE_CHANCE = 200
  37.  
  38. --index constants
  39.  
  40. local IDX_VOXELS = 1
  41. local IDX_EDITMESH = 2
  42. local IDX_CHUNKPOS = 3
  43.  
  44. local IDX_COLORMIN = 1
  45. local IDX_COLORMAX = 2
  46.  
  47. --
  48.  
  49. local POS_LIMIT = CHUNK_WIDTH-1
  50. local CHUNK_WIDTH_SQUARED = CHUNK_WIDTH^2
  51. local CHUNK_WIDTH_CUBED = CHUNK_WIDTH^3
  52. local CHUNK_VECTOR = Vector3.new(CHUNK_WIDTH, CHUNK_WIDTH, CHUNK_WIDTH)
  53.  
  54. --
  55.  
  56. local function Flatten(x: number , y: number, z: number)
  57.     return x + (y * CHUNK_WIDTH) + (z * CHUNK_WIDTH_SQUARED)
  58. end
  59.  
  60. local function IsBufferEmpty(buff: buffer)
  61.     return string.rep("\0", buffer.len(buff)) == buffer.tostring(buff)
  62. end
  63.  
  64. local function GetBufferSize(buff: buffer)
  65.     local count = 0
  66.  
  67.     for i = 0, buffer.len(buff)-1, 4 do
  68.         local data = buffer.readu32(buff, i)
  69.         if bit32.btest(data, 0xFF) then count += 1 end
  70.         if bit32.btest(data, 0xFF00) then count += 1 end
  71.         if bit32.btest(data, 0xFF0000) then count += 1 end
  72.         if bit32.btest(data, 0xFF000000) then count += 1 end
  73.     end --idk if this is faster than just reading each byte individually but we ball
  74.  
  75.     return count
  76. end
  77.  
  78. local function GenerateNoise(Z: number, X: number)
  79.     local noise = 0
  80.     local amplitude = 1
  81.  
  82.     for _ = 1, OCTAVES do
  83.         noise += math.noise(X / NOISE_WIDTH, Z / NOISE_WIDTH, NOISE_SEED) * amplitude
  84.  
  85.         X *= LUCANARITY
  86.         Z *= LUCANARITY
  87.         amplitude *= PERSISTANCE
  88.     end
  89.  
  90.     return noise >= 0 and noise^1.25 or -(math.abs(noise)^0.5)
  91. end
  92.  
  93. local function GetChunkNeighbors(chunkPos: Vector3)
  94.     return
  95.         chunks[chunkPos + Vector3.xAxis],
  96.         chunks[chunkPos - Vector3.xAxis],
  97.         chunks[chunkPos + Vector3.yAxis],
  98.         chunks[chunkPos - Vector3.yAxis],
  99.         chunks[chunkPos + Vector3.zAxis],
  100.         chunks[chunkPos - Vector3.zAxis]
  101. end
  102.  
  103. local function RemoveChunk(chunkPos: Vector3)
  104.     local chunk = chunks[chunkPos][IDX_EDITMESH].Parent:Destroy()
  105.     chunks[chunkPos] = nil
  106. end
  107.  
  108. local function AddChunk(chunkPos: Vector3)
  109.     local mesh = Instance.fromExisting(chunkReference)
  110.  
  111.     local chunk = {
  112.         buffer.create(CHUNK_WIDTH_CUBED),
  113.         Instance.new("EditableMesh", mesh),
  114.         chunkPos
  115.     }
  116.  
  117.     chunks[chunkPos] = chunk
  118.     mesh.Position = chunkPos * CHUNK_WIDTH
  119.     mesh.Parent = chunkFolder
  120.  
  121.     return chunk
  122. end
  123.  
  124. local function GetChunksInRange(centerPos: Vector3, radius: number, createChunks: boolean?)
  125.     local radiusSquared = radius^2
  126.     local radiusVector = Vector3.new(radius, radius, radius)
  127.     local chunkRadius = radius // CHUNK_WIDTH
  128.  
  129.     local minPos = (centerPos - radiusVector) // CHUNK_WIDTH
  130.     local maxPos = (centerPos + radiusVector) // CHUNK_WIDTH --+ Vector3.one
  131.  
  132.     local touchingChunks = {}
  133.     local count = 0
  134.  
  135.     for z = minPos.Z, maxPos.Z do
  136.         for y = minPos.Y, maxPos.Y do
  137.             for x = minPos.X, maxPos.X do
  138.                 local chunkPos = Vector3.new(x,y,z)
  139.  
  140.                 local chunkMin = chunkPos * CHUNK_WIDTH
  141.                 local chunkMax = chunkMin + CHUNK_VECTOR
  142.  
  143.                 local difference = Vector3.new(
  144.                     math.clamp(centerPos.X, chunkMin.X, chunkMax.X),
  145.                     math.clamp(centerPos.Y, chunkMin.Y, chunkMax.Y),
  146.                     math.clamp(centerPos.Z, chunkMin.Z, chunkMax.Z)
  147.                 ) - centerPos
  148.  
  149.                 if (difference.X^2 + difference.Y^2 + difference.Z^2) <= radiusSquared then
  150.                     local chunk = chunks[chunkPos]
  151.  
  152.                     if not chunk then
  153.                         if not createChunks then
  154.                             continue
  155.                         end
  156.                         chunk = AddChunk(chunkPos)
  157.                     end
  158.  
  159.                     count += 1
  160.                     touchingChunks[count] = chunk
  161.                 end
  162.             end
  163.         end
  164.     end
  165.  
  166.     return touchingChunks
  167. end
  168.  
  169. local function GetChunksInBounds(centerPos: Vector3, size: Vector3, createChunks: boolean?)
  170.     size //= 2
  171.     local minPos = (centerPos - size) // CHUNK_WIDTH
  172.     local maxPos = (centerPos + size) // CHUNK_WIDTH
  173.  
  174.     local touchingChunks = {}
  175.     local count = 0
  176.  
  177.     for z = minPos.Z, maxPos.Z do
  178.         for y = minPos.Y, maxPos.Y do
  179.             for x = minPos.X, maxPos.X do
  180.                 local chunkPos = Vector3.new(x,y,z)
  181.                 local chunk = chunks[chunkPos]
  182.  
  183.                 if not chunk then
  184.                     if not createChunks then
  185.                         continue
  186.                     end
  187.                     chunk = AddChunk(chunkPos)
  188.                 end
  189.  
  190.                 count += 1
  191.                 touchingChunks[count] = chunk
  192.             end
  193.         end
  194.     end
  195.  
  196.     return touchingChunks
  197. end
  198.  
  199. local function UpdateChunk(chunk)
  200.     if not chunk then return end
  201.  
  202.     local oldEditMesh = chunk[IDX_EDITMESH]
  203.     local replaceEditMesh = #oldEditMesh:GetVertices() ~= 0
  204.     local editMesh = if replaceEditMesh then Instance.new("EditableMesh") else oldEditMesh
  205.  
  206.     local voxels = chunk[IDX_VOXELS]
  207.     local chunkPos = chunk[IDX_CHUNKPOS]
  208.     local frontNeighbor, backNeighbor, topNeighbor, bottomNeighbor, rightNeighbor, leftNeighbor = GetChunkNeighbors(chunkPos)
  209.    
  210.     if not IsBufferEmpty(voxels) then
  211.         for Z = 0, POS_LIMIT do
  212.             local Zindex = Z * CHUNK_WIDTH_SQUARED
  213.            
  214.             for Y = 0, POS_LIMIT do
  215.                 local Yindex = Y * CHUNK_WIDTH
  216.                 local YZindex = Zindex + Yindex
  217.                
  218.                 for X = 0, POS_LIMIT do
  219.                     local Xindex = X
  220.                     local gridIndex = YZindex + Xindex
  221.                     local blockID = buffer.readu8(voxels, gridIndex)
  222.  
  223.                     if blockID == 0 then continue end -- skip air
  224.  
  225.                     local centerPos = Vector3.new(X,Y,Z)
  226.                     local blockTypeData = blockData[blockID]
  227.  
  228.                     local colorMin = blockTypeData[IDX_COLORMIN]
  229.                     local colorMax = blockTypeData[IDX_COLORMAX]
  230.  
  231.                     math.randomseed((chunkPos.X*4.3 + chunkPos.Y*7.2 + chunkPos.Z*1.9)*CHUNK_WIDTH + gridIndex)
  232.                     local color = if colorMax then colorMin:Lerp(colorMax, math.random()) else colorMin
  233.  
  234.                     local p1 = centerPos + VERT_OFFS_1
  235.                     local p2 = centerPos + VERT_OFFS_2
  236.                     local p3 = centerPos + VERT_OFFS_3
  237.                     local p4 = centerPos + VERT_OFFS_4
  238.                     local p5 = centerPos - VERT_OFFS_4
  239.                     local p6 = centerPos - VERT_OFFS_3
  240.                     local p7 = centerPos - VERT_OFFS_2
  241.                     local p8 = centerPos - VERT_OFFS_1
  242.  
  243.                     --
  244.  
  245.                     --front face
  246.                     local drawFace
  247.                     if X == POS_LIMIT then
  248.                         if not frontNeighbor or buffer.readu8(frontNeighbor[IDX_VOXELS], YZindex) == 0 then
  249.                             drawFace = true
  250.                         end
  251.                     elseif buffer.readu8(voxels, gridIndex+1) == 0 then
  252.                         drawFace = true
  253.                     end
  254.  
  255.                     if drawFace then
  256.                         DrawFront(editMesh, color, p2,p4,p6,p8)
  257.                     end
  258.                     --
  259.  
  260.                     --back face
  261.                     local drawFace
  262.                     if X == 0 then
  263.                         if not backNeighbor or buffer.readu8(backNeighbor[IDX_VOXELS], POS_LIMIT+YZindex) == 0 then
  264.                             drawFace = true
  265.                         end
  266.                     elseif buffer.readu8(voxels, gridIndex-1) == 0 then
  267.                         drawFace = true
  268.                     end
  269.  
  270.                     if drawFace then
  271.                         DrawBack(editMesh, color, p1,p3,p5,p7)
  272.                     end
  273.                     --
  274.  
  275.                     --top face
  276.                     local drawFace
  277.                     if Y == POS_LIMIT then
  278.                         if not topNeighbor or buffer.readu8(topNeighbor[IDX_VOXELS], Xindex + Zindex) == 0 then
  279.                             drawFace = true
  280.                         end
  281.                     elseif buffer.readu8(voxels, Xindex + (Yindex+CHUNK_WIDTH) + Zindex) == 0 then
  282.                         drawFace = true
  283.                     end
  284.  
  285.                     if drawFace then
  286.                         DrawTop(editMesh, color, p5,p6,p7,p8)
  287.                     end
  288.                     --
  289.  
  290.                     --bottom face
  291.                     local drawFace
  292.                     if Y == 0 then
  293.                         if not bottomNeighbor or buffer.readu8(bottomNeighbor[IDX_VOXELS], Xindex + (POS_LIMIT*CHUNK_WIDTH) + Zindex) == 0 then
  294.                             drawFace = true
  295.                         end
  296.                     elseif buffer.readu8(voxels, Xindex + (Yindex-CHUNK_WIDTH) + Zindex) == 0 then
  297.                         drawFace = true
  298.                     end
  299.  
  300.                     if drawFace then
  301.                         DrawBottom(editMesh, color, p1,p2,p3,p4)
  302.                     end
  303.                     --
  304.  
  305.                     --right face
  306.                     local drawFace
  307.                     if Z == POS_LIMIT then
  308.                         if not rightNeighbor or buffer.readu8(rightNeighbor[IDX_VOXELS], Xindex + Yindex) == 0 then
  309.                             drawFace = true
  310.                         end
  311.                     elseif buffer.readu8(voxels, Xindex + Yindex + (Zindex+CHUNK_WIDTH_SQUARED)) == 0 then
  312.                         drawFace = true
  313.                     end
  314.  
  315.                     if drawFace then
  316.                         DrawRight(editMesh, color, p3,p4,p7,p8)
  317.                     end
  318.                     --
  319.  
  320.                     --left face
  321.                     local drawFace
  322.                     if Z == 0 then
  323.                         if not leftNeighbor or buffer.readu8(leftNeighbor[IDX_VOXELS], Xindex + Yindex + (POS_LIMIT*CHUNK_WIDTH_SQUARED)) == 0 then
  324.                             drawFace = true
  325.                         end
  326.                     elseif buffer.readu8(voxels, Xindex + Yindex + (Zindex-CHUNK_WIDTH_SQUARED)) == 0 then
  327.                         drawFace = true
  328.                     end
  329.  
  330.                     if drawFace then
  331.                         DrawLeft(editMesh, color, p1,p2,p5,p6)
  332.                     end
  333.                     --
  334.                 end
  335.             end
  336.         end
  337.     end
  338.  
  339.     if replaceEditMesh then
  340.         chunk[IDX_EDITMESH] = editMesh
  341.         editMesh.Parent = oldEditMesh.Parent
  342.         oldEditMesh:Destroy()
  343.     end
  344. end
  345.  
  346. local function GenerateStructure(at: Vector3, structureID: number, createChunks: boolean?, writeAir: boolean?)
  347.     local structure = structureData[structureID]
  348.     local size = structure[1]
  349.     local data = structure[2]
  350.  
  351.     local sizeX = size.X
  352.     local sizeY = size.Y
  353.     local sizeZ = size.Z
  354.     local sizeXY = sizeX*sizeY
  355.  
  356.     for Z = 0, sizeZ-1 do
  357.         local Zindex = Z * sizeXY
  358.         for Y = 0, sizeY-1 do
  359.             local YZindex = Zindex + (Y * sizeX)
  360.             for X = 0, sizeX-1 do
  361.                 local blockID = buffer.readu8(data, YZindex+X)
  362.  
  363.                 if blockID == 0 and not writeAir then
  364.                     continue
  365.                 end
  366.  
  367.                 local vectorPos = at+Vector3.new(X,Y,Z)
  368.                 local chunkPos = vectorPos // CHUNK_WIDTH
  369.                 local chunk = chunks[chunkPos]
  370.  
  371.                 if not chunk then
  372.                     if not createChunks then
  373.                         continue
  374.                     end
  375.                     chunk = AddChunk(chunkPos)
  376.                 end
  377.  
  378.                 local offset = vectorPos-(chunkPos*CHUNK_WIDTH)
  379.                 buffer.writeu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z), blockID)
  380.             end
  381.         end
  382.     end
  383. end
  384.  
  385. local function GetVoxelType(voxelY: number, noiseHeight: number)
  386.     if voxelY < noiseHeight-105 then
  387.         return 9
  388.     elseif voxelY < noiseHeight-10 then
  389.         return 3 --stone
  390.     elseif voxelY < noiseHeight-1.5 then
  391.         return 5 --dirt
  392.     elseif noiseHeight <= OCEAN_HEIGHT then
  393.         return 2 --water
  394.     elseif noiseHeight < OCEAN_HEIGHT + 1.8 then
  395.         return 4 --sand
  396.     elseif voxelY > (NOISE_HEIGHT*0.55) and voxelY > noiseHeight-3 then
  397.         return 6 --snow
  398.     else
  399.         return 1 --grass
  400.     end
  401. end
  402.  
  403. local function GenerateChunk(x: number, y: number, z: number)
  404.     local trueChunkX = x*CHUNK_WIDTH
  405.     local trueChunkY = y*CHUNK_WIDTH
  406.     local trueChunkZ = z*CHUNK_WIDTH
  407.  
  408.     local chunkPos = Vector3.new(x,y,z)
  409.     local maxY = trueChunkY+POS_LIMIT
  410.     local chunk = chunks[chunkPos]
  411.     local voxels = chunk and chunk[IDX_VOXELS]
  412.  
  413.     for Z = 0, POS_LIMIT do
  414.         local trueVoxelZ = trueChunkZ+Z
  415.         local indexZ = Z*CHUNK_WIDTH_SQUARED
  416.  
  417.         for X = 0, POS_LIMIT do
  418.             local trueVoxelX = trueChunkX+X
  419.  
  420.             local noise = GenerateNoise(trueVoxelZ, trueVoxelX)
  421.             local noiseHeight = math.max(noise * NOISE_HEIGHT, OCEAN_HEIGHT)
  422.  
  423.             if noiseHeight >= trueChunkY then
  424.                 if not chunk then
  425.                     chunk = AddChunk(chunkPos)
  426.                     voxels = chunk[IDX_VOXELS]
  427.                 end
  428.  
  429.                 local maxHeight = math.min(math.floor(noiseHeight), maxY) - trueChunkY
  430.                 local voxelType
  431.  
  432.                 for Y = 0, maxHeight do
  433.                     voxelType = GetVoxelType(trueChunkY + Y, noiseHeight)
  434.                     buffer.writeu8(voxels, X + (Y*CHUNK_WIDTH) + indexZ, voxelType)
  435.                 end
  436.  
  437.                 if voxelType == 1 and math.random(TREE_CHANCE) == 1 then
  438.                     GenerateStructure(Vector3.new(trueVoxelX-2, trueChunkY+maxHeight+1, trueVoxelZ-2), 2, true)
  439.                 end
  440.             end
  441.         end
  442.     end
  443.  
  444.     return chunk
  445. end
  446.  
  447. --
  448.  
  449. local module = {}
  450.  
  451. module.RemoveVoxelsInRange = function(centerPos: Vector3, range: number)
  452.     local radius = range/2
  453.     local radiusSquared = radius^2
  454.     local radiusFloored = math.floor(radius)
  455.  
  456.     for Z = -radiusFloored, radiusFloored do
  457.         local zSquared = Z^2
  458.         local yRange = math.floor(math.sqrt(radiusSquared - zSquared))
  459.  
  460.         for Y = -yRange, yRange do
  461.             local xRange = math.floor(math.sqrt(radiusSquared - (zSquared + Y^2)))
  462.  
  463.             for X = -xRange, xRange do
  464.                 local vectorPos = centerPos+Vector3.new(X,Y,Z)
  465.                 local chunkPos = vectorPos // CHUNK_WIDTH
  466.                 local chunk = chunks[chunkPos]
  467.  
  468.                 if chunk then
  469.                     local offset = vectorPos-(chunkPos*CHUNK_WIDTH)
  470.                     buffer.writeu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z), 0)
  471.                 end
  472.             end
  473.         end
  474.     end
  475.  
  476.     for _, chunk in GetChunksInRange(centerPos, radius+1) do
  477.         if IsBufferEmpty(chunk[IDX_VOXELS]) then
  478.             RemoveChunk(chunk[IDX_CHUNKPOS])
  479.         else
  480.             --RevealChunk(chunk)
  481.             task.defer(UpdateChunk, chunk)
  482.         end
  483.     end
  484. end
  485.  
  486. module.AddVoxelsInRange = function(centerPos: Vector3, range: number, blockID: number)
  487.     local radius = range/2
  488.     local radiusSquared = radius^2
  489.     local radiusFloored = math.floor(radius)
  490.  
  491.     local touchingChunks = GetChunksInRange(centerPos, radius+1, true)
  492.  
  493.     for Z = -radiusFloored, radiusFloored do
  494.         local zSquared = Z^2
  495.         local yRange = math.floor(math.sqrt(radiusSquared - zSquared))
  496.  
  497.         for Y = -yRange, yRange do
  498.             local xRange = math.floor(math.sqrt(radiusSquared - (zSquared + Y^2)))
  499.  
  500.             for X = -xRange, xRange do
  501.                 local vectorPos = centerPos+Vector3.new(X,Y,Z)
  502.                 local chunkPos = vectorPos // CHUNK_WIDTH
  503.                 local chunk = chunks[chunkPos]
  504.  
  505.                 local offset = vectorPos-(chunkPos*CHUNK_WIDTH)
  506.                 buffer.writeu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z), blockID)
  507.             end
  508.         end
  509.     end
  510.  
  511.     for _, chunk in touchingChunks do
  512.         --RevealChunk(chunk)
  513.         task.defer(UpdateChunk, chunk)
  514.     end
  515. end
  516.  
  517. module.AddVoxel = function(pos: Vector3, ID: number)
  518.     local chunkPos = pos // CHUNK_WIDTH
  519.     local chunk = chunks[chunkPos] or AddChunk(chunkPos)
  520.  
  521.     local offset = pos-(chunkPos*CHUNK_WIDTH)
  522.     buffer.writeu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z), ID)
  523.     UpdateChunk(chunk)
  524. end
  525.  
  526. module.RemoveVoxel = function(pos: Vector3)
  527.     local chunkPos = pos // CHUNK_WIDTH
  528.     local chunk = chunks[chunkPos]
  529.  
  530.     if chunk then
  531.         local offset = pos-(chunkPos*CHUNK_WIDTH)
  532.         buffer.writeu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z), 0)
  533.         UpdateChunk(chunk)
  534.     end
  535. end
  536.  
  537. module.PlaceStructure = function(at: Vector3, strucID: number)
  538.     local size = structureData[strucID][1]
  539.     local touchingChunks = GetChunksInBounds(at + size//2, size+Vector3.one, true)
  540.  
  541.     GenerateStructure(at, strucID)
  542.  
  543.     for _, chunk in touchingChunks do
  544.         --RevealChunk(chunk)
  545.         task.defer(UpdateChunk, chunk)
  546.     end
  547. end
  548.  
  549. module.Raycast = function(from: Vector3, dir: Vector3, res: number, inside: boolean?)
  550.     local length = (dir.X^2 + dir.Y^2 + dir.Z^2)^0.5
  551.     local unit = dir/length
  552.     local step = unit*res
  553.  
  554.     for i = 0,length/res do
  555.         local halfFrom = from + VECTOR3HALF
  556.         local pos = (halfFrom + (step*i)) // 1
  557.  
  558.         local chunkPos = pos // CHUNK_WIDTH
  559.         local chunk = chunks[chunkPos]
  560.  
  561.         if chunk then
  562.             local offset = pos-(chunkPos*CHUNK_WIDTH)
  563.             if buffer.readu8(chunk[IDX_VOXELS], Flatten(offset.X, offset.Y, offset.Z)) ~= 0 then
  564.                 return if inside then pos else (halfFrom + (step*(i-1))) // 1
  565.             end
  566.         end
  567.     end
  568. end
  569.  
  570. module.ClearChunks = function()
  571.     chunkFolder:ClearAllChildren()
  572.     table.clear(chunks)
  573. end
  574.  
  575. module.Chunks = chunks
  576. module.UpdateChunk = UpdateChunk
  577. module.GenerateChunk = GenerateChunk
  578.  
  579. return module
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement