CustomGeometry.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. //
  2. // Copyright (c) 2008-2020 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 "../Core/Context.h"
  24. #include "../Core/Profiler.h"
  25. #include "../Graphics/Batch.h"
  26. #include "../Graphics/Camera.h"
  27. #include "../Graphics/CustomGeometry.h"
  28. #include "../Graphics/Geometry.h"
  29. #include "../Graphics/Material.h"
  30. #include "../Graphics/OcclusionBuffer.h"
  31. #include "../Graphics/OctreeQuery.h"
  32. #include "../Graphics/VertexBuffer.h"
  33. #include "../IO/Log.h"
  34. #include "../IO/MemoryBuffer.h"
  35. #include "../Resource/ResourceCache.h"
  36. #include "../Scene/Node.h"
  37. #include "../DebugNew.h"
  38. namespace Urho3D
  39. {
  40. extern const char* GEOMETRY_CATEGORY;
  41. CustomGeometry::CustomGeometry(Context* context)
  42. : Drawable(context, DRAWABLE_GEOMETRY)
  43. , vertexBuffer_(new VertexBuffer(context))
  44. , elementMask_(MASK_POSITION)
  45. , geometryIndex_(0)
  46. , materialsAttr_(Material::GetTypeStatic())
  47. , dynamic_(false)
  48. {
  49. vertexBuffer_->SetShadowed(true);
  50. SetNumGeometries(1);
  51. }
  52. CustomGeometry::~CustomGeometry() = default;
  53. void CustomGeometry::RegisterObject(Context* context)
  54. {
  55. context->RegisterFactory<CustomGeometry>(GEOMETRY_CATEGORY);
  56. URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  57. URHO3D_ATTRIBUTE("Dynamic Vertex Buffer", bool, dynamic_, false, AM_DEFAULT);
  58. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Geometry Data", GetGeometryDataAttr, SetGeometryDataAttr, PODVector<unsigned char>,
  59. Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
  60. URHO3D_ACCESSOR_ATTRIBUTE("Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList,
  61. ResourceRefList(Material::GetTypeStatic()), AM_DEFAULT);
  62. URHO3D_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
  63. URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
  64. URHO3D_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
  65. URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
  66. URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
  67. URHO3D_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
  68. URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
  69. }
  70. void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
  71. {
  72. RayQueryLevel level = query.level_;
  73. switch (level)
  74. {
  75. case RAY_AABB:
  76. Drawable::ProcessRayQuery(query, results);
  77. break;
  78. case RAY_OBB:
  79. case RAY_TRIANGLE:
  80. {
  81. Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
  82. Ray localRay = query.ray_.Transformed(inverse);
  83. float distance = localRay.HitDistance(boundingBox_);
  84. Vector3 normal = -query.ray_.direction_;
  85. if (level == RAY_TRIANGLE && distance < query.maxDistance_)
  86. {
  87. distance = M_INFINITY;
  88. for (unsigned i = 0; i < batches_.Size(); ++i)
  89. {
  90. Geometry* geometry = batches_[i].geometry_;
  91. if (geometry)
  92. {
  93. Vector3 geometryNormal;
  94. float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal);
  95. if (geometryDistance < query.maxDistance_ && geometryDistance < distance)
  96. {
  97. distance = geometryDistance;
  98. normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized();
  99. }
  100. }
  101. }
  102. }
  103. if (distance < query.maxDistance_)
  104. {
  105. RayQueryResult result;
  106. result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
  107. result.normal_ = normal;
  108. result.distance_ = distance;
  109. result.drawable_ = this;
  110. result.node_ = node_;
  111. result.subObject_ = M_MAX_UNSIGNED;
  112. results.Push(result);
  113. }
  114. }
  115. break;
  116. case RAY_TRIANGLE_UV:
  117. URHO3D_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component");
  118. break;
  119. }
  120. }
  121. Geometry* CustomGeometry::GetLodGeometry(unsigned batchIndex, unsigned level)
  122. {
  123. return batchIndex < geometries_.Size() ? geometries_[batchIndex] : nullptr;
  124. }
  125. unsigned CustomGeometry::GetNumOccluderTriangles()
  126. {
  127. unsigned triangles = 0;
  128. for (unsigned i = 0; i < batches_.Size(); ++i)
  129. {
  130. Geometry* geometry = GetLodGeometry(i, 0);
  131. if (!geometry)
  132. continue;
  133. // Check that the material is suitable for occlusion (default material always is)
  134. Material* mat = batches_[i].material_;
  135. if (mat && !mat->GetOcclusion())
  136. continue;
  137. triangles += geometry->GetVertexCount() / 3;
  138. }
  139. return triangles;
  140. }
  141. bool CustomGeometry::DrawOcclusion(OcclusionBuffer* buffer)
  142. {
  143. bool success = true;
  144. for (unsigned i = 0; i < batches_.Size(); ++i)
  145. {
  146. Geometry* geometry = GetLodGeometry(i, 0);
  147. if (!geometry)
  148. continue;
  149. // Check that the material is suitable for occlusion (default material always is) and set culling mode
  150. Material* material = batches_[i].material_;
  151. if (material)
  152. {
  153. if (!material->GetOcclusion())
  154. continue;
  155. buffer->SetCullMode(material->GetCullMode());
  156. }
  157. else
  158. buffer->SetCullMode(CULL_CCW);
  159. const unsigned char* vertexData;
  160. unsigned vertexSize;
  161. const unsigned char* indexData;
  162. unsigned indexSize;
  163. const PODVector<VertexElement>* elements;
  164. geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
  165. // Check for valid geometry data
  166. if (!vertexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
  167. continue;
  168. // Draw and check for running out of triangles
  169. success = buffer->AddTriangles(node_->GetWorldTransform(), vertexData, vertexSize, geometry->GetVertexStart(),
  170. geometry->GetVertexCount());
  171. if (!success)
  172. break;
  173. }
  174. return success;
  175. }
  176. Vector<Vector3> CustomGeometry::GetCircleShape(float radius, size_t iterations, float startTheta, float endTheta)
  177. {
  178. float stepSize = (endTheta - startTheta) / (float)iterations;
  179. Vector<Vector3> shapeList;
  180. for (int i = 0; i < iterations; i++)
  181. {
  182. float curTheta1 = startTheta + ((float)i * stepSize);
  183. float curTheta2 = startTheta + ((float)(i + 1) * stepSize);
  184. float curX1 = radius * cos(curTheta1);
  185. float curY1 = radius * sin(curTheta1);
  186. float curX2 = radius * cos(curTheta2);
  187. float curY2 = radius * sin(curTheta2);
  188. shapeList.Push(Urho3D::Vector3(curX1, 0, curY1));
  189. shapeList.Push(Urho3D::Vector3(curX2, 0, curY2));
  190. if (i >= iterations - 1)
  191. {
  192. float curTheta = 0;
  193. if (Abs(endTheta - startTheta) < (2 * M_PI))
  194. curTheta = endTheta;
  195. float curX = radius * cos(curTheta);
  196. float curY = radius * sin(curTheta);
  197. shapeList.Push(Vector3(curX, 0, curY));
  198. }
  199. }
  200. return shapeList;
  201. }
  202. Vector<Vector3> CustomGeometry::GetSquareShape(float size)
  203. {
  204. Vector<Vector3> mSquareList = {
  205. Urho3D::Vector3(-(size / 2.0f), 0, (size / 2.0f)), Urho3D::Vector3(-(size / 2.0f), 0, -(size / 2.0f)),
  206. Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)), Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f))};
  207. return mSquareList;
  208. }
  209. void CustomGeometry::MakeCircle(float radius, size_t iterations, float startTheta, float endTheta, bool clear,
  210. int geomNum)
  211. {
  212. Vector<Vector3> mCircleShape = GetCircleShape(radius, iterations, startTheta, endTheta);
  213. FillShape(mCircleShape, false, clear, geomNum);
  214. }
  215. void CustomGeometry::MakeCircleGraph(const Vector<Pair<float, Urho3D::SharedPtr<Urho3D::Material>>>& parts, int radius,
  216. int iterations)
  217. {
  218. if (parts.Size() > 0)
  219. {
  220. float totalWeight = 0;
  221. SetNumGeometries(parts.Size());
  222. auto it = parts.Begin();
  223. while (it != parts.End())
  224. {
  225. const auto current = (*it);
  226. totalWeight += current.first_;
  227. it++;
  228. }
  229. it = parts.Begin();
  230. float currentStartTheta = 0;
  231. float currentEndTheta = 0;
  232. int count = 0;
  233. while (it != parts.End())
  234. {
  235. const auto current = (*it);
  236. currentEndTheta = ((current.first_ / totalWeight) * (2 * M_PI)) + currentStartTheta;
  237. MakeCircle(radius, (iterations / parts.Size()), currentStartTheta, currentEndTheta, false, count);
  238. if (current.second_.NotNull())
  239. SetMaterial(count, current.second_);
  240. it++;
  241. count++;
  242. currentStartTheta = currentEndTheta;
  243. }
  244. }
  245. }
  246. void CustomGeometry::MakeShape(const Vector<Vector3>& pointList, bool connectTail)
  247. {
  248. Clear();
  249. SetNumGeometries(1);
  250. BeginGeometry(0, Urho3D::PrimitiveType::LINE_STRIP);
  251. Vector3 current;
  252. Vector3 next;
  253. for (size_t i = 0; i < pointList.Size(); i++)
  254. {
  255. if ((connectTail && i >= pointList.Size() - 1) || i < pointList.Size() - 1)
  256. {
  257. current = pointList[i];
  258. next = pointList[0];
  259. if (i < pointList.Size() - 1)
  260. next = pointList[i + 1];
  261. DefineVertex(current);
  262. DefineVertex(next);
  263. }
  264. }
  265. Commit();
  266. }
  267. void CustomGeometry::FillShape(const Vector<Vector3>& shapeList, bool connectTail, bool clear, int geomNum)
  268. {
  269. if (shapeList.Size() > 0)
  270. {
  271. int usedGeomNum = geomNum;
  272. if (clear)
  273. {
  274. Clear();
  275. SetNumGeometries(1);
  276. usedGeomNum = 0;
  277. }
  278. BeginGeometry(usedGeomNum, PrimitiveType::TRIANGLE_STRIP);
  279. auto centerPoint = Vector3(0, 0, 0);
  280. if (connectTail)
  281. {
  282. auto centerPoint = Average(shapeList.Begin(), shapeList.End());
  283. }
  284. Vector<Vector3> vertices(3);
  285. Vector3 current;
  286. Vector3 next;
  287. Vector3 normal;
  288. auto it = shapeList.Begin();
  289. auto nextIt = it;
  290. while (it != shapeList.End())
  291. {
  292. nextIt = it + 1;
  293. if ((connectTail && nextIt == shapeList.End()) || nextIt != shapeList.End())
  294. {
  295. current = (*it);
  296. if (nextIt != shapeList.End())
  297. {
  298. next = (*nextIt);
  299. }
  300. else
  301. {
  302. next = (*shapeList.Begin());
  303. }
  304. vertices = {centerPoint, current, next};
  305. normal = Average(vertices.Begin(), vertices.End());
  306. normal.Normalize();
  307. normal.Orthogonalize(normal);
  308. DefineVertex(vertices.At(0));
  309. DefineNormal(normal);
  310. DefineVertex(vertices.At(1));
  311. DefineNormal(normal);
  312. DefineVertex(vertices.At(2));
  313. DefineNormal(normal);
  314. }
  315. it++;
  316. }
  317. Commit();
  318. }
  319. }
  320. void CustomGeometry::MakeSphere(float radius, size_t iterations)
  321. {
  322. // Create the geometry buffer
  323. float angleStepSize = (2.0f * M_PI) / (float)iterations;
  324. Vector<Vector3> m_xyPoints;
  325. for (int i = 0; i < iterations; i++)
  326. {
  327. float curTheta = i * angleStepSize;
  328. for (int j = 0; j < iterations; j++)
  329. {
  330. float curPhi = j * angleStepSize;
  331. float curX = radius * cos(curTheta) * sin(curPhi);
  332. float curY = radius * sin(curTheta) * sin(curPhi);
  333. float curZ = radius * cos(curPhi);
  334. m_xyPoints.Push(Vector3(curX, curY, curZ));
  335. }
  336. }
  337. CreateQuadsFromBuffer(m_xyPoints, iterations, iterations, true);
  338. }
  339. void CustomGeometry::ProtrudeShape(const Vector<Vector3>& mShapeList, const Vector<Vector3>& mPointList,
  340. bool connectTail)
  341. {
  342. Vector3 centerPoint = Average(mShapeList.Begin(), mShapeList.End());
  343. Vector3 pointCurrent;
  344. Vector3 shapeCurrent;
  345. Vector3 shapePointVec;
  346. Vector3 shapePointDir;
  347. Vector<Vector3> mPointBuffer(mShapeList.Size() * mPointList.Size() + mShapeList.Size());
  348. Vector<Vector3> mLastShapePos = mShapeList;
  349. auto pointIter = mPointList.Begin();
  350. auto shapeIter = mLastShapePos.Begin();
  351. int bufferCount = 0;
  352. while (shapeIter != mLastShapePos.End())
  353. {
  354. mPointBuffer.At(bufferCount) = (*shapeIter);
  355. shapeIter++;
  356. bufferCount++;
  357. }
  358. int count = 0;
  359. while (pointIter != mPointList.End())
  360. {
  361. shapeIter = mLastShapePos.Begin();
  362. pointCurrent = (*pointIter);
  363. count = 0;
  364. while (shapeIter != mLastShapePos.End())
  365. {
  366. shapeCurrent = (*shapeIter);
  367. if (shapeIter == mLastShapePos.Begin())
  368. { // protrude from first point of the shape and create dir Vector to point
  369. shapePointVec = pointCurrent - centerPoint;
  370. centerPoint = pointCurrent;
  371. }
  372. // protrude from the rest of the points on the shape to the next point given a dir and length vector
  373. shapePointDir = shapePointVec;
  374. shapePointDir.Normalize();
  375. mLastShapePos[count] = mLastShapePos[count] + shapePointDir * shapePointVec.Length();
  376. mPointBuffer.At(bufferCount) = mLastShapePos[count];
  377. bufferCount++;
  378. shapeIter++;
  379. count++;
  380. }
  381. pointIter++;
  382. }
  383. CreateQuadsFromBuffer(mPointBuffer, mPointList.Size() + 1, mShapeList.Size(), connectTail);
  384. }
  385. void CustomGeometry::CreateQuadsFromBuffer(const Vector<Vector3>& pointList, size_t zIterations, size_t thetaIterations,
  386. bool connectTail)
  387. {
  388. if (!connectTail)
  389. {
  390. SetNumGeometries(3);
  391. }
  392. else
  393. {
  394. SetNumGeometries(1);
  395. }
  396. // Create the quads from the buffer
  397. BeginGeometry(0, Urho3D::PrimitiveType::TRIANGLE_STRIP);
  398. for (size_t i = 0; i < zIterations; i++)
  399. {
  400. if ((i >= zIterations - 1 && connectTail) || i < zIterations - 1)
  401. {
  402. for (size_t j = 0; j < thetaIterations; j++)
  403. {
  404. // if at the end connect to the beginning to complete pass
  405. size_t iplus = i + 1;
  406. size_t jplus = j + 1;
  407. if (i >= zIterations - 1)
  408. {
  409. iplus = 0;
  410. }
  411. if (j >= thetaIterations - 1)
  412. {
  413. jplus = 0;
  414. }
  415. Vector<Vector3> avList;
  416. avList = {pointList.At((i * thetaIterations) + j), pointList.At((iplus * thetaIterations) + j),
  417. pointList.At((i * thetaIterations) + jplus)};
  418. Vector3 normal = Average(avList.Begin(), avList.End());
  419. normal.Normalize();
  420. DefineVertex(avList.At(0));
  421. DefineVertex(avList.At(1));
  422. DefineVertex(avList.At(2));
  423. DefineNormal(normal);
  424. avList.Clear();
  425. avList = {pointList.At((i * thetaIterations) + j), pointList.At((iplus * thetaIterations) + j),
  426. pointList.At((iplus * thetaIterations) + jplus)};
  427. normal = Average(avList.Begin(), avList.End());
  428. normal.Normalize();
  429. DefineVertex(avList.At(0));
  430. DefineVertex(avList.At(1));
  431. DefineVertex(avList.At(2));
  432. DefineNormal(normal);
  433. avList.Clear();
  434. }
  435. }
  436. }
  437. Commit();
  438. if (!connectTail)
  439. {
  440. // fill in the head and tail
  441. auto tailBegin = pointList.Begin();
  442. auto tailEnd = pointList.Begin() + thetaIterations;
  443. Vector<Vector3> tail(tailBegin, tailEnd);
  444. auto headBegin = pointList.Begin() + pointList.Size() - thetaIterations;
  445. auto headEnd = pointList.Begin() + pointList.Size();
  446. Vector<Vector3> head(headBegin, headEnd);
  447. FillShape(tail, true, false, 1);
  448. FillShape(head, true, false, 2);
  449. }
  450. }
  451. void CustomGeometry::MakeSquare(float size)
  452. {
  453. Vector<Vector3> mSquareList = {
  454. Urho3D::Vector3(-(size / 2.0f), 0, (size / 2.0f)), Urho3D::Vector3(-(size / 2.0f), 0, -(size / 2.0f)),
  455. Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)), Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f))};
  456. FillShape(mSquareList, true);
  457. }
  458. void CustomGeometry::Clear()
  459. {
  460. elementMask_ = MASK_POSITION;
  461. batches_.Clear();
  462. geometries_.Clear();
  463. primitiveTypes_.Clear();
  464. vertices_.Clear();
  465. }
  466. void CustomGeometry::SetNumGeometries(unsigned num)
  467. {
  468. batches_.Resize(num);
  469. geometries_.Resize(num);
  470. primitiveTypes_.Resize(num);
  471. vertices_.Resize(num);
  472. for (unsigned i = 0; i < geometries_.Size(); ++i)
  473. {
  474. if (!geometries_[i])
  475. geometries_[i] = new Geometry(context_);
  476. batches_[i].geometry_ = geometries_[i];
  477. }
  478. }
  479. void CustomGeometry::SetDynamic(bool enable)
  480. {
  481. dynamic_ = enable;
  482. MarkNetworkUpdate();
  483. }
  484. void CustomGeometry::BeginGeometry(unsigned index, PrimitiveType type)
  485. {
  486. if (index > geometries_.Size())
  487. {
  488. URHO3D_LOGERROR("Geometry index out of bounds");
  489. return;
  490. }
  491. geometryIndex_ = index;
  492. primitiveTypes_[index] = type;
  493. vertices_[index].Clear();
  494. // If beginning the first geometry, reset the element mask
  495. if (!index)
  496. elementMask_ = MASK_POSITION;
  497. }
  498. void CustomGeometry::DefineVertex(const Vector3& position)
  499. {
  500. if (vertices_.Size() < geometryIndex_)
  501. return;
  502. vertices_[geometryIndex_].Resize(vertices_[geometryIndex_].Size() + 1);
  503. vertices_[geometryIndex_].Back().position_ = position;
  504. }
  505. void CustomGeometry::DefineNormal(const Vector3& normal)
  506. {
  507. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  508. return;
  509. vertices_[geometryIndex_].Back().normal_ = normal;
  510. elementMask_ |= MASK_NORMAL;
  511. }
  512. void CustomGeometry::DefineColor(const Color& color)
  513. {
  514. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  515. return;
  516. vertices_[geometryIndex_].Back().color_ = color.ToUInt();
  517. elementMask_ |= MASK_COLOR;
  518. }
  519. void CustomGeometry::DefineTexCoord(const Vector2& texCoord)
  520. {
  521. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  522. return;
  523. vertices_[geometryIndex_].Back().texCoord_ = texCoord;
  524. elementMask_ |= MASK_TEXCOORD1;
  525. }
  526. void CustomGeometry::DefineTangent(const Vector4& tangent)
  527. {
  528. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  529. return;
  530. vertices_[geometryIndex_].Back().tangent_ = tangent;
  531. elementMask_ |= MASK_TANGENT;
  532. }
  533. void CustomGeometry::DefineGeometry(unsigned index, PrimitiveType type, unsigned numVertices, bool hasNormals,
  534. bool hasColors, bool hasTexCoords, bool hasTangents)
  535. {
  536. if (index > geometries_.Size())
  537. {
  538. URHO3D_LOGERROR("Geometry index out of bounds");
  539. return;
  540. }
  541. geometryIndex_ = index;
  542. primitiveTypes_[index] = type;
  543. vertices_[index].Resize(numVertices);
  544. // If defining the first geometry, reset the element mask
  545. if (!index)
  546. elementMask_ = MASK_POSITION;
  547. if (hasNormals)
  548. elementMask_ |= MASK_NORMAL;
  549. if (hasColors)
  550. elementMask_ |= MASK_COLOR;
  551. if (hasTexCoords)
  552. elementMask_ |= MASK_TEXCOORD1;
  553. if (hasTangents)
  554. elementMask_ |= MASK_TANGENT;
  555. }
  556. void CustomGeometry::Commit()
  557. {
  558. URHO3D_PROFILE(CommitCustomGeometry);
  559. unsigned totalVertices = 0;
  560. boundingBox_.Clear();
  561. for (unsigned i = 0; i < vertices_.Size(); ++i)
  562. {
  563. totalVertices += vertices_[i].Size();
  564. for (unsigned j = 0; j < vertices_[i].Size(); ++j)
  565. boundingBox_.Merge(vertices_[i][j].position_);
  566. }
  567. // Make sure world-space bounding box will be updated
  568. OnMarkedDirty(node_);
  569. // Resize (recreate) the vertex buffer only if necessary
  570. if (vertexBuffer_->GetVertexCount() != totalVertices || vertexBuffer_->GetElementMask() != elementMask_ ||
  571. vertexBuffer_->IsDynamic() != dynamic_)
  572. vertexBuffer_->SetSize(totalVertices, elementMask_, dynamic_);
  573. if (totalVertices)
  574. {
  575. auto* dest = (unsigned char*)vertexBuffer_->Lock(0, totalVertices, true);
  576. if (dest)
  577. {
  578. unsigned vertexStart = 0;
  579. for (unsigned i = 0; i < vertices_.Size(); ++i)
  580. {
  581. unsigned vertexCount = 0;
  582. for (unsigned j = 0; j < vertices_[i].Size(); ++j)
  583. {
  584. *((Vector3*)dest) = vertices_[i][j].position_;
  585. dest += sizeof(Vector3);
  586. if (elementMask_ & MASK_NORMAL)
  587. {
  588. *((Vector3*)dest) = vertices_[i][j].normal_;
  589. dest += sizeof(Vector3);
  590. }
  591. if (elementMask_ & MASK_COLOR)
  592. {
  593. *((unsigned*)dest) = vertices_[i][j].color_;
  594. dest += sizeof(unsigned);
  595. }
  596. if (elementMask_ & MASK_TEXCOORD1)
  597. {
  598. *((Vector2*)dest) = vertices_[i][j].texCoord_;
  599. dest += sizeof(Vector2);
  600. }
  601. if (elementMask_ & MASK_TANGENT)
  602. {
  603. *((Vector4*)dest) = vertices_[i][j].tangent_;
  604. dest += sizeof(Vector4);
  605. }
  606. ++vertexCount;
  607. }
  608. geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
  609. geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, vertexStart, vertexCount);
  610. vertexStart += vertexCount;
  611. }
  612. vertexBuffer_->Unlock();
  613. }
  614. else
  615. URHO3D_LOGERROR("Failed to lock custom geometry vertex buffer");
  616. }
  617. else
  618. {
  619. for (unsigned i = 0; i < geometries_.Size(); ++i)
  620. {
  621. geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
  622. geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, 0, 0);
  623. }
  624. }
  625. vertexBuffer_->ClearDataLost();
  626. }
  627. void CustomGeometry::SetMaterial(Material* material)
  628. {
  629. for (unsigned i = 0; i < batches_.Size(); ++i)
  630. batches_[i].material_ = material;
  631. MarkNetworkUpdate();
  632. }
  633. bool CustomGeometry::SetMaterial(unsigned index, Material* material)
  634. {
  635. if (index >= batches_.Size())
  636. {
  637. URHO3D_LOGERROR("Material index out of bounds");
  638. return false;
  639. }
  640. batches_[index].material_ = material;
  641. MarkNetworkUpdate();
  642. return true;
  643. }
  644. unsigned CustomGeometry::GetNumVertices(unsigned index) const
  645. {
  646. return index < vertices_.Size() ? vertices_[index].Size() : 0;
  647. }
  648. Material* CustomGeometry::GetMaterial(unsigned index) const
  649. {
  650. return index < batches_.Size() ? batches_[index].material_ : nullptr;
  651. }
  652. CustomGeometryVertex* CustomGeometry::GetVertex(unsigned geometryIndex, unsigned vertexNum)
  653. {
  654. return (geometryIndex < vertices_.Size() && vertexNum < vertices_[geometryIndex].Size())
  655. ? &vertices_[geometryIndex][vertexNum]
  656. : nullptr;
  657. }
  658. void CustomGeometry::SetGeometryDataAttr(const PODVector<unsigned char>& value)
  659. {
  660. if (value.Empty())
  661. return;
  662. MemoryBuffer buffer(value);
  663. SetNumGeometries(buffer.ReadVLE());
  664. elementMask_ = VertexMaskFlags(buffer.ReadUInt());
  665. for (unsigned i = 0; i < geometries_.Size(); ++i)
  666. {
  667. unsigned numVertices = buffer.ReadVLE();
  668. vertices_[i].Resize(numVertices);
  669. primitiveTypes_[i] = (PrimitiveType)buffer.ReadUByte();
  670. for (unsigned j = 0; j < numVertices; ++j)
  671. {
  672. if (elementMask_ & MASK_POSITION)
  673. vertices_[i][j].position_ = buffer.ReadVector3();
  674. if (elementMask_ & MASK_NORMAL)
  675. vertices_[i][j].normal_ = buffer.ReadVector3();
  676. if (elementMask_ & MASK_COLOR)
  677. vertices_[i][j].color_ = buffer.ReadUInt();
  678. if (elementMask_ & MASK_TEXCOORD1)
  679. vertices_[i][j].texCoord_ = buffer.ReadVector2();
  680. if (elementMask_ & MASK_TANGENT)
  681. vertices_[i][j].tangent_ = buffer.ReadVector4();
  682. }
  683. }
  684. Commit();
  685. }
  686. void CustomGeometry::SetMaterialsAttr(const ResourceRefList& value)
  687. {
  688. auto* cache = GetSubsystem<ResourceCache>();
  689. for (unsigned i = 0; i < value.names_.Size(); ++i)
  690. SetMaterial(i, cache->GetResource<Material>(value.names_[i]));
  691. }
  692. PODVector<unsigned char> CustomGeometry::GetGeometryDataAttr() const
  693. {
  694. VectorBuffer ret;
  695. ret.WriteVLE(geometries_.Size());
  696. ret.WriteUInt(elementMask_);
  697. for (unsigned i = 0; i < geometries_.Size(); ++i)
  698. {
  699. unsigned numVertices = vertices_[i].Size();
  700. ret.WriteVLE(numVertices);
  701. ret.WriteUByte(primitiveTypes_[i]);
  702. for (unsigned j = 0; j < numVertices; ++j)
  703. {
  704. if (elementMask_ & MASK_POSITION)
  705. ret.WriteVector3(vertices_[i][j].position_);
  706. if (elementMask_ & MASK_NORMAL)
  707. ret.WriteVector3(vertices_[i][j].normal_);
  708. if (elementMask_ & MASK_COLOR)
  709. ret.WriteUInt(vertices_[i][j].color_);
  710. if (elementMask_ & MASK_TEXCOORD1)
  711. ret.WriteVector2(vertices_[i][j].texCoord_);
  712. if (elementMask_ & MASK_TANGENT)
  713. ret.WriteVector4(vertices_[i][j].tangent_);
  714. }
  715. }
  716. return ret.GetBuffer();
  717. }
  718. const ResourceRefList& CustomGeometry::GetMaterialsAttr() const
  719. {
  720. materialsAttr_.names_.Resize(batches_.Size());
  721. for (unsigned i = 0; i < batches_.Size(); ++i)
  722. materialsAttr_.names_[i] = GetResourceName(batches_[i].material_);
  723. return materialsAttr_;
  724. }
  725. void CustomGeometry::OnWorldBoundingBoxUpdate()
  726. {
  727. worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
  728. }
  729. } // namespace Urho3D