RectangleGeometry.js 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. import BoundingSphere from "./BoundingSphere.js";
  2. import Cartesian2 from "./Cartesian2.js";
  3. import Cartesian3 from "./Cartesian3.js";
  4. import Cartographic from "./Cartographic.js";
  5. import Check from "./Check.js";
  6. import ComponentDatatype from "./ComponentDatatype.js";
  7. import defaultValue from "./defaultValue.js";
  8. import defined from "./defined.js";
  9. import DeveloperError from "./DeveloperError.js";
  10. import Ellipsoid from "./Ellipsoid.js";
  11. import Geometry from "./Geometry.js";
  12. import GeometryAttribute from "./GeometryAttribute.js";
  13. import GeometryAttributes from "./GeometryAttributes.js";
  14. import GeometryInstance from "./GeometryInstance.js";
  15. import GeometryOffsetAttribute from "./GeometryOffsetAttribute.js";
  16. import GeometryPipeline from "./GeometryPipeline.js";
  17. import IndexDatatype from "./IndexDatatype.js";
  18. import CesiumMath from "./Math.js";
  19. import Matrix2 from "./Matrix2.js";
  20. import Matrix3 from "./Matrix3.js";
  21. import PolygonPipeline from "./PolygonPipeline.js";
  22. import PrimitiveType from "./PrimitiveType.js";
  23. import Quaternion from "./Quaternion.js";
  24. import Rectangle from "./Rectangle.js";
  25. import RectangleGeometryLibrary from "./RectangleGeometryLibrary.js";
  26. import VertexFormat from "./VertexFormat.js";
  27. const positionScratch = new Cartesian3();
  28. const normalScratch = new Cartesian3();
  29. const tangentScratch = new Cartesian3();
  30. const bitangentScratch = new Cartesian3();
  31. const rectangleScratch = new Rectangle();
  32. const stScratch = new Cartesian2();
  33. const bottomBoundingSphere = new BoundingSphere();
  34. const topBoundingSphere = new BoundingSphere();
  35. function createAttributes(vertexFormat, attributes) {
  36. const geo = new Geometry({
  37. attributes: new GeometryAttributes(),
  38. primitiveType: PrimitiveType.TRIANGLES,
  39. });
  40. geo.attributes.position = new GeometryAttribute({
  41. componentDatatype: ComponentDatatype.DOUBLE,
  42. componentsPerAttribute: 3,
  43. values: attributes.positions,
  44. });
  45. if (vertexFormat.normal) {
  46. geo.attributes.normal = new GeometryAttribute({
  47. componentDatatype: ComponentDatatype.FLOAT,
  48. componentsPerAttribute: 3,
  49. values: attributes.normals,
  50. });
  51. }
  52. if (vertexFormat.tangent) {
  53. geo.attributes.tangent = new GeometryAttribute({
  54. componentDatatype: ComponentDatatype.FLOAT,
  55. componentsPerAttribute: 3,
  56. values: attributes.tangents,
  57. });
  58. }
  59. if (vertexFormat.bitangent) {
  60. geo.attributes.bitangent = new GeometryAttribute({
  61. componentDatatype: ComponentDatatype.FLOAT,
  62. componentsPerAttribute: 3,
  63. values: attributes.bitangents,
  64. });
  65. }
  66. return geo;
  67. }
  68. function calculateAttributes(
  69. positions,
  70. vertexFormat,
  71. ellipsoid,
  72. tangentRotationMatrix
  73. ) {
  74. const length = positions.length;
  75. const normals = vertexFormat.normal ? new Float32Array(length) : undefined;
  76. const tangents = vertexFormat.tangent ? new Float32Array(length) : undefined;
  77. const bitangents = vertexFormat.bitangent
  78. ? new Float32Array(length)
  79. : undefined;
  80. let attrIndex = 0;
  81. const bitangent = bitangentScratch;
  82. const tangent = tangentScratch;
  83. let normal = normalScratch;
  84. if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) {
  85. for (let i = 0; i < length; i += 3) {
  86. const p = Cartesian3.fromArray(positions, i, positionScratch);
  87. const attrIndex1 = attrIndex + 1;
  88. const attrIndex2 = attrIndex + 2;
  89. normal = ellipsoid.geodeticSurfaceNormal(p, normal);
  90. if (vertexFormat.tangent || vertexFormat.bitangent) {
  91. Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
  92. Matrix3.multiplyByVector(tangentRotationMatrix, tangent, tangent);
  93. Cartesian3.normalize(tangent, tangent);
  94. if (vertexFormat.bitangent) {
  95. Cartesian3.normalize(
  96. Cartesian3.cross(normal, tangent, bitangent),
  97. bitangent
  98. );
  99. }
  100. }
  101. if (vertexFormat.normal) {
  102. normals[attrIndex] = normal.x;
  103. normals[attrIndex1] = normal.y;
  104. normals[attrIndex2] = normal.z;
  105. }
  106. if (vertexFormat.tangent) {
  107. tangents[attrIndex] = tangent.x;
  108. tangents[attrIndex1] = tangent.y;
  109. tangents[attrIndex2] = tangent.z;
  110. }
  111. if (vertexFormat.bitangent) {
  112. bitangents[attrIndex] = bitangent.x;
  113. bitangents[attrIndex1] = bitangent.y;
  114. bitangents[attrIndex2] = bitangent.z;
  115. }
  116. attrIndex += 3;
  117. }
  118. }
  119. return createAttributes(vertexFormat, {
  120. positions: positions,
  121. normals: normals,
  122. tangents: tangents,
  123. bitangents: bitangents,
  124. });
  125. }
  126. const v1Scratch = new Cartesian3();
  127. const v2Scratch = new Cartesian3();
  128. function calculateAttributesWall(positions, vertexFormat, ellipsoid) {
  129. const length = positions.length;
  130. const normals = vertexFormat.normal ? new Float32Array(length) : undefined;
  131. const tangents = vertexFormat.tangent ? new Float32Array(length) : undefined;
  132. const bitangents = vertexFormat.bitangent
  133. ? new Float32Array(length)
  134. : undefined;
  135. let normalIndex = 0;
  136. let tangentIndex = 0;
  137. let bitangentIndex = 0;
  138. let recomputeNormal = true;
  139. let bitangent = bitangentScratch;
  140. let tangent = tangentScratch;
  141. let normal = normalScratch;
  142. if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) {
  143. for (let i = 0; i < length; i += 6) {
  144. const p = Cartesian3.fromArray(positions, i, positionScratch);
  145. const p1 = Cartesian3.fromArray(positions, (i + 6) % length, v1Scratch);
  146. if (recomputeNormal) {
  147. const p2 = Cartesian3.fromArray(positions, (i + 3) % length, v2Scratch);
  148. Cartesian3.subtract(p1, p, p1);
  149. Cartesian3.subtract(p2, p, p2);
  150. normal = Cartesian3.normalize(Cartesian3.cross(p2, p1, normal), normal);
  151. recomputeNormal = false;
  152. }
  153. if (Cartesian3.equalsEpsilon(p1, p, CesiumMath.EPSILON10)) {
  154. // if we've reached a corner
  155. recomputeNormal = true;
  156. }
  157. if (vertexFormat.tangent || vertexFormat.bitangent) {
  158. bitangent = ellipsoid.geodeticSurfaceNormal(p, bitangent);
  159. if (vertexFormat.tangent) {
  160. tangent = Cartesian3.normalize(
  161. Cartesian3.cross(bitangent, normal, tangent),
  162. tangent
  163. );
  164. }
  165. }
  166. if (vertexFormat.normal) {
  167. normals[normalIndex++] = normal.x;
  168. normals[normalIndex++] = normal.y;
  169. normals[normalIndex++] = normal.z;
  170. normals[normalIndex++] = normal.x;
  171. normals[normalIndex++] = normal.y;
  172. normals[normalIndex++] = normal.z;
  173. }
  174. if (vertexFormat.tangent) {
  175. tangents[tangentIndex++] = tangent.x;
  176. tangents[tangentIndex++] = tangent.y;
  177. tangents[tangentIndex++] = tangent.z;
  178. tangents[tangentIndex++] = tangent.x;
  179. tangents[tangentIndex++] = tangent.y;
  180. tangents[tangentIndex++] = tangent.z;
  181. }
  182. if (vertexFormat.bitangent) {
  183. bitangents[bitangentIndex++] = bitangent.x;
  184. bitangents[bitangentIndex++] = bitangent.y;
  185. bitangents[bitangentIndex++] = bitangent.z;
  186. bitangents[bitangentIndex++] = bitangent.x;
  187. bitangents[bitangentIndex++] = bitangent.y;
  188. bitangents[bitangentIndex++] = bitangent.z;
  189. }
  190. }
  191. }
  192. return createAttributes(vertexFormat, {
  193. positions: positions,
  194. normals: normals,
  195. tangents: tangents,
  196. bitangents: bitangents,
  197. });
  198. }
  199. function constructRectangle(rectangleGeometry, computedOptions) {
  200. const vertexFormat = rectangleGeometry._vertexFormat;
  201. const ellipsoid = rectangleGeometry._ellipsoid;
  202. const height = computedOptions.height;
  203. const width = computedOptions.width;
  204. const northCap = computedOptions.northCap;
  205. const southCap = computedOptions.southCap;
  206. let rowStart = 0;
  207. let rowEnd = height;
  208. let rowHeight = height;
  209. let size = 0;
  210. if (northCap) {
  211. rowStart = 1;
  212. rowHeight -= 1;
  213. size += 1;
  214. }
  215. if (southCap) {
  216. rowEnd -= 1;
  217. rowHeight -= 1;
  218. size += 1;
  219. }
  220. size += width * rowHeight;
  221. const positions = vertexFormat.position
  222. ? new Float64Array(size * 3)
  223. : undefined;
  224. const textureCoordinates = vertexFormat.st
  225. ? new Float32Array(size * 2)
  226. : undefined;
  227. let posIndex = 0;
  228. let stIndex = 0;
  229. const position = positionScratch;
  230. const st = stScratch;
  231. let minX = Number.MAX_VALUE;
  232. let minY = Number.MAX_VALUE;
  233. let maxX = -Number.MAX_VALUE;
  234. let maxY = -Number.MAX_VALUE;
  235. for (let row = rowStart; row < rowEnd; ++row) {
  236. for (let col = 0; col < width; ++col) {
  237. RectangleGeometryLibrary.computePosition(
  238. computedOptions,
  239. ellipsoid,
  240. vertexFormat.st,
  241. row,
  242. col,
  243. position,
  244. st
  245. );
  246. positions[posIndex++] = position.x;
  247. positions[posIndex++] = position.y;
  248. positions[posIndex++] = position.z;
  249. if (vertexFormat.st) {
  250. textureCoordinates[stIndex++] = st.x;
  251. textureCoordinates[stIndex++] = st.y;
  252. minX = Math.min(minX, st.x);
  253. minY = Math.min(minY, st.y);
  254. maxX = Math.max(maxX, st.x);
  255. maxY = Math.max(maxY, st.y);
  256. }
  257. }
  258. }
  259. if (northCap) {
  260. RectangleGeometryLibrary.computePosition(
  261. computedOptions,
  262. ellipsoid,
  263. vertexFormat.st,
  264. 0,
  265. 0,
  266. position,
  267. st
  268. );
  269. positions[posIndex++] = position.x;
  270. positions[posIndex++] = position.y;
  271. positions[posIndex++] = position.z;
  272. if (vertexFormat.st) {
  273. textureCoordinates[stIndex++] = st.x;
  274. textureCoordinates[stIndex++] = st.y;
  275. minX = st.x;
  276. minY = st.y;
  277. maxX = st.x;
  278. maxY = st.y;
  279. }
  280. }
  281. if (southCap) {
  282. RectangleGeometryLibrary.computePosition(
  283. computedOptions,
  284. ellipsoid,
  285. vertexFormat.st,
  286. height - 1,
  287. 0,
  288. position,
  289. st
  290. );
  291. positions[posIndex++] = position.x;
  292. positions[posIndex++] = position.y;
  293. positions[posIndex] = position.z;
  294. if (vertexFormat.st) {
  295. textureCoordinates[stIndex++] = st.x;
  296. textureCoordinates[stIndex] = st.y;
  297. minX = Math.min(minX, st.x);
  298. minY = Math.min(minY, st.y);
  299. maxX = Math.max(maxX, st.x);
  300. maxY = Math.max(maxY, st.y);
  301. }
  302. }
  303. if (
  304. vertexFormat.st &&
  305. (minX < 0.0 || minY < 0.0 || maxX > 1.0 || maxY > 1.0)
  306. ) {
  307. for (let k = 0; k < textureCoordinates.length; k += 2) {
  308. textureCoordinates[k] = (textureCoordinates[k] - minX) / (maxX - minX);
  309. textureCoordinates[k + 1] =
  310. (textureCoordinates[k + 1] - minY) / (maxY - minY);
  311. }
  312. }
  313. const geo = calculateAttributes(
  314. positions,
  315. vertexFormat,
  316. ellipsoid,
  317. computedOptions.tangentRotationMatrix
  318. );
  319. let indicesSize = 6 * (width - 1) * (rowHeight - 1);
  320. if (northCap) {
  321. indicesSize += 3 * (width - 1);
  322. }
  323. if (southCap) {
  324. indicesSize += 3 * (width - 1);
  325. }
  326. const indices = IndexDatatype.createTypedArray(size, indicesSize);
  327. let index = 0;
  328. let indicesIndex = 0;
  329. let i;
  330. for (i = 0; i < rowHeight - 1; ++i) {
  331. for (let j = 0; j < width - 1; ++j) {
  332. const upperLeft = index;
  333. const lowerLeft = upperLeft + width;
  334. const lowerRight = lowerLeft + 1;
  335. const upperRight = upperLeft + 1;
  336. indices[indicesIndex++] = upperLeft;
  337. indices[indicesIndex++] = lowerLeft;
  338. indices[indicesIndex++] = upperRight;
  339. indices[indicesIndex++] = upperRight;
  340. indices[indicesIndex++] = lowerLeft;
  341. indices[indicesIndex++] = lowerRight;
  342. ++index;
  343. }
  344. ++index;
  345. }
  346. if (northCap || southCap) {
  347. let northIndex = size - 1;
  348. const southIndex = size - 1;
  349. if (northCap && southCap) {
  350. northIndex = size - 2;
  351. }
  352. let p1;
  353. let p2;
  354. index = 0;
  355. if (northCap) {
  356. for (i = 0; i < width - 1; i++) {
  357. p1 = index;
  358. p2 = p1 + 1;
  359. indices[indicesIndex++] = northIndex;
  360. indices[indicesIndex++] = p1;
  361. indices[indicesIndex++] = p2;
  362. ++index;
  363. }
  364. }
  365. if (southCap) {
  366. index = (rowHeight - 1) * width;
  367. for (i = 0; i < width - 1; i++) {
  368. p1 = index;
  369. p2 = p1 + 1;
  370. indices[indicesIndex++] = p1;
  371. indices[indicesIndex++] = southIndex;
  372. indices[indicesIndex++] = p2;
  373. ++index;
  374. }
  375. }
  376. }
  377. geo.indices = indices;
  378. if (vertexFormat.st) {
  379. geo.attributes.st = new GeometryAttribute({
  380. componentDatatype: ComponentDatatype.FLOAT,
  381. componentsPerAttribute: 2,
  382. values: textureCoordinates,
  383. });
  384. }
  385. return geo;
  386. }
  387. function addWallPositions(
  388. wallPositions,
  389. posIndex,
  390. i,
  391. topPositions,
  392. bottomPositions
  393. ) {
  394. wallPositions[posIndex++] = topPositions[i];
  395. wallPositions[posIndex++] = topPositions[i + 1];
  396. wallPositions[posIndex++] = topPositions[i + 2];
  397. wallPositions[posIndex++] = bottomPositions[i];
  398. wallPositions[posIndex++] = bottomPositions[i + 1];
  399. wallPositions[posIndex] = bottomPositions[i + 2];
  400. return wallPositions;
  401. }
  402. function addWallTextureCoordinates(wallTextures, stIndex, i, st) {
  403. wallTextures[stIndex++] = st[i];
  404. wallTextures[stIndex++] = st[i + 1];
  405. wallTextures[stIndex++] = st[i];
  406. wallTextures[stIndex] = st[i + 1];
  407. return wallTextures;
  408. }
  409. const scratchVertexFormat = new VertexFormat();
  410. function constructExtrudedRectangle(rectangleGeometry, computedOptions) {
  411. const shadowVolume = rectangleGeometry._shadowVolume;
  412. const offsetAttributeValue = rectangleGeometry._offsetAttribute;
  413. const vertexFormat = rectangleGeometry._vertexFormat;
  414. const minHeight = rectangleGeometry._extrudedHeight;
  415. const maxHeight = rectangleGeometry._surfaceHeight;
  416. const ellipsoid = rectangleGeometry._ellipsoid;
  417. const height = computedOptions.height;
  418. const width = computedOptions.width;
  419. let i;
  420. if (shadowVolume) {
  421. const newVertexFormat = VertexFormat.clone(
  422. vertexFormat,
  423. scratchVertexFormat
  424. );
  425. newVertexFormat.normal = true;
  426. rectangleGeometry._vertexFormat = newVertexFormat;
  427. }
  428. const topBottomGeo = constructRectangle(rectangleGeometry, computedOptions);
  429. if (shadowVolume) {
  430. rectangleGeometry._vertexFormat = vertexFormat;
  431. }
  432. let topPositions = PolygonPipeline.scaleToGeodeticHeight(
  433. topBottomGeo.attributes.position.values,
  434. maxHeight,
  435. ellipsoid,
  436. false
  437. );
  438. topPositions = new Float64Array(topPositions);
  439. let length = topPositions.length;
  440. const newLength = length * 2;
  441. const positions = new Float64Array(newLength);
  442. positions.set(topPositions);
  443. const bottomPositions = PolygonPipeline.scaleToGeodeticHeight(
  444. topBottomGeo.attributes.position.values,
  445. minHeight,
  446. ellipsoid
  447. );
  448. positions.set(bottomPositions, length);
  449. topBottomGeo.attributes.position.values = positions;
  450. const normals = vertexFormat.normal ? new Float32Array(newLength) : undefined;
  451. const tangents = vertexFormat.tangent
  452. ? new Float32Array(newLength)
  453. : undefined;
  454. const bitangents = vertexFormat.bitangent
  455. ? new Float32Array(newLength)
  456. : undefined;
  457. const textures = vertexFormat.st
  458. ? new Float32Array((newLength / 3) * 2)
  459. : undefined;
  460. let topSt;
  461. let topNormals;
  462. if (vertexFormat.normal) {
  463. topNormals = topBottomGeo.attributes.normal.values;
  464. normals.set(topNormals);
  465. for (i = 0; i < length; i++) {
  466. topNormals[i] = -topNormals[i];
  467. }
  468. normals.set(topNormals, length);
  469. topBottomGeo.attributes.normal.values = normals;
  470. }
  471. if (shadowVolume) {
  472. topNormals = topBottomGeo.attributes.normal.values;
  473. if (!vertexFormat.normal) {
  474. topBottomGeo.attributes.normal = undefined;
  475. }
  476. const extrudeNormals = new Float32Array(newLength);
  477. for (i = 0; i < length; i++) {
  478. topNormals[i] = -topNormals[i];
  479. }
  480. extrudeNormals.set(topNormals, length); //only get normals for bottom layer that's going to be pushed down
  481. topBottomGeo.attributes.extrudeDirection = new GeometryAttribute({
  482. componentDatatype: ComponentDatatype.FLOAT,
  483. componentsPerAttribute: 3,
  484. values: extrudeNormals,
  485. });
  486. }
  487. let offsetValue;
  488. const hasOffsets = defined(offsetAttributeValue);
  489. if (hasOffsets) {
  490. const size = (length / 3) * 2;
  491. let offsetAttribute = new Uint8Array(size);
  492. if (offsetAttributeValue === GeometryOffsetAttribute.TOP) {
  493. offsetAttribute = offsetAttribute.fill(1, 0, size / 2);
  494. } else {
  495. offsetValue =
  496. offsetAttributeValue === GeometryOffsetAttribute.NONE ? 0 : 1;
  497. offsetAttribute = offsetAttribute.fill(offsetValue);
  498. }
  499. topBottomGeo.attributes.applyOffset = new GeometryAttribute({
  500. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  501. componentsPerAttribute: 1,
  502. values: offsetAttribute,
  503. });
  504. }
  505. if (vertexFormat.tangent) {
  506. const topTangents = topBottomGeo.attributes.tangent.values;
  507. tangents.set(topTangents);
  508. for (i = 0; i < length; i++) {
  509. topTangents[i] = -topTangents[i];
  510. }
  511. tangents.set(topTangents, length);
  512. topBottomGeo.attributes.tangent.values = tangents;
  513. }
  514. if (vertexFormat.bitangent) {
  515. const topBitangents = topBottomGeo.attributes.bitangent.values;
  516. bitangents.set(topBitangents);
  517. bitangents.set(topBitangents, length);
  518. topBottomGeo.attributes.bitangent.values = bitangents;
  519. }
  520. if (vertexFormat.st) {
  521. topSt = topBottomGeo.attributes.st.values;
  522. textures.set(topSt);
  523. textures.set(topSt, (length / 3) * 2);
  524. topBottomGeo.attributes.st.values = textures;
  525. }
  526. const indices = topBottomGeo.indices;
  527. const indicesLength = indices.length;
  528. const posLength = length / 3;
  529. const newIndices = IndexDatatype.createTypedArray(
  530. newLength / 3,
  531. indicesLength * 2
  532. );
  533. newIndices.set(indices);
  534. for (i = 0; i < indicesLength; i += 3) {
  535. newIndices[i + indicesLength] = indices[i + 2] + posLength;
  536. newIndices[i + 1 + indicesLength] = indices[i + 1] + posLength;
  537. newIndices[i + 2 + indicesLength] = indices[i] + posLength;
  538. }
  539. topBottomGeo.indices = newIndices;
  540. const northCap = computedOptions.northCap;
  541. const southCap = computedOptions.southCap;
  542. let rowHeight = height;
  543. let widthMultiplier = 2;
  544. let perimeterPositions = 0;
  545. let corners = 4;
  546. let dupliateCorners = 4;
  547. if (northCap) {
  548. widthMultiplier -= 1;
  549. rowHeight -= 1;
  550. perimeterPositions += 1;
  551. corners -= 2;
  552. dupliateCorners -= 1;
  553. }
  554. if (southCap) {
  555. widthMultiplier -= 1;
  556. rowHeight -= 1;
  557. perimeterPositions += 1;
  558. corners -= 2;
  559. dupliateCorners -= 1;
  560. }
  561. perimeterPositions += widthMultiplier * width + 2 * rowHeight - corners;
  562. const wallCount = (perimeterPositions + dupliateCorners) * 2;
  563. let wallPositions = new Float64Array(wallCount * 3);
  564. const wallExtrudeNormals = shadowVolume
  565. ? new Float32Array(wallCount * 3)
  566. : undefined;
  567. let wallOffsetAttribute = hasOffsets ? new Uint8Array(wallCount) : undefined;
  568. let wallTextures = vertexFormat.st
  569. ? new Float32Array(wallCount * 2)
  570. : undefined;
  571. const computeTopOffsets =
  572. offsetAttributeValue === GeometryOffsetAttribute.TOP;
  573. if (hasOffsets && !computeTopOffsets) {
  574. offsetValue = offsetAttributeValue === GeometryOffsetAttribute.ALL ? 1 : 0;
  575. wallOffsetAttribute = wallOffsetAttribute.fill(offsetValue);
  576. }
  577. let posIndex = 0;
  578. let stIndex = 0;
  579. let extrudeNormalIndex = 0;
  580. let wallOffsetIndex = 0;
  581. const area = width * rowHeight;
  582. let threeI;
  583. for (i = 0; i < area; i += width) {
  584. threeI = i * 3;
  585. wallPositions = addWallPositions(
  586. wallPositions,
  587. posIndex,
  588. threeI,
  589. topPositions,
  590. bottomPositions
  591. );
  592. posIndex += 6;
  593. if (vertexFormat.st) {
  594. wallTextures = addWallTextureCoordinates(
  595. wallTextures,
  596. stIndex,
  597. i * 2,
  598. topSt
  599. );
  600. stIndex += 4;
  601. }
  602. if (shadowVolume) {
  603. extrudeNormalIndex += 3;
  604. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  605. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  606. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  607. }
  608. if (computeTopOffsets) {
  609. wallOffsetAttribute[wallOffsetIndex++] = 1;
  610. wallOffsetIndex += 1;
  611. }
  612. }
  613. if (!southCap) {
  614. for (i = area - width; i < area; i++) {
  615. threeI = i * 3;
  616. wallPositions = addWallPositions(
  617. wallPositions,
  618. posIndex,
  619. threeI,
  620. topPositions,
  621. bottomPositions
  622. );
  623. posIndex += 6;
  624. if (vertexFormat.st) {
  625. wallTextures = addWallTextureCoordinates(
  626. wallTextures,
  627. stIndex,
  628. i * 2,
  629. topSt
  630. );
  631. stIndex += 4;
  632. }
  633. if (shadowVolume) {
  634. extrudeNormalIndex += 3;
  635. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  636. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  637. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  638. }
  639. if (computeTopOffsets) {
  640. wallOffsetAttribute[wallOffsetIndex++] = 1;
  641. wallOffsetIndex += 1;
  642. }
  643. }
  644. } else {
  645. const southIndex = northCap ? area + 1 : area;
  646. threeI = southIndex * 3;
  647. for (i = 0; i < 2; i++) {
  648. // duplicate corner points
  649. wallPositions = addWallPositions(
  650. wallPositions,
  651. posIndex,
  652. threeI,
  653. topPositions,
  654. bottomPositions
  655. );
  656. posIndex += 6;
  657. if (vertexFormat.st) {
  658. wallTextures = addWallTextureCoordinates(
  659. wallTextures,
  660. stIndex,
  661. southIndex * 2,
  662. topSt
  663. );
  664. stIndex += 4;
  665. }
  666. if (shadowVolume) {
  667. extrudeNormalIndex += 3;
  668. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  669. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  670. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  671. }
  672. if (computeTopOffsets) {
  673. wallOffsetAttribute[wallOffsetIndex++] = 1;
  674. wallOffsetIndex += 1;
  675. }
  676. }
  677. }
  678. for (i = area - 1; i > 0; i -= width) {
  679. threeI = i * 3;
  680. wallPositions = addWallPositions(
  681. wallPositions,
  682. posIndex,
  683. threeI,
  684. topPositions,
  685. bottomPositions
  686. );
  687. posIndex += 6;
  688. if (vertexFormat.st) {
  689. wallTextures = addWallTextureCoordinates(
  690. wallTextures,
  691. stIndex,
  692. i * 2,
  693. topSt
  694. );
  695. stIndex += 4;
  696. }
  697. if (shadowVolume) {
  698. extrudeNormalIndex += 3;
  699. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  700. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  701. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  702. }
  703. if (computeTopOffsets) {
  704. wallOffsetAttribute[wallOffsetIndex++] = 1;
  705. wallOffsetIndex += 1;
  706. }
  707. }
  708. if (!northCap) {
  709. for (i = width - 1; i >= 0; i--) {
  710. threeI = i * 3;
  711. wallPositions = addWallPositions(
  712. wallPositions,
  713. posIndex,
  714. threeI,
  715. topPositions,
  716. bottomPositions
  717. );
  718. posIndex += 6;
  719. if (vertexFormat.st) {
  720. wallTextures = addWallTextureCoordinates(
  721. wallTextures,
  722. stIndex,
  723. i * 2,
  724. topSt
  725. );
  726. stIndex += 4;
  727. }
  728. if (shadowVolume) {
  729. extrudeNormalIndex += 3;
  730. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  731. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  732. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  733. }
  734. if (computeTopOffsets) {
  735. wallOffsetAttribute[wallOffsetIndex++] = 1;
  736. wallOffsetIndex += 1;
  737. }
  738. }
  739. } else {
  740. const northIndex = area;
  741. threeI = northIndex * 3;
  742. for (i = 0; i < 2; i++) {
  743. // duplicate corner points
  744. wallPositions = addWallPositions(
  745. wallPositions,
  746. posIndex,
  747. threeI,
  748. topPositions,
  749. bottomPositions
  750. );
  751. posIndex += 6;
  752. if (vertexFormat.st) {
  753. wallTextures = addWallTextureCoordinates(
  754. wallTextures,
  755. stIndex,
  756. northIndex * 2,
  757. topSt
  758. );
  759. stIndex += 4;
  760. }
  761. if (shadowVolume) {
  762. extrudeNormalIndex += 3;
  763. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI];
  764. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1];
  765. wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2];
  766. }
  767. if (computeTopOffsets) {
  768. wallOffsetAttribute[wallOffsetIndex++] = 1;
  769. wallOffsetIndex += 1;
  770. }
  771. }
  772. }
  773. let geo = calculateAttributesWall(wallPositions, vertexFormat, ellipsoid);
  774. if (vertexFormat.st) {
  775. geo.attributes.st = new GeometryAttribute({
  776. componentDatatype: ComponentDatatype.FLOAT,
  777. componentsPerAttribute: 2,
  778. values: wallTextures,
  779. });
  780. }
  781. if (shadowVolume) {
  782. geo.attributes.extrudeDirection = new GeometryAttribute({
  783. componentDatatype: ComponentDatatype.FLOAT,
  784. componentsPerAttribute: 3,
  785. values: wallExtrudeNormals,
  786. });
  787. }
  788. if (hasOffsets) {
  789. geo.attributes.applyOffset = new GeometryAttribute({
  790. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  791. componentsPerAttribute: 1,
  792. values: wallOffsetAttribute,
  793. });
  794. }
  795. const wallIndices = IndexDatatype.createTypedArray(
  796. wallCount,
  797. perimeterPositions * 6
  798. );
  799. let upperLeft;
  800. let lowerLeft;
  801. let lowerRight;
  802. let upperRight;
  803. length = wallPositions.length / 3;
  804. let index = 0;
  805. for (i = 0; i < length - 1; i += 2) {
  806. upperLeft = i;
  807. upperRight = (upperLeft + 2) % length;
  808. const p1 = Cartesian3.fromArray(wallPositions, upperLeft * 3, v1Scratch);
  809. const p2 = Cartesian3.fromArray(wallPositions, upperRight * 3, v2Scratch);
  810. if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON10)) {
  811. continue;
  812. }
  813. lowerLeft = (upperLeft + 1) % length;
  814. lowerRight = (lowerLeft + 2) % length;
  815. wallIndices[index++] = upperLeft;
  816. wallIndices[index++] = lowerLeft;
  817. wallIndices[index++] = upperRight;
  818. wallIndices[index++] = upperRight;
  819. wallIndices[index++] = lowerLeft;
  820. wallIndices[index++] = lowerRight;
  821. }
  822. geo.indices = wallIndices;
  823. geo = GeometryPipeline.combineInstances([
  824. new GeometryInstance({
  825. geometry: topBottomGeo,
  826. }),
  827. new GeometryInstance({
  828. geometry: geo,
  829. }),
  830. ]);
  831. return geo[0];
  832. }
  833. const scratchRectanglePoints = [
  834. new Cartesian3(),
  835. new Cartesian3(),
  836. new Cartesian3(),
  837. new Cartesian3(),
  838. ];
  839. const nwScratch = new Cartographic();
  840. const stNwScratch = new Cartographic();
  841. function computeRectangle(rectangle, granularity, rotation, ellipsoid, result) {
  842. if (rotation === 0.0) {
  843. return Rectangle.clone(rectangle, result);
  844. }
  845. const computedOptions = RectangleGeometryLibrary.computeOptions(
  846. rectangle,
  847. granularity,
  848. rotation,
  849. 0,
  850. rectangleScratch,
  851. nwScratch
  852. );
  853. const height = computedOptions.height;
  854. const width = computedOptions.width;
  855. const positions = scratchRectanglePoints;
  856. RectangleGeometryLibrary.computePosition(
  857. computedOptions,
  858. ellipsoid,
  859. false,
  860. 0,
  861. 0,
  862. positions[0]
  863. );
  864. RectangleGeometryLibrary.computePosition(
  865. computedOptions,
  866. ellipsoid,
  867. false,
  868. 0,
  869. width - 1,
  870. positions[1]
  871. );
  872. RectangleGeometryLibrary.computePosition(
  873. computedOptions,
  874. ellipsoid,
  875. false,
  876. height - 1,
  877. 0,
  878. positions[2]
  879. );
  880. RectangleGeometryLibrary.computePosition(
  881. computedOptions,
  882. ellipsoid,
  883. false,
  884. height - 1,
  885. width - 1,
  886. positions[3]
  887. );
  888. return Rectangle.fromCartesianArray(positions, ellipsoid, result);
  889. }
  890. /**
  891. * A description of a cartographic rectangle on an ellipsoid centered at the origin. Rectangle geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}.
  892. *
  893. * @alias RectangleGeometry
  894. * @constructor
  895. *
  896. * @param {object} options Object with the following properties:
  897. * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians.
  898. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
  899. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle lies.
  900. * @param {number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
  901. * @param {number} [options.height=0.0] The distance in meters between the rectangle and the ellipsoid surface.
  902. * @param {number} [options.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise.
  903. * @param {number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise.
  904. * @param {number} [options.extrudedHeight] The distance in meters between the rectangle's extruded face and the ellipsoid surface.
  905. *
  906. * @exception {DeveloperError} <code>options.rectangle.north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  907. * @exception {DeveloperError} <code>options.rectangle.south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  908. * @exception {DeveloperError} <code>options.rectangle.east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  909. * @exception {DeveloperError} <code>options.rectangle.west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  910. * @exception {DeveloperError} <code>options.rectangle.north</code> must be greater than <code>options.rectangle.south</code>.
  911. *
  912. * @see RectangleGeometry#createGeometry
  913. *
  914. * @demo {@link https://sandcastle.cesium.com/index.html?src=Rectangle.html|Cesium Sandcastle Rectangle Demo}
  915. *
  916. * @example
  917. * // 1. create a rectangle
  918. * const rectangle = new Cesium.RectangleGeometry({
  919. * ellipsoid : Cesium.Ellipsoid.WGS84,
  920. * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0),
  921. * height : 10000.0
  922. * });
  923. * const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
  924. *
  925. * // 2. create an extruded rectangle without a top
  926. * const rectangle = new Cesium.RectangleGeometry({
  927. * ellipsoid : Cesium.Ellipsoid.WGS84,
  928. * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0),
  929. * height : 10000.0,
  930. * extrudedHeight: 300000
  931. * });
  932. * const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
  933. */
  934. function RectangleGeometry(options) {
  935. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  936. const rectangle = options.rectangle;
  937. //>>includeStart('debug', pragmas.debug);
  938. Check.typeOf.object("rectangle", rectangle);
  939. Rectangle.validate(rectangle);
  940. if (rectangle.north < rectangle.south) {
  941. throw new DeveloperError(
  942. "options.rectangle.north must be greater than or equal to options.rectangle.south"
  943. );
  944. }
  945. //>>includeEnd('debug');
  946. const height = defaultValue(options.height, 0.0);
  947. const extrudedHeight = defaultValue(options.extrudedHeight, height);
  948. this._rectangle = Rectangle.clone(rectangle);
  949. this._granularity = defaultValue(
  950. options.granularity,
  951. CesiumMath.RADIANS_PER_DEGREE
  952. );
  953. this._ellipsoid = Ellipsoid.clone(
  954. defaultValue(options.ellipsoid, Ellipsoid.WGS84)
  955. );
  956. this._surfaceHeight = Math.max(height, extrudedHeight);
  957. this._rotation = defaultValue(options.rotation, 0.0);
  958. this._stRotation = defaultValue(options.stRotation, 0.0);
  959. this._vertexFormat = VertexFormat.clone(
  960. defaultValue(options.vertexFormat, VertexFormat.DEFAULT)
  961. );
  962. this._extrudedHeight = Math.min(height, extrudedHeight);
  963. this._shadowVolume = defaultValue(options.shadowVolume, false);
  964. this._workerName = "createRectangleGeometry";
  965. this._offsetAttribute = options.offsetAttribute;
  966. this._rotatedRectangle = undefined;
  967. this._textureCoordinateRotationPoints = undefined;
  968. }
  969. /**
  970. * The number of elements used to pack the object into an array.
  971. * @type {number}
  972. */
  973. RectangleGeometry.packedLength =
  974. Rectangle.packedLength +
  975. Ellipsoid.packedLength +
  976. VertexFormat.packedLength +
  977. 7;
  978. /**
  979. * Stores the provided instance into the provided array.
  980. *
  981. * @param {RectangleGeometry} value The value to pack.
  982. * @param {number[]} array The array to pack into.
  983. * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
  984. *
  985. * @returns {number[]} The array that was packed into
  986. */
  987. RectangleGeometry.pack = function (value, array, startingIndex) {
  988. //>>includeStart('debug', pragmas.debug);
  989. Check.typeOf.object("value", value);
  990. Check.defined("array", array);
  991. //>>includeEnd('debug');
  992. startingIndex = defaultValue(startingIndex, 0);
  993. Rectangle.pack(value._rectangle, array, startingIndex);
  994. startingIndex += Rectangle.packedLength;
  995. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  996. startingIndex += Ellipsoid.packedLength;
  997. VertexFormat.pack(value._vertexFormat, array, startingIndex);
  998. startingIndex += VertexFormat.packedLength;
  999. array[startingIndex++] = value._granularity;
  1000. array[startingIndex++] = value._surfaceHeight;
  1001. array[startingIndex++] = value._rotation;
  1002. array[startingIndex++] = value._stRotation;
  1003. array[startingIndex++] = value._extrudedHeight;
  1004. array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0;
  1005. array[startingIndex] = defaultValue(value._offsetAttribute, -1);
  1006. return array;
  1007. };
  1008. const scratchRectangle = new Rectangle();
  1009. const scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  1010. const scratchOptions = {
  1011. rectangle: scratchRectangle,
  1012. ellipsoid: scratchEllipsoid,
  1013. vertexFormat: scratchVertexFormat,
  1014. granularity: undefined,
  1015. height: undefined,
  1016. rotation: undefined,
  1017. stRotation: undefined,
  1018. extrudedHeight: undefined,
  1019. shadowVolume: undefined,
  1020. offsetAttribute: undefined,
  1021. };
  1022. /**
  1023. * Retrieves an instance from a packed array.
  1024. *
  1025. * @param {number[]} array The packed array.
  1026. * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
  1027. * @param {RectangleGeometry} [result] The object into which to store the result.
  1028. * @returns {RectangleGeometry} The modified result parameter or a new RectangleGeometry instance if one was not provided.
  1029. */
  1030. RectangleGeometry.unpack = function (array, startingIndex, result) {
  1031. //>>includeStart('debug', pragmas.debug);
  1032. Check.defined("array", array);
  1033. //>>includeEnd('debug');
  1034. startingIndex = defaultValue(startingIndex, 0);
  1035. const rectangle = Rectangle.unpack(array, startingIndex, scratchRectangle);
  1036. startingIndex += Rectangle.packedLength;
  1037. const ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  1038. startingIndex += Ellipsoid.packedLength;
  1039. const vertexFormat = VertexFormat.unpack(
  1040. array,
  1041. startingIndex,
  1042. scratchVertexFormat
  1043. );
  1044. startingIndex += VertexFormat.packedLength;
  1045. const granularity = array[startingIndex++];
  1046. const surfaceHeight = array[startingIndex++];
  1047. const rotation = array[startingIndex++];
  1048. const stRotation = array[startingIndex++];
  1049. const extrudedHeight = array[startingIndex++];
  1050. const shadowVolume = array[startingIndex++] === 1.0;
  1051. const offsetAttribute = array[startingIndex];
  1052. if (!defined(result)) {
  1053. scratchOptions.granularity = granularity;
  1054. scratchOptions.height = surfaceHeight;
  1055. scratchOptions.rotation = rotation;
  1056. scratchOptions.stRotation = stRotation;
  1057. scratchOptions.extrudedHeight = extrudedHeight;
  1058. scratchOptions.shadowVolume = shadowVolume;
  1059. scratchOptions.offsetAttribute =
  1060. offsetAttribute === -1 ? undefined : offsetAttribute;
  1061. return new RectangleGeometry(scratchOptions);
  1062. }
  1063. result._rectangle = Rectangle.clone(rectangle, result._rectangle);
  1064. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  1065. result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat);
  1066. result._granularity = granularity;
  1067. result._surfaceHeight = surfaceHeight;
  1068. result._rotation = rotation;
  1069. result._stRotation = stRotation;
  1070. result._extrudedHeight = extrudedHeight;
  1071. result._shadowVolume = shadowVolume;
  1072. result._offsetAttribute =
  1073. offsetAttribute === -1 ? undefined : offsetAttribute;
  1074. return result;
  1075. };
  1076. /**
  1077. * Computes the bounding rectangle based on the provided options
  1078. *
  1079. * @param {object} options Object with the following properties:
  1080. * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians.
  1081. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle lies.
  1082. * @param {number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
  1083. * @param {number} [options.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise.
  1084. * @param {Rectangle} [result] An object in which to store the result.
  1085. *
  1086. * @returns {Rectangle} The result rectangle
  1087. */
  1088. RectangleGeometry.computeRectangle = function (options, result) {
  1089. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  1090. const rectangle = options.rectangle;
  1091. //>>includeStart('debug', pragmas.debug);
  1092. Check.typeOf.object("rectangle", rectangle);
  1093. Rectangle.validate(rectangle);
  1094. if (rectangle.north < rectangle.south) {
  1095. throw new DeveloperError(
  1096. "options.rectangle.north must be greater than or equal to options.rectangle.south"
  1097. );
  1098. }
  1099. //>>includeEnd('debug');
  1100. const granularity = defaultValue(
  1101. options.granularity,
  1102. CesiumMath.RADIANS_PER_DEGREE
  1103. );
  1104. const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  1105. const rotation = defaultValue(options.rotation, 0.0);
  1106. return computeRectangle(rectangle, granularity, rotation, ellipsoid, result);
  1107. };
  1108. const tangentRotationMatrixScratch = new Matrix3();
  1109. const quaternionScratch = new Quaternion();
  1110. const centerScratch = new Cartographic();
  1111. /**
  1112. * Computes the geometric representation of a rectangle, including its vertices, indices, and a bounding sphere.
  1113. *
  1114. * @param {RectangleGeometry} rectangleGeometry A description of the rectangle.
  1115. * @returns {Geometry|undefined} The computed vertices and indices.
  1116. *
  1117. * @exception {DeveloperError} Rotated rectangle is invalid.
  1118. */
  1119. RectangleGeometry.createGeometry = function (rectangleGeometry) {
  1120. if (
  1121. CesiumMath.equalsEpsilon(
  1122. rectangleGeometry._rectangle.north,
  1123. rectangleGeometry._rectangle.south,
  1124. CesiumMath.EPSILON10
  1125. ) ||
  1126. CesiumMath.equalsEpsilon(
  1127. rectangleGeometry._rectangle.east,
  1128. rectangleGeometry._rectangle.west,
  1129. CesiumMath.EPSILON10
  1130. )
  1131. ) {
  1132. return undefined;
  1133. }
  1134. let rectangle = rectangleGeometry._rectangle;
  1135. const ellipsoid = rectangleGeometry._ellipsoid;
  1136. const rotation = rectangleGeometry._rotation;
  1137. const stRotation = rectangleGeometry._stRotation;
  1138. const vertexFormat = rectangleGeometry._vertexFormat;
  1139. const computedOptions = RectangleGeometryLibrary.computeOptions(
  1140. rectangle,
  1141. rectangleGeometry._granularity,
  1142. rotation,
  1143. stRotation,
  1144. rectangleScratch,
  1145. nwScratch,
  1146. stNwScratch
  1147. );
  1148. const tangentRotationMatrix = tangentRotationMatrixScratch;
  1149. if (stRotation !== 0 || rotation !== 0) {
  1150. const center = Rectangle.center(rectangle, centerScratch);
  1151. const axis = ellipsoid.geodeticSurfaceNormalCartographic(center, v1Scratch);
  1152. Quaternion.fromAxisAngle(axis, -stRotation, quaternionScratch);
  1153. Matrix3.fromQuaternion(quaternionScratch, tangentRotationMatrix);
  1154. } else {
  1155. Matrix3.clone(Matrix3.IDENTITY, tangentRotationMatrix);
  1156. }
  1157. const surfaceHeight = rectangleGeometry._surfaceHeight;
  1158. const extrudedHeight = rectangleGeometry._extrudedHeight;
  1159. const extrude = !CesiumMath.equalsEpsilon(
  1160. surfaceHeight,
  1161. extrudedHeight,
  1162. 0,
  1163. CesiumMath.EPSILON2
  1164. );
  1165. computedOptions.lonScalar = 1.0 / rectangleGeometry._rectangle.width;
  1166. computedOptions.latScalar = 1.0 / rectangleGeometry._rectangle.height;
  1167. computedOptions.tangentRotationMatrix = tangentRotationMatrix;
  1168. let geometry;
  1169. let boundingSphere;
  1170. rectangle = rectangleGeometry._rectangle;
  1171. if (extrude) {
  1172. geometry = constructExtrudedRectangle(rectangleGeometry, computedOptions);
  1173. const topBS = BoundingSphere.fromRectangle3D(
  1174. rectangle,
  1175. ellipsoid,
  1176. surfaceHeight,
  1177. topBoundingSphere
  1178. );
  1179. const bottomBS = BoundingSphere.fromRectangle3D(
  1180. rectangle,
  1181. ellipsoid,
  1182. extrudedHeight,
  1183. bottomBoundingSphere
  1184. );
  1185. boundingSphere = BoundingSphere.union(topBS, bottomBS);
  1186. } else {
  1187. geometry = constructRectangle(rectangleGeometry, computedOptions);
  1188. geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(
  1189. geometry.attributes.position.values,
  1190. surfaceHeight,
  1191. ellipsoid,
  1192. false
  1193. );
  1194. if (defined(rectangleGeometry._offsetAttribute)) {
  1195. const length = geometry.attributes.position.values.length;
  1196. const offsetValue =
  1197. rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE
  1198. ? 0
  1199. : 1;
  1200. const applyOffset = new Uint8Array(length / 3).fill(offsetValue);
  1201. geometry.attributes.applyOffset = new GeometryAttribute({
  1202. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  1203. componentsPerAttribute: 1,
  1204. values: applyOffset,
  1205. });
  1206. }
  1207. boundingSphere = BoundingSphere.fromRectangle3D(
  1208. rectangle,
  1209. ellipsoid,
  1210. surfaceHeight
  1211. );
  1212. }
  1213. if (!vertexFormat.position) {
  1214. delete geometry.attributes.position;
  1215. }
  1216. return new Geometry({
  1217. attributes: geometry.attributes,
  1218. indices: geometry.indices,
  1219. primitiveType: geometry.primitiveType,
  1220. boundingSphere: boundingSphere,
  1221. offsetAttribute: rectangleGeometry._offsetAttribute,
  1222. });
  1223. };
  1224. /**
  1225. * @private
  1226. */
  1227. RectangleGeometry.createShadowVolume = function (
  1228. rectangleGeometry,
  1229. minHeightFunc,
  1230. maxHeightFunc
  1231. ) {
  1232. const granularity = rectangleGeometry._granularity;
  1233. const ellipsoid = rectangleGeometry._ellipsoid;
  1234. const minHeight = minHeightFunc(granularity, ellipsoid);
  1235. const maxHeight = maxHeightFunc(granularity, ellipsoid);
  1236. return new RectangleGeometry({
  1237. rectangle: rectangleGeometry._rectangle,
  1238. rotation: rectangleGeometry._rotation,
  1239. ellipsoid: ellipsoid,
  1240. stRotation: rectangleGeometry._stRotation,
  1241. granularity: granularity,
  1242. extrudedHeight: maxHeight,
  1243. height: minHeight,
  1244. vertexFormat: VertexFormat.POSITION_ONLY,
  1245. shadowVolume: true,
  1246. });
  1247. };
  1248. const unrotatedTextureRectangleScratch = new Rectangle();
  1249. const points2DScratch = [new Cartesian2(), new Cartesian2(), new Cartesian2()];
  1250. const rotation2DScratch = new Matrix2();
  1251. const rectangleCenterScratch = new Cartographic();
  1252. function textureCoordinateRotationPoints(rectangleGeometry) {
  1253. if (rectangleGeometry._stRotation === 0.0) {
  1254. return [0, 0, 0, 1, 1, 0];
  1255. }
  1256. const rectangle = Rectangle.clone(
  1257. rectangleGeometry._rectangle,
  1258. unrotatedTextureRectangleScratch
  1259. );
  1260. const granularity = rectangleGeometry._granularity;
  1261. const ellipsoid = rectangleGeometry._ellipsoid;
  1262. // Rotate to align the texture coordinates with ENU
  1263. const rotation = rectangleGeometry._rotation - rectangleGeometry._stRotation;
  1264. const unrotatedTextureRectangle = computeRectangle(
  1265. rectangle,
  1266. granularity,
  1267. rotation,
  1268. ellipsoid,
  1269. unrotatedTextureRectangleScratch
  1270. );
  1271. // Assume a computed "east-north" texture coordinate system based on spherical or planar tricks, bounded by `boundingRectangle`.
  1272. // The "desired" texture coordinate system forms an oriented rectangle (un-oriented computed) around the geometry that completely and tightly bounds it.
  1273. // We want to map from the "east-north" texture coordinate system into the "desired" system using a pair of lines (analagous planes in 2D)
  1274. // Compute 3 corners of the "desired" texture coordinate system in "east-north" texture space by the following in cartographic space:
  1275. // - rotate 3 of the corners in unrotatedTextureRectangle by stRotation around the center of the bounding rectangle
  1276. // - apply the "east-north" system's normalization formula to the rotated cartographics, even though this is likely to produce values outside [0-1].
  1277. // This gives us a set of points in the "east-north" texture coordinate system that can be used to map "east-north" texture coordinates to "desired."
  1278. const points2D = points2DScratch;
  1279. points2D[0].x = unrotatedTextureRectangle.west;
  1280. points2D[0].y = unrotatedTextureRectangle.south;
  1281. points2D[1].x = unrotatedTextureRectangle.west;
  1282. points2D[1].y = unrotatedTextureRectangle.north;
  1283. points2D[2].x = unrotatedTextureRectangle.east;
  1284. points2D[2].y = unrotatedTextureRectangle.south;
  1285. const boundingRectangle = rectangleGeometry.rectangle;
  1286. const toDesiredInComputed = Matrix2.fromRotation(
  1287. rectangleGeometry._stRotation,
  1288. rotation2DScratch
  1289. );
  1290. const boundingRectangleCenter = Rectangle.center(
  1291. boundingRectangle,
  1292. rectangleCenterScratch
  1293. );
  1294. for (let i = 0; i < 3; ++i) {
  1295. const point2D = points2D[i];
  1296. point2D.x -= boundingRectangleCenter.longitude;
  1297. point2D.y -= boundingRectangleCenter.latitude;
  1298. Matrix2.multiplyByVector(toDesiredInComputed, point2D, point2D);
  1299. point2D.x += boundingRectangleCenter.longitude;
  1300. point2D.y += boundingRectangleCenter.latitude;
  1301. // Convert point into east-north texture coordinate space
  1302. point2D.x = (point2D.x - boundingRectangle.west) / boundingRectangle.width;
  1303. point2D.y =
  1304. (point2D.y - boundingRectangle.south) / boundingRectangle.height;
  1305. }
  1306. const minXYCorner = points2D[0];
  1307. const maxYCorner = points2D[1];
  1308. const maxXCorner = points2D[2];
  1309. const result = new Array(6);
  1310. Cartesian2.pack(minXYCorner, result);
  1311. Cartesian2.pack(maxYCorner, result, 2);
  1312. Cartesian2.pack(maxXCorner, result, 4);
  1313. return result;
  1314. }
  1315. Object.defineProperties(RectangleGeometry.prototype, {
  1316. /**
  1317. * @private
  1318. */
  1319. rectangle: {
  1320. get: function () {
  1321. if (!defined(this._rotatedRectangle)) {
  1322. this._rotatedRectangle = computeRectangle(
  1323. this._rectangle,
  1324. this._granularity,
  1325. this._rotation,
  1326. this._ellipsoid
  1327. );
  1328. }
  1329. return this._rotatedRectangle;
  1330. },
  1331. },
  1332. /**
  1333. * For remapping texture coordinates when rendering RectangleGeometries as GroundPrimitives.
  1334. * This version permits skew in textures by computing offsets directly in cartographic space and
  1335. * more accurately approximates rendering RectangleGeometries with height as standard Primitives.
  1336. * @see Geometry#_textureCoordinateRotationPoints
  1337. * @private
  1338. */
  1339. textureCoordinateRotationPoints: {
  1340. get: function () {
  1341. if (!defined(this._textureCoordinateRotationPoints)) {
  1342. this._textureCoordinateRotationPoints = textureCoordinateRotationPoints(
  1343. this
  1344. );
  1345. }
  1346. return this._textureCoordinateRotationPoints;
  1347. },
  1348. },
  1349. });
  1350. export default RectangleGeometry;