PageRenderTime 30ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/samples/3D/rayTracedMandelbulb/mandelbulb.ec

https://github.com/thexa4/sdk
C | 900 lines | 727 code | 99 blank | 74 comment | 126 complexity | 757be8c692206b2f65d2156d8ac8e2cf MD5 | raw file
  1. /********************************************************
  2. This sample renders the Mandelbulb with a simple
  3. raytracer. The Mandelbulb is a 3D version of the
  4. Mandelbrot set according to the formula:
  5. Z -> Z^8 + C where Z and C are hypercomplex number
  6. representing spherical coordinates.
  7. For more information about the Mandelbulb visit:
  8. http://www.skytopia.com/project/fractal/mandelbulb.html
  9. ********************************************************/
  10. import "ecere"
  11. public struct ColorARGBd { double a, r, g, b; };
  12. struct HyperComplex
  13. {
  14. double a, b, c;
  15. };
  16. define numThreads = 6;
  17. define iters = 5;//10;
  18. define power = 8;
  19. double epsilon = 0.001;
  20. double startOffDistance = 0.01;
  21. double minDistance = 0.1; //angleOffset * 2;
  22. define angleOffset = 0.0001;
  23. // Normals Computing
  24. //define angleOffset = 0.02;
  25. //define safetyFactor = 1.1;//1.6;
  26. //define angleOffset = 1;
  27. //define angleOffset = 0.03;
  28. //define angleOffset = 0.0001;
  29. //double minDistance = angleOffset*2;
  30. //double minDistance = 0.01
  31. Light light
  32. {
  33. multiplier = 0.75f;
  34. diffuse = lightSkyBlue;
  35. specular = blue;
  36. //orientation = Euler { pitch = 35, yaw = 15 };
  37. //orientation = Euler { pitch = -55, yaw = -25 };
  38. //orientation = Euler { Degrees { 0 } };
  39. //orientation = Euler { pitch = 140, 75 };
  40. //orientation = Euler { 40, -24 };
  41. //orientation = Euler { 0,0 };
  42. orientation = Euler { 15, -24 };
  43. };
  44. Light light2
  45. {
  46. multiplier = 0.75f;
  47. diffuse = lightYellow;
  48. specular = white;
  49. //orientation = Euler { pitch = 35, yaw = 15 };
  50. //orientation = Euler { pitch = -55, yaw = -25 };
  51. //orientation = Euler { Degrees { 0 } };
  52. orientation = Euler { yaw = 35, pitch = 40 };
  53. //orientation = Euler { 40, -24 };
  54. //orientation = Euler { 0,0 };
  55. //orientation = Euler { 15, 24 };
  56. };
  57. /*Light light3
  58. {
  59. multiplier = 1;
  60. ambient = { 0.2, 0.2, 0.2 };
  61. diffuse = gray;
  62. specular = white;
  63. orientation = Euler { yaw = 35, pitch = -40};
  64. };*/
  65. Camera camera
  66. {
  67. attachedQuaternion,
  68. position = { 0, 0, -80 };
  69. //orientation = Euler { -30, 0, 0 },
  70. //orientation = Euler { 30, 50, 0 },
  71. //orientation = Euler { 0, 179, 0 },
  72. fov = 53;
  73. };
  74. class BaseView3D : Window
  75. {
  76. hasMaximize = true;
  77. hasMinimize = true;
  78. hasClose = true;
  79. borderStyle = sizable;
  80. clientSize = { 400, 400 };
  81. text = "Hit Space to start rendering, 'S' to save";
  82. bool moving, lightMoving;
  83. Point startPosition;
  84. Euler startOrientation;
  85. bool OnLeftButtonDown(int x, int y, Modifiers mods)
  86. {
  87. if(!moving && !lightMoving)
  88. {
  89. startPosition.x = x;
  90. startPosition.y = y;
  91. startOrientation = camera.orientation;
  92. Capture();
  93. moving = true;
  94. }
  95. return true;
  96. }
  97. bool OnLeftButtonUp(int x, int y, Modifiers mods)
  98. {
  99. if(moving)
  100. {
  101. ReleaseCapture();
  102. moving = false;
  103. }
  104. return true;
  105. }
  106. bool OnRightButtonDown(int x, int y, Modifiers mods)
  107. {
  108. if(!moving && !lightMoving)
  109. {
  110. startPosition.x = x;
  111. startPosition.y = y;
  112. startOrientation = light.orientation;
  113. Capture();
  114. lightMoving = true;
  115. }
  116. return true;
  117. }
  118. bool OnRightButtonUp(int x, int y, Modifiers mods)
  119. {
  120. if(lightMoving)
  121. {
  122. ReleaseCapture();
  123. lightMoving = false;
  124. }
  125. return true;
  126. }
  127. bool OnMouseMove(int x, int y, Modifiers mods)
  128. {
  129. if(moving)
  130. {
  131. Euler euler
  132. {
  133. startOrientation.yaw - (x - startPosition.x),
  134. startOrientation.pitch + (y - startPosition.y),
  135. startOrientation.roll
  136. };
  137. camera.orientation = euler;
  138. Update(null);
  139. }
  140. else if(lightMoving)
  141. {
  142. light.orientation = Euler
  143. {
  144. startOrientation.yaw - (x - startPosition.x),
  145. startOrientation.pitch + (y - startPosition.y),
  146. startOrientation.roll
  147. };
  148. Update(null);
  149. }
  150. return true;
  151. }
  152. bool OnKeyHit(Key key, unichar ch)
  153. {
  154. switch((SmartKey) key)
  155. {
  156. case wheelDown: case minus: camera.position.z *= 1.1111111f; Update(null); break;
  157. case wheelUp: case plus: camera.position.z *= 0.9f; Update(null); break;
  158. }
  159. return true;
  160. }
  161. }
  162. class RenderThread : Thread
  163. {
  164. int start, end;
  165. bool done;
  166. unsigned int Main()
  167. {
  168. Bitmap bitmap = view3D.bitmap;
  169. ColorAlpha * picture = (ColorAlpha *)bitmap.picture;
  170. uint stride = bitmap.stride;
  171. int y, x;
  172. Line ray;
  173. ray.p0 = camera.cPosition;
  174. for(y = start; y <= end && !view3D.abort; y++)
  175. {
  176. Box box { 0, y, bitmap.width, y };
  177. for(x = 0; x < bitmap.width && !view3D.abort; x++)
  178. {
  179. Vector3D v, end;
  180. double a = 0, r = 0, g = 0, b = 0;
  181. double xx, yy;
  182. int xi, yi;
  183. ColorARGBd color;
  184. int count = 3;
  185. for(xi = 0, xx = x - 0.5; xi < count; xi++, xx += 0.5)
  186. {
  187. for(yi = 0, yy = y - 0.5; yi < count; yi++, yy += 0.5)
  188. {
  189. camera.Unproject({ xx, yy, camera.zMin }, v);
  190. camera.Untransform(v, end);
  191. ray.delta.Subtract(end, ray.p0);
  192. if(scene.Trace(ray, color))
  193. {
  194. a += color.a;
  195. r += color.r;
  196. g += color.g;
  197. b += color.b;
  198. }
  199. }
  200. }
  201. a /= count*count;
  202. r /= count*count;
  203. g /= count*count;
  204. b /= count*count;
  205. picture[y * stride + x] =
  206. {
  207. (byte)Min(255, Max(0, a*255)),
  208. { (byte)Min(255, Max(0, r*255)), (byte)Min(255, Max(0, g*255)), (byte)Min(255, Max(0, b*255)) }
  209. };
  210. }
  211. }
  212. done = true;
  213. return 0;
  214. }
  215. }
  216. class RTView : BaseView3D
  217. {
  218. bool processing;
  219. bool abort;
  220. Bitmap bitmap { };
  221. void OnResize(int width, int height)
  222. {
  223. BaseView3D::OnResize(width, height);
  224. }
  225. void OnRedraw(Surface surface)
  226. {
  227. if(bitmap.width == clientSize.w && bitmap.height && clientSize.h)
  228. surface.Blit(bitmap, 0,0, 0, 0, bitmap.width, bitmap.height);
  229. else
  230. surface.Filter(bitmap, 0,0, 0, 0, clientSize.w, clientSize.h, bitmap.width, bitmap.height);
  231. }
  232. bool OnClose(bool parentClosing)
  233. {
  234. abort = true;
  235. return true;
  236. }
  237. bool OnKeyDown(Key key, unichar ch)
  238. {
  239. if(key == s)
  240. {
  241. if(bitmap.Save("output.png", null, null))
  242. ShellOpen("output.png");
  243. }
  244. if(key == space)
  245. {
  246. if(!processing)
  247. {
  248. int t;
  249. RenderThread threads[numThreads];
  250. processing = true;
  251. abort = false;
  252. if(bitmap.width != clientSize.w || bitmap.height != clientSize.h)
  253. bitmap.Allocate(null, clientSize.w, clientSize.h, 0, pixelFormat888, false);
  254. memset(bitmap.picture, 0xFF, bitmap.sizeBytes);
  255. Update(null);
  256. UpdateDisplay();
  257. camera.Setup(bitmap.width, bitmap.height, null);
  258. camera.Update();
  259. for(lt : scene.lights)
  260. {
  261. Light * light = lt;
  262. Matrix mat;
  263. Vector3D vector { 0,0,1 };
  264. mat.RotationQuaternion(light->orientation);
  265. light->direction.MultMatrix(vector, mat);
  266. light->direction.Normalize(light->direction);
  267. vector = light->direction;
  268. mat.RotationQuaternion(camera.cOrientation);
  269. light->direction.MultMatrix(vector, mat);
  270. }
  271. scene.Compute();
  272. for(t = 0; t < numThreads; t++)
  273. {
  274. threads[t] = RenderThread { start = t * (bitmap.height / numThreads), end = Min(bitmap.height-1, (t+1) * (bitmap.height / numThreads) -1) };
  275. if(t == numThreads-1) threads[t].end = bitmap.height-1;
  276. incref threads[t];
  277. threads[t].Create();
  278. }
  279. while(true)
  280. {
  281. for(t = 0; t < numThreads; t++)
  282. {
  283. if(!threads[t].done) break;
  284. }
  285. if(t == numThreads)
  286. break;
  287. if(app.ProcessInput(true))
  288. app.Wait();
  289. Update(null);
  290. UpdateDisplay();
  291. }
  292. for(t = 0; t < numThreads; t++)
  293. delete threads[t];
  294. processing = false;
  295. }
  296. else
  297. abort = true;
  298. }
  299. return true;
  300. }
  301. }
  302. class RTObject
  303. {
  304. Material material { diffuse = { 40/255.0f, 200/255.0f, 1.0f } };
  305. Quaternion orientation { 1, 0, 0, 0 };
  306. Vector3D localLightDirection[2];
  307. Vector3D localCameraDirection;
  308. virtual void Compute();
  309. virtual bool Render(Line ray, ColorARGBd * color, Vector3D intersect, Vector3D vIntersect)
  310. {
  311. return false;
  312. }
  313. }
  314. class RTScene
  315. {
  316. List<RTObject> objects { };
  317. List<Light *> lights { };
  318. void Compute()
  319. {
  320. for(o : objects)
  321. o.Compute();
  322. }
  323. bool Trace(Line ray, ColorARGBd color)
  324. {
  325. Vector3D vIntersect;
  326. double z = MAXDOUBLE;
  327. bool result = false;
  328. color = { };
  329. for(o : objects)
  330. {
  331. ColorARGBd newColor;
  332. // TOFIX: &ray here has different behavior?
  333. if(o.Render(ray, newColor, null, vIntersect) && vIntersect.z < z)
  334. {
  335. z = vIntersect.z;
  336. // TODO: Add blending support here
  337. color = newColor;
  338. result = true;
  339. }
  340. }
  341. return result;
  342. }
  343. }
  344. class RTCube : RTObject
  345. {
  346. Vector3D size;
  347. Vector3D center;
  348. Plane planes[6];
  349. Vector3D normals[6];
  350. Matrix mat { };
  351. Matrix inverse { };
  352. void Compute()
  353. {
  354. int c;
  355. Vector3D vertices[24] =
  356. {
  357. { -size.x/2,-size.y/2,-size.z/2 },
  358. { size.x/2,-size.y/2,-size.z/2 },
  359. { size.x/2, size.y/2,-size.z/2 },
  360. { -size.x/2, size.y/2,-size.z/2 },
  361. { -size.x/2,-size.y/2, size.z/2 },
  362. { size.x/2,-size.y/2, size.z/2 },
  363. { size.x/2, size.y/2, size.z/2 },
  364. { -size.x/2, size.y/2, size.z/2 },
  365. { -size.x/2,-size.y/2,-size.z/2 },
  366. { size.x/2,-size.y/2,-size.z/2 },
  367. { size.x/2, size.y/2,-size.z/2 },
  368. { -size.x/2, size.y/2,-size.z/2 },
  369. { -size.x/2,-size.y/2, size.z/2 },
  370. { size.x/2,-size.y/2, size.z/2 },
  371. { size.x/2, size.y/2, size.z/2 },
  372. { -size.x/2, size.y/2, size.z/2 },
  373. { -size.x/2,-size.y/2,-size.z/2 },
  374. { size.x/2,-size.y/2,-size.z/2 },
  375. { size.x/2, size.y/2,-size.z/2 },
  376. { -size.x/2, size.y/2,-size.z/2 },
  377. { -size.x/2,-size.y/2, size.z/2 },
  378. { size.x/2,-size.y/2, size.z/2 },
  379. { size.x/2, size.y/2, size.z/2 },
  380. { -size.x/2, size.y/2, size.z/2 }
  381. };
  382. uint16 indices[6][4] =
  383. {
  384. // up, front, down, back, right, left
  385. { 17,21,20,16 },
  386. { 0,3,2,1 },
  387. { 22,18,19,23 },
  388. { 5,6,7,4 },
  389. { 9,10,14,13 },
  390. { 12,15,11,8 }
  391. //{ 8,11,15,12 }
  392. };
  393. Matrix m2;
  394. Vector3D vector { 0, 0, 1 };
  395. mat.RotationQuaternion(orientation);
  396. m2.RotationQuaternion(orientation);
  397. inverse.Transpose(m2);
  398. c = 0;
  399. for(lt : scene.lights)
  400. {
  401. Light * light = lt;
  402. localLightDirection[c].MultMatrix(light->direction, inverse);
  403. localLightDirection[c].Normalize(localLightDirection[c]);
  404. c++;
  405. }
  406. localCameraDirection.MultMatrix(vector, inverse);
  407. localCameraDirection.Normalize(localCameraDirection);
  408. for(c = 0; c < 24; c++)
  409. {
  410. Vector3D v;
  411. v.MultMatrix(vertices[c], mat);
  412. vertices[c].Add(v, center);
  413. }
  414. for(c = 0; c < 6; c++)
  415. planes[c].FromPoints(vertices[indices[c][0]], vertices[indices[c][1]], vertices[indices[c][2]]);
  416. }
  417. bool Render(Line ray, ColorARGBd * color, Vector3D i, Vector3D vi)
  418. {
  419. int plane;
  420. Vector3D intersect, lastIntersect;
  421. bool gotIntersect = false;
  422. Color colors[6] =
  423. {
  424. yellow, blue, red, green, orange, purple
  425. };
  426. for(plane = 0; plane < 6; plane++)
  427. {
  428. double divisor = planes[plane].a * ray.delta.x + planes[plane].b * ray.delta.y + planes[plane].c * ray.delta.z;
  429. if(divisor)
  430. {
  431. int c;
  432. bool visible = true;
  433. int sign = 0;
  434. planes[plane].IntersectLine(ray, intersect);
  435. for(c = 0; c < 6; c++)
  436. {
  437. if(c != plane)
  438. {
  439. double result = planes[c].a * intersect.x + planes[c].b * intersect.y + planes[c].c * intersect.z + planes[c].d;
  440. if(result)
  441. {
  442. if(result < 0)
  443. {
  444. visible = false;
  445. break;
  446. }
  447. }
  448. }
  449. }
  450. if(visible && gotIntersect)
  451. {
  452. Vector3D v;
  453. camera.TransformPoint(v, intersect);
  454. if(v.z > lastIntersect.z)
  455. visible = false;
  456. }
  457. if(visible)
  458. {
  459. if(i != null) i = intersect;
  460. {
  461. double l = planes[plane].normal.DotProduct(localLightDirection[0]);
  462. ColorRGB c = colors[plane];
  463. if(l < 0) l = 0;
  464. l += 0.1;
  465. if(l > 1) l = 1;
  466. //if(color) *color = { 255, (byte)(c.r * l), (byte)(c.g * l), (byte)(c.b * l) };
  467. c.r = Min(1,Max(0, c.r));
  468. c.g = Min(1,Max(0, c.g));
  469. c.b = Min(1,Max(0, c.b));
  470. //*color = { 255, (byte)Min(255, Max(0, r*255)), (byte)Min(255, Max(0, g*255)), (byte)Min(255, Max(0, b*255)) };
  471. if(color) *color = { 1.0, c.r, c.g, c.b };
  472. }
  473. gotIntersect = true;
  474. camera.TransformPoint(lastIntersect, intersect);
  475. if(vi != null) vi = lastIntersect;
  476. }
  477. }
  478. }
  479. return gotIntersect;
  480. }
  481. }
  482. struct Mandelbulb
  483. {
  484. Vector3D size;
  485. double power;
  486. bool IsPointInside(Vector3D p, int iters)
  487. {
  488. HyperComplex Z { };
  489. HyperComplex C { p.x * 4 / size.x, p.y * 4 / size.y, p.z * 4 / size.z};
  490. int i;
  491. double zm;
  492. for(i = 0; i < iters; i++)
  493. {
  494. if(i > 0)
  495. {
  496. if(0)//power == 8)
  497. {
  498. double a;
  499. double x = Z.a, y = Z.b, z = Z.c;
  500. double x2 = x*x;
  501. double x4 = x2*x2;
  502. double x6 = x4*x2;
  503. double x8 = x6*x2;
  504. double y2 = y*y;
  505. double y4 = y2*y2;
  506. double y6 = y4*y2;
  507. double y8 = y6*y2;
  508. double z2 = z*z;
  509. double z4 = z2*z2;
  510. double z6 = z4*z2;
  511. double z8 = z6*z2;
  512. double r2xy = x2 + y2;
  513. double r4xy = r2xy*r2xy;
  514. double r6xy = r4xy*r2xy;
  515. double r8xy = r4xy*r4xy;
  516. a = 1.0 + (z8 - 28*z6*r2xy + 70*z4 * r4xy - 28*z2 * r6xy) / r8xy;
  517. Z.a = a * (x8 - 28*x6*y2 + 70*x4*y4 - 28*x2*y6 + y8);
  518. Z.b = 8*a*x*y * (x6 - 7*x4*y2 + 7*x2*y4 - y6);
  519. Z.c = 8*z*sqrt(r2xy)*(z2 - r2xy)*(z4 - 6*z2*r2xy + r4xy);
  520. }
  521. else
  522. {
  523. double theta = atan2(sqrt(Z.a*Z.a + Z.b*Z.b), Z.c) * power;
  524. double phi = atan2(Z.b, Z.a) * power;
  525. double raised = zm*zm*zm*zm*zm*zm*zm*zm;
  526. double sinT = sin(theta);
  527. Z.a = raised * sinT * cos(phi) + C.a;
  528. Z.b = raised * sinT * sin(phi) + C.b;
  529. Z.c = raised * cos(theta) + C.c;
  530. }
  531. //Z.a += C.a;
  532. //Z.b += C.b;
  533. //Z.c += C.c;
  534. }
  535. else
  536. Z = C;
  537. zm = sqrt(Z.a * Z.a + Z.b * Z.b + Z.c * Z.c);
  538. if(zm - epsilon >= 2)
  539. return false;
  540. }
  541. return i == iters;
  542. }
  543. };
  544. class RTMandelbulb : RTCube
  545. {
  546. Mandelbulb bulb { };
  547. void Compute()
  548. {
  549. Quaternion q;
  550. bulb.size = size;
  551. bulb.power = power;
  552. RTCube::Compute();
  553. }
  554. bool WalkTowards(Line l, Vector3D is, double dt, int refineCount, bool backwards)
  555. {
  556. double t = 0;
  557. int refine = 0;
  558. double refineDT = dt;
  559. double stopT = MAXDOUBLE;
  560. bool firstPointInside = false;
  561. if(backwards && bulb.IsPointInside(l.p0, iters))
  562. {
  563. firstPointInside = true;
  564. l.delta.Scale(l.delta, -1);
  565. }
  566. while(true)
  567. {
  568. Vector3D p
  569. {
  570. l.p0.x - l.delta.x * t,
  571. l.p0.y - l.delta.y * t,
  572. l.p0.z - l.delta.z * t
  573. };
  574. if(p.x + refineDT*10 < -size.x / 2 || p.x - refineDT*10 > size.x / 2 ||
  575. p.y + refineDT*10 < -size.y / 2 || p.y - refineDT*10 > size.y / 2 ||
  576. p.z + refineDT*10 < -size.z / 2 || p.z - refineDT*10 > size.z / 2)
  577. {
  578. Print("");
  579. break;
  580. }
  581. if(bulb.IsPointInside(p, iters) != firstPointInside)
  582. {
  583. refine++;
  584. stopT = t;
  585. t -= refineDT;
  586. refineDT /= 2;
  587. }
  588. if(t > stopT)
  589. {
  590. Vector3D p
  591. {
  592. l.p0.x - l.delta.x * stopT,
  593. l.p0.y - l.delta.y * stopT,
  594. l.p0.z - l.delta.z * stopT
  595. };
  596. is = p;
  597. return true;
  598. }
  599. if(refine == refineCount)
  600. {
  601. if(is != null) is = p;
  602. return true;
  603. }
  604. t += refineDT;
  605. }
  606. return false;
  607. }
  608. bool Render(Line ray, ColorARGBd * color, Vector3D i, Vector3D vi)
  609. {
  610. Vector3D intersect;
  611. if(RTCube::Render(ray, null, intersect, vi))
  612. {
  613. Vector3D rayDirection;
  614. Vector3D localIntersect;
  615. Vector3D p;
  616. Line toBulb;
  617. rayDirection.MultMatrix(ray.delta, inverse);
  618. localIntersect.MultMatrix(intersect, inverse);
  619. rayDirection.Normalize(rayDirection);
  620. toBulb.p0 = localIntersect;
  621. toBulb.delta = rayDirection;
  622. if(WalkTowards(toBulb, p, 0.5, 30, false))
  623. {
  624. if(color)
  625. {
  626. Angle offset;
  627. double r = p.length;
  628. Angle theta = acos(p.z / r);
  629. Angle phi = atan2(p.y, p.x);
  630. Vector3D normal { };
  631. int count = 0;
  632. //Angle
  633. offset = 2 * asin(angleOffset / (2*r));
  634. /*{
  635. Vector3D w, v;
  636. double focal = camera.focal.w;
  637. w.MultMatrix(p, mat);
  638. camera.TransformPoint(v, w);
  639. v.x = v.y = angleOffset;
  640. offset = v.x * v.z / focal;
  641. // offset = v.x*focal/v.z;
  642. minDistance = offset * 2;
  643. }*/
  644. //offset = startOffset;
  645. //for(offset = startOffset - offset/2.0; offset < startOffset + offset/2.0; offset += startOffset/10.0)
  646. {
  647. Vector3D points[4];
  648. bool found[4] = { 0 };
  649. int c;
  650. int numFound = 0;
  651. double distances[4];
  652. if(i != null) i = p;
  653. if(vi != null)
  654. {
  655. Vector3D wp;
  656. wp.MultMatrix(p, mat);
  657. camera.TransformPoint(vi, wp);
  658. }
  659. for(c = 0; c < 4; c++)
  660. {
  661. Line toBulb2;
  662. Vector3D pp;
  663. Angle otheta = theta, ophi = phi;
  664. double sinT;
  665. switch(c)
  666. {
  667. case 0: otheta += offset; break;
  668. case 1: ophi += offset; break;
  669. case 2: otheta -= offset; break;
  670. case 3: ophi -= offset; break;
  671. }
  672. sinT = sin(otheta);
  673. toBulb2.p0.x = (r + startOffDistance) /** safetyFactor*/ * sinT * cos(ophi);
  674. toBulb2.p0.y = (r + startOffDistance) /** safetyFactor*/ * sinT * sin(ophi);
  675. toBulb2.p0.z = (r + startOffDistance) /** safetyFactor*/ * cos(otheta);
  676. toBulb2.delta = toBulb2.p0;
  677. toBulb2.delta.Normalize(toBulb2.delta);
  678. //if(WalkTowards(toBulb2, pp, ((r * safetyFactor) - r) / 2.0, 30))
  679. if(WalkTowards(toBulb2, pp, (r + startOffDistance - r) / 2.0, 30, true))
  680. {
  681. Vector3D dif { pp.x - p.x, pp.y - p.y, pp.z - p.z };
  682. double d = dif.x*dif.x+dif.y*dif.y+dif.z*dif.z;
  683. distances[c] = d;
  684. found[c] = true;
  685. numFound++;
  686. points[c] = pp;
  687. }
  688. }
  689. if(numFound >= 2)
  690. {
  691. Plane normalPlane { };
  692. double d1 = (found[0] && found[2]) ? (distances[0] * distances[1]) : MAXDOUBLE;
  693. double d2 = (found[1] && found[2]) ? (distances[1] * distances[2]) : MAXDOUBLE;
  694. double d3 = (found[2] && found[3]) ? (distances[2] * distances[3]) : MAXDOUBLE;
  695. double d4 = (found[3] && found[0]) ? (distances[3] * distances[0]) : MAXDOUBLE;
  696. if(d1 < minDistance || d1 < d2 && d1 < d3 && d1 < d4)
  697. {
  698. normalPlane.FromPoints(points[1], p, points[0]);
  699. normal.Add(normal, normalPlane.normal);
  700. count++;
  701. }
  702. if(d2 < minDistance || d2 < d1 && d2 < d3 && d2 < d4)
  703. {
  704. normalPlane.FromPoints(points[2], p, points[1]);
  705. normal.Add(normal, normalPlane.normal);
  706. count++;
  707. }
  708. if(d1 < minDistance || d3 < d1 && d3 < d2 && d3 < d4)
  709. {
  710. normalPlane.FromPoints(points[3], p, points[2]);
  711. normal.Add(normal, normalPlane.normal);
  712. count++;
  713. }
  714. if(d4 < minDistance || d4 < d1 && d4 < d2 && d4 < d2)
  715. {
  716. normalPlane.FromPoints(points[0], p, points[3]);
  717. normal.Add(normal, normalPlane.normal);
  718. count++;
  719. }
  720. }
  721. }
  722. normal.Scale(normal, 1.0/count);
  723. normal.Normalize(normal);
  724. {
  725. double r = 0, g = 0, b = 0;
  726. int lid = 0;
  727. for(lt : scene.lights)
  728. {
  729. Light * light = lt;
  730. double l = normal.DotProduct(localLightDirection[lid]);
  731. double sl = l;
  732. bool shadowed = false;
  733. Line toLight { p };
  734. toLight.delta = localLightDirection[lid];
  735. //toLight.p0.Add(toLight.p0, toLight.delta);
  736. toLight.p0.Subtract(toLight.p0, toLight.delta);
  737. if(l > 0)
  738. {
  739. if(WalkTowards(toLight, null, 0.5, 30, false))
  740. shadowed = true;
  741. }
  742. if(l < 0) l = 0;
  743. if(shadowed) l /= 3;
  744. r += light->multiplier * light->ambient.r * material.ambient.r + l * material.diffuse.r * light->multiplier * light->diffuse.r;
  745. g += light->multiplier * light->ambient.g * material.ambient.g + l * material.diffuse.g * light->multiplier * light->diffuse.g;
  746. b += light->multiplier * light->ambient.b * material.ambient.b + l * material.diffuse.b * light->multiplier * light->diffuse.b;
  747. if(!shadowed)
  748. {
  749. double n2 = normal.DotProduct(normal);
  750. Vector3D R = normal;
  751. R.Scale(R, 2*sl/n2);
  752. R.Subtract(R, localLightDirection[lid]);
  753. R.Normalize(R);
  754. l = R.DotProduct(localCameraDirection);
  755. if(l < 0) l = 0;
  756. r += pow(l, material.power) * light->multiplier * light->specular.r * material.specular.r;
  757. g += pow(l, material.power) * light->multiplier * light->specular.g * material.specular.g;
  758. b += pow(l, material.power) * light->multiplier * light->specular.b * material.specular.b;
  759. }
  760. lid++;
  761. }
  762. if(r < 0) r = 0; if(r > 1) r = 1;
  763. if(g < 0) g = 0; if(g > 1) g = 1;
  764. if(b < 0) b = 0; if(b > 1) b = 1;
  765. *color = { 1.0, r, g, b };
  766. //*color = { 255, (byte)Min(255, Max(0, r*255)), (byte)Min(255, Max(0, g*255)), (byte)Min(255, Max(0, b*255)) };
  767. }
  768. // *color = red;
  769. }
  770. return true;
  771. }
  772. }
  773. return false;
  774. }
  775. }
  776. RTScene scene { };
  777. RTView view3D { };
  778. class MandelbulbApp : GuiApplication
  779. {
  780. bool Init()
  781. {
  782. scene.lights.Add(&light);
  783. scene.lights.Add(&light2);
  784. //scene.lights.Add(&light3);
  785. //scene.objects.Add(RTCube { center = { }, size = { 100, 100, 100 } });
  786. scene.objects.Add(RTMandelbulb {
  787. material.power = 16;
  788. material.ambient = blanchedAlmond;
  789. material.specular = blanchedAlmond;
  790. material.diffuse = blanchedAlmond,
  791. center = { }, orientation = Euler { yaw = 0, pitch = 220 /*-140*/ }, size = { 100, 100, 100 } });
  792. //scene.objects.Add(RTCube { center = { -40, 0, 0 }, orientation = Euler { 143, -138, 0 }, size = { 50, 50, 50 } });
  793. //scene.objects.Add(RTCube { center = { 40, 0, 0}, orientation = Euler { -145, -22, 0 }, size = { 30, 30, 30 } });
  794. return true;
  795. }
  796. }
  797. define app = (MandelbulbApp)__thisModule;