Blocks.cpp 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. // BLOCKS OCCLUSION
  6. /******************************************************************************
  7. Some nodes can repeat themself in other parents, but it's all good,
  8. because they have occlusion calculated based on their parents.
  9. For example:
  10. -> Node B -> Node D0 (same coordinates as Node D1)
  11. Node A
  12. -> Node C -> Node D1 (same coordinates as Node D0)
  13. /******************************************************************************
  14. Following code iterates through points in a cube, and processes only those inside a Ball
  15. It then calculates the average normalized direction vector X component, which in the end gives a result of 0.5
  16. Int r=256, num=0;
  17. Dbl sum=0;
  18. for(Int x=-r; x<=r; x++)
  19. for(Int y=-r; y<=r; y++)
  20. for(Int z=-r; z<=r; z++)
  21. {
  22. VecD dir(x, y, z);
  23. if(dir.length()>r)continue;
  24. dir.normalize(); dir.abs();
  25. sum+=dir.x;
  26. num++;
  27. }
  28. sum/=num;
  29. // sum==2
  30. /******************************************************************************/
  31. static Int MaxBlocks(C Blocks &blocks, C BlocksOcclusion *occl)
  32. {
  33. if(occl)if(Int steps=occl->maxSteps())
  34. {
  35. if(blocks.subDivision()>1)steps=DivCeil(steps, blocks.subDivision());
  36. return steps;
  37. }
  38. return 0;
  39. }
  40. /******************************************************************************/
  41. static void PrepareAndClean(Memc<BlocksOcclusion::Node> &nodes, C BlocksOcclusion &occl)
  42. {
  43. const Flt threshold=0.01f;
  44. REPA(nodes)
  45. {
  46. BlocksOcclusion::Node &node=nodes[i];
  47. Flt frac;
  48. switch(occl.aoCombineMode())
  49. {
  50. default : frac= node.fraction[0]+ node.fraction[1] ; break; // ADD
  51. case BlocksOcclusion::MAX: frac=Max(node.fraction[0], node.fraction[1]); break; // MAX
  52. }
  53. if(frac<=threshold)nodes.remove(i);else
  54. {
  55. node.fraction_axis[3]=RoundU(frac*65536);
  56. if(occl.angles())
  57. {
  58. Vec dir=node.pos+0.5f; dir.normalize(); dir.abs(); dir*=frac*(65536/0.5f); // div by 0.5 because the average normal component based on codes at the top is 0.5, so we need to recompensate that
  59. node.fraction_axis[0]=Round(dir.x);
  60. node.fraction_axis[1]=Round(dir.y);
  61. node.fraction_axis[2]=Round(dir.z);
  62. }else
  63. {
  64. node.fraction_axis[0]=
  65. node.fraction_axis[1]=
  66. node.fraction_axis[2]=node.fraction_axis[3];
  67. }
  68. node.fraction_axis[3]/=2; // div by 2 because for this "axis" we're checking both hemispheres
  69. PrepareAndClean(node.nodes, occl);
  70. }
  71. }
  72. }
  73. BlocksOcclusion::BlocksOcclusion()
  74. {
  75. REPAO(_steps)=0; REPAO(_strength)=0;
  76. _aocm=ADD;
  77. _angles=false;
  78. }
  79. void BlocksOcclusion::create(Int steps, Flt strength, Int steps_1, Flt strength_1, COMBINE_MODE ao_combine_mode, Bool angles)
  80. {
  81. _nodes.clear();
  82. _steps [0]=Clamp(steps , 0, 1024);
  83. _steps [1]=Clamp(steps_1, 0, 1024);
  84. _strength[0]=SAT (strength );
  85. _strength[1]=SAT (strength_1);
  86. _angles =angles;
  87. _aocm =ao_combine_mode;
  88. if(steps_1>steps){Swap(steps, steps_1); Swap(strength, strength_1);} // make #0 always the higher
  89. Int steps_1_sqr=Sqr(steps_1);
  90. Int rays=4096,
  91. rays_res=Max(1, SqrtI(rays/6)); // rays resolution in 2D, in one of 6 cube faces
  92. rays=Sqr(rays_res)*6; // total number of rays
  93. if(steps>0)
  94. {
  95. Flt ray_fraction =1.0f/rays;
  96. ray_fraction*= 2; // multiply by 2 because we're interested in hemispheres only (when calculating AO only half of nodes will be processed and we can get only up to half of rays)
  97. ray_fraction*=255; // multiply by 255 because we're creating Color objects which are made from bytes
  98. Flt ray_fraction_0=ray_fraction*strength ,
  99. ray_fraction_1=ray_fraction*strength_1;
  100. Flt ray_step=PI_2/rays_res;
  101. Vec ray_base; ray_base.z=1;
  102. REPD(x, rays_res)
  103. {
  104. ray_base.x=Tan(x*ray_step-PI_4);
  105. REPD(y, rays_res)
  106. {
  107. ray_base.y=Tan(y*ray_step-PI_4);
  108. Vec n=ray_base; n.normalize(); // normalized
  109. REPD(f, 6) // 6 cube faces
  110. {
  111. Vec ray; switch(f)
  112. {
  113. case 0: ray.set( n.x, n.y, n.z); break;
  114. case 1: ray.set(-n.z, n.y, n.x); break;
  115. case 2: ray.set(-n.x, -n.y, -n.z); break;
  116. case 3: ray.set( n.z, -n.y, -n.x); break;
  117. case 4: ray.set( n.x, n.z, -n.y); break;
  118. case 5: ray.set(-n.x, -n.z, n.y); break;
  119. }
  120. Memc<Node> *nodes=&_nodes;
  121. for(VoxelWalker walker(ray*EPS, ray*(steps-EPS)); walker.active(); walker.step()) // don't start at zero, but from a small step along the ray, also end a bit earlier so we don't accidentally add blocks out of range
  122. {
  123. C VecI &pos =walker.pos();
  124. Node *node=null; REPA(*nodes)if((*nodes)[i].pos==pos){node=&(*nodes)[i]; break;} if(!node)node=&nodes->New().init(pos); // get node with such position (create new if not found)
  125. node->fraction[0]+=ray_fraction_0; // add ray to ray counter (as fraction of total amount of rays)
  126. if((pos+0.5f).length2()<=steps_1_sqr)node->fraction[1]+=ray_fraction_1; // add ray to ray counter (as fraction of total amount of rays)
  127. nodes=&node->nodes;
  128. }
  129. }
  130. }
  131. }
  132. PrepareAndClean(_nodes, T);
  133. }
  134. }
  135. BlocksOcclusion& BlocksOcclusion::steps (Int steps ) {create( steps , T.strength(), T.steps1(), T.strength1(), T.aoCombineMode(), T.angles()); return T;}
  136. BlocksOcclusion& BlocksOcclusion::steps1 (Int steps ) {create(T.steps(), T.strength(), steps , T.strength1(), T.aoCombineMode(), T.angles()); return T;}
  137. BlocksOcclusion& BlocksOcclusion::strength (Flt strength) {create(T.steps(), strength , T.steps1(), T.strength1(), T.aoCombineMode(), T.angles()); return T;}
  138. BlocksOcclusion& BlocksOcclusion::strength1 (Flt strength) {create(T.steps(), T.strength(), T.steps1(), strength , T.aoCombineMode(), T.angles()); return T;}
  139. BlocksOcclusion& BlocksOcclusion::aoCombineMode(COMBINE_MODE aocm ) {create(T.steps(), T.strength(), T.steps1(), T.strength1(), aocm , T.angles()); return T;}
  140. BlocksOcclusion& BlocksOcclusion::angles (Bool on ) {create(T.steps(), T.strength(), T.steps1(), T.strength1(), T.aoCombineMode(), on ); return T;}
  141. BlocksOcclusion::Node& BlocksOcclusion::Node::init(C VecI &pos)
  142. {
  143. fraction[0]=fraction[1]=0;
  144. dir=(((pos.x>=0) ? DIRF_RIGHT : DIRF_LEFT)
  145. | ((pos.y>=0) ? DIRF_UP : DIRF_DOWN)
  146. | ((pos.z>=0) ? DIRF_FORWARD : DIRF_BACK));
  147. T.pos=pos; return T;
  148. }
  149. /*static void TestBlocksOcclusion()
  150. {
  151. void save(FileText &f)
  152. {
  153. f++;
  154. f.put("pos = ", pos);
  155. f.put("fraction = ", (Dbl)fraction);
  156. FREPAO(nodes).save(f);
  157. f--;
  158. }
  159. FileText f; f.write("c:/rays.txt");
  160. BlocksOcclusion occl; occl.create(BlocksBorder); FREPAO(occl._nodes).save(f);
  161. }
  162. /******************************************************************************/
  163. // BLOCKS MAP
  164. /******************************************************************************/
  165. BlocksMap::BlocksMap() {_resolution=0; _base_matrix.zero();}
  166. void BlocksMap::create(Int resolution, C Matrix &base_matrix)
  167. {
  168. T._resolution =resolution;
  169. T._base_matrix=base_matrix;
  170. }
  171. Matrix BlocksMap::matrix(Int x, Int y)C
  172. {
  173. Matrix m=_base_matrix;
  174. m.pos+=m.x*(x*resolution());
  175. m.pos+=m.z*(y*resolution());
  176. return m;
  177. }
  178. Bool BlocksMap::raycast(C Vec &start, C Vec &move, Vec *hit_pos, Vec *hit_normal)C
  179. {
  180. Vec block_start=start,
  181. block_move =move ;
  182. block_start/=_base_matrix ; block_start/=resolution();
  183. block_move /=_base_matrix.orn(); block_move /=resolution();
  184. for(PixelWalker walker(block_start.xz(), (block_start+block_move).xz()); walker.active(); walker.step())
  185. if(Blocks *blocks=findBlocks(walker.pos().x, walker.pos().y))
  186. if(blocks->raycast(start, move, hit_pos, hit_normal, &matrix(walker.pos().x, walker.pos().y)))return true;
  187. return false;
  188. }
  189. Vec BlocksMap::light(C Vec &pos, C BlocksOcclusion *occl, C MemPtr<Blocks::Light> &lights)C
  190. {
  191. Vec local_pos=pos/_base_matrix; Vec block_pos=local_pos/resolution();
  192. VecI2 xz=Floor(block_pos.xz());
  193. Vec lum=1; // assume full light at start
  194. if(occl)
  195. if(Flt max_strength=((occl->steps()>occl->steps1()) ? occl->strength() : occl->strength1()))
  196. if(Blocks *blocks=findBlocks(xz.x, xz.y))
  197. {
  198. local_pos.x-=xz.x*resolution();
  199. local_pos.z-=xz.y*resolution();
  200. Int min_level_i=Max(0, blocks->findLevelI(Round(local_pos.y)-MaxBlocks(*blocks, occl)));
  201. if(blocks->subDivision()>1)local_pos*=blocks->subDivision();
  202. VecI local_posi=Round(local_pos);
  203. Byte b=blocks->brightness(occl, local_posi.x, local_posi.y, local_posi.z, AXIS_TYPE(3), DIRF_ALL, min_level_i, Blocks::Neighbors(this, xz.x, xz.y, resolution()));
  204. Flt o=(255-b)/(255.0f*max_strength); // take normalized occlusion 0..1
  205. lum=Sat(1-(o*2-1)*max_strength); // convert it to brightness, where full brightness is at 0.5 occl, and zero brightness is at 1.0 occl (and take occlusion strength into account)
  206. }
  207. REPA(lights)
  208. {
  209. C Blocks::Light &light=lights[i];
  210. Vec delta=light.ball.pos-pos;
  211. Flt dist2=delta.length2(), radius2=Sqr(light.ball.r);
  212. if( dist2<radius2)if(!raycast(pos, delta))lum+=light.color*(1-dist2/radius2);
  213. }
  214. return lum;
  215. }
  216. /******************************************************************************/
  217. // BLOCKS NEIGHBORS
  218. /******************************************************************************/
  219. void Blocks::Neighbors::clear()
  220. {
  221. Zero(T);
  222. }
  223. void Blocks::Neighbors::set(C Blocks *l, C Blocks *r, C Blocks *b, C Blocks *f, C Blocks *lb, C Blocks *lf, C Blocks *rb, C Blocks *rf, Int res)
  224. {
  225. T.l =((l && l ->resolution()==res) ? l : null);
  226. T.r =((r && r ->resolution()==res) ? r : null);
  227. T.b =((b && b ->resolution()==res) ? b : null);
  228. T.f =((f && f ->resolution()==res) ? f : null);
  229. T.lb=((lb && lb->resolution()==res) ? lb : null);
  230. T.lf=((lf && lf->resolution()==res) ? lf : null);
  231. T.rb=((rb && rb->resolution()==res) ? rb : null);
  232. T.rf=((rf && rf->resolution()==res) ? rf : null);
  233. }
  234. Blocks::Neighbors::Neighbors(C Blocks *l, C Blocks *r, C Blocks *b, C Blocks *f, C Blocks *lb, C Blocks *lf, C Blocks *rb, C Blocks *rf, Int resolution)
  235. {
  236. set(l, r, b, f, lb, lf, rb, rf, resolution);
  237. }
  238. Blocks::Neighbors::Neighbors(C BlocksMap *map, Int x, Int y, Int resolution)
  239. {
  240. if(map)set(map->findBlocks(x-1, y ), map->findBlocks(x+1, y ), map->findBlocks(x , y-1), map->findBlocks(x , y+1),
  241. map->findBlocks(x-1, y-1), map->findBlocks(x-1, y+1), map->findBlocks(x+1, y-1), map->findBlocks(x+1, y+1), resolution);
  242. else clear();
  243. }
  244. /******************************************************************************/
  245. // BLOCKS LEVEL
  246. /******************************************************************************/
  247. void Blocks::Level::del()
  248. {
  249. Free(_map);
  250. actor.del(); // delete actor before physical body
  251. phys .del();
  252. }
  253. void Blocks::Level::createPhysBody(C Matrix &matrix, Int res, C Level *down, C Level *up)
  254. {
  255. Memt<Vec > vtxs;
  256. Memt<VecI4> quads;
  257. #if 0 // double-sided physics mesh - unavailable on PhysX
  258. // X walls
  259. FREPD(x, res+1)
  260. {
  261. FREPD(z, res)if(wallX(x, z, res))
  262. {
  263. Int to=z; for(; ; to++)
  264. {
  265. if(InRange(to+1, res) && wallX(x, to+1, res))continue;
  266. break;
  267. }
  268. Int v=vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  269. vtxs.New().set(x, y , z );
  270. vtxs.New().set(x, y+1, z );
  271. vtxs.New().set(x, y+1, to+1);
  272. vtxs.New().set(x, y , to+1);
  273. z=to;
  274. }
  275. }
  276. // Z walls
  277. FREPD(z, res)
  278. {
  279. FREPD(x, res)if(wallZ(z, x, res))
  280. {
  281. Int to=x; for(; ; to++)
  282. {
  283. if(InRange(to+1, res) && wallZ(to+1, z, res))continue;
  284. break;
  285. }
  286. Int v=vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  287. vtxs.New().set(x , y , z);
  288. vtxs.New().set(x , y+1, z);
  289. vtxs.New().set(to+1, y+1, z);
  290. vtxs.New().set(to+1, y , z);
  291. x=to;
  292. }
  293. }
  294. #else
  295. const Bool continuous=true; // if generate fewer, but larger faces
  296. // back/forward walls
  297. FREPD(z, res)
  298. {
  299. // back
  300. FREPD(x, res)if(wallBack(x, z, res))
  301. {
  302. Int to=x; for(Int i=x+1; i<res; i++)if(wallBack(i, z, res))to=i;else if(!continuous || !map(i, z, res))break;
  303. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  304. vtxs.New().set(x , y , z);
  305. vtxs.New().set(x , y+1, z);
  306. vtxs.New().set(to+1, y+1, z);
  307. vtxs.New().set(to+1, y , z);
  308. x=to;
  309. }
  310. // forward
  311. FREPD(x, res)if(wallForward(x, z, res))
  312. {
  313. Int to=x; for(Int i=x+1; i<res; i++)if(wallForward(i, z, res))to=i;else if(!continuous || !map(i, z, res))break;
  314. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  315. vtxs.New().set(to+1, y , z+1);
  316. vtxs.New().set(to+1, y+1, z+1);
  317. vtxs.New().set(x , y+1, z+1);
  318. vtxs.New().set(x , y , z+1);
  319. x=to;
  320. }
  321. }
  322. // left/right walls
  323. FREPD(x, res)
  324. {
  325. // left
  326. FREPD(z, res)if(wallLeft(x, z, res))
  327. {
  328. Int to=z; for(Int i=z+1; i<res; i++)if(wallLeft(x, i, res))to=i;else if(!continuous || !map(x, i, res))break;
  329. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  330. vtxs.New().set(x, y , to+1);
  331. vtxs.New().set(x, y+1, to+1);
  332. vtxs.New().set(x, y+1, z );
  333. vtxs.New().set(x, y , z );
  334. z=to;
  335. }
  336. // right
  337. FREPD(z, res)if(wallRight(x, z, res))
  338. {
  339. Int to=z; for(Int i=z+1; i<res; i++)if(wallRight(x, i, res))to=i;else if(!continuous || !map(x, i, res))break;
  340. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  341. vtxs.New().set(x+1, y , z );
  342. vtxs.New().set(x+1, y+1, z );
  343. vtxs.New().set(x+1, y+1, to+1);
  344. vtxs.New().set(x+1, y , to+1);
  345. z=to;
  346. }
  347. }
  348. #endif
  349. // bottom/top walls
  350. FREPD(z, res)
  351. {
  352. // bottom
  353. FREPD(x, res)if(wallBottom(x, z, res, down))
  354. {
  355. Int to=x; for(Int i=x+1; i<res; i++)if(wallBottom(i, z, res, down))to=i;else if(!continuous || !map(i, z, res))break;
  356. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  357. vtxs.New().set(x , y, z+1);
  358. vtxs.New().set(x , y, z );
  359. vtxs.New().set(to+1, y, z );
  360. vtxs.New().set(to+1, y, z+1);
  361. x=to;
  362. }
  363. // top
  364. FREPD(x, res)if(wallTop(x, z, res, up))
  365. {
  366. Int to=x; for(Int i=x+1; i<res; i++)if(wallTop(i, z, res, up))to=i;else if(!continuous || !map(i, z, res))break;
  367. Int v =vtxs.elms(); quads.New().set(v, v+1, v+2, v+3);
  368. vtxs.New().set(x , y+1, z );
  369. vtxs.New().set(x , y+1, z+1);
  370. vtxs.New().set(to+1, y+1, z+1);
  371. vtxs.New().set(to+1, y+1, z );
  372. x=to;
  373. }
  374. }
  375. MeshBase base(vtxs.elms(), 0, 0, quads.elms());
  376. CopyN(base.vtx .pos(), vtxs .data(), vtxs .elms());
  377. CopyN(base.quad.ind(), quads.data(), quads.elms());
  378. base.transform(matrix);
  379. actor.del(); // delete actor before changing physical body
  380. phys .createMesh(base);
  381. }
  382. /******************************************************************************/
  383. Bool Blocks::Level::save(File &f, Int resolution, Bool include_mesh_and_phys_body)C
  384. {
  385. f.cmpUIntV(0);
  386. f.cmpIntV (y);
  387. f.putN (_map, resolution*resolution);
  388. if(include_mesh_and_phys_body)if(!phys.save(f))return false;
  389. return f.ok();
  390. }
  391. Bool Blocks::Level::load(File &f, Int resolution, Bool include_mesh_and_phys_body)
  392. {
  393. actor.del(); // manually delete actor before changing physical body
  394. switch(f.decUIntV())
  395. {
  396. case 0:
  397. {
  398. init(resolution, f.decIntV());
  399. if(f.getN(_map, resolution*resolution))
  400. {
  401. if(include_mesh_and_phys_body)if(!phys.load(f))goto error;
  402. if(f.ok())return true;
  403. }
  404. }break;
  405. }
  406. error:
  407. del(); return false;
  408. }
  409. /******************************************************************************/
  410. // BLOCKS::PART
  411. /******************************************************************************/
  412. void Blocks::Part::create(MeshBase &base)
  413. {
  414. base.create(vtxs.elms(), 0, 0, quads.elms(), VTX_TEX0|VTX_COLOR);
  415. // vertexes
  416. REPA(vtxs)
  417. {
  418. C Vtx &vtx=vtxs[i];
  419. base.vtx.pos (i)=vtx.pos;
  420. base.vtx.tex0 (i)=vtx.tex;
  421. base.vtx.color(i)=vtx.col;
  422. }
  423. // quads
  424. CopyN(base.quad.ind(), quads.data(), base.quads());
  425. }
  426. /******************************************************************************/
  427. // BLOCKS::LEVEL BRIGHTNESS
  428. /******************************************************************************/
  429. Blocks::LevelBrightness::LevelBrightness(C Blocks &blocks, C Neighbors &neighbors, C BlocksOcclusion *occl, C BlocksMap *map, C VecI2 &blocks_pos, C MemtN<LightEx, 256> &lights) : blocks(blocks), neighbors(neighbors), occl(occl), map(map), blocks_pos(blocks_pos), lights(lights)
  430. {
  431. T.size=blocks.resolution()*blocks.subDivision()+1;
  432. T.points.setNum(size*size);
  433. }
  434. void Blocks::LevelBrightness::clear()
  435. {
  436. REPD(z, size)
  437. REPD(x, size)point(x, z).clear();
  438. }
  439. void Blocks::LevelBrightness::clear(Int min_x, Int min_z, Int max_x, Int max_z)
  440. {
  441. Int sd=blocks.subDivision(); if(sd>1){min_x*=sd; min_z*=sd; max_x*=sd; max_z*=sd;}
  442. max_x++; // increase x because when computing brightness for block x we use "x" and "x+1" points
  443. max_z++; // increase z because when computing brightness for block z we use "z" and "z+1" points
  444. for(Int z=min_z; z<=max_z; z++)
  445. for(Int x=min_x; x<=max_x; x++)point(x, z).clear();
  446. }
  447. Blocks::LevelBrightness& Blocks::LevelBrightness::setLevelI(Int level_i)
  448. {
  449. min_level_i=Max(0, level_i-MaxBlocks(blocks, occl));
  450. return T;
  451. }
  452. C Color& Blocks::LevelBrightness::brightness(Int x, Int y, Int z, DIR_ENUM dir)
  453. {
  454. Point &point=T.point(x, z);
  455. #if DEBUG && 0 // confirm that we're reusing pre-computed brightness
  456. if(point.computed_y==y && (point.computed_dir&DirToFlag(dir)))return 0;
  457. #endif
  458. if(point.computed_y!=y) // point Y is different than requested
  459. {
  460. point.computed_y =y;
  461. point.computed_dir=0; // clear all computed directions
  462. }
  463. UInt dir_flag=DirToFlag(dir);
  464. if(!(point.computed_dir&dir_flag)) // if direction was not yet computed
  465. {
  466. point.computed_dir|=dir_flag; // set as computed
  467. Byte brightness=blocks.brightness(occl, x, y, z, DirToAxis(dir), dir_flag, min_level_i, neighbors); // calculate
  468. Color color(brightness, brightness, brightness);
  469. if( brightness<255) // if not max then add local lights
  470. {
  471. Vec col=0;
  472. REPA(lights)
  473. {
  474. C LightEx &light=lights[i];
  475. Vec pos(x, y, z); if(blocks.subDivision()>1)pos/=blocks.subDivision();
  476. Vec delta=light.ball.pos-pos;
  477. Flt dist2=delta.length2(), radius2=Sqr(light.ball.r);
  478. if( dist2<radius2)
  479. {
  480. Flt dot=1;
  481. if(dist2){Flt dist=Sqrt(dist2); delta/=dist; dot=Dot(VecDir[dir], delta); pos+=delta*EPSL;} // normalize and move slightly towards the light, so we don't start from the wall itself
  482. if(dot>0)
  483. {
  484. if(map)pos*=map->matrix(blocks_pos.x, blocks_pos.y); // convert to world space, needed for 'BlocksMap.raycast'
  485. if(map ? !map->raycast(pos, light.world_pos-pos) : !blocks.raycast(pos, light.ball.pos-pos))col+=((1-dist2/radius2)*dot)*light.color;
  486. }
  487. }
  488. }
  489. color=ColorAdd(color, col);
  490. }
  491. point.brightness[dir]=color;
  492. }
  493. return point.brightness[dir];
  494. }
  495. /******************************************************************************/
  496. // BLOCKS
  497. /******************************************************************************/
  498. void Blocks::zero()
  499. {
  500. _resolution=_sub_division=0;
  501. }
  502. Blocks::Blocks() {zero();}
  503. Blocks& Blocks::del()
  504. {
  505. _mesh .del();
  506. _levels .del();
  507. _materials .del();
  508. _mtrl_combos.del();
  509. zero(); return T;
  510. }
  511. Blocks& Blocks::create(Int resolution, Int sub_division)
  512. {
  513. del();
  514. T._resolution=Max(resolution, 0);
  515. subDivision(sub_division);
  516. return T;
  517. }
  518. /******************************************************************************/
  519. Bool Blocks::hasBlock(C VecI &pos)C
  520. {
  521. if(InRange(pos.x, resolution())
  522. && InRange(pos.z, resolution()))
  523. if(C Level *level=findLevel(pos.y))
  524. return level->map(pos.x, pos.z, resolution())!=0;
  525. return false;
  526. }
  527. Bool Blocks::hasBlock(C VecI &pos, Int min_level_i)C
  528. {
  529. if(InRange(pos.x, resolution())
  530. && InRange(pos.z, resolution()))
  531. if(C Level *level=findLevel(pos.y, min_level_i))
  532. return level->map(pos.x, pos.z, resolution())!=0;
  533. return false;
  534. }
  535. MaterialPtr Blocks::material(C VecI &pos)C
  536. {
  537. if(InRange(pos.x, resolution())
  538. && InRange(pos.z, resolution()))
  539. if(C Level *level=findLevel(pos.y))
  540. {
  541. Byte m=level->map(pos.x, pos.z, resolution());
  542. if(m && InRange(m, _mtrl_combos))
  543. {
  544. C MtrlCombo &mc=_mtrl_combos[m];
  545. if(InRange(mc.top, _materials))return _materials[mc.top];
  546. }
  547. }
  548. return null;
  549. }
  550. void Blocks::get(C VecI &pos, MaterialPtr &top, MaterialPtr &side, MaterialPtr &bottom)C
  551. {
  552. // don't clear materials at start, because those are unnecessary operations, which can be done on fail instead
  553. if(InRange(pos.x, resolution())
  554. && InRange(pos.z, resolution()))
  555. if(C Level *level=findLevel(pos.y))
  556. {
  557. Byte m=level->map(pos.x, pos.z, resolution());
  558. if(m && InRange(m, _mtrl_combos))
  559. {
  560. C MtrlCombo &mc=_mtrl_combos[m];
  561. if(InRange(mc.top , _materials))top =_materials[mc.top ];else top .clear();
  562. if(InRange(mc.side , _materials))side =_materials[mc.side ];else side .clear();
  563. if(InRange(mc.bottom, _materials))bottom=_materials[mc.bottom];else bottom.clear();
  564. return;
  565. }
  566. }
  567. top.clear(); side.clear(); bottom.clear();
  568. }
  569. Bool Blocks::set(Int x, Int y, Int z, Byte b)
  570. {
  571. if(InRange(x, resolution())
  572. && InRange(z, resolution()))
  573. if(Level *level=(b ? &getLevel(y) : findLevel(y))) // insert block ? always get level : find if exists to remove
  574. {
  575. Byte &m=level->map(x, z, resolution());
  576. if(m!=b){m=b; return true;}
  577. }
  578. return false;
  579. }
  580. Bool Blocks::set(Int x, Int y, Int z, C MaterialPtr &top, C MaterialPtr &side, C MaterialPtr &bottom)
  581. {
  582. if(!top && !side && !bottom)return set(x, y, z, 0); // if clear
  583. if( top && side && bottom) // if all are set (we can't set materials only if some of them are set)
  584. {
  585. Int t=_materials.getMaterialIndex(top ),
  586. s=_materials.getMaterialIndex(side ),
  587. b=_materials.getMaterialIndex(bottom);
  588. if(t>=0 && s>=0 && b>=0) // if all were found
  589. {
  590. Int mc=-1;
  591. if(!_mtrl_combos.elms())_mtrl_combos.New().set(0, 0, 0); // 0-th material combo is reserved for null materials
  592. FREPA(_mtrl_combos){C MtrlCombo &mtrl_combo=_mtrl_combos[i]; if(mtrl_combo.top==t && mtrl_combo.side==s && mtrl_combo.bottom==b){mc=i; break;}} // find existing
  593. if(mc< 0 && _mtrl_combos.elms()<256){mc=_mtrl_combos.elms(); _mtrl_combos.New().set(t, s, b);} // support indexes only up to 255
  594. if(mc>=0)return set(x, y, z, mc);
  595. }
  596. }
  597. return false; // no change was made
  598. }
  599. Bool Blocks::set(Int x, Int y, Int z, C MaterialPtr &material)
  600. {
  601. return set(x, y, z, material, material, material);
  602. }
  603. /******************************************************************************/
  604. Blocks& Blocks::subDivision(Int steps)
  605. {
  606. T._sub_division=Max(1, steps);
  607. return T;
  608. }
  609. Blocks& Blocks::setMesh(Flt tex_scale, C BlocksOcclusion *occl, C BoxI *local_box, C VecI2 &blocks_pos, C BlocksMap *map, C MemPtr<Light> &lights, Bool optimize, Flt max_face_length)
  610. {
  611. BoxI box; if(local_box)
  612. {
  613. box=*local_box; box.extend(Max(1, MaxBlocks(T, occl))); // we must extend the box by at least one, because block face creation depends on its neighbors, we also extend it by 'BlocksOcclusion' because the blocks can affect the lighting of the nearby blocks
  614. if(box.min.x>=resolution() || box.min.z>=resolution() || box.max.x<0 || box.max.z<0)return T; // update box is outside of range
  615. Clamp(box.min.x, 0, resolution()-1);
  616. Clamp(box.max.x, 0, resolution()-1);
  617. Clamp(box.min.z, 0, resolution()-1);
  618. Clamp(box.max.z, 0, resolution()-1);
  619. }else
  620. {
  621. box.setX(0, resolution()-1).setZ(0, resolution()-1);
  622. if(_levels.elms())box.setY(_levels.first().y, _levels.last().y);else box.setY(0, -1);
  623. }
  624. Neighbors neighbors(map, blocks_pos.x, blocks_pos.y, resolution());
  625. MemtN<Part, 256> parts;
  626. if(_levels.elms())
  627. {
  628. // add local lights
  629. MemtN<LightEx, 256> local_lights;
  630. if(lights.elms())
  631. {
  632. Box boxf=box; boxf.max+=1;
  633. Matrix m=(map ? map->matrix(blocks_pos.x, blocks_pos.y) : MatrixIdentity);
  634. REPA(lights)
  635. {
  636. LightEx light; SCAST(Light, light)=lights[i];
  637. light.world_pos=light.ball.pos; if(map)light.ball/=m;
  638. if(Cuts(boxf, light.ball))Swap(local_lights.New(), light);
  639. }
  640. }
  641. LevelBrightness level_brightness[2]={LevelBrightness(T, neighbors, occl, map, blocks_pos, local_lights),
  642. LevelBrightness(T, neighbors, occl, map, blocks_pos, local_lights)};
  643. REPA(level_brightness)
  644. {
  645. LevelBrightness &lb=level_brightness[i]; if(local_box)lb.clear(box.min.x, box.min.z, box.max.x, box.max.z);else lb.clear();
  646. }
  647. for(Int i=(local_box ? findLevelI(box.min.y) : 0); i<_levels.elms(); i++)
  648. {
  649. Level &level=_levels[i]; if(local_box && level.y>box.max.y)break;
  650. buildLevel(tex_scale, parts, box.min.x, box.min.z, box.max.x, box.max.z, i, neighbors, level_brightness);
  651. }
  652. }
  653. // mesh
  654. Memt<Int> sel;
  655. _mesh.parts.setNum(local_box ? Max(_mesh.parts.elms(), parts.elms()) : parts.elms()); // for updating only include new parts if any
  656. REPA(parts)if(InRange(i+1, _materials)) // 'parts[i]' are mapped to 'mesh.parts[i]' and 'materials[i+1]'
  657. {
  658. Part & part= parts[i];
  659. MeshPart &mpart=_mesh.parts[i];
  660. MeshBase & base=mpart.base;
  661. Bool removed=false;
  662. if(local_box) // for update, we need to first remove existing faces in the box
  663. {
  664. REPA(base.quad)
  665. {
  666. C VecI4 &ind =base.quad.ind(i);
  667. Vec center=Avg(base.vtx.pos(ind.x), base.vtx.pos(ind.y), base.vtx.pos(ind.z)) - base.vtx.nrm(ind.x)*0.5f;
  668. VecI pos =Floor(center);
  669. if(Cuts(pos, box))sel.add(i^SIGN_BIT);
  670. }
  671. if(sel.elms())
  672. {
  673. removed=true;
  674. base.removeFaces(sel); sel.clear();
  675. }
  676. }
  677. if(!local_box || removed || part.is()) // process only if resetting, something was removed or something is to be added
  678. {
  679. if(!local_box || !base.is())part.create(base);else{MeshBase temp; part.create(temp); base+=temp;} // if resetting or the base is empty then create it, otherwise add to it
  680. mpart.material(_materials[i+1]);
  681. base.setNormals(); // set vtx normals before welding because they need to be tested as well (otherwise holes on the sides could appear on blocks)
  682. base.weldVtx(VTX_TEX0|VTX_COLOR|VTX_NRM, 0.01f, EPS_COL_COS, -1);
  683. mpart.setAutoTanBin();
  684. if(optimize)
  685. {
  686. MeshBase optimized(base); optimized.weldCoplanarFaces(EPS_COL_COS, -1, false, max_face_length);
  687. mpart.render.create(optimized, ~0, false);
  688. mpart.setShader(0);
  689. }else mpart.setRender(false);
  690. }
  691. }
  692. _mesh.setBox();
  693. return T;
  694. }
  695. /******************************************************************************/
  696. Blocks& Blocks::setPhysBody(C Matrix &matrix, C BoxI *local_box, Bool create_actor)
  697. {
  698. BoxI box;
  699. if(local_box)
  700. {
  701. box=*local_box; box.extend(1); // we must extend the box by at least one, because block face creation depends on its neighbors
  702. if(box.min.x>=resolution() || box.min.z>=resolution() || box.max.x<0 || box.max.z<0)return T;
  703. }
  704. for(Int i=(local_box ? findLevelI(box.min.y) : 0); i<_levels.elms(); i++)
  705. {
  706. Level &level=_levels[i]; Int y=level.y; if(local_box && y>box.max.y)break;
  707. C Level *down =_levels.addr(i-1); if(down && down->y!=y-1)down=null;
  708. C Level *up =_levels.addr(i+1); if(up && up ->y!=y+1)up =null;
  709. level.createPhysBody(matrix, resolution(), down, up); if(create_actor)level.createActor();
  710. }
  711. return T;
  712. }
  713. Blocks& Blocks:: delActor() {REPAO(_levels).actor.del (); return T;}
  714. Blocks& Blocks::createActor() {REPAO(_levels).createActor(); return T;}
  715. /******************************************************************************/
  716. Bool Blocks::raycast(C Vec &start, C Vec &move, Vec *hit_pos, Vec *hit_normal, C Matrix *matrix)C
  717. {
  718. if(_levels.elms())
  719. {
  720. Vec local_start=start, local_move=move;
  721. if(matrix)
  722. {
  723. local_start/=*matrix;
  724. local_move /= matrix->orn();
  725. }
  726. Flt min_y=_levels.first().y ,
  727. max_y=_levels.last ().y+1;
  728. Box box(Vec(0, min_y, 0), Vec(resolution(), max_y, resolution())); box.extend(0.1f); // extend the box so we won't start inside, but from 1 block outside
  729. Vec hp; if(SweepPointBox(local_start, local_move, box, null, null, &hp))
  730. {
  731. Int last_level_i=SIGN_BIT;
  732. VecI last_pos=SIGN_BIT;
  733. Vec local_end=local_start+local_move;
  734. Bool was_inside=false;
  735. for(VoxelWalker walker(hp, local_end); walker.active(); walker.step())
  736. {
  737. C VecI &pos=walker.pos();
  738. if(InRange(pos.x, resolution())
  739. && InRange(pos.z, resolution()))
  740. {
  741. was_inside=true;
  742. if(last_level_i==SIGN_BIT)last_level_i=findLevelI(pos.y);
  743. if(C Level *level=toLevel(pos.y, last_level_i))
  744. {
  745. if(level->map(pos.x, pos.z, resolution()))
  746. {
  747. if(hit_pos)
  748. {
  749. if(last_pos.x==SIGN_BIT)
  750. {
  751. *hit_pos=hp;
  752. if(matrix)*hit_pos*=*matrix;
  753. }else
  754. {
  755. Flt frac=1;
  756. if(pos.x>last_pos.x)MIN(frac, LerpR(local_start.x, local_end.x, (Flt) pos.x));else // moving right
  757. if(pos.x<last_pos.x)MIN(frac, LerpR(local_start.x, local_end.x, (Flt)last_pos.x)); // moving left
  758. if(pos.y>last_pos.y)MIN(frac, LerpR(local_start.y, local_end.y, (Flt) pos.y));else // moving up
  759. if(pos.y<last_pos.y)MIN(frac, LerpR(local_start.y, local_end.y, (Flt)last_pos.y)); // moving down
  760. if(pos.z>last_pos.z)MIN(frac, LerpR(local_start.z, local_end.z, (Flt) pos.z));else // moving forward
  761. if(pos.z<last_pos.z)MIN(frac, LerpR(local_start.z, local_end.z, (Flt)last_pos.z)); // moving back
  762. *hit_pos=start+move*frac;
  763. }
  764. }
  765. if(hit_normal)
  766. {
  767. if(last_pos.x==SIGN_BIT)hit_normal->zero();
  768. else {*hit_normal=last_pos-pos; if(matrix)*hit_normal*=matrix->orn(); hit_normal->normalize();}
  769. }
  770. return true;
  771. }
  772. }
  773. }else if(was_inside)return false; // moved outside of range
  774. last_pos=pos;
  775. }
  776. }
  777. }
  778. return false;
  779. }
  780. /******************************************************************************/
  781. Blocks& Blocks::cleanLevels()
  782. {
  783. REPA(_levels)
  784. {
  785. C Level &level=_levels[i];
  786. REPD(z, resolution())
  787. REPD(x, resolution())if(level.map(x, z, resolution()))goto have_block;
  788. _levels.remove(i, true); // when no blocks were found on this level then remove it
  789. have_block:;
  790. }
  791. return T;
  792. }
  793. /******************************************************************************/
  794. Bool Blocks::cleanMtrlCombos(Bool is[256], Byte remap[256])
  795. {
  796. for(Int i=1; i<_mtrl_combos.elms(); i++)if(!is[i])
  797. {
  798. remap[0]=0;
  799. for(Int i=1, next=1; i<_mtrl_combos.elms(); i++)if( is[i])remap[i]=next++;else remap[i]=0;
  800. for(Int i=_mtrl_combos.elms(); --i>=1 ; )if(!is[i])_mtrl_combos.remove(i, true); // need to go from the end
  801. return true;
  802. }
  803. return false;
  804. }
  805. Blocks& Blocks::cleanMaterials()
  806. {
  807. if(_materials.elms()>1)
  808. {
  809. // get used material combos
  810. Bool mc_is[256]; Zero(mc_is); // assume all are unused
  811. REPA(_levels)
  812. {
  813. Level &level=_levels[i];
  814. REPD(z, resolution())
  815. REPD(x, resolution())mc_is[level.map(x, z, resolution())]=true;
  816. }
  817. // get used materials
  818. Bool mtrl_is[256]; ZeroN(mtrl_is, _materials.elms()); // assume all are unused
  819. REPA(_mtrl_combos)if(mc_is[i])
  820. {
  821. MtrlCombo &mc=_mtrl_combos[i];
  822. mtrl_is[mc.top ]=true;
  823. mtrl_is[mc.side ]=true;
  824. mtrl_is[mc.bottom]=true;
  825. }
  826. // 'mesh.parts[i]' are mapped to 'materials[i+1]'
  827. REPA(_materials)if(!mtrl_is[i] && i)_mesh.remove(i-1, false); // need to go from the end
  828. Byte remap[256];
  829. // remap material combos
  830. if(cleanMtrlCombos(mc_is, remap))REPA(_levels)
  831. {
  832. Level &level=_levels[i];
  833. REPD(z, resolution())
  834. REPD(x, resolution()){Byte &m=level.map(x, z, resolution()); m=remap[m];}
  835. }
  836. // remap materials
  837. if(_materials.clean(mtrl_is, remap))REPA(_mtrl_combos)
  838. {
  839. MtrlCombo &mc=_mtrl_combos[i];
  840. mc.top =remap[mc.top ];
  841. mc.side =remap[mc.side ];
  842. mc.bottom=remap[mc.bottom];
  843. }
  844. }
  845. return T;
  846. }
  847. /******************************************************************************/
  848. Bool Blocks::save(File &f, Bool include_mesh_and_phys_body, CChar *path)C
  849. {
  850. f.cmpUIntV(1); // version
  851. f.cmpUIntV(resolution()).cmpUIntV(subDivision());
  852. if(_materials .save (f, path))
  853. if(_mtrl_combos.saveRaw(f))
  854. {
  855. f.putBool (include_mesh_and_phys_body);
  856. f.cmpUIntV(_levels.elms()); FREPA(_levels)if(!_levels[i].save(f, resolution(), include_mesh_and_phys_body))return false;
  857. if(include_mesh_and_phys_body)if(!_mesh.save(f))return false;
  858. return f.ok();
  859. }
  860. return false;
  861. }
  862. /******************************************************************************/
  863. Bool Blocks::load(File &f, CChar *path)
  864. {
  865. del(); switch(f.decUIntV()) // version
  866. {
  867. case 1:
  868. {
  869. _resolution=f.decUIntV(); _sub_division=f.decUIntV();
  870. if(_materials .load (f, path))
  871. if(_mtrl_combos.loadRaw(f))
  872. {
  873. Bool include_mesh_and_phys_body=f.getBool();
  874. _levels.setNum(f.decUIntV()); FREPA(_levels)if(!_levels[i].load(f, resolution(), include_mesh_and_phys_body))goto error;
  875. if(include_mesh_and_phys_body)if(!_mesh.load(f))goto error;
  876. if(f.ok())return true;
  877. }
  878. }break;
  879. case 0:
  880. {
  881. f>>_resolution; _sub_division=1;
  882. if(_materials.load(f, path))
  883. {
  884. _mtrl_combos.setNum(_materials.elms()); REPA(_mtrl_combos){MtrlCombo &mc=_mtrl_combos[i]; mc.top=mc.side=mc.bottom=i;}
  885. Bool include_mesh_and_phys_body=f.getBool();
  886. _levels.setNum(f.getInt()); FREPA(_levels)if(!_levels[i].load(f, resolution(), include_mesh_and_phys_body))goto error;
  887. if(include_mesh_and_phys_body)if(!_mesh.load(f))goto error;
  888. if(f.ok())return true;
  889. }
  890. }break;
  891. }
  892. error:;
  893. del(); return false;
  894. }
  895. /******************************************************************************/
  896. Int Blocks::findLevelI(Int y )C {Int i; _levels.binarySearch(y, i, Blocks::CompareLevel); return i;}
  897. Blocks::Level* Blocks::findLevel (Int y, Int from) {for(; from<_levels.elms(); from++){Level &l=_levels[from]; if(l.y==y)return &l; if(l.y>y)break;} return null;}
  898. Blocks::Level* Blocks::findLevel (Int y ) { return _levels.binaryFind (y, Blocks::CompareLevel);}
  899. Blocks::Level& Blocks:: getLevel (Int y ) {Int i; if(!_levels.binarySearch(y, i, Blocks::CompareLevel))_levels.NewAt(i).init(resolution(), y); return _levels[i];}
  900. Blocks::Level* Blocks:: toLevel (Int y, Int &last)
  901. {
  902. if(_levels.elms())
  903. {
  904. Clamp(last, 0, _levels.elms()-1);
  905. Level &level=_levels[last]; Int dir=Sign(y-level.y); if(!dir)return &level;
  906. for(last+=dir; InRange(last, _levels); )
  907. {
  908. Level &level=_levels[last]; Int new_dir=Sign(y-level.y); if(!new_dir)return &level;
  909. if(dir!=new_dir)break; // if went too far and didn't find along the way
  910. }
  911. }
  912. return null;
  913. }
  914. /******************************************************************************/
  915. UInt Blocks::occlusion(C BlocksOcclusion::Node &node, C VecI &pos, AXIS_TYPE axis, Int min_level_i, C Neighbors &neighbors)C
  916. {
  917. VecI p=pos+node.pos; if(subDivision()>1)
  918. {
  919. p.x=DivFloor(p.x, subDivision());
  920. p.y=DivFloor(p.y, subDivision());
  921. p.z=DivFloor(p.z, subDivision());
  922. }
  923. if(InRange(p.x, resolution())
  924. && InRange(p.z, resolution())) // center
  925. {
  926. if(C Level *level=findLevel(p.y, min_level_i))if(level->map(p.x, p.z, resolution()))return node.fraction_axis[axis]; // if(hasBlock(p, min_level_i))
  927. }else
  928. if(p.x<0) // left
  929. {
  930. if(p.z<0) // back
  931. {
  932. if(neighbors.lb && neighbors.lb->hasBlock(p+VecI(resolution(), 0, resolution())))return node.fraction_axis[axis];
  933. }else
  934. if(p.z<resolution()) // middle
  935. {
  936. if(neighbors.l && neighbors.l ->hasBlock(p+VecI(resolution(), 0, 0)))return node.fraction_axis[axis];
  937. }else // forward
  938. {
  939. if(neighbors.lf && neighbors.lf->hasBlock(p+VecI(resolution(), 0, -resolution())))return node.fraction_axis[axis];
  940. }
  941. }else
  942. if(p.x<resolution()) // middle
  943. {
  944. if(p.z<0) // back
  945. {
  946. if(neighbors.b && neighbors.b->hasBlock(p+VecI(0, 0, resolution())))return node.fraction_axis[axis];
  947. }else // forward
  948. {
  949. if(neighbors.f && neighbors.f->hasBlock(p+VecI(0, 0, -resolution())))return node.fraction_axis[axis];
  950. }
  951. }else // right
  952. {
  953. if(p.z<0) // back
  954. {
  955. if(neighbors.rb && neighbors.rb->hasBlock(p+VecI(-resolution(), 0, resolution())))return node.fraction_axis[axis];
  956. }else
  957. if(p.z<resolution()) // middle
  958. {
  959. if(neighbors.r && neighbors.r ->hasBlock(p+VecI(-resolution(), 0, 0)))return node.fraction_axis[axis];
  960. }else // forward
  961. {
  962. if(neighbors.rf && neighbors.rf->hasBlock(p+VecI(-resolution(), 0, -resolution())))return node.fraction_axis[axis];
  963. }
  964. }
  965. UInt occl=0; REPA(node.nodes)occl+=occlusion(node.nodes[i], pos, axis, min_level_i, neighbors);
  966. return occl;
  967. }
  968. Byte Blocks::brightness(C BlocksOcclusion *occl, Int x, Int y, Int z, AXIS_TYPE axis, UInt dir_flag, Int min_level_i, C Neighbors &neighbors)C
  969. {
  970. if(occl)
  971. {
  972. UInt o=0;
  973. VecI pos(x, y, z);
  974. REPA(occl->_nodes)
  975. {
  976. C BlocksOcclusion::Node &node=occl->_nodes[i];
  977. if(node.dir&dir_flag) // this node is on the positive side of the AO hemisphere
  978. o+=occlusion(node, pos, axis, min_level_i, neighbors);
  979. }
  980. return 255-Min(o>>16, 255);
  981. }
  982. return 255;
  983. }
  984. /******************************************************************************/
  985. static void AdjustOrder(Blocks::Part &part) // depending on color difference, rotate quad order, so 2 triangles will have more smooth lighting
  986. {
  987. Int i=part.vtxs.elms()-4;
  988. C Blocks::Part::Vtx &a=part.vtxs[i], &b=part.vtxs[i+1], &c=part.vtxs[i+2], &d=part.vtxs[i+3];
  989. if(ColorDiffSum(a.col, c.col)<ColorDiffSum(b.col, d.col))part.quads.last().rotateOrder();
  990. }
  991. void Blocks::buildLevel(Flt tex_scale, MemPtrN<Part, 256> parts, Int min_x, Int min_z, Int max_x, Int max_z, Int level_i, C Neighbors &neighbors, LevelBrightness (&lb)[2])
  992. {
  993. Level &level=_levels [level_i ]; Int y=level.y;
  994. C Level *d_lvl=_levels.addr(level_i-1); if(d_lvl && d_lvl->y!=y-1)d_lvl=null;
  995. C Level *u_lvl=_levels.addr(level_i+1); if(u_lvl && u_lvl->y!=y+1)u_lvl=null;
  996. C Level *l_lvl=(neighbors.l ? neighbors.l->findLevel(y) : null),
  997. *r_lvl=(neighbors.r ? neighbors.r->findLevel(y) : null),
  998. *b_lvl=(neighbors.b ? neighbors.b->findLevel(y) : null),
  999. *f_lvl=(neighbors.f ? neighbors.f->findLevel(y) : null);
  1000. Flt sd=1.0f/subDivision();
  1001. FREPD(sy, subDivision())
  1002. {
  1003. Int Y=y*subDivision()+sy, Y1=Y+1;
  1004. Flt fy=Y*sd, fy1=Y1*sd;
  1005. // swap 'lb_low' and 'lb_high' with each step to reuse their values
  1006. LevelBrightness &lb_low =lb[Y &1].setLevelI(level_i),
  1007. &lb_high=lb[Y1&1].setLevelI(level_i+(sy==subDivision()-1)); // increase min level only if we're at the last step
  1008. for(Int z=min_z; z<=max_z; z++)
  1009. for(Int x=min_x; x<=max_x; x++)if(Byte b=level.map(x, z, resolution()))if(InRange(b, _mtrl_combos))
  1010. {
  1011. MtrlCombo &mc =_mtrl_combos[b]; // 'parts[i]' are mapped to 'mesh.parts[i]' and 'materials[i+1]'
  1012. Part &top =parts(mc.top -1),
  1013. &side =parts(mc.side -1),
  1014. &bottom=parts(mc.bottom-1);
  1015. Int L=x*subDivision(), R=L+subDivision();
  1016. Int B=z*subDivision(), F=B+subDivision();
  1017. // left
  1018. if((x<=0) ? (l_lvl ? !l_lvl->map(resolution()-1, z, resolution()) : true) : !level.map(x-1, z, resolution()))
  1019. {
  1020. if(subDivision()==1)
  1021. {
  1022. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1023. side.vtxs.New().set(Vec(x, y+1, z+1), Vec2(-z-1, -y-1)*tex_scale, lb_high.brightness(x, y+1, z+1, DIR_LEFT)); // lu
  1024. side.vtxs.New().set(Vec(x, y+1, z ), Vec2(-z , -y-1)*tex_scale, lb_high.brightness(x, y+1, z , DIR_LEFT)); // ru
  1025. side.vtxs.New().set(Vec(x, y , z ), Vec2(-z , -y )*tex_scale, lb_low .brightness(x, y , z , DIR_LEFT)); // rd
  1026. side.vtxs.New().set(Vec(x, y , z+1), Vec2(-z-1, -y )*tex_scale, lb_low .brightness(x, y , z+1, DIR_LEFT)); // ld
  1027. AdjustOrder(side);
  1028. }else
  1029. REPD(sz, subDivision())
  1030. {
  1031. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1032. Int Z=z*subDivision()+sz, Z1=Z+1; Flt fz=Z*sd, fz1=fz+sd;
  1033. side.vtxs.New().set(Vec(x, fy1, fz1), Vec2(-fz1, -fy1)*tex_scale, lb_high.brightness(L, Y1, Z1, DIR_LEFT)); // lu
  1034. side.vtxs.New().set(Vec(x, fy1, fz ), Vec2(-fz , -fy1)*tex_scale, lb_high.brightness(L, Y1, Z , DIR_LEFT)); // ru
  1035. side.vtxs.New().set(Vec(x, fy , fz ), Vec2(-fz , -fy )*tex_scale, lb_low .brightness(L, Y , Z , DIR_LEFT)); // rd
  1036. side.vtxs.New().set(Vec(x, fy , fz1), Vec2(-fz1, -fy )*tex_scale, lb_low .brightness(L, Y , Z1, DIR_LEFT)); // ld
  1037. AdjustOrder(side);
  1038. }
  1039. }
  1040. // right
  1041. if((x>=resolution()-1) ? (r_lvl ? !r_lvl->map(0, z, resolution()) : true) : !level.map(x+1, z, resolution()))
  1042. {
  1043. if(subDivision()==1)
  1044. {
  1045. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1046. side.vtxs.New().set(Vec(x+1, y+1, z ), Vec2(z , -y-1)*tex_scale, lb_high.brightness(x+1, y+1, z , DIR_RIGHT)); // lu
  1047. side.vtxs.New().set(Vec(x+1, y+1, z+1), Vec2(z+1, -y-1)*tex_scale, lb_high.brightness(x+1, y+1, z+1, DIR_RIGHT)); // ru
  1048. side.vtxs.New().set(Vec(x+1, y , z+1), Vec2(z+1, -y )*tex_scale, lb_low .brightness(x+1, y , z+1, DIR_RIGHT)); // rd
  1049. side.vtxs.New().set(Vec(x+1, y , z ), Vec2(z , -y )*tex_scale, lb_low .brightness(x+1, y , z , DIR_RIGHT)); // ld
  1050. AdjustOrder(side);
  1051. }else
  1052. REPD(sz, subDivision())
  1053. {
  1054. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1055. Int Z=z*subDivision()+sz, Z1=Z+1; Flt fz=Z*sd, fz1=fz+sd;
  1056. side.vtxs.New().set(Vec(x+1, fy1, fz ), Vec2(fz , -fy1)*tex_scale, lb_high.brightness(R, Y1, Z , DIR_RIGHT)); // lu
  1057. side.vtxs.New().set(Vec(x+1, fy1, fz1), Vec2(fz1, -fy1)*tex_scale, lb_high.brightness(R, Y1, Z1, DIR_RIGHT)); // ru
  1058. side.vtxs.New().set(Vec(x+1, fy , fz1), Vec2(fz1, -fy )*tex_scale, lb_low .brightness(R, Y , Z1, DIR_RIGHT)); // rd
  1059. side.vtxs.New().set(Vec(x+1, fy , fz ), Vec2(fz , -fy )*tex_scale, lb_low .brightness(R, Y , Z , DIR_RIGHT)); // ld
  1060. AdjustOrder(side);
  1061. }
  1062. }
  1063. // back
  1064. if((z<=0) ? (b_lvl ? !b_lvl->map(x, resolution()-1, resolution()) : true) : !level.map(x, z-1, resolution()))
  1065. {
  1066. if(subDivision()==1)
  1067. {
  1068. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1069. side.vtxs.New().set(Vec(x , y+1, z), Vec2(x , -y-1)*tex_scale, lb_high.brightness(x , y+1, z, DIR_BACK)); // lu
  1070. side.vtxs.New().set(Vec(x+1, y+1, z), Vec2(x+1, -y-1)*tex_scale, lb_high.brightness(x+1, y+1, z, DIR_BACK)); // ru
  1071. side.vtxs.New().set(Vec(x+1, y , z), Vec2(x+1, -y )*tex_scale, lb_low .brightness(x+1, y , z, DIR_BACK)); // rd
  1072. side.vtxs.New().set(Vec(x , y , z), Vec2(x , -y )*tex_scale, lb_low .brightness(x , y , z, DIR_BACK)); // ld
  1073. AdjustOrder(side);
  1074. }else
  1075. REPD(sx, subDivision())
  1076. {
  1077. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1078. Int X=x*subDivision()+sx, X1=X+1; Flt fx=X*sd, fx1=fx+sd;
  1079. side.vtxs.New().set(Vec(fx , fy1, z), Vec2(fx , -fy1)*tex_scale, lb_high.brightness(X , Y1, B, DIR_BACK)); // lu
  1080. side.vtxs.New().set(Vec(fx1, fy1, z), Vec2(fx1, -fy1)*tex_scale, lb_high.brightness(X1, Y1, B, DIR_BACK)); // ru
  1081. side.vtxs.New().set(Vec(fx1, fy , z), Vec2(fx1, -fy )*tex_scale, lb_low .brightness(X1, Y , B, DIR_BACK)); // rd
  1082. side.vtxs.New().set(Vec(fx , fy , z), Vec2(fx , -fy )*tex_scale, lb_low .brightness(X , Y , B, DIR_BACK)); // ld
  1083. AdjustOrder(side);
  1084. }
  1085. }
  1086. // forward
  1087. if((z>=resolution()-1) ? (f_lvl ? !f_lvl->map(x, 0, resolution()) : true) : !level.map(x, z+1, resolution()))
  1088. {
  1089. if(subDivision()==1)
  1090. {
  1091. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1092. side.vtxs.New().set(Vec(x+1, y+1, z+1), Vec2(-x-1, -y-1)*tex_scale, lb_high.brightness(x+1, y+1, z+1, DIR_FORWARD)); // lu
  1093. side.vtxs.New().set(Vec(x , y+1, z+1), Vec2(-x , -y-1)*tex_scale, lb_high.brightness(x , y+1, z+1, DIR_FORWARD)); // ru
  1094. side.vtxs.New().set(Vec(x , y , z+1), Vec2(-x , -y )*tex_scale, lb_low .brightness(x , y , z+1, DIR_FORWARD)); // rd
  1095. side.vtxs.New().set(Vec(x+1, y , z+1), Vec2(-x-1, -y )*tex_scale, lb_low .brightness(x+1, y , z+1, DIR_FORWARD)); // ld
  1096. AdjustOrder(side);
  1097. }else
  1098. REPD(sx, subDivision())
  1099. {
  1100. Int v=side.vtxs.elms(); side.quads.New().set(v, v+1, v+2, v+3);
  1101. Int X=x*subDivision()+sx, X1=X+1; Flt fx=X*sd, fx1=fx+sd;
  1102. side.vtxs.New().set(Vec(fx1, fy1, z+1), Vec2(-fx1, -fy1)*tex_scale, lb_high.brightness(X1, Y1, F, DIR_FORWARD)); // lu
  1103. side.vtxs.New().set(Vec(fx , fy1, z+1), Vec2(-fx , -fy1)*tex_scale, lb_high.brightness(X , Y1, F, DIR_FORWARD)); // ru
  1104. side.vtxs.New().set(Vec(fx , fy , z+1), Vec2(-fx , -fy )*tex_scale, lb_low .brightness(X , Y , F, DIR_FORWARD)); // rd
  1105. side.vtxs.New().set(Vec(fx1, fy , z+1), Vec2(-fx1, -fy )*tex_scale, lb_low .brightness(X1, Y , F, DIR_FORWARD)); // ld
  1106. AdjustOrder(side);
  1107. }
  1108. }
  1109. // down
  1110. if(sy==0)
  1111. if(!d_lvl || !d_lvl->map(x, z, resolution()))
  1112. {
  1113. if(subDivision()==1)
  1114. {
  1115. Int v=bottom.vtxs.elms(); bottom.quads.New().set(v, v+1, v+2, v+3);
  1116. bottom.vtxs.New().set(Vec(x , y , z ), Vec2(x , z )*tex_scale, lb_low.brightness(x , y, z , DIR_DOWN)); // lu
  1117. bottom.vtxs.New().set(Vec(x+1, y , z ), Vec2(x+1, z )*tex_scale, lb_low.brightness(x+1, y, z , DIR_DOWN)); // ru
  1118. bottom.vtxs.New().set(Vec(x+1, y , z+1), Vec2(x+1, z+1)*tex_scale, lb_low.brightness(x+1, y, z+1, DIR_DOWN)); // rd
  1119. bottom.vtxs.New().set(Vec(x , y , z+1), Vec2(x , z+1)*tex_scale, lb_low.brightness(x , y, z+1, DIR_DOWN)); // ld
  1120. AdjustOrder(bottom);
  1121. }else
  1122. REPD(sx, subDivision()){Int X=x*subDivision()+sx; Flt fx=X*sd, fx1=fx+sd;
  1123. REPD(sz, subDivision())
  1124. {
  1125. Int v=bottom.vtxs.elms(); bottom.quads.New().set(v, v+1, v+2, v+3);
  1126. Int Z=z*subDivision()+sz; Flt fz=Z*sd, fz1=fz+sd;
  1127. bottom.vtxs.New().set(Vec(fx , y , fz ), Vec2(fx , fz )*tex_scale, lb_low.brightness(X , Y, Z , DIR_DOWN)); // lu
  1128. bottom.vtxs.New().set(Vec(fx1, y , fz ), Vec2(fx1, fz )*tex_scale, lb_low.brightness(X+1, Y, Z , DIR_DOWN)); // ru
  1129. bottom.vtxs.New().set(Vec(fx1, y , fz1), Vec2(fx1, fz1)*tex_scale, lb_low.brightness(X+1, Y, Z+1, DIR_DOWN)); // rd
  1130. bottom.vtxs.New().set(Vec(fx , y , fz1), Vec2(fx , fz1)*tex_scale, lb_low.brightness(X , Y, Z+1, DIR_DOWN)); // ld
  1131. AdjustOrder(bottom);
  1132. }}
  1133. }
  1134. // up
  1135. if(sy==subDivision()-1)
  1136. if(!u_lvl || !u_lvl->map(x, z, resolution()))
  1137. {
  1138. if(subDivision()==1)
  1139. {
  1140. Int v=top.vtxs.elms(); top.quads.New().set(v, v+1, v+2, v+3);
  1141. top.vtxs.New().set(Vec(x , y+1, z+1), Vec2(x , -z-1)*tex_scale, lb_high.brightness(x , y+1, z+1, DIR_UP)); // lu
  1142. top.vtxs.New().set(Vec(x+1, y+1, z+1), Vec2(x+1, -z-1)*tex_scale, lb_high.brightness(x+1, y+1, z+1, DIR_UP)); // ru
  1143. top.vtxs.New().set(Vec(x+1, y+1, z ), Vec2(x+1, -z )*tex_scale, lb_high.brightness(x+1, y+1, z , DIR_UP)); // rd
  1144. top.vtxs.New().set(Vec(x , y+1, z ), Vec2(x , -z )*tex_scale, lb_high.brightness(x , y+1, z , DIR_UP)); // ld
  1145. AdjustOrder(top);
  1146. }else
  1147. REPD(sx, subDivision()){Int X=x*subDivision()+sx; Flt fx=X*sd, fx1=fx+sd;
  1148. REPD(sz, subDivision())
  1149. {
  1150. Int v=top.vtxs.elms(); top.quads.New().set(v, v+1, v+2, v+3);
  1151. Int Z=z*subDivision()+sz; Flt fz=Z*sd, fz1=fz+sd;
  1152. top.vtxs.New().set(Vec(fx , y+1, fz1), Vec2(fx , -fz1)*tex_scale, lb_high.brightness(X , Y1, Z+1, DIR_UP)); // lu
  1153. top.vtxs.New().set(Vec(fx1, y+1, fz1), Vec2(fx1, -fz1)*tex_scale, lb_high.brightness(X+1, Y1, Z+1, DIR_UP)); // ru
  1154. top.vtxs.New().set(Vec(fx1, y+1, fz ), Vec2(fx1, -fz )*tex_scale, lb_high.brightness(X+1, Y1, Z , DIR_UP)); // rd
  1155. top.vtxs.New().set(Vec(fx , y+1, fz ), Vec2(fx , -fz )*tex_scale, lb_high.brightness(X , Y1, Z , DIR_UP)); // ld
  1156. AdjustOrder(top);
  1157. }}
  1158. }
  1159. }
  1160. }
  1161. }
  1162. /******************************************************************************/
  1163. }
  1164. /******************************************************************************/