Changed api to prepare for mapgen rewrite.
[rocks.git] / mapgen.lua
blob5fb54735a1fbdc62a65ab7378edbce4efddf56af
1 --
2 -- layer generator
3 --
5 local print2=function(text)
6 minetest.log("info","rocks/gen/ "..text)
7 end
9 rocksl.seedseq=0
10 rocksl.GetNextSeed=function()
11 rocksl.seedseq=rocksl.seedseq+20
12 print2("seed "..rocksl.seedseq)
13 return rocksl.seedseq
14 end
16 local layers={}
18 rocks.layer_initialize=function(layer)
19 layer.stats={ count=0, total=0, node={}, totalnodes=0 }
20 table.insert(layers,layer)
21 end
23 rocksl.register_stratus=function(layer,name,param)
24 table.insert(layer.localized,{
25 primary=name,
26 spread=(param.spread or 20),
27 height=(param.height or 15),
28 treshold=(param.treshold or 0.85),
29 secondary=param.secondary,
30 seed=(rocksl.GetNextSeed()),
32 layer.stats.node[name]=0
33 end
35 local veins={}
37 rocks.register_vein=function(name,param)
38 local d={
39 primary=name,
40 wherein=param.wherein,
41 miny=param.miny, maxy=param.maxy,
42 radius={ average=param.radius.average, amplitude=param.radius.amplitude, frequency=param.radius.frequency },
43 density=(param.density or 1),
44 rarity=param.rarity,
45 secondary=(param.ores or {}),
47 table.insert(veins,d)
48 end
50 -- TODO: rewrite above function to register normal minetest ore, with
51 -- special params, so the below func is not necesary. The mt oregen runs in
52 -- separate therad (emerge) and it does not block server.
53 -- params: type=scatter scacrity=1 size=3 ores=27 : full chance of spawning, only limited by noise thr
54 -- EDIT: Need to edit mg_ore.cpp and Add following ore_types: layer, blob, region.
55 -- layer=2Dheightmap style layer with specific thickness
56 -- blob= 3D noise deformed sphere
57 -- region= 3D noise / treshold ore placement.
59 rocksl.layergen=function(layer, minp, maxp, seed)
60 if ( (layer.top.offset+layer.top.scale)>minp.y )
61 and ( (layer.bot.offset-layer.bot.scale)<maxp.y )
62 then
63 local stone_ctx= minetest.get_content_id("default:stone")
64 local air_ctx= minetest.get_content_id("air")
65 local dirt_ctx= minetest.get_content_id("default:dirt")
66 if layer.debugging then
67 layer.primary.ctx= air_ctx
68 else
69 layer.primary.ctx= minetest.get_content_id(layer.primary.name)
70 end
71 local timebefore=os.clock();
72 local manipulator, emin, emax = minetest.get_mapgen_object("voxelmanip")
73 local nodes = manipulator:get_data()
74 local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
75 local side_length = (maxp.x - minp.x) + 1
76 local map_lengths_xyz = {x=side_length, y=side_length, z=side_length}
77 -- noises:
78 local bottom=minetest.get_perlin_map(layer.bot,map_lengths_xyz):get2dMap_flat({x=minp.x, y=minp.z})
79 local localized={}
80 for _,loc in ipairs(layer.localized) do
81 --defaults and overrides
82 local np={ offset = 0, scale = 1, octaves = 1, persist = 0.7,
83 spread = {x=loc.spread, y=loc.height, z=loc.spread}, seed=loc.seed}
84 --get noise and content ids
85 table.insert(localized,
87 noise=minetest.get_perlin_map(np,map_lengths_xyz):get3dMap_flat(minp),
88 treshold=loc.treshold,
89 ctx= minetest.get_content_id(loc.primary),
90 ndn=loc.primary
92 end
93 local noise2d_ix = 1
94 local noise3d_ix = 1
95 print2("after noise: "..(os.clock()-timebefore))
96 for z=minp.z,maxp.z,1 do
97 for y=minp.y,maxp.y,1 do
98 for x=minp.x,maxp.x,1 do
99 local pos = area:index(x, y, z)
100 if (y>bottom[noise2d_ix]) and (y<layer.top.offset)
101 and ( (nodes[pos]==stone_ctx) or (nodes[pos]==dirt_ctx) )
102 then
103 layer.stats.totalnodes=layer.stats.totalnodes+1
104 if nodes[pos]==stone_ctx then nodes[pos] = layer.primary.ctx end
105 for k,loc in pairs(localized) do
106 if ( loc.noise[noise3d_ix] > loc.treshold) then
107 nodes[pos]=loc.ctx
108 layer.stats.node[loc.ndn]=layer.stats.node[loc.ndn]+1
109 break
113 noise2d_ix=noise2d_ix+1
114 noise3d_ix=noise3d_ix+1
116 noise2d_ix=noise2d_ix-side_length
118 noise2d_ix=noise2d_ix+side_length
120 print2("after loop: "..(os.clock()-timebefore))
121 manipulator:set_data(nodes)
122 --manipulator:calc_lighting()
123 --manipulator:update_liquids()
124 if layer.debugging then
125 manipulator:set_lighting({day=15,night=15})
127 manipulator:write_to_map()
128 print2("after commit: "..(os.clock()-timebefore))
129 layer.stats.count=layer.stats.count+1
130 layer.stats.total=layer.stats.total+(os.clock()-timebefore)
131 layer.stats.side=side_length
135 local ignore_wherein=nil
137 rocksl.veingen=function(veins,minp,maxp,seed)
138 local side_length=(maxp.y-minp.y)
139 local random=PseudoRandom(seed-79)
140 local timebefore=os.clock();
141 local manipulator, emin, emax = minetest.get_mapgen_object("voxelmanip")
142 local nodes = manipulator:get_data()
143 local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
144 local did_generate=nil
145 for _,vein in ipairs(veins) do
146 if (minp.y<vein.maxy) and (maxp.y>vein.maxy) then
147 local vr2=vein.radius.average^2
148 local vrm=vein.radius.average+vein.radius.amplitude
149 local noise_map=minetest.get_perlin_map(
151 seed=-79,
152 scale=vein.radius.amplitude,
153 offset=0, octaves=1, persist=0.7,
154 spread={x=vein.radius.frequency, y=vein.radius.frequency, z=vein.radius.frequency}
155 },{x=(vrm*2)+1, y=(vrm*2)+1, z=(vrm*2)+1}
157 local iterations_count= (vein.rarity*side_length)^3
158 -- Resolve node names to id's
159 iterations_count=iterations_count+(random:next(0,100)/100)
160 local primary_ctx=minetest.get_content_id(vein.primary)
161 for _,sec in pairs(vein.secondary) do sec.ctx=minetest.get_content_id(sec.ore) end
162 --old:local wherein_ctx=minetest.get_content_id(vein.wherein)
163 local wherein_set={}
164 for _,wi in pairs(vein.wherein) do wherein_set[minetest.get_content_id(wi)]=true end
165 --print("vein "..vein.primary.." ic="..iterations_count.." p="..primary_ctx)
166 for iteration=1, iterations_count do
167 local x0=minp.x+ random:next(0,side_length)
168 local y0=minp.y+ random:next(0,side_length)
169 local z0=minp.z+ random:next(0,side_length)
170 local noise=noise_map:get3dMap_flat({x=x0-vrm, y=y0-vrm, z=z0-vrm})
171 local noise_ix=1
172 local posi = area:index(x0, y0, z0)
173 if ignore_wherein or wherein_set[nodes[posi]] then
174 print("vein "..vein.primary.." @ "..x0..","..y0..","..z0.." vrm="..vrm)
175 did_generate=1
176 for x=-vrm, vrm do
177 for y=-vrm, vrm do
178 for z=-vrm, vrm do
179 local posc = {x=x+x0,y=y+y0,z=z+z0}
180 posi = area:index(posc.x, posc.y, posc.z)
181 local nv=noise[noise_ix]
182 if (ignore_wherein or wherein_set[nodes[posi]]) and (((x^2)+(y^2)+(z^2))<((vein.radius.average+nv)^2)) then
183 nodes[posi]=primary_ctx
184 local luck=random:next(0,99)
185 for _,sec in pairs(vein.secondary) do
186 luck=luck-sec.percent
187 if luck<=0 then
188 nodes[posi]=sec.ctx
189 break
193 noise_ix=noise_ix+1
194 end end end
195 else
196 --print("vein "..vein.primary.." bad environmnent -"..minetest.get_node({x0,y0,z0}).name.."="..nodes[posi])
201 if did_generate then
202 manipulator:set_data(nodes)
203 --manipulator:calc_lighting()
204 manipulator:write_to_map()
205 print("end veingen "..(os.clock()-timebefore))
206 else
207 --print("end veingen (nothin generated)")
211 minetest.register_on_generated(function(minp, maxp, seed)
212 for _,layer in pairs(layers) do rocksl.layergen(layer,minp,maxp,seed) end
213 rocksl.veingen(veins,minp,maxp,seed)
214 end)
216 minetest.register_on_shutdown(function()
217 for _,ign in pairs(layers) do
218 print("[rocks] Stats for layer "..ign.name)
219 if (ign.stats.count==0) then
220 print("[rocks] |- stats not available, no chunks generated")
221 else
222 print("[rocks] |- generated total "..ign.stats.count.." chunks in "..ign.stats.total.." seconds ("..(ign.stats.total/ign.stats.count).." seconds per "..ign.stats.side.."^3 chunk)")
223 for name,total in pairs(ign.stats.node) do
224 print("[rocks] |- "..name..": "..total.." nodes placed ("..(total*100)/(ign.stats.totalnodes).." %)")
228 end)
230 -- ~ Tomas Brod