OcclusionBuffer.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Camera.h"
  25. #include "IndexBuffer.h"
  26. #include "Log.h"
  27. #include "OcclusionBuffer.h"
  28. #include "Profiler.h"
  29. #include "StringUtils.h"
  30. #include "VertexBuffer.h"
  31. #include <cstring>
  32. #include "DebugNew.h"
  33. static const unsigned CLIPMASK_X_POS = 0x1;
  34. static const unsigned CLIPMASK_X_NEG = 0x2;
  35. static const unsigned CLIPMASK_Y_POS = 0x4;
  36. static const unsigned CLIPMASK_Y_NEG = 0x8;
  37. static const unsigned CLIPMASK_Z_POS = 0x10;
  38. static const unsigned CLIPMASK_Z_NEG = 0x20;
  39. OcclusionBuffer::OcclusionBuffer() :
  40. mBuffer(0),
  41. mWidth(0),
  42. mHeight(0),
  43. mNumTriangles(0),
  44. mMaxTriangles(OCCLUSION_DEFAULT_MAX_TRIANGLES),
  45. mCullMode(CULL_CCW),
  46. mHierarchyDirty(true),
  47. mNearClip(0.0f),
  48. mFarClip(0.0f),
  49. mDepthBias(0.0f)
  50. {
  51. }
  52. OcclusionBuffer::~OcclusionBuffer()
  53. {
  54. }
  55. bool OcclusionBuffer::setSize(int width, int height)
  56. {
  57. if ((width == mWidth) && (height == mHeight))
  58. return true;
  59. if ((width <= 0) || (height <= 0))
  60. return false;
  61. if (!isPowerOfTwo(width))
  62. {
  63. LOGERROR("Width is not a power of two");
  64. return false;
  65. }
  66. PROFILE(Occlusion_SetSize);
  67. mWidth = width;
  68. mHeight = height;
  69. // Reserve extra memory in case 3D clipping is not exact
  70. mFullBuffer = new int[width * (height + 2)];
  71. mBuffer = mFullBuffer.getPtr() + 1 * width;
  72. mReducedBuffers.clear();
  73. // Build buffers for reduced levels
  74. for (;;)
  75. {
  76. width = (width + 1) / 2;
  77. height = (height + 1) / 2;
  78. mReducedBuffers.push_back(SharedArrayPtr<DepthValue>(new DepthValue[width * height]));
  79. if ((width <= OCCLUSION_MIN_SIZE) && (height <= OCCLUSION_MIN_SIZE))
  80. break;
  81. }
  82. LOGINFO("Set occlusion buffer size " + toString(mWidth) + "x" + toString(mHeight) + " with " +
  83. toString(mReducedBuffers.size()) + " reduced levels");
  84. calculateViewport();
  85. return true;
  86. }
  87. void OcclusionBuffer::setView(Camera& camera)
  88. {
  89. mView = camera.getInverseWorldTransform();
  90. mProjection = camera.getProjection();
  91. mViewProj = mProjection * mView;
  92. mNearClip = camera.getNearClip();
  93. mFarClip = camera.getFarClip();
  94. mDepthBias = mFarClip * 0.00002f;
  95. calculateViewport();
  96. }
  97. void OcclusionBuffer::setMaxTriangles(unsigned triangles)
  98. {
  99. mMaxTriangles = triangles;
  100. }
  101. void OcclusionBuffer::setCullMode(CullMode mode)
  102. {
  103. mCullMode = mode;
  104. }
  105. void OcclusionBuffer::reset()
  106. {
  107. mNumTriangles = 0;
  108. }
  109. void OcclusionBuffer::clear()
  110. {
  111. PROFILE(Occlusion_Clear);
  112. if (!mBuffer)
  113. return;
  114. reset();
  115. int* dest = mBuffer;
  116. int count = mWidth * mHeight;
  117. while (count)
  118. {
  119. *dest++ = 0x7fffffff;
  120. --count;
  121. }
  122. mHierarchyDirty = true;
  123. }
  124. bool OcclusionBuffer::draw(const Matrix4x3& model, const unsigned char* vertexData, unsigned vertexSize,
  125. const unsigned char* indexData, unsigned indexSize, unsigned indexStart, unsigned indexCount)
  126. {
  127. PROFILE(Occlusion_DrawGeometry);
  128. Matrix4 modelViewProj = mViewProj * model;
  129. mHierarchyDirty = true;
  130. // Theoretical max. amount of vertices if each of the 6 clipping planes doubles the triangle count
  131. Vector4 vertices[64 * 3];
  132. // 16-bit indices
  133. if (indexSize == sizeof(unsigned short))
  134. {
  135. const unsigned short* indices = (const unsigned short*)indexData;
  136. for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
  137. {
  138. if (mNumTriangles >= mMaxTriangles)
  139. return false;
  140. const Vector3& v0 = *((const Vector3*)(&vertexData[indices[i] * vertexSize]));
  141. const Vector3& v1 = *((const Vector3*)(&vertexData[indices[i + 1] * vertexSize]));
  142. const Vector3& v2 = *((const Vector3*)(&vertexData[indices[i + 2] * vertexSize]));
  143. vertices[0] = modelTransform(modelViewProj, v0);
  144. vertices[1] = modelTransform(modelViewProj, v1);
  145. vertices[2] = modelTransform(modelViewProj, v2);
  146. drawTriangle(vertices);
  147. }
  148. }
  149. else
  150. {
  151. const unsigned* indices = (const unsigned*)indexData;
  152. for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
  153. {
  154. if (mNumTriangles >= mMaxTriangles)
  155. return false;
  156. const Vector3& v0 = *((const Vector3*)(&vertexData[indices[i] * vertexSize]));
  157. const Vector3& v1 = *((const Vector3*)(&vertexData[indices[i + 1] * vertexSize]));
  158. const Vector3& v2 = *((const Vector3*)(&vertexData[indices[i + 2] * vertexSize]));
  159. vertices[0] = modelTransform(modelViewProj, v0);
  160. vertices[1] = modelTransform(modelViewProj, v1);
  161. vertices[2] = modelTransform(modelViewProj, v2);
  162. drawTriangle(vertices);
  163. }
  164. }
  165. return true;
  166. }
  167. void OcclusionBuffer::buildDepthHierarchy()
  168. {
  169. PROFILE(Occlusion_BuildDepthHierarchy);
  170. if (!mBuffer)
  171. return;
  172. // Build the first reduced level from the pixel-level data
  173. int width = (mWidth + 1) / 2;
  174. int height = (mHeight + 1) / 2;
  175. if (mReducedBuffers.size())
  176. {
  177. for (int y = 0; y < height; ++y)
  178. {
  179. int* src = mBuffer + (y * 2) * mWidth;
  180. DepthValue* dest = mReducedBuffers[0].getPtr() + y * width;
  181. DepthValue* end = dest + width;
  182. if (y * 2 + 1 < mHeight)
  183. {
  184. int* src2 = src + mWidth;
  185. while (dest < end)
  186. {
  187. int minUpper = min(src[0], src[1]);
  188. int minLower = min(src2[0], src2[1]);
  189. dest->mMin = min(minUpper, minLower);
  190. int maxUpper = max(src[0], src[1]);
  191. int maxLower = max(src2[0], src2[1]);
  192. dest->mMax = max(maxUpper, maxLower);
  193. src += 2;
  194. src2 += 2;
  195. ++dest;
  196. }
  197. }
  198. else
  199. {
  200. while (dest < end)
  201. {
  202. dest->mMin = min(src[0], src[1]);
  203. dest->mMax = max(src[0], src[1]);
  204. src += 2;
  205. ++dest;
  206. }
  207. }
  208. }
  209. }
  210. // Build the rest of the reduced levels
  211. for (unsigned i = 1; i < mReducedBuffers.size(); ++i)
  212. {
  213. int prevWidth = width;
  214. int prevHeight = height;
  215. width = (width + 1) / 2;
  216. height = (height + 1) / 2;
  217. for (int y = 0; y < height; ++y)
  218. {
  219. DepthValue* src = mReducedBuffers[i - 1].getPtr() + (y * 2) * prevWidth;
  220. DepthValue* dest = mReducedBuffers[i].getPtr() + y * width;
  221. DepthValue* end = dest + width;
  222. if (y * 2 + 1 < prevHeight)
  223. {
  224. DepthValue* src2 = src + prevWidth;
  225. while (dest < end)
  226. {
  227. int minUpper = min(src[0].mMin, src[1].mMin);
  228. int minLower = min(src2[0].mMin, src2[1].mMin);
  229. dest->mMin = min(minUpper, minLower);
  230. int maxUpper = max(src[0].mMax, src[1].mMax);
  231. int maxLower = max(src2[0].mMax, src2[1].mMax);
  232. dest->mMax = max(maxUpper, maxLower);
  233. src += 2;
  234. src2 += 2;
  235. ++dest;
  236. }
  237. }
  238. else
  239. {
  240. while (dest < end)
  241. {
  242. dest->mMin = min(src[0].mMin, src[1].mMin);
  243. dest->mMax = max(src[0].mMax, src[1].mMax);
  244. src += 2;
  245. ++dest;
  246. }
  247. }
  248. }
  249. }
  250. mHierarchyDirty = false;
  251. }
  252. bool OcclusionBuffer::isVisible(const BoundingBox& worldSpaceBox) const
  253. {
  254. if (!mBuffer)
  255. return true;
  256. Vector3 vertices[8];
  257. // Transform corners to view space. Note: do not directly transform the bounding box, as this would expand it unnecessarily
  258. vertices[0] = mView * worldSpaceBox.mMin;
  259. vertices[1] = mView * Vector3(worldSpaceBox.mMax.mX, worldSpaceBox.mMin.mY, worldSpaceBox.mMin.mZ);
  260. vertices[2] = mView * Vector3(worldSpaceBox.mMin.mX, worldSpaceBox.mMax.mY, worldSpaceBox.mMin.mZ);
  261. vertices[3] = mView * Vector3(worldSpaceBox.mMax.mX, worldSpaceBox.mMax.mY, worldSpaceBox.mMin.mZ);
  262. vertices[4] = mView * Vector3(worldSpaceBox.mMin.mX, worldSpaceBox.mMin.mY, worldSpaceBox.mMax.mZ);
  263. vertices[5] = mView * Vector3(worldSpaceBox.mMax.mX, worldSpaceBox.mMin.mY, worldSpaceBox.mMax.mZ);
  264. vertices[6] = mView * Vector3(worldSpaceBox.mMin.mX, worldSpaceBox.mMax.mY, worldSpaceBox.mMax.mZ);
  265. vertices[7] = mView * worldSpaceBox.mMax;
  266. // Project to screen. If any of the corners cross the near plane, assume visible
  267. float minX, maxX, minY, maxY, minZ;
  268. // Subtract a small bias to prevent self-occlusion artifacts
  269. // (need for bias results from different floating point transformations producing different errors)
  270. vertices[0].mZ -= mDepthBias;
  271. if (vertices[0].mZ <= mNearClip)
  272. return true;
  273. // Project the first corner to initialize the rectangle
  274. float invW = 1.0f / (vertices[0].mZ * mProjection.m32 + mProjection.m33);
  275. minX = maxX = invW * (mProjOffsetScaleX * vertices[0].mX) + mOffsetX;
  276. minY = maxY = invW * (mProjOffsetScaleY * vertices[0].mY) + mOffsetY;
  277. minZ = invW * (mProjection.m22 * vertices[0].mZ + mProjection.m23);
  278. // Project the rest
  279. for (unsigned i = 1; i < 8; ++i)
  280. {
  281. // Subtract a small bias to prevent self-occlusion artifacts
  282. vertices[i].mZ -= mDepthBias;
  283. if (vertices[i].mZ <= mNearClip)
  284. return true;
  285. float invW = 1.0f / (vertices[i].mZ * mProjection.m32 + mProjection.m33);
  286. float x = invW * (mProjOffsetScaleX * vertices[i].mX) + mOffsetX;
  287. float y = invW * (mProjOffsetScaleY * vertices[i].mY) + mOffsetY;
  288. float z = invW * (mProjection.m22 * vertices[i].mZ + mProjection.m23);
  289. if (x < minX) minX = x;
  290. if (x > maxX) maxX = x;
  291. if (y < minY) minY = y;
  292. if (y > maxY) maxY = y;
  293. if (z < minZ) minZ = z;
  294. }
  295. // Expand the bounding box 1 pixel in each direction to be conservative and correct rasterization offset
  296. IntRect rect(
  297. (int)(minX - 1.5f), (int)(minY - 1.5f),
  298. (int)(maxX + 0.5f), (int)(maxY + 0.5f)
  299. );
  300. // If outside, can not test, so assume visible (should be frustum culled in that case)
  301. if ((rect.mRight < 0) || (rect.mBottom < 0))
  302. return true;
  303. if ((rect.mLeft >= mWidth) || (rect.mTop >= mHeight))
  304. return true;
  305. // Clipping of rect
  306. if (rect.mLeft < 0)
  307. rect.mLeft = 0;
  308. if (rect.mTop < 0)
  309. rect.mTop = 0;
  310. if (rect.mRight >= mWidth)
  311. rect.mRight = mWidth - 1;
  312. if (rect.mBottom >= mHeight)
  313. rect.mBottom = mHeight - 1;
  314. int z = ((int)(minZ * OCCLUSION_Z_SCALE));
  315. if (!mHierarchyDirty)
  316. {
  317. // Start from lowest reduced level and check if a conclusive result can be found
  318. for (int i = mReducedBuffers.size() - 1; i >= 0; --i)
  319. {
  320. int shift = i + 1;
  321. int width = mWidth >> shift;
  322. int left = rect.mLeft >> shift;
  323. int right = rect.mRight >> shift;
  324. DepthValue* buffer = mReducedBuffers[i].getPtr();
  325. DepthValue* row = buffer + (rect.mTop >> shift) * width;
  326. DepthValue* endRow = buffer + (rect.mBottom >> shift) * width;
  327. bool allOccluded = true;
  328. while (row <= endRow)
  329. {
  330. DepthValue* src = row + left;
  331. DepthValue* end = row + right;
  332. while (src <= end)
  333. {
  334. if (z <= src->mMin)
  335. return true;
  336. if (z <= src->mMax)
  337. allOccluded = false;
  338. ++src;
  339. }
  340. row += width;
  341. }
  342. if (allOccluded)
  343. return false;
  344. }
  345. }
  346. // If no conclusive result, finally check the pixel-level data
  347. int* row = mBuffer + rect.mTop * mWidth;
  348. int* endRow = mBuffer + rect.mBottom * mWidth;
  349. while (row <= endRow)
  350. {
  351. int* src = row + rect.mLeft;
  352. int* end = row + rect.mRight;
  353. while (src <= end)
  354. {
  355. if (z <= *src)
  356. return true;
  357. ++src;
  358. }
  359. row += mWidth;
  360. }
  361. return false;
  362. }
  363. inline Vector4 OcclusionBuffer::modelTransform(const Matrix4& transform, const Vector3& vertex) const
  364. {
  365. return Vector4(
  366. transform.m00 * vertex.mX + transform.m01 * vertex.mY + transform.m02 * vertex.mZ + transform.m03,
  367. transform.m10 * vertex.mX + transform.m11 * vertex.mY + transform.m12 * vertex.mZ + transform.m13,
  368. transform.m20 * vertex.mX + transform.m21 * vertex.mY + transform.m22 * vertex.mZ + transform.m23,
  369. transform.m30 * vertex.mX + transform.m31 * vertex.mY + transform.m32 * vertex.mZ + transform.m33
  370. );
  371. }
  372. inline Vector3 OcclusionBuffer::viewportTransform(const Vector4& vertex) const
  373. {
  374. float invW = 1.0f / vertex.mW;
  375. return Vector3(
  376. invW * vertex.mX * mScaleX + mOffsetX,
  377. invW * vertex.mY * mScaleY + mOffsetY,
  378. invW * vertex.mZ * OCCLUSION_Z_SCALE
  379. );
  380. }
  381. inline Vector4 OcclusionBuffer::clipEdge(const Vector4& v0, const Vector4& v1, float d0, float d1) const
  382. {
  383. float t = d0 / (d0 - d1);
  384. return v0 + t * (v1 - v0);
  385. }
  386. inline bool OcclusionBuffer::checkFacing(const Vector3& v0, const Vector3& v1, const Vector3& v2) const
  387. {
  388. if (mCullMode == CULL_NONE)
  389. return true;
  390. float aX = v0.mX - v1.mX;
  391. float aY = v0.mY - v1.mY;
  392. float bX = v2.mX - v1.mX;
  393. float bY = v2.mY - v1.mY;
  394. float signedArea = aX * bY - aY * bX;
  395. if (mCullMode == CULL_CCW)
  396. return signedArea < 0.0f;
  397. else
  398. return signedArea > 0.0f;
  399. }
  400. void OcclusionBuffer::calculateViewport()
  401. {
  402. // Add half pixel offset due to 3D frustum culling
  403. mScaleX = 0.5f * mWidth;
  404. mScaleY = -0.5f * mHeight;
  405. mOffsetX = 0.5f * mWidth + 0.5f;
  406. mOffsetY = 0.5f * mHeight + 0.5f;
  407. mProjOffsetScaleX = mProjection.m00 * mScaleX;
  408. mProjOffsetScaleY = mProjection.m11 * mScaleY;
  409. }
  410. void OcclusionBuffer::drawTriangle(Vector4* vertices)
  411. {
  412. unsigned clipMask = 0;
  413. unsigned andClipMask = 0;
  414. Vector3 projected[3];
  415. // Build the clip plane mask for the triangle
  416. for (unsigned i = 0; i < 3; ++i)
  417. {
  418. unsigned vertexClipMask = 0;
  419. if (vertices[i].mX > vertices[i].mW)
  420. vertexClipMask |= CLIPMASK_X_POS;
  421. if (vertices[i].mX < -vertices[i].mW)
  422. vertexClipMask |= CLIPMASK_X_NEG;
  423. if (vertices[i].mY > vertices[i].mW)
  424. vertexClipMask |= CLIPMASK_Y_POS;
  425. if (vertices[i].mY < -vertices[i].mW)
  426. vertexClipMask |= CLIPMASK_Y_NEG;
  427. if (vertices[i].mZ > vertices[i].mW)
  428. vertexClipMask |= CLIPMASK_Z_POS;
  429. if (vertices[i].mZ < 0.0f)
  430. vertexClipMask |= CLIPMASK_Z_NEG;
  431. clipMask |= vertexClipMask;
  432. if (!i)
  433. andClipMask = vertexClipMask;
  434. else
  435. andClipMask &= vertexClipMask;
  436. }
  437. // If triangle is fully behind any clip plane, can reject quickly
  438. if (andClipMask)
  439. return;
  440. // Check if triangle is fully inside
  441. if (!clipMask)
  442. {
  443. projected[0] = viewportTransform(vertices[0]);
  444. projected[1] = viewportTransform(vertices[1]);
  445. projected[2] = viewportTransform(vertices[2]);
  446. if (checkFacing(projected[0], projected[1], projected[2]))
  447. drawTriangle2D(projected);
  448. }
  449. else
  450. {
  451. bool triangles[64];
  452. // Initial triangle
  453. triangles[0] = true;
  454. unsigned numTriangles = 1;
  455. if (clipMask & CLIPMASK_X_POS)
  456. clipVertices(Vector4(-1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  457. if (clipMask & CLIPMASK_X_NEG)
  458. clipVertices(Vector4(1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  459. if (clipMask & CLIPMASK_Y_POS)
  460. clipVertices(Vector4(0.0f, -1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  461. if (clipMask & CLIPMASK_Y_NEG)
  462. clipVertices(Vector4(0.0f, 1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  463. if (clipMask & CLIPMASK_Z_POS)
  464. clipVertices(Vector4(0.0f, 0.0f, -1.0f, 1.0f), vertices, triangles, numTriangles);
  465. if (clipMask & CLIPMASK_Z_NEG)
  466. clipVertices(Vector4(0.0f, 0.0f, 1.0f, 0.0f), vertices, triangles, numTriangles);
  467. // Draw each accepted triangle
  468. for (unsigned i = 0; i < numTriangles; ++i)
  469. {
  470. if (triangles[i])
  471. {
  472. unsigned index = i * 3;
  473. projected[0] = viewportTransform(vertices[index]);
  474. projected[1] = viewportTransform(vertices[index + 1]);
  475. projected[2] = viewportTransform(vertices[index + 2]);
  476. if (checkFacing(projected[0], projected[1], projected[2]))
  477. drawTriangle2D(projected);
  478. }
  479. }
  480. }
  481. }
  482. void OcclusionBuffer::clipVertices(const Vector4& plane, Vector4* vertices, bool* triangles, unsigned& numTriangles)
  483. {
  484. unsigned num = numTriangles;
  485. for (unsigned i = 0; i < num; ++i)
  486. {
  487. if (triangles[i])
  488. {
  489. unsigned index = i * 3;
  490. float d0 = plane.dotProduct(vertices[index]);
  491. float d1 = plane.dotProduct(vertices[index + 1]);
  492. float d2 = plane.dotProduct(vertices[index + 2]);
  493. // If all vertices behind the plane, reject triangle
  494. if ((d0 < 0.0f) && (d1 < 0.0f) && (d2 < 0.0f))
  495. {
  496. triangles[i] = false;
  497. continue;
  498. }
  499. // If 2 vertices behind the plane, create a new triangle in-place
  500. else if ((d0 < 0.0f) && (d1 < 0.0f))
  501. {
  502. vertices[index] = clipEdge(vertices[index], vertices[index + 2], d0, d2);
  503. vertices[index + 1] = clipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  504. }
  505. else if ((d0 < 0.0f) && (d2 < 0.0f))
  506. {
  507. vertices[index] = clipEdge(vertices[index], vertices[index + 1], d0, d1);
  508. vertices[index + 2] = clipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  509. }
  510. else if ((d1 < 0.0f) && (d2 < 0.0f))
  511. {
  512. vertices[index + 1] = clipEdge(vertices[index + 1], vertices[index], d1, d0);
  513. vertices[index + 2] = clipEdge(vertices[index + 2], vertices[index], d2, d0);
  514. }
  515. // 1 vertex behind the plane: create one new triangle, and modify one in-place
  516. else if (d0 < 0.0f)
  517. {
  518. unsigned newIdx = numTriangles * 3;
  519. triangles[numTriangles] = true;
  520. ++numTriangles;
  521. vertices[newIdx] = clipEdge(vertices[index], vertices[index + 1], d0, d1);
  522. vertices[newIdx + 1] = vertices[index + 1];
  523. vertices[newIdx + 2] = vertices[index] = clipEdge(vertices[index], vertices[index + 2], d0, d2);
  524. }
  525. else if (d1 < 0.0f)
  526. {
  527. unsigned newIdx = numTriangles * 3;
  528. triangles[numTriangles] = true;
  529. ++numTriangles;
  530. vertices[newIdx + 1] = clipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  531. vertices[newIdx + 2] = vertices[index + 2];
  532. vertices[newIdx] = vertices[index + 1] = clipEdge(vertices[index + 1], vertices[index], d1, d0);
  533. }
  534. else if (d2 < 0.0f)
  535. {
  536. unsigned newIdx = numTriangles * 3;
  537. triangles[numTriangles] = true;
  538. ++numTriangles;
  539. vertices[newIdx + 2] = clipEdge(vertices[index + 2], vertices[index], d2, d0);
  540. vertices[newIdx] = vertices[index];
  541. vertices[newIdx + 1] = vertices[index + 2] = clipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  542. }
  543. }
  544. }
  545. }
  546. // Code based on Chris Hecker's Perspective Texture Mapping series in the Game Developer magazine
  547. // Also available online at http://chrishecker.com/Miscellaneous_Technical_Articles
  548. //! Gradients of a software rasterized triangle
  549. struct Gradients
  550. {
  551. //! Construct from vertices
  552. Gradients(const Vector3* vertices)
  553. {
  554. float invdX = 1.0f / (((vertices[1].mX - vertices[2].mX) *
  555. (vertices[0].mY - vertices[2].mY)) -
  556. ((vertices[0].mX - vertices[2].mX) *
  557. (vertices[1].mY - vertices[2].mY)));
  558. float invdY = -invdX;
  559. mDInvZdX = invdX * (((vertices[1].mZ - vertices[2].mZ) * (vertices[0].mY - vertices[2].mY)) -
  560. ((vertices[0].mZ - vertices[2].mZ) * (vertices[1].mY - vertices[2].mY)));
  561. mDInvZdY = invdY * (((vertices[1].mZ - vertices[2].mZ) * (vertices[0].mX - vertices[2].mX)) -
  562. ((vertices[0].mZ - vertices[2].mZ) * (vertices[1].mX - vertices[2].mX)));
  563. mDInvZdXInt = (int)mDInvZdX;
  564. }
  565. //! Integer horizontal gradient
  566. int mDInvZdXInt;
  567. //! Horizontal gradient
  568. float mDInvZdX;
  569. //! Vertical gradient
  570. float mDInvZdY;
  571. };
  572. //! Edge of a software rasterized triangle
  573. struct Edge
  574. {
  575. //! Construct from gradients and top & bottom vertices
  576. Edge(const Gradients& gradients, const Vector3& top, const Vector3& bottom, int topY)
  577. {
  578. float slope = (bottom.mX - top.mX) / (bottom.mY - top.mY);
  579. float yPreStep = (float)(topY + 1) - top.mY;
  580. float xPreStep = slope * yPreStep;
  581. mX = (int)((xPreStep + top.mX) * OCCLUSION_X_SCALE);
  582. mXStep = (int)(slope * OCCLUSION_X_SCALE);
  583. mInvZ = (int)(top.mZ + xPreStep * gradients.mDInvZdX + yPreStep * gradients.mDInvZdY);
  584. mInvZStep = (int)(slope * gradients.mDInvZdX + gradients.mDInvZdY);
  585. }
  586. //! X coordinate
  587. int mX;
  588. //! X coordinate step
  589. int mXStep;
  590. //! Inverse Z
  591. int mInvZ;
  592. //! Inverse Z step
  593. int mInvZStep;
  594. };
  595. void OcclusionBuffer::drawTriangle2D(const Vector3* vertices)
  596. {
  597. int top, middle, bottom;
  598. bool middleIsRight;
  599. mNumTriangles++;
  600. // Sort vertices in Y-direction
  601. if (vertices[0].mY < vertices[1].mY)
  602. {
  603. if (vertices[2].mY < vertices[0].mY)
  604. {
  605. top = 2; middle = 0; bottom = 1;
  606. middleIsRight = true;
  607. }
  608. else
  609. {
  610. top = 0;
  611. if (vertices[1].mY < vertices[2].mY)
  612. {
  613. middle = 1; bottom = 2;
  614. middleIsRight = true;
  615. }
  616. else
  617. {
  618. middle = 2; bottom = 1;
  619. middleIsRight = false;
  620. }
  621. }
  622. }
  623. else
  624. {
  625. if (vertices[2].mY < vertices[1].mY)
  626. {
  627. top = 2; middle = 1; bottom = 0;
  628. middleIsRight = false;
  629. }
  630. else
  631. {
  632. top = 1;
  633. if (vertices[0].mY < vertices[2].mY)
  634. {
  635. middle = 0; bottom = 2;
  636. middleIsRight = false;
  637. }
  638. else
  639. {
  640. middle = 2; bottom = 0;
  641. middleIsRight = true;
  642. }
  643. }
  644. }
  645. int topY = (int)vertices[top].mY;
  646. int middleY = (int)vertices[middle].mY;
  647. int bottomY = (int)vertices[bottom].mY;
  648. // Check for degenerate triangle
  649. if (topY == bottomY)
  650. return;
  651. Gradients gradients(vertices);
  652. Edge topToMiddle(gradients, vertices[top], vertices[middle], topY);
  653. Edge topToBottom(gradients, vertices[top], vertices[bottom], topY);
  654. Edge middleToBottom(gradients, vertices[middle], vertices[bottom], middleY);
  655. // The triangle is clockwise, so if bottom > middle then middle is right
  656. if (middleIsRight)
  657. {
  658. // Top half
  659. int* row = mBuffer + topY * mWidth;
  660. int* endRow = mBuffer + middleY * mWidth;
  661. while (row < endRow)
  662. {
  663. int invZ = topToBottom.mInvZ;
  664. int* dest = row + (topToBottom.mX >> 16);
  665. int* end = row + (topToMiddle.mX >> 16);
  666. while (dest < end)
  667. {
  668. if (invZ < *dest)
  669. *dest = invZ;
  670. invZ += gradients.mDInvZdXInt;
  671. ++dest;
  672. }
  673. topToBottom.mX += topToBottom.mXStep;
  674. topToBottom.mInvZ += topToBottom.mInvZStep;
  675. topToMiddle.mX += topToMiddle.mXStep;
  676. row += mWidth;
  677. }
  678. // Bottom half
  679. row = mBuffer + middleY * mWidth;
  680. endRow = mBuffer + bottomY * mWidth;
  681. while (row < endRow)
  682. {
  683. int invZ = topToBottom.mInvZ;
  684. int* dest = row + (topToBottom.mX >> 16);
  685. int* end = row + (middleToBottom.mX >> 16);
  686. while (dest < end)
  687. {
  688. if (invZ < *dest)
  689. *dest = invZ;
  690. invZ += gradients.mDInvZdXInt;
  691. ++dest;
  692. }
  693. topToBottom.mX += topToBottom.mXStep;
  694. topToBottom.mInvZ += topToBottom.mInvZStep;
  695. middleToBottom.mX += middleToBottom.mXStep;
  696. row += mWidth;
  697. }
  698. }
  699. else
  700. {
  701. // Top half
  702. int* row = mBuffer + topY * mWidth;
  703. int* endRow = mBuffer + middleY * mWidth;
  704. while (row < endRow)
  705. {
  706. int invZ = topToMiddle.mInvZ;
  707. int* dest = row + (topToMiddle.mX >> 16);
  708. int* end = row + (topToBottom.mX >> 16);
  709. while (dest < end)
  710. {
  711. if (invZ < *dest)
  712. *dest = invZ;
  713. invZ += gradients.mDInvZdXInt;
  714. ++dest;
  715. }
  716. topToMiddle.mX += topToMiddle.mXStep;
  717. topToMiddle.mInvZ += topToMiddle.mInvZStep;
  718. topToBottom.mX += topToBottom.mXStep;
  719. row += mWidth;
  720. }
  721. // Bottom half
  722. row = mBuffer + middleY * mWidth;
  723. endRow = mBuffer + bottomY * mWidth;
  724. while (row < endRow)
  725. {
  726. int invZ = middleToBottom.mInvZ;
  727. int* dest = row + (middleToBottom.mX >> 16);
  728. int* end = row + (topToBottom.mX >> 16);
  729. while (dest < end)
  730. {
  731. if (invZ < *dest)
  732. *dest = invZ;
  733. invZ += gradients.mDInvZdXInt;
  734. ++dest;
  735. }
  736. middleToBottom.mX += middleToBottom.mXStep;
  737. middleToBottom.mInvZ += middleToBottom.mInvZStep;
  738. topToBottom.mX += topToBottom.mXStep;
  739. row += mWidth;
  740. }
  741. }
  742. }