OcclusionBuffer.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. //
  2. // Copyright (c) 2008-2015 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Graphics/Camera.h"
  24. #include "../Graphics/OcclusionBuffer.h"
  25. #include "../IO/Log.h"
  26. #include "../DebugNew.h"
  27. namespace Atomic
  28. {
  29. static const unsigned CLIPMASK_X_POS = 0x1;
  30. static const unsigned CLIPMASK_X_NEG = 0x2;
  31. static const unsigned CLIPMASK_Y_POS = 0x4;
  32. static const unsigned CLIPMASK_Y_NEG = 0x8;
  33. static const unsigned CLIPMASK_Z_POS = 0x10;
  34. static const unsigned CLIPMASK_Z_NEG = 0x20;
  35. OcclusionBuffer::OcclusionBuffer(Context* context) :
  36. Object(context),
  37. buffer_(0),
  38. width_(0),
  39. height_(0),
  40. numTriangles_(0),
  41. maxTriangles_(OCCLUSION_DEFAULT_MAX_TRIANGLES),
  42. cullMode_(CULL_CCW),
  43. depthHierarchyDirty_(true),
  44. reverseCulling_(false),
  45. nearClip_(0.0f),
  46. farClip_(0.0f)
  47. {
  48. }
  49. OcclusionBuffer::~OcclusionBuffer()
  50. {
  51. }
  52. bool OcclusionBuffer::SetSize(int width, int height)
  53. {
  54. // Force the height to an even amount of pixels for better mip generation
  55. if (height & 1)
  56. ++height;
  57. if (width == width_ && height == height_)
  58. return true;
  59. if (width <= 0 || height <= 0)
  60. return false;
  61. if (!IsPowerOfTwo((unsigned)width))
  62. {
  63. LOGERROR("Width is not a power of two");
  64. return false;
  65. }
  66. width_ = width;
  67. height_ = height;
  68. // Reserve extra memory in case 3D clipping is not exact
  69. fullBuffer_ = new int[width * (height + 2) + 2];
  70. buffer_ = fullBuffer_.Get() + width + 1;
  71. mipBuffers_.Clear();
  72. // Build buffers for mip levels
  73. for (;;)
  74. {
  75. width = (width + 1) / 2;
  76. height = (height + 1) / 2;
  77. mipBuffers_.Push(SharedArrayPtr<DepthValue>(new DepthValue[width * height]));
  78. if (width <= OCCLUSION_MIN_SIZE && height <= OCCLUSION_MIN_SIZE)
  79. break;
  80. }
  81. LOGDEBUG("Set occlusion buffer size " + String(width_) + "x" + String(height_) + " with " +
  82. String(mipBuffers_.Size()) + " mip levels");
  83. CalculateViewport();
  84. return true;
  85. }
  86. void OcclusionBuffer::SetView(Camera* camera)
  87. {
  88. if (!camera)
  89. return;
  90. view_ = camera->GetView();
  91. projection_ = camera->GetProjection(false);
  92. viewProj_ = projection_ * view_;
  93. nearClip_ = camera->GetNearClip();
  94. farClip_ = camera->GetFarClip();
  95. reverseCulling_ = camera->GetReverseCulling();
  96. CalculateViewport();
  97. }
  98. void OcclusionBuffer::SetMaxTriangles(unsigned triangles)
  99. {
  100. maxTriangles_ = triangles;
  101. }
  102. void OcclusionBuffer::SetCullMode(CullMode mode)
  103. {
  104. if (reverseCulling_)
  105. {
  106. if (mode == CULL_CW)
  107. mode = CULL_CCW;
  108. else if (mode == CULL_CCW)
  109. mode = CULL_CW;
  110. }
  111. cullMode_ = mode;
  112. }
  113. void OcclusionBuffer::Reset()
  114. {
  115. numTriangles_ = 0;
  116. }
  117. void OcclusionBuffer::Clear()
  118. {
  119. if (!buffer_)
  120. return;
  121. Reset();
  122. int* dest = buffer_;
  123. int count = width_ * height_;
  124. int fillValue = (int)OCCLUSION_Z_SCALE;
  125. while (count--)
  126. *dest++ = fillValue;
  127. depthHierarchyDirty_ = true;
  128. }
  129. bool OcclusionBuffer::Draw(const Matrix3x4& model, const void* vertexData, unsigned vertexSize, unsigned vertexStart,
  130. unsigned vertexCount)
  131. {
  132. const unsigned char* srcData = ((const unsigned char*)vertexData) + vertexStart * vertexSize;
  133. Matrix4 modelViewProj = viewProj_ * model;
  134. depthHierarchyDirty_ = true;
  135. // Theoretical max. amount of vertices if each of the 6 clipping planes doubles the triangle count
  136. Vector4 vertices[64 * 3];
  137. // 16-bit indices
  138. unsigned index = 0;
  139. while (index + 2 < vertexCount)
  140. {
  141. if (numTriangles_ >= maxTriangles_)
  142. return false;
  143. const Vector3& v0 = *((const Vector3*)(&srcData[index * vertexSize]));
  144. const Vector3& v1 = *((const Vector3*)(&srcData[(index + 1) * vertexSize]));
  145. const Vector3& v2 = *((const Vector3*)(&srcData[(index + 2) * vertexSize]));
  146. vertices[0] = ModelTransform(modelViewProj, v0);
  147. vertices[1] = ModelTransform(modelViewProj, v1);
  148. vertices[2] = ModelTransform(modelViewProj, v2);
  149. DrawTriangle(vertices);
  150. index += 3;
  151. }
  152. return true;
  153. }
  154. bool OcclusionBuffer::Draw(const Matrix3x4& model, const void* vertexData, unsigned vertexSize, const void* indexData,
  155. unsigned indexSize, unsigned indexStart, unsigned indexCount)
  156. {
  157. const unsigned char* srcData = (const unsigned char*)vertexData;
  158. Matrix4 modelViewProj = viewProj_ * model;
  159. depthHierarchyDirty_ = true;
  160. // Theoretical max. amount of vertices if each of the 6 clipping planes doubles the triangle count
  161. Vector4 vertices[64 * 3];
  162. // 16-bit indices
  163. if (indexSize == sizeof(unsigned short))
  164. {
  165. const unsigned short* indices = ((const unsigned short*)indexData) + indexStart;
  166. const unsigned short* indicesEnd = indices + indexCount;
  167. while (indices < indicesEnd)
  168. {
  169. if (numTriangles_ >= maxTriangles_)
  170. return false;
  171. const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
  172. const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
  173. const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
  174. vertices[0] = ModelTransform(modelViewProj, v0);
  175. vertices[1] = ModelTransform(modelViewProj, v1);
  176. vertices[2] = ModelTransform(modelViewProj, v2);
  177. DrawTriangle(vertices);
  178. indices += 3;
  179. }
  180. }
  181. else
  182. {
  183. const unsigned* indices = ((const unsigned*)indexData) + indexStart;
  184. const unsigned* indicesEnd = indices + indexCount;
  185. while (indices < indicesEnd)
  186. {
  187. if (numTriangles_ >= maxTriangles_)
  188. return false;
  189. const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
  190. const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
  191. const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
  192. vertices[0] = ModelTransform(modelViewProj, v0);
  193. vertices[1] = ModelTransform(modelViewProj, v1);
  194. vertices[2] = ModelTransform(modelViewProj, v2);
  195. DrawTriangle(vertices);
  196. indices += 3;
  197. }
  198. }
  199. return true;
  200. }
  201. void OcclusionBuffer::BuildDepthHierarchy()
  202. {
  203. if (!buffer_)
  204. return;
  205. // Build the first mip level from the pixel-level data
  206. int width = (width_ + 1) / 2;
  207. int height = (height_ + 1) / 2;
  208. if (mipBuffers_.Size())
  209. {
  210. for (int y = 0; y < height; ++y)
  211. {
  212. int* src = buffer_ + (y * 2) * width_;
  213. DepthValue* dest = mipBuffers_[0].Get() + y * width;
  214. DepthValue* end = dest + width;
  215. if (y * 2 + 1 < height_)
  216. {
  217. int* src2 = src + width_;
  218. while (dest < end)
  219. {
  220. int minUpper = Min(src[0], src[1]);
  221. int minLower = Min(src2[0], src2[1]);
  222. dest->min_ = Min(minUpper, minLower);
  223. int maxUpper = Max(src[0], src[1]);
  224. int maxLower = Max(src2[0], src2[1]);
  225. dest->max_ = Max(maxUpper, maxLower);
  226. src += 2;
  227. src2 += 2;
  228. ++dest;
  229. }
  230. }
  231. else
  232. {
  233. while (dest < end)
  234. {
  235. dest->min_ = Min(src[0], src[1]);
  236. dest->max_ = Max(src[0], src[1]);
  237. src += 2;
  238. ++dest;
  239. }
  240. }
  241. }
  242. }
  243. // Build the rest of the mip levels
  244. for (unsigned i = 1; i < mipBuffers_.Size(); ++i)
  245. {
  246. int prevWidth = width;
  247. int prevHeight = height;
  248. width = (width + 1) / 2;
  249. height = (height + 1) / 2;
  250. for (int y = 0; y < height; ++y)
  251. {
  252. DepthValue* src = mipBuffers_[i - 1].Get() + (y * 2) * prevWidth;
  253. DepthValue* dest = mipBuffers_[i].Get() + y * width;
  254. DepthValue* end = dest + width;
  255. if (y * 2 + 1 < prevHeight)
  256. {
  257. DepthValue* src2 = src + prevWidth;
  258. while (dest < end)
  259. {
  260. int minUpper = Min(src[0].min_, src[1].min_);
  261. int minLower = Min(src2[0].min_, src2[1].min_);
  262. dest->min_ = Min(minUpper, minLower);
  263. int maxUpper = Max(src[0].max_, src[1].max_);
  264. int maxLower = Max(src2[0].max_, src2[1].max_);
  265. dest->max_ = Max(maxUpper, maxLower);
  266. src += 2;
  267. src2 += 2;
  268. ++dest;
  269. }
  270. }
  271. else
  272. {
  273. while (dest < end)
  274. {
  275. dest->min_ = Min(src[0].min_, src[1].min_);
  276. dest->max_ = Max(src[0].max_, src[1].max_);
  277. src += 2;
  278. ++dest;
  279. }
  280. }
  281. }
  282. }
  283. depthHierarchyDirty_ = false;
  284. }
  285. void OcclusionBuffer::ResetUseTimer()
  286. {
  287. useTimer_.Reset();
  288. }
  289. bool OcclusionBuffer::IsVisible(const BoundingBox& worldSpaceBox) const
  290. {
  291. if (!buffer_)
  292. return true;
  293. // Transform corners to projection space
  294. Vector4 vertices[8];
  295. vertices[0] = ModelTransform(viewProj_, worldSpaceBox.min_);
  296. vertices[1] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.min_.z_));
  297. vertices[2] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_));
  298. vertices[3] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_));
  299. vertices[4] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_));
  300. vertices[5] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_));
  301. vertices[6] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.max_.z_));
  302. vertices[7] = ModelTransform(viewProj_, worldSpaceBox.max_);
  303. // Apply a far clip relative bias
  304. for (unsigned i = 0; i < 8; ++i)
  305. vertices[i].z_ -= OCCLUSION_RELATIVE_BIAS;
  306. // Transform to screen space. If any of the corners cross the near plane, assume visible
  307. float minX, maxX, minY, maxY, minZ;
  308. if (vertices[0].z_ <= 0.0f)
  309. return true;
  310. Vector3 projected = ViewportTransform(vertices[0]);
  311. minX = maxX = projected.x_;
  312. minY = maxY = projected.y_;
  313. minZ = projected.z_;
  314. // Project the rest
  315. for (unsigned i = 1; i < 8; ++i)
  316. {
  317. if (vertices[i].z_ <= 0.0f)
  318. return true;
  319. projected = ViewportTransform(vertices[i]);
  320. if (projected.x_ < minX) minX = projected.x_;
  321. if (projected.x_ > maxX) maxX = projected.x_;
  322. if (projected.y_ < minY) minY = projected.y_;
  323. if (projected.y_ > maxY) maxY = projected.y_;
  324. if (projected.z_ < minZ) minZ = projected.z_;
  325. }
  326. // Expand the bounding box 1 pixel in each direction to be conservative and correct rasterization offset
  327. IntRect rect(
  328. (int)(minX - 1.5f), (int)(minY - 1.5f),
  329. (int)(maxX + 0.5f), (int)(maxY + 0.5f)
  330. );
  331. // If the rect is outside, let frustum culling handle
  332. if (rect.right_ < 0 || rect.bottom_ < 0)
  333. return true;
  334. if (rect.left_ >= width_ || rect.top_ >= height_)
  335. return true;
  336. // Clipping of rect
  337. if (rect.left_ < 0)
  338. rect.left_ = 0;
  339. if (rect.top_ < 0)
  340. rect.top_ = 0;
  341. if (rect.right_ >= width_)
  342. rect.right_ = width_ - 1;
  343. if (rect.bottom_ >= height_)
  344. rect.bottom_ = height_ - 1;
  345. // Convert depth to integer and apply final bias
  346. int z = (int)(minZ + 0.5f) - OCCLUSION_FIXED_BIAS;
  347. if (!depthHierarchyDirty_)
  348. {
  349. // Start from lowest mip level and check if a conclusive result can be found
  350. for (int i = mipBuffers_.Size() - 1; i >= 0; --i)
  351. {
  352. int shift = i + 1;
  353. int width = width_ >> shift;
  354. int left = rect.left_ >> shift;
  355. int right = rect.right_ >> shift;
  356. DepthValue* buffer = mipBuffers_[i].Get();
  357. DepthValue* row = buffer + (rect.top_ >> shift) * width;
  358. DepthValue* endRow = buffer + (rect.bottom_ >> shift) * width;
  359. bool allOccluded = true;
  360. while (row <= endRow)
  361. {
  362. DepthValue* src = row + left;
  363. DepthValue* end = row + right;
  364. while (src <= end)
  365. {
  366. if (z <= src->min_)
  367. return true;
  368. if (z <= src->max_)
  369. allOccluded = false;
  370. ++src;
  371. }
  372. row += width;
  373. }
  374. if (allOccluded)
  375. return false;
  376. }
  377. }
  378. // If no conclusive result, finally check the pixel-level data
  379. int* row = buffer_ + rect.top_ * width_;
  380. int* endRow = buffer_ + rect.bottom_ * width_;
  381. while (row <= endRow)
  382. {
  383. int* src = row + rect.left_;
  384. int* end = row + rect.right_;
  385. while (src <= end)
  386. {
  387. if (z <= *src)
  388. return true;
  389. ++src;
  390. }
  391. row += width_;
  392. }
  393. return false;
  394. }
  395. unsigned OcclusionBuffer::GetUseTimer()
  396. {
  397. return useTimer_.GetMSec(false);
  398. }
  399. inline Vector4 OcclusionBuffer::ModelTransform(const Matrix4& transform, const Vector3& vertex) const
  400. {
  401. return Vector4(
  402. transform.m00_ * vertex.x_ + transform.m01_ * vertex.y_ + transform.m02_ * vertex.z_ + transform.m03_,
  403. transform.m10_ * vertex.x_ + transform.m11_ * vertex.y_ + transform.m12_ * vertex.z_ + transform.m13_,
  404. transform.m20_ * vertex.x_ + transform.m21_ * vertex.y_ + transform.m22_ * vertex.z_ + transform.m23_,
  405. transform.m30_ * vertex.x_ + transform.m31_ * vertex.y_ + transform.m32_ * vertex.z_ + transform.m33_
  406. );
  407. }
  408. inline Vector3 OcclusionBuffer::ViewportTransform(const Vector4& vertex) const
  409. {
  410. float invW = 1.0f / vertex.w_;
  411. return Vector3(
  412. invW * vertex.x_ * scaleX_ + offsetX_,
  413. invW * vertex.y_ * scaleY_ + offsetY_,
  414. invW * vertex.z_ * OCCLUSION_Z_SCALE
  415. );
  416. }
  417. inline Vector4 OcclusionBuffer::ClipEdge(const Vector4& v0, const Vector4& v1, float d0, float d1) const
  418. {
  419. float t = d0 / (d0 - d1);
  420. return v0 + t * (v1 - v0);
  421. }
  422. inline float OcclusionBuffer::SignedArea(const Vector3& v0, const Vector3& v1, const Vector3& v2) const
  423. {
  424. float aX = v0.x_ - v1.x_;
  425. float aY = v0.y_ - v1.y_;
  426. float bX = v2.x_ - v1.x_;
  427. float bY = v2.y_ - v1.y_;
  428. return aX * bY - aY * bX;
  429. }
  430. void OcclusionBuffer::CalculateViewport()
  431. {
  432. // Add half pixel offset due to 3D frustum culling
  433. scaleX_ = 0.5f * width_;
  434. scaleY_ = -0.5f * height_;
  435. offsetX_ = 0.5f * width_ + 0.5f;
  436. offsetY_ = 0.5f * height_ + 0.5f;
  437. projOffsetScaleX_ = projection_.m00_ * scaleX_;
  438. projOffsetScaleY_ = projection_.m11_ * scaleY_;
  439. }
  440. void OcclusionBuffer::DrawTriangle(Vector4* vertices)
  441. {
  442. unsigned clipMask = 0;
  443. unsigned andClipMask = 0;
  444. bool drawOk = false;
  445. Vector3 projected[3];
  446. // Build the clip plane mask for the triangle
  447. for (unsigned i = 0; i < 3; ++i)
  448. {
  449. unsigned vertexClipMask = 0;
  450. if (vertices[i].x_ > vertices[i].w_)
  451. vertexClipMask |= CLIPMASK_X_POS;
  452. if (vertices[i].x_ < -vertices[i].w_)
  453. vertexClipMask |= CLIPMASK_X_NEG;
  454. if (vertices[i].y_ > vertices[i].w_)
  455. vertexClipMask |= CLIPMASK_Y_POS;
  456. if (vertices[i].y_ < -vertices[i].w_)
  457. vertexClipMask |= CLIPMASK_Y_NEG;
  458. if (vertices[i].z_ > vertices[i].w_)
  459. vertexClipMask |= CLIPMASK_Z_POS;
  460. if (vertices[i].z_ < 0.0f)
  461. vertexClipMask |= CLIPMASK_Z_NEG;
  462. clipMask |= vertexClipMask;
  463. if (!i)
  464. andClipMask = vertexClipMask;
  465. else
  466. andClipMask &= vertexClipMask;
  467. }
  468. // If triangle is fully behind any clip plane, can reject quickly
  469. if (andClipMask)
  470. return;
  471. // Check if triangle is fully inside
  472. if (!clipMask)
  473. {
  474. projected[0] = ViewportTransform(vertices[0]);
  475. projected[1] = ViewportTransform(vertices[1]);
  476. projected[2] = ViewportTransform(vertices[2]);
  477. bool clockwise = SignedArea(projected[0], projected[1], projected[2]) < 0.0f;
  478. if (cullMode_ == CULL_NONE || (cullMode_ == CULL_CCW && clockwise) || (cullMode_ == CULL_CW && !clockwise))
  479. {
  480. DrawTriangle2D(projected, clockwise);
  481. drawOk = true;
  482. }
  483. }
  484. else
  485. {
  486. bool triangles[64];
  487. // Initial triangle
  488. triangles[0] = true;
  489. unsigned numTriangles = 1;
  490. if (clipMask & CLIPMASK_X_POS)
  491. ClipVertices(Vector4(-1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  492. if (clipMask & CLIPMASK_X_NEG)
  493. ClipVertices(Vector4(1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  494. if (clipMask & CLIPMASK_Y_POS)
  495. ClipVertices(Vector4(0.0f, -1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  496. if (clipMask & CLIPMASK_Y_NEG)
  497. ClipVertices(Vector4(0.0f, 1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  498. if (clipMask & CLIPMASK_Z_POS)
  499. ClipVertices(Vector4(0.0f, 0.0f, -1.0f, 1.0f), vertices, triangles, numTriangles);
  500. if (clipMask & CLIPMASK_Z_NEG)
  501. ClipVertices(Vector4(0.0f, 0.0f, 1.0f, 0.0f), vertices, triangles, numTriangles);
  502. // Draw each accepted triangle
  503. for (unsigned i = 0; i < numTriangles; ++i)
  504. {
  505. if (triangles[i])
  506. {
  507. unsigned index = i * 3;
  508. projected[0] = ViewportTransform(vertices[index]);
  509. projected[1] = ViewportTransform(vertices[index + 1]);
  510. projected[2] = ViewportTransform(vertices[index + 2]);
  511. bool clockwise = SignedArea(projected[0], projected[1], projected[2]) < 0.0f;
  512. if (cullMode_ == CULL_NONE || (cullMode_ == CULL_CCW && clockwise) || (cullMode_ == CULL_CW && !clockwise))
  513. {
  514. DrawTriangle2D(projected, clockwise);
  515. drawOk = true;
  516. }
  517. }
  518. }
  519. }
  520. if (drawOk)
  521. ++numTriangles_;
  522. }
  523. void OcclusionBuffer::ClipVertices(const Vector4& plane, Vector4* vertices, bool* triangles, unsigned& numTriangles)
  524. {
  525. unsigned num = numTriangles;
  526. for (unsigned i = 0; i < num; ++i)
  527. {
  528. if (triangles[i])
  529. {
  530. unsigned index = i * 3;
  531. float d0 = plane.DotProduct(vertices[index]);
  532. float d1 = plane.DotProduct(vertices[index + 1]);
  533. float d2 = plane.DotProduct(vertices[index + 2]);
  534. // If all vertices behind the plane, reject triangle
  535. if (d0 < 0.0f && d1 < 0.0f && d2 < 0.0f)
  536. {
  537. triangles[i] = false;
  538. continue;
  539. }
  540. // If 2 vertices behind the plane, create a new triangle in-place
  541. else if (d0 < 0.0f && d1 < 0.0f)
  542. {
  543. vertices[index] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  544. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  545. }
  546. else if (d0 < 0.0f && d2 < 0.0f)
  547. {
  548. vertices[index] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  549. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  550. }
  551. else if (d1 < 0.0f && d2 < 0.0f)
  552. {
  553. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  554. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  555. }
  556. // 1 vertex behind the plane: create one new triangle, and modify one in-place
  557. else if (d0 < 0.0f)
  558. {
  559. unsigned newIdx = numTriangles * 3;
  560. triangles[numTriangles] = true;
  561. ++numTriangles;
  562. vertices[newIdx] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  563. vertices[newIdx + 1] = vertices[index] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  564. vertices[newIdx + 2] = vertices[index + 2];
  565. }
  566. else if (d1 < 0.0f)
  567. {
  568. unsigned newIdx = numTriangles * 3;
  569. triangles[numTriangles] = true;
  570. ++numTriangles;
  571. vertices[newIdx + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  572. vertices[newIdx + 2] = vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  573. vertices[newIdx] = vertices[index];
  574. }
  575. else if (d2 < 0.0f)
  576. {
  577. unsigned newIdx = numTriangles * 3;
  578. triangles[numTriangles] = true;
  579. ++numTriangles;
  580. vertices[newIdx + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  581. vertices[newIdx] = vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  582. vertices[newIdx + 1] = vertices[index + 1];
  583. }
  584. }
  585. }
  586. }
  587. // Code based on Chris Hecker's Perspective Texture Mapping series in the Game Developer magazine
  588. // Also available online at http://chrishecker.com/Miscellaneous_Technical_Articles
  589. /// %Gradients of a software rasterized triangle.
  590. struct Gradients
  591. {
  592. /// Construct from vertices.
  593. Gradients(const Vector3* vertices)
  594. {
  595. float invdX = 1.0f / (((vertices[1].x_ - vertices[2].x_) *
  596. (vertices[0].y_ - vertices[2].y_)) -
  597. ((vertices[0].x_ - vertices[2].x_) *
  598. (vertices[1].y_ - vertices[2].y_)));
  599. float invdY = -invdX;
  600. dInvZdX_ = invdX * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].y_ - vertices[2].y_)) -
  601. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].y_ - vertices[2].y_)));
  602. dInvZdY_ = invdY * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].x_ - vertices[2].x_)) -
  603. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].x_ - vertices[2].x_)));
  604. dInvZdXInt_ = (int)dInvZdX_;
  605. }
  606. /// Integer horizontal gradient.
  607. int dInvZdXInt_;
  608. /// Horizontal gradient.
  609. float dInvZdX_;
  610. /// Vertical gradient.
  611. float dInvZdY_;
  612. };
  613. /// %Edge of a software rasterized triangle.
  614. struct Edge
  615. {
  616. /// Construct from gradients and top & bottom vertices.
  617. Edge(const Gradients& gradients, const Vector3& top, const Vector3& bottom, int topY)
  618. {
  619. float height = (bottom.y_ - top.y_);
  620. float slope = (height != 0.0f) ? (bottom.x_ - top.x_) / height : 0.0f;
  621. float yPreStep = (float)(topY + 1) - top.y_;
  622. float xPreStep = slope * yPreStep;
  623. x_ = (int)((xPreStep + top.x_) * OCCLUSION_X_SCALE + 0.5f);
  624. xStep_ = (int)(slope * OCCLUSION_X_SCALE + 0.5f);
  625. invZ_ = (int)(top.z_ + xPreStep * gradients.dInvZdX_ + yPreStep * gradients.dInvZdY_ + 0.5f);
  626. invZStep_ = (int)(slope * gradients.dInvZdX_ + gradients.dInvZdY_ + 0.5f);
  627. }
  628. /// X coordinate.
  629. int x_;
  630. /// X coordinate step.
  631. int xStep_;
  632. /// Inverse Z.
  633. int invZ_;
  634. /// Inverse Z step.
  635. int invZStep_;
  636. };
  637. void OcclusionBuffer::DrawTriangle2D(const Vector3* vertices, bool clockwise)
  638. {
  639. int top, middle, bottom;
  640. bool middleIsRight;
  641. // Sort vertices in Y-direction
  642. if (vertices[0].y_ < vertices[1].y_)
  643. {
  644. if (vertices[2].y_ < vertices[0].y_)
  645. {
  646. top = 2;
  647. middle = 0;
  648. bottom = 1;
  649. middleIsRight = true;
  650. }
  651. else
  652. {
  653. top = 0;
  654. if (vertices[1].y_ < vertices[2].y_)
  655. {
  656. middle = 1;
  657. bottom = 2;
  658. middleIsRight = true;
  659. }
  660. else
  661. {
  662. middle = 2;
  663. bottom = 1;
  664. middleIsRight = false;
  665. }
  666. }
  667. }
  668. else
  669. {
  670. if (vertices[2].y_ < vertices[1].y_)
  671. {
  672. top = 2;
  673. middle = 1;
  674. bottom = 0;
  675. middleIsRight = false;
  676. }
  677. else
  678. {
  679. top = 1;
  680. if (vertices[0].y_ < vertices[2].y_)
  681. {
  682. middle = 0;
  683. bottom = 2;
  684. middleIsRight = false;
  685. }
  686. else
  687. {
  688. middle = 2;
  689. bottom = 0;
  690. middleIsRight = true;
  691. }
  692. }
  693. }
  694. int topY = (int)vertices[top].y_;
  695. int middleY = (int)vertices[middle].y_;
  696. int bottomY = (int)vertices[bottom].y_;
  697. // Check for degenerate triangle
  698. if (topY == bottomY)
  699. return;
  700. // Reverse middleIsRight test if triangle is counterclockwise
  701. if (!clockwise)
  702. middleIsRight = !middleIsRight;
  703. Gradients gradients(vertices);
  704. Edge topToMiddle(gradients, vertices[top], vertices[middle], topY);
  705. Edge topToBottom(gradients, vertices[top], vertices[bottom], topY);
  706. Edge middleToBottom(gradients, vertices[middle], vertices[bottom], middleY);
  707. if (middleIsRight)
  708. {
  709. // Top half
  710. int* row = buffer_ + topY * width_;
  711. int* endRow = buffer_ + middleY * width_;
  712. while (row < endRow)
  713. {
  714. int invZ = topToBottom.invZ_;
  715. int* dest = row + (topToBottom.x_ >> 16);
  716. int* end = row + (topToMiddle.x_ >> 16);
  717. while (dest < end)
  718. {
  719. if (invZ < *dest)
  720. *dest = invZ;
  721. invZ += gradients.dInvZdXInt_;
  722. ++dest;
  723. }
  724. topToBottom.x_ += topToBottom.xStep_;
  725. topToBottom.invZ_ += topToBottom.invZStep_;
  726. topToMiddle.x_ += topToMiddle.xStep_;
  727. row += width_;
  728. }
  729. // Bottom half
  730. row = buffer_ + middleY * width_;
  731. endRow = buffer_ + bottomY * width_;
  732. while (row < endRow)
  733. {
  734. int invZ = topToBottom.invZ_;
  735. int* dest = row + (topToBottom.x_ >> 16);
  736. int* end = row + (middleToBottom.x_ >> 16);
  737. while (dest < end)
  738. {
  739. if (invZ < *dest)
  740. *dest = invZ;
  741. invZ += gradients.dInvZdXInt_;
  742. ++dest;
  743. }
  744. topToBottom.x_ += topToBottom.xStep_;
  745. topToBottom.invZ_ += topToBottom.invZStep_;
  746. middleToBottom.x_ += middleToBottom.xStep_;
  747. row += width_;
  748. }
  749. }
  750. else
  751. {
  752. // Top half
  753. int* row = buffer_ + topY * width_;
  754. int* endRow = buffer_ + middleY * width_;
  755. while (row < endRow)
  756. {
  757. int invZ = topToMiddle.invZ_;
  758. int* dest = row + (topToMiddle.x_ >> 16);
  759. int* end = row + (topToBottom.x_ >> 16);
  760. while (dest < end)
  761. {
  762. if (invZ < *dest)
  763. *dest = invZ;
  764. invZ += gradients.dInvZdXInt_;
  765. ++dest;
  766. }
  767. topToMiddle.x_ += topToMiddle.xStep_;
  768. topToMiddle.invZ_ += topToMiddle.invZStep_;
  769. topToBottom.x_ += topToBottom.xStep_;
  770. row += width_;
  771. }
  772. // Bottom half
  773. row = buffer_ + middleY * width_;
  774. endRow = buffer_ + bottomY * width_;
  775. while (row < endRow)
  776. {
  777. int invZ = middleToBottom.invZ_;
  778. int* dest = row + (middleToBottom.x_ >> 16);
  779. int* end = row + (topToBottom.x_ >> 16);
  780. while (dest < end)
  781. {
  782. if (invZ < *dest)
  783. *dest = invZ;
  784. invZ += gradients.dInvZdXInt_;
  785. ++dest;
  786. }
  787. middleToBottom.x_ += middleToBottom.xStep_;
  788. middleToBottom.invZ_ += middleToBottom.invZStep_;
  789. topToBottom.x_ += topToBottom.xStep_;
  790. row += width_;
  791. }
  792. }
  793. }
  794. }