CustomGeometry.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  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, ResourceRefList(Material::GetTypeStatic()),
  61. 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. float curTheta1 = startTheta + ((float)i * stepSize);
  182. float curTheta2 = startTheta + ((float)(i+1) * stepSize);
  183. float curX1 = radius * cos(curTheta1);
  184. float curY1 = radius * sin(curTheta1);
  185. float curX2 = radius * cos(curTheta2);
  186. float curY2 = radius * sin(curTheta2);
  187. shapeList.Push(Urho3D::Vector3(curX1, 0, curY1));
  188. shapeList.Push(Urho3D::Vector3(curX2, 0, curY2));
  189. if (i >= iterations - 1) {
  190. float curTheta =0;
  191. if (Abs(endTheta - startTheta) < (2*M_PI)) {
  192. curTheta = endTheta;
  193. }
  194. float curX = radius * cos(curTheta);
  195. float curY = radius * sin(curTheta);
  196. shapeList.Push(Urho3D::Vector3(curX, 0, curY));
  197. }
  198. }
  199. return shapeList;
  200. }
  201. Vector<Vector3> CustomGeometry::GetSquareShape(float size)
  202. {
  203. Vector<Vector3> mSquareList = { Urho3D::Vector3(-(size / 2.0f),0,(size / 2.0f)),Urho3D::Vector3(-(size / 2.0f),0,-(size / 2.0f)),
  204. Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)),Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f)) };
  205. return mSquareList;
  206. }
  207. void CustomGeometry::MakeCircle(float radius, size_t iterations, float startTheta,
  208. float endTheta, bool clear, int geomNum)
  209. {
  210. Vector<Vector3> mCircleShape = GetCircleShape(radius, iterations, startTheta, endTheta);
  211. FillShape(mCircleShape,false,clear,geomNum);
  212. }
  213. void CustomGeometry::MakeCircleGraph(const Vector<Pair<float, Urho3D::SharedPtr<Urho3D::Material> > >& parts,
  214. int radius, int iterations)
  215. {
  216. if (parts.Size() > 0) {
  217. float totalWeight = 0;
  218. SetNumGeometries(parts.Size());
  219. auto it = parts.Begin();
  220. while (it != parts.End()) {
  221. const auto current = (*it);
  222. totalWeight += current.first_;
  223. it++;
  224. }
  225. it = parts.Begin();
  226. float currentStartTheta = 0;
  227. float currentEndTheta = 0;
  228. int count = 0;
  229. while (it != parts.End()) {
  230. const auto current = (*it);
  231. currentEndTheta = ((current.first_ / totalWeight)*(2 * M_PI)) + currentStartTheta;
  232. MakeCircle(radius, (iterations / parts.Size()), currentStartTheta, currentEndTheta,false,count);
  233. if (current.second_.NotNull()) {
  234. SetMaterial(count, current.second_);
  235. }
  236. it++;
  237. count++;
  238. currentStartTheta = currentEndTheta;
  239. }
  240. }
  241. }
  242. void CustomGeometry::MakeShape(const Vector<Vector3>& pointList , bool connectTail)
  243. {
  244. Clear();
  245. SetNumGeometries(1);
  246. BeginGeometry(0, Urho3D::PrimitiveType::LINE_STRIP);
  247. Vector3 current;
  248. Vector3 next;
  249. for (size_t i = 0; i < pointList.Size(); i++) {
  250. if ((connectTail && i >= pointList.Size() - 1) || i < pointList.Size() - 1) {
  251. current = pointList[i];
  252. next = pointList[0];
  253. if (i < pointList.Size() - 1) {
  254. next = pointList[i + 1];
  255. }
  256. DefineVertex(current);
  257. DefineVertex(next);
  258. }
  259. }
  260. Commit();
  261. }
  262. void CustomGeometry::FillShape(const Vector<Vector3>& shapeList, bool connectTail, bool clear, int geomNum)
  263. {
  264. if (shapeList.Size() > 0) {
  265. int usedGeomNum = geomNum;
  266. if (clear) {
  267. Clear();
  268. SetNumGeometries(1);
  269. usedGeomNum = 0;
  270. }
  271. BeginGeometry(usedGeomNum, PrimitiveType::TRIANGLE_STRIP);
  272. auto centerPoint = Vector3(0, 0, 0);
  273. if (connectTail) {
  274. auto centerPoint = Average(shapeList.Begin(), shapeList.End());
  275. }
  276. Vector<Vector3> vertices(3);
  277. Vector3 current;
  278. Vector3 next;
  279. Vector3 normal;
  280. auto it = shapeList.Begin();
  281. auto nextIt = it;
  282. while (it != shapeList.End()) {
  283. nextIt = it + 1;
  284. if (connectTail && nextIt == shapeList.End() || nextIt != shapeList.End())
  285. {
  286. current = (*it);
  287. if (nextIt != shapeList.End()) {
  288. next = (*nextIt);
  289. }
  290. else {
  291. next = (*shapeList.Begin());
  292. }
  293. vertices = { centerPoint, current, next };
  294. normal = Average(vertices.Begin(), vertices.End());
  295. normal.Normalize();
  296. normal.Orthogonalize(normal);
  297. DefineVertex(vertices.At(0));
  298. DefineNormal(normal);
  299. DefineVertex(vertices.At(1));
  300. DefineNormal(normal);
  301. DefineVertex(vertices.At(2));
  302. DefineNormal(normal);
  303. }
  304. it++;
  305. }
  306. Commit();
  307. }
  308. }
  309. void CustomGeometry::MakeSphere(float radius, size_t iterations)
  310. {
  311. //Create the geometry buffer
  312. float angleStepSize = (2.0f * M_PI) / (float)iterations;
  313. Vector<Vector3> m_xyPoints;
  314. for (int i = 0; i < iterations; i++) {
  315. float curTheta = i * angleStepSize;
  316. for (int j = 0; j < iterations; j++) {
  317. float curPhi = j * angleStepSize;
  318. float curX = radius * cos(curTheta) * sin(curPhi);
  319. float curY = radius * sin(curTheta) * sin(curPhi);
  320. float curZ = radius * cos(curPhi);
  321. m_xyPoints.Push(Vector3(curX,curY,curZ));
  322. }
  323. }
  324. CreateQuadsFromBuffer(m_xyPoints, iterations, iterations, true);
  325. }
  326. void CustomGeometry::ProtrudeShape(const Vector<Vector3>& mShapeList,
  327. const Vector<Vector3>& mPointList, bool connectTail)
  328. {
  329. Vector3 centerPoint = Average(mShapeList.Begin(), mShapeList.End());
  330. Vector3 pointCurrent;
  331. Vector3 shapeCurrent;
  332. Vector3 shapePointVec;
  333. Vector3 shapePointDir;
  334. Vector<Vector3> mPointBuffer(mShapeList.Size()*mPointList.Size()+mShapeList.Size());
  335. Vector<Vector3> mLastShapePos = mShapeList;
  336. auto pointIter = mPointList.Begin();
  337. auto shapeIter = mLastShapePos.Begin();
  338. int bufferCount = 0;
  339. while (shapeIter != mLastShapePos.End()) {
  340. mPointBuffer.At(bufferCount) = (*shapeIter);
  341. shapeIter++;
  342. bufferCount++;
  343. }
  344. int count = 0;
  345. while (pointIter != mPointList.End()) {
  346. shapeIter = mLastShapePos.Begin();
  347. pointCurrent = (*pointIter);
  348. count = 0;
  349. while (shapeIter != mLastShapePos.End()) {
  350. shapeCurrent = (*shapeIter);
  351. if (shapeIter == mLastShapePos.Begin()) { //protrude from first point of the shape and create dir Vector to point
  352. shapePointVec = pointCurrent - centerPoint;
  353. centerPoint = pointCurrent;
  354. }
  355. // protrude from the rest of the points on the shape to the next point given a dir and length vector
  356. shapePointDir = shapePointVec;
  357. shapePointDir.Normalize();
  358. mLastShapePos[count] = mLastShapePos[count] + shapePointDir * shapePointVec.Length();
  359. mPointBuffer.At(bufferCount) = mLastShapePos[count];
  360. bufferCount++;
  361. shapeIter++;
  362. count++;
  363. }
  364. pointIter++;
  365. }
  366. CreateQuadsFromBuffer(mPointBuffer, mPointList.Size() + 1, mShapeList.Size(), connectTail);
  367. }
  368. void CustomGeometry::CreateQuadsFromBuffer(const Vector<Vector3>& pointList, size_t zIterations,
  369. size_t thetaIterations, bool connectTail)
  370. {
  371. if (!connectTail) {
  372. SetNumGeometries(3);
  373. }
  374. else {
  375. SetNumGeometries(1);
  376. }
  377. //Create the quads from the buffer
  378. BeginGeometry(0, Urho3D::PrimitiveType::TRIANGLE_STRIP);
  379. for (size_t i = 0; i < zIterations; i++) {
  380. if ((i >= zIterations - 1 && connectTail) || i < zIterations - 1) {
  381. for (size_t j = 0; j < thetaIterations; j++) {
  382. //if at the end connect to the beginning to complete pass
  383. size_t iplus = i + 1;
  384. size_t jplus = j + 1;
  385. if (i >= zIterations - 1) {
  386. iplus = 0;
  387. }
  388. if (j >= thetaIterations - 1) {
  389. jplus = 0;
  390. }
  391. Vector<Vector3> avList;
  392. avList = { pointList.At((i*thetaIterations) + j) ,pointList.At((iplus*thetaIterations)+
  393. j) ,pointList.At((i*thetaIterations) + jplus) };
  394. Vector3 normal = Average(avList.Begin(), avList.End());
  395. normal.Normalize();
  396. DefineVertex(avList.At(0));
  397. DefineVertex(avList.At(1));
  398. DefineVertex(avList.At(2));
  399. DefineNormal(normal);
  400. avList.Clear();
  401. avList = { pointList.At((i*thetaIterations) + j) ,pointList.At((iplus*thetaIterations)+
  402. j) ,pointList.At((iplus*thetaIterations) + jplus) };
  403. normal = Average(avList.Begin(), avList.End());
  404. normal.Normalize();
  405. DefineVertex(avList.At(0));
  406. DefineVertex(avList.At(1));
  407. DefineVertex(avList.At(2));
  408. DefineNormal(normal);
  409. avList.Clear();
  410. }
  411. }
  412. }
  413. Commit();
  414. if (!connectTail) {
  415. //fill in the head and tail
  416. auto tailBegin = pointList.Begin();
  417. auto tailEnd = pointList.Begin() + thetaIterations;
  418. Vector<Vector3> tail(tailBegin, tailEnd);
  419. auto headBegin = pointList.Begin() + pointList.Size() - thetaIterations;
  420. auto headEnd = pointList.Begin() + pointList.Size();
  421. Vector<Vector3> head(headBegin, headEnd);
  422. FillShape(tail, true, false, 1);
  423. FillShape(head, true, false, 2);
  424. }
  425. }
  426. void CustomGeometry::MakeSquare(float size)
  427. {
  428. Vector<Vector3> mSquareList = { Urho3D::Vector3(-(size / 2.0f),0,(size / 2.0f)),Urho3D::Vector3(-(size / 2.0f),0,-(size / 2.0f)),
  429. Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)),Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f)) };
  430. FillShape(mSquareList,true);
  431. }
  432. void CustomGeometry::Clear()
  433. {
  434. elementMask_ = MASK_POSITION;
  435. batches_.Clear();
  436. geometries_.Clear();
  437. primitiveTypes_.Clear();
  438. vertices_.Clear();
  439. }
  440. void CustomGeometry::SetNumGeometries(unsigned num)
  441. {
  442. batches_.Resize(num);
  443. geometries_.Resize(num);
  444. primitiveTypes_.Resize(num);
  445. vertices_.Resize(num);
  446. for (unsigned i = 0; i < geometries_.Size(); ++i)
  447. {
  448. if (!geometries_[i])
  449. geometries_[i] = new Geometry(context_);
  450. batches_[i].geometry_ = geometries_[i];
  451. }
  452. }
  453. void CustomGeometry::SetDynamic(bool enable)
  454. {
  455. dynamic_ = enable;
  456. MarkNetworkUpdate();
  457. }
  458. void CustomGeometry::BeginGeometry(unsigned index, PrimitiveType type)
  459. {
  460. if (index > geometries_.Size())
  461. {
  462. URHO3D_LOGERROR("Geometry index out of bounds");
  463. return;
  464. }
  465. geometryIndex_ = index;
  466. primitiveTypes_[index] = type;
  467. vertices_[index].Clear();
  468. // If beginning the first geometry, reset the element mask
  469. if (!index)
  470. elementMask_ = MASK_POSITION;
  471. }
  472. void CustomGeometry::DefineVertex(const Vector3& position)
  473. {
  474. if (vertices_.Size() < geometryIndex_)
  475. return;
  476. vertices_[geometryIndex_].Resize(vertices_[geometryIndex_].Size() + 1);
  477. vertices_[geometryIndex_].Back().position_ = position;
  478. }
  479. void CustomGeometry::DefineNormal(const Vector3& normal)
  480. {
  481. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  482. return;
  483. vertices_[geometryIndex_].Back().normal_ = normal;
  484. elementMask_ |= MASK_NORMAL;
  485. }
  486. void CustomGeometry::DefineColor(const Color& color)
  487. {
  488. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  489. return;
  490. vertices_[geometryIndex_].Back().color_ = color.ToUInt();
  491. elementMask_ |= MASK_COLOR;
  492. }
  493. void CustomGeometry::DefineTexCoord(const Vector2& texCoord)
  494. {
  495. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  496. return;
  497. vertices_[geometryIndex_].Back().texCoord_ = texCoord;
  498. elementMask_ |= MASK_TEXCOORD1;
  499. }
  500. void CustomGeometry::DefineTangent(const Vector4& tangent)
  501. {
  502. if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
  503. return;
  504. vertices_[geometryIndex_].Back().tangent_ = tangent;
  505. elementMask_ |= MASK_TANGENT;
  506. }
  507. void CustomGeometry::DefineGeometry(unsigned index, PrimitiveType type, unsigned numVertices, bool hasNormals, bool hasColors,
  508. bool hasTexCoords, bool hasTangents)
  509. {
  510. if (index > geometries_.Size())
  511. {
  512. URHO3D_LOGERROR("Geometry index out of bounds");
  513. return;
  514. }
  515. geometryIndex_ = index;
  516. primitiveTypes_[index] = type;
  517. vertices_[index].Resize(numVertices);
  518. // If defining the first geometry, reset the element mask
  519. if (!index)
  520. elementMask_ = MASK_POSITION;
  521. if (hasNormals)
  522. elementMask_ |= MASK_NORMAL;
  523. if (hasColors)
  524. elementMask_ |= MASK_COLOR;
  525. if (hasTexCoords)
  526. elementMask_ |= MASK_TEXCOORD1;
  527. if (hasTangents)
  528. elementMask_ |= MASK_TANGENT;
  529. }
  530. void CustomGeometry::Commit()
  531. {
  532. URHO3D_PROFILE(CommitCustomGeometry);
  533. unsigned totalVertices = 0;
  534. boundingBox_.Clear();
  535. for (unsigned i = 0; i < vertices_.Size(); ++i)
  536. {
  537. totalVertices += vertices_[i].Size();
  538. for (unsigned j = 0; j < vertices_[i].Size(); ++j)
  539. boundingBox_.Merge(vertices_[i][j].position_);
  540. }
  541. // Make sure world-space bounding box will be updated
  542. OnMarkedDirty(node_);
  543. // Resize (recreate) the vertex buffer only if necessary
  544. if (vertexBuffer_->GetVertexCount() != totalVertices || vertexBuffer_->GetElementMask() != elementMask_ ||
  545. vertexBuffer_->IsDynamic() != dynamic_)
  546. vertexBuffer_->SetSize(totalVertices, elementMask_, dynamic_);
  547. if (totalVertices)
  548. {
  549. auto* dest = (unsigned char*)vertexBuffer_->Lock(0, totalVertices, true);
  550. if (dest)
  551. {
  552. unsigned vertexStart = 0;
  553. for (unsigned i = 0; i < vertices_.Size(); ++i)
  554. {
  555. unsigned vertexCount = 0;
  556. for (unsigned j = 0; j < vertices_[i].Size(); ++j)
  557. {
  558. *((Vector3*)dest) = vertices_[i][j].position_;
  559. dest += sizeof(Vector3);
  560. if (elementMask_ & MASK_NORMAL)
  561. {
  562. *((Vector3*)dest) = vertices_[i][j].normal_;
  563. dest += sizeof(Vector3);
  564. }
  565. if (elementMask_ & MASK_COLOR)
  566. {
  567. *((unsigned*)dest) = vertices_[i][j].color_;
  568. dest += sizeof(unsigned);
  569. }
  570. if (elementMask_ & MASK_TEXCOORD1)
  571. {
  572. *((Vector2*)dest) = vertices_[i][j].texCoord_;
  573. dest += sizeof(Vector2);
  574. }
  575. if (elementMask_ & MASK_TANGENT)
  576. {
  577. *((Vector4*)dest) = vertices_[i][j].tangent_;
  578. dest += sizeof(Vector4);
  579. }
  580. ++vertexCount;
  581. }
  582. geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
  583. geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, vertexStart, vertexCount);
  584. vertexStart += vertexCount;
  585. }
  586. vertexBuffer_->Unlock();
  587. }
  588. else
  589. URHO3D_LOGERROR("Failed to lock custom geometry vertex buffer");
  590. }
  591. else
  592. {
  593. for (unsigned i = 0; i < geometries_.Size(); ++i)
  594. {
  595. geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
  596. geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, 0, 0);
  597. }
  598. }
  599. vertexBuffer_->ClearDataLost();
  600. }
  601. void CustomGeometry::SetMaterial(Material* material)
  602. {
  603. for (unsigned i = 0; i < batches_.Size(); ++i)
  604. batches_[i].material_ = material;
  605. MarkNetworkUpdate();
  606. }
  607. bool CustomGeometry::SetMaterial(unsigned index, Material* material)
  608. {
  609. if (index >= batches_.Size())
  610. {
  611. URHO3D_LOGERROR("Material index out of bounds");
  612. return false;
  613. }
  614. batches_[index].material_ = material;
  615. MarkNetworkUpdate();
  616. return true;
  617. }
  618. unsigned CustomGeometry::GetNumVertices(unsigned index) const
  619. {
  620. return index < vertices_.Size() ? vertices_[index].Size() : 0;
  621. }
  622. Material* CustomGeometry::GetMaterial(unsigned index) const
  623. {
  624. return index < batches_.Size() ? batches_[index].material_ : nullptr;
  625. }
  626. CustomGeometryVertex* CustomGeometry::GetVertex(unsigned geometryIndex, unsigned vertexNum)
  627. {
  628. return (geometryIndex < vertices_.Size() && vertexNum < vertices_[geometryIndex].Size()) ?
  629. &vertices_[geometryIndex][vertexNum] : nullptr;
  630. }
  631. void CustomGeometry::SetGeometryDataAttr(const PODVector<unsigned char>& value)
  632. {
  633. if (value.Empty())
  634. return;
  635. MemoryBuffer buffer(value);
  636. SetNumGeometries(buffer.ReadVLE());
  637. elementMask_ = VertexMaskFlags(buffer.ReadUInt());
  638. for (unsigned i = 0; i < geometries_.Size(); ++i)
  639. {
  640. unsigned numVertices = buffer.ReadVLE();
  641. vertices_[i].Resize(numVertices);
  642. primitiveTypes_[i] = (PrimitiveType)buffer.ReadUByte();
  643. for (unsigned j = 0; j < numVertices; ++j)
  644. {
  645. if (elementMask_ & MASK_POSITION)
  646. vertices_[i][j].position_ = buffer.ReadVector3();
  647. if (elementMask_ & MASK_NORMAL)
  648. vertices_[i][j].normal_ = buffer.ReadVector3();
  649. if (elementMask_ & MASK_COLOR)
  650. vertices_[i][j].color_ = buffer.ReadUInt();
  651. if (elementMask_ & MASK_TEXCOORD1)
  652. vertices_[i][j].texCoord_ = buffer.ReadVector2();
  653. if (elementMask_ & MASK_TANGENT)
  654. vertices_[i][j].tangent_ = buffer.ReadVector4();
  655. }
  656. }
  657. Commit();
  658. }
  659. void CustomGeometry::SetMaterialsAttr(const ResourceRefList& value)
  660. {
  661. auto* cache = GetSubsystem<ResourceCache>();
  662. for (unsigned i = 0; i < value.names_.Size(); ++i)
  663. SetMaterial(i, cache->GetResource<Material>(value.names_[i]));
  664. }
  665. PODVector<unsigned char> CustomGeometry::GetGeometryDataAttr() const
  666. {
  667. VectorBuffer ret;
  668. ret.WriteVLE(geometries_.Size());
  669. ret.WriteUInt(elementMask_);
  670. for (unsigned i = 0; i < geometries_.Size(); ++i)
  671. {
  672. unsigned numVertices = vertices_[i].Size();
  673. ret.WriteVLE(numVertices);
  674. ret.WriteUByte(primitiveTypes_[i]);
  675. for (unsigned j = 0; j < numVertices; ++j)
  676. {
  677. if (elementMask_ & MASK_POSITION)
  678. ret.WriteVector3(vertices_[i][j].position_);
  679. if (elementMask_ & MASK_NORMAL)
  680. ret.WriteVector3(vertices_[i][j].normal_);
  681. if (elementMask_ & MASK_COLOR)
  682. ret.WriteUInt(vertices_[i][j].color_);
  683. if (elementMask_ & MASK_TEXCOORD1)
  684. ret.WriteVector2(vertices_[i][j].texCoord_);
  685. if (elementMask_ & MASK_TANGENT)
  686. ret.WriteVector4(vertices_[i][j].tangent_);
  687. }
  688. }
  689. return ret.GetBuffer();
  690. }
  691. const ResourceRefList& CustomGeometry::GetMaterialsAttr() const
  692. {
  693. materialsAttr_.names_.Resize(batches_.Size());
  694. for (unsigned i = 0; i < batches_.Size(); ++i)
  695. materialsAttr_.names_[i] = GetResourceName(batches_[i].material_);
  696. return materialsAttr_;
  697. }
  698. void CustomGeometry::OnWorldBoundingBoxUpdate()
  699. {
  700. worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
  701. }
  702. }