GeometryPipeline.js 102 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592
  1. import AttributeCompression from './AttributeCompression.js';
  2. import barycentricCoordinates from './barycentricCoordinates.js';
  3. import BoundingSphere from './BoundingSphere.js';
  4. import Cartesian2 from './Cartesian2.js';
  5. import Cartesian3 from './Cartesian3.js';
  6. import Cartesian4 from './Cartesian4.js';
  7. import Cartographic from './Cartographic.js';
  8. import ComponentDatatype from './ComponentDatatype.js';
  9. import defaultValue from './defaultValue.js';
  10. import defined from './defined.js';
  11. import DeveloperError from './DeveloperError.js';
  12. import EncodedCartesian3 from './EncodedCartesian3.js';
  13. import GeographicProjection from './GeographicProjection.js';
  14. import Geometry from './Geometry.js';
  15. import GeometryAttribute from './GeometryAttribute.js';
  16. import GeometryType from './GeometryType.js';
  17. import IndexDatatype from './IndexDatatype.js';
  18. import Intersect from './Intersect.js';
  19. import IntersectionTests from './IntersectionTests.js';
  20. import CesiumMath from './Math.js';
  21. import Matrix3 from './Matrix3.js';
  22. import Matrix4 from './Matrix4.js';
  23. import Plane from './Plane.js';
  24. import PrimitiveType from './PrimitiveType.js';
  25. import Tipsify from './Tipsify.js';
  26. /**
  27. * Content pipeline functions for geometries.
  28. *
  29. * @exports GeometryPipeline
  30. *
  31. * @see Geometry
  32. */
  33. var GeometryPipeline = {};
  34. function addTriangle(lines, index, i0, i1, i2) {
  35. lines[index++] = i0;
  36. lines[index++] = i1;
  37. lines[index++] = i1;
  38. lines[index++] = i2;
  39. lines[index++] = i2;
  40. lines[index] = i0;
  41. }
  42. function trianglesToLines(triangles) {
  43. var count = triangles.length;
  44. var size = (count / 3) * 6;
  45. var lines = IndexDatatype.createTypedArray(count, size);
  46. var index = 0;
  47. for ( var i = 0; i < count; i += 3, index += 6) {
  48. addTriangle(lines, index, triangles[i], triangles[i + 1], triangles[i + 2]);
  49. }
  50. return lines;
  51. }
  52. function triangleStripToLines(triangles) {
  53. var count = triangles.length;
  54. if (count >= 3) {
  55. var size = (count - 2) * 6;
  56. var lines = IndexDatatype.createTypedArray(count, size);
  57. addTriangle(lines, 0, triangles[0], triangles[1], triangles[2]);
  58. var index = 6;
  59. for ( var i = 3; i < count; ++i, index += 6) {
  60. addTriangle(lines, index, triangles[i - 1], triangles[i], triangles[i - 2]);
  61. }
  62. return lines;
  63. }
  64. return new Uint16Array();
  65. }
  66. function triangleFanToLines(triangles) {
  67. if (triangles.length > 0) {
  68. var count = triangles.length - 1;
  69. var size = (count - 1) * 6;
  70. var lines = IndexDatatype.createTypedArray(count, size);
  71. var base = triangles[0];
  72. var index = 0;
  73. for ( var i = 1; i < count; ++i, index += 6) {
  74. addTriangle(lines, index, base, triangles[i], triangles[i + 1]);
  75. }
  76. return lines;
  77. }
  78. return new Uint16Array();
  79. }
  80. /**
  81. * Converts a geometry's triangle indices to line indices. If the geometry has an <code>indices</code>
  82. * and its <code>primitiveType</code> is <code>TRIANGLES</code>, <code>TRIANGLE_STRIP</code>,
  83. * <code>TRIANGLE_FAN</code>, it is converted to <code>LINES</code>; otherwise, the geometry is not changed.
  84. * <p>
  85. * This is commonly used to create a wireframe geometry for visual debugging.
  86. * </p>
  87. *
  88. * @param {Geometry} geometry The geometry to modify.
  89. * @returns {Geometry} The modified <code>geometry</code> argument, with its triangle indices converted to lines.
  90. *
  91. * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.
  92. *
  93. * @example
  94. * geometry = Cesium.GeometryPipeline.toWireframe(geometry);
  95. */
  96. GeometryPipeline.toWireframe = function(geometry) {
  97. //>>includeStart('debug', pragmas.debug);
  98. if (!defined(geometry)) {
  99. throw new DeveloperError('geometry is required.');
  100. }
  101. //>>includeEnd('debug');
  102. var indices = geometry.indices;
  103. if (defined(indices)) {
  104. switch (geometry.primitiveType) {
  105. case PrimitiveType.TRIANGLES:
  106. geometry.indices = trianglesToLines(indices);
  107. break;
  108. case PrimitiveType.TRIANGLE_STRIP:
  109. geometry.indices = triangleStripToLines(indices);
  110. break;
  111. case PrimitiveType.TRIANGLE_FAN:
  112. geometry.indices = triangleFanToLines(indices);
  113. break;
  114. //>>includeStart('debug', pragmas.debug);
  115. default:
  116. throw new DeveloperError('geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.');
  117. //>>includeEnd('debug');
  118. }
  119. geometry.primitiveType = PrimitiveType.LINES;
  120. }
  121. return geometry;
  122. };
  123. /**
  124. * Creates a new {@link Geometry} with <code>LINES</code> representing the provided
  125. * attribute (<code>attributeName</code>) for the provided geometry. This is used to
  126. * visualize vector attributes like normals, tangents, and bitangents.
  127. *
  128. * @param {Geometry} geometry The <code>Geometry</code> instance with the attribute.
  129. * @param {String} [attributeName='normal'] The name of the attribute.
  130. * @param {Number} [length=10000.0] The length of each line segment in meters. This can be negative to point the vector in the opposite direction.
  131. * @returns {Geometry} A new <code>Geometry</code> instance with line segments for the vector.
  132. *
  133. * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter.
  134. *
  135. * @example
  136. * var geometry = Cesium.GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'bitangent', 100000.0);
  137. */
  138. GeometryPipeline.createLineSegmentsForVectors = function(geometry, attributeName, length) {
  139. attributeName = defaultValue(attributeName, 'normal');
  140. //>>includeStart('debug', pragmas.debug);
  141. if (!defined(geometry)) {
  142. throw new DeveloperError('geometry is required.');
  143. }
  144. if (!defined(geometry.attributes.position)) {
  145. throw new DeveloperError('geometry.attributes.position is required.');
  146. }
  147. if (!defined(geometry.attributes[attributeName])) {
  148. throw new DeveloperError('geometry.attributes must have an attribute with the same name as the attributeName parameter, ' + attributeName + '.');
  149. }
  150. //>>includeEnd('debug');
  151. length = defaultValue(length, 10000.0);
  152. var positions = geometry.attributes.position.values;
  153. var vectors = geometry.attributes[attributeName].values;
  154. var positionsLength = positions.length;
  155. var newPositions = new Float64Array(2 * positionsLength);
  156. var j = 0;
  157. for (var i = 0; i < positionsLength; i += 3) {
  158. newPositions[j++] = positions[i];
  159. newPositions[j++] = positions[i + 1];
  160. newPositions[j++] = positions[i + 2];
  161. newPositions[j++] = positions[i] + (vectors[i] * length);
  162. newPositions[j++] = positions[i + 1] + (vectors[i + 1] * length);
  163. newPositions[j++] = positions[i + 2] + (vectors[i + 2] * length);
  164. }
  165. var newBoundingSphere;
  166. var bs = geometry.boundingSphere;
  167. if (defined(bs)) {
  168. newBoundingSphere = new BoundingSphere(bs.center, bs.radius + length);
  169. }
  170. return new Geometry({
  171. attributes : {
  172. position : new GeometryAttribute({
  173. componentDatatype : ComponentDatatype.DOUBLE,
  174. componentsPerAttribute : 3,
  175. values : newPositions
  176. })
  177. },
  178. primitiveType : PrimitiveType.LINES,
  179. boundingSphere : newBoundingSphere
  180. });
  181. };
  182. /**
  183. * Creates an object that maps attribute names to unique locations (indices)
  184. * for matching vertex attributes and shader programs.
  185. *
  186. * @param {Geometry} geometry The geometry, which is not modified, to create the object for.
  187. * @returns {Object} An object with attribute name / index pairs.
  188. *
  189. * @example
  190. * var attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry);
  191. * // Example output
  192. * // {
  193. * // 'position' : 0,
  194. * // 'normal' : 1
  195. * // }
  196. */
  197. GeometryPipeline.createAttributeLocations = function(geometry) {
  198. //>>includeStart('debug', pragmas.debug);
  199. if (!defined(geometry)) {
  200. throw new DeveloperError('geometry is required.');
  201. }
  202. //>>includeEnd('debug')
  203. // There can be a WebGL performance hit when attribute 0 is disabled, so
  204. // assign attribute locations to well-known attributes.
  205. var semantics = [
  206. 'position',
  207. 'positionHigh',
  208. 'positionLow',
  209. // From VertexFormat.position - after 2D projection and high-precision encoding
  210. 'position3DHigh',
  211. 'position3DLow',
  212. 'position2DHigh',
  213. 'position2DLow',
  214. // From Primitive
  215. 'pickColor',
  216. // From VertexFormat
  217. 'normal',
  218. 'st',
  219. 'tangent',
  220. 'bitangent',
  221. // For shadow volumes
  222. 'extrudeDirection',
  223. // From compressing texture coordinates and normals
  224. 'compressedAttributes'
  225. ];
  226. var attributes = geometry.attributes;
  227. var indices = {};
  228. var j = 0;
  229. var i;
  230. var len = semantics.length;
  231. // Attribute locations for well-known attributes
  232. for (i = 0; i < len; ++i) {
  233. var semantic = semantics[i];
  234. if (defined(attributes[semantic])) {
  235. indices[semantic] = j++;
  236. }
  237. }
  238. // Locations for custom attributes
  239. for (var name in attributes) {
  240. if (attributes.hasOwnProperty(name) && (!defined(indices[name]))) {
  241. indices[name] = j++;
  242. }
  243. }
  244. return indices;
  245. };
  246. /**
  247. * Reorders a geometry's attributes and <code>indices</code> to achieve better performance from the GPU's pre-vertex-shader cache.
  248. *
  249. * @param {Geometry} geometry The geometry to modify.
  250. * @returns {Geometry} The modified <code>geometry</code> argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache.
  251. *
  252. * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes.
  253. *
  254. *
  255. * @example
  256. * geometry = Cesium.GeometryPipeline.reorderForPreVertexCache(geometry);
  257. *
  258. * @see GeometryPipeline.reorderForPostVertexCache
  259. */
  260. GeometryPipeline.reorderForPreVertexCache = function(geometry) {
  261. //>>includeStart('debug', pragmas.debug);
  262. if (!defined(geometry)) {
  263. throw new DeveloperError('geometry is required.');
  264. }
  265. //>>includeEnd('debug');
  266. var numVertices = Geometry.computeNumberOfVertices(geometry);
  267. var indices = geometry.indices;
  268. if (defined(indices)) {
  269. var indexCrossReferenceOldToNew = new Int32Array(numVertices);
  270. for ( var i = 0; i < numVertices; i++) {
  271. indexCrossReferenceOldToNew[i] = -1;
  272. }
  273. // Construct cross reference and reorder indices
  274. var indicesIn = indices;
  275. var numIndices = indicesIn.length;
  276. var indicesOut = IndexDatatype.createTypedArray(numVertices, numIndices);
  277. var intoIndicesIn = 0;
  278. var intoIndicesOut = 0;
  279. var nextIndex = 0;
  280. var tempIndex;
  281. while (intoIndicesIn < numIndices) {
  282. tempIndex = indexCrossReferenceOldToNew[indicesIn[intoIndicesIn]];
  283. if (tempIndex !== -1) {
  284. indicesOut[intoIndicesOut] = tempIndex;
  285. } else {
  286. tempIndex = indicesIn[intoIndicesIn];
  287. indexCrossReferenceOldToNew[tempIndex] = nextIndex;
  288. indicesOut[intoIndicesOut] = nextIndex;
  289. ++nextIndex;
  290. }
  291. ++intoIndicesIn;
  292. ++intoIndicesOut;
  293. }
  294. geometry.indices = indicesOut;
  295. // Reorder attributes
  296. var attributes = geometry.attributes;
  297. for ( var property in attributes) {
  298. if (attributes.hasOwnProperty(property) &&
  299. defined(attributes[property]) &&
  300. defined(attributes[property].values)) {
  301. var attribute = attributes[property];
  302. var elementsIn = attribute.values;
  303. var intoElementsIn = 0;
  304. var numComponents = attribute.componentsPerAttribute;
  305. var elementsOut = ComponentDatatype.createTypedArray(attribute.componentDatatype, nextIndex * numComponents);
  306. while (intoElementsIn < numVertices) {
  307. var temp = indexCrossReferenceOldToNew[intoElementsIn];
  308. if (temp !== -1) {
  309. for (var j = 0; j < numComponents; j++) {
  310. elementsOut[numComponents * temp + j] = elementsIn[numComponents * intoElementsIn + j];
  311. }
  312. }
  313. ++intoElementsIn;
  314. }
  315. attribute.values = elementsOut;
  316. }
  317. }
  318. }
  319. return geometry;
  320. };
  321. /**
  322. * Reorders a geometry's <code>indices</code> to achieve better performance from the GPU's
  323. * post vertex-shader cache by using the Tipsify algorithm. If the geometry <code>primitiveType</code>
  324. * is not <code>TRIANGLES</code> or the geometry does not have an <code>indices</code>, this function has no effect.
  325. *
  326. * @param {Geometry} geometry The geometry to modify.
  327. * @param {Number} [cacheCapacity=24] The number of vertices that can be held in the GPU's vertex cache.
  328. * @returns {Geometry} The modified <code>geometry</code> argument, with its indices reordered for the post-vertex-shader cache.
  329. *
  330. * @exception {DeveloperError} cacheCapacity must be greater than two.
  331. *
  332. *
  333. * @example
  334. * geometry = Cesium.GeometryPipeline.reorderForPostVertexCache(geometry);
  335. *
  336. * @see GeometryPipeline.reorderForPreVertexCache
  337. * @see {@link http://gfx.cs.princ0eton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf|Fast Triangle Reordering for Vertex Locality and Reduced Overdraw}
  338. * by Sander, Nehab, and Barczak
  339. */
  340. GeometryPipeline.reorderForPostVertexCache = function(geometry, cacheCapacity) {
  341. //>>includeStart('debug', pragmas.debug);
  342. if (!defined(geometry)) {
  343. throw new DeveloperError('geometry is required.');
  344. }
  345. //>>includeEnd('debug');
  346. var indices = geometry.indices;
  347. if ((geometry.primitiveType === PrimitiveType.TRIANGLES) && (defined(indices))) {
  348. var numIndices = indices.length;
  349. var maximumIndex = 0;
  350. for ( var j = 0; j < numIndices; j++) {
  351. if (indices[j] > maximumIndex) {
  352. maximumIndex = indices[j];
  353. }
  354. }
  355. geometry.indices = Tipsify.tipsify({
  356. indices : indices,
  357. maximumIndex : maximumIndex,
  358. cacheSize : cacheCapacity
  359. });
  360. }
  361. return geometry;
  362. };
  363. function copyAttributesDescriptions(attributes) {
  364. var newAttributes = {};
  365. for ( var attribute in attributes) {
  366. if (attributes.hasOwnProperty(attribute) &&
  367. defined(attributes[attribute]) &&
  368. defined(attributes[attribute].values)) {
  369. var attr = attributes[attribute];
  370. newAttributes[attribute] = new GeometryAttribute({
  371. componentDatatype : attr.componentDatatype,
  372. componentsPerAttribute : attr.componentsPerAttribute,
  373. normalize : attr.normalize,
  374. values : []
  375. });
  376. }
  377. }
  378. return newAttributes;
  379. }
  380. function copyVertex(destinationAttributes, sourceAttributes, index) {
  381. for ( var attribute in sourceAttributes) {
  382. if (sourceAttributes.hasOwnProperty(attribute) &&
  383. defined(sourceAttributes[attribute]) &&
  384. defined(sourceAttributes[attribute].values)) {
  385. var attr = sourceAttributes[attribute];
  386. for ( var k = 0; k < attr.componentsPerAttribute; ++k) {
  387. destinationAttributes[attribute].values.push(attr.values[(index * attr.componentsPerAttribute) + k]);
  388. }
  389. }
  390. }
  391. }
  392. /**
  393. * Splits a geometry into multiple geometries, if necessary, to ensure that indices in the
  394. * <code>indices</code> fit into unsigned shorts. This is used to meet the WebGL requirements
  395. * when unsigned int indices are not supported.
  396. * <p>
  397. * If the geometry does not have any <code>indices</code>, this function has no effect.
  398. * </p>
  399. *
  400. * @param {Geometry} geometry The geometry to be split into multiple geometries.
  401. * @returns {Geometry[]} An array of geometries, each with indices that fit into unsigned shorts.
  402. *
  403. * @exception {DeveloperError} geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS
  404. * @exception {DeveloperError} All geometry attribute lists must have the same number of attributes.
  405. *
  406. * @example
  407. * var geometries = Cesium.GeometryPipeline.fitToUnsignedShortIndices(geometry);
  408. */
  409. GeometryPipeline.fitToUnsignedShortIndices = function(geometry) {
  410. //>>includeStart('debug', pragmas.debug);
  411. if (!defined(geometry)) {
  412. throw new DeveloperError('geometry is required.');
  413. }
  414. if ((defined(geometry.indices)) &&
  415. ((geometry.primitiveType !== PrimitiveType.TRIANGLES) &&
  416. (geometry.primitiveType !== PrimitiveType.LINES) &&
  417. (geometry.primitiveType !== PrimitiveType.POINTS))) {
  418. throw new DeveloperError('geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS.');
  419. }
  420. //>>includeEnd('debug');
  421. var geometries = [];
  422. // If there's an index list and more than 64K attributes, it is possible that
  423. // some indices are outside the range of unsigned short [0, 64K - 1]
  424. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  425. if (defined(geometry.indices) && (numberOfVertices >= CesiumMath.SIXTY_FOUR_KILOBYTES)) {
  426. var oldToNewIndex = [];
  427. var newIndices = [];
  428. var currentIndex = 0;
  429. var newAttributes = copyAttributesDescriptions(geometry.attributes);
  430. var originalIndices = geometry.indices;
  431. var numberOfIndices = originalIndices.length;
  432. var indicesPerPrimitive;
  433. if (geometry.primitiveType === PrimitiveType.TRIANGLES) {
  434. indicesPerPrimitive = 3;
  435. } else if (geometry.primitiveType === PrimitiveType.LINES) {
  436. indicesPerPrimitive = 2;
  437. } else if (geometry.primitiveType === PrimitiveType.POINTS) {
  438. indicesPerPrimitive = 1;
  439. }
  440. for ( var j = 0; j < numberOfIndices; j += indicesPerPrimitive) {
  441. for (var k = 0; k < indicesPerPrimitive; ++k) {
  442. var x = originalIndices[j + k];
  443. var i = oldToNewIndex[x];
  444. if (!defined(i)) {
  445. i = currentIndex++;
  446. oldToNewIndex[x] = i;
  447. copyVertex(newAttributes, geometry.attributes, x);
  448. }
  449. newIndices.push(i);
  450. }
  451. if (currentIndex + indicesPerPrimitive >= CesiumMath.SIXTY_FOUR_KILOBYTES) {
  452. geometries.push(new Geometry({
  453. attributes : newAttributes,
  454. indices : newIndices,
  455. primitiveType : geometry.primitiveType,
  456. boundingSphere : geometry.boundingSphere,
  457. boundingSphereCV : geometry.boundingSphereCV
  458. }));
  459. // Reset for next vertex-array
  460. oldToNewIndex = [];
  461. newIndices = [];
  462. currentIndex = 0;
  463. newAttributes = copyAttributesDescriptions(geometry.attributes);
  464. }
  465. }
  466. if (newIndices.length !== 0) {
  467. geometries.push(new Geometry({
  468. attributes : newAttributes,
  469. indices : newIndices,
  470. primitiveType : geometry.primitiveType,
  471. boundingSphere : geometry.boundingSphere,
  472. boundingSphereCV : geometry.boundingSphereCV
  473. }));
  474. }
  475. } else {
  476. // No need to split into multiple geometries
  477. geometries.push(geometry);
  478. }
  479. return geometries;
  480. };
  481. var scratchProjectTo2DCartesian3 = new Cartesian3();
  482. var scratchProjectTo2DCartographic = new Cartographic();
  483. /**
  484. * Projects a geometry's 3D <code>position</code> attribute to 2D, replacing the <code>position</code>
  485. * attribute with separate <code>position3D</code> and <code>position2D</code> attributes.
  486. * <p>
  487. * If the geometry does not have a <code>position</code>, this function has no effect.
  488. * </p>
  489. *
  490. * @param {Geometry} geometry The geometry to modify.
  491. * @param {String} attributeName The name of the attribute.
  492. * @param {String} attributeName3D The name of the attribute in 3D.
  493. * @param {String} attributeName2D The name of the attribute in 2D.
  494. * @param {Object} [projection=new GeographicProjection()] The projection to use.
  495. * @returns {Geometry} The modified <code>geometry</code> argument with <code>position3D</code> and <code>position2D</code> attributes.
  496. *
  497. * @exception {DeveloperError} geometry must have attribute matching the attributeName argument.
  498. * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE.
  499. * @exception {DeveloperError} Could not project a point to 2D.
  500. *
  501. * @example
  502. * geometry = Cesium.GeometryPipeline.projectTo2D(geometry, 'position', 'position3D', 'position2D');
  503. */
  504. GeometryPipeline.projectTo2D = function(geometry, attributeName, attributeName3D, attributeName2D, projection) {
  505. //>>includeStart('debug', pragmas.debug);
  506. if (!defined(geometry)) {
  507. throw new DeveloperError('geometry is required.');
  508. }
  509. if (!defined(attributeName)) {
  510. throw new DeveloperError('attributeName is required.');
  511. }
  512. if (!defined(attributeName3D)) {
  513. throw new DeveloperError('attributeName3D is required.');
  514. }
  515. if (!defined(attributeName2D)) {
  516. throw new DeveloperError('attributeName2D is required.');
  517. }
  518. if (!defined(geometry.attributes[attributeName])) {
  519. throw new DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.');
  520. }
  521. if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.DOUBLE) {
  522. throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.');
  523. }
  524. //>>includeEnd('debug');
  525. var attribute = geometry.attributes[attributeName];
  526. projection = (defined(projection)) ? projection : new GeographicProjection();
  527. var ellipsoid = projection.ellipsoid;
  528. // Project original values to 2D.
  529. var values3D = attribute.values;
  530. var projectedValues = new Float64Array(values3D.length);
  531. var index = 0;
  532. for ( var i = 0; i < values3D.length; i += 3) {
  533. var value = Cartesian3.fromArray(values3D, i, scratchProjectTo2DCartesian3);
  534. var lonLat = ellipsoid.cartesianToCartographic(value, scratchProjectTo2DCartographic);
  535. //>>includeStart('debug', pragmas.debug);
  536. if (!defined(lonLat)) {
  537. throw new DeveloperError('Could not project point (' + value.x + ', ' + value.y + ', ' + value.z + ') to 2D.');
  538. }
  539. //>>includeEnd('debug');
  540. var projectedLonLat = projection.project(lonLat, scratchProjectTo2DCartesian3);
  541. projectedValues[index++] = projectedLonLat.x;
  542. projectedValues[index++] = projectedLonLat.y;
  543. projectedValues[index++] = projectedLonLat.z;
  544. }
  545. // Rename original cartesians to WGS84 cartesians.
  546. geometry.attributes[attributeName3D] = attribute;
  547. // Replace original cartesians with 2D projected cartesians
  548. geometry.attributes[attributeName2D] = new GeometryAttribute({
  549. componentDatatype : ComponentDatatype.DOUBLE,
  550. componentsPerAttribute : 3,
  551. values : projectedValues
  552. });
  553. delete geometry.attributes[attributeName];
  554. return geometry;
  555. };
  556. var encodedResult = {
  557. high : 0.0,
  558. low : 0.0
  559. };
  560. /**
  561. * Encodes floating-point geometry attribute values as two separate attributes to improve
  562. * rendering precision.
  563. * <p>
  564. * This is commonly used to create high-precision position vertex attributes.
  565. * </p>
  566. *
  567. * @param {Geometry} geometry The geometry to modify.
  568. * @param {String} attributeName The name of the attribute.
  569. * @param {String} attributeHighName The name of the attribute for the encoded high bits.
  570. * @param {String} attributeLowName The name of the attribute for the encoded low bits.
  571. * @returns {Geometry} The modified <code>geometry</code> argument, with its encoded attribute.
  572. *
  573. * @exception {DeveloperError} geometry must have attribute matching the attributeName argument.
  574. * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE.
  575. *
  576. * @example
  577. * geometry = Cesium.GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow');
  578. */
  579. GeometryPipeline.encodeAttribute = function(geometry, attributeName, attributeHighName, attributeLowName) {
  580. //>>includeStart('debug', pragmas.debug);
  581. if (!defined(geometry)) {
  582. throw new DeveloperError('geometry is required.');
  583. }
  584. if (!defined(attributeName)) {
  585. throw new DeveloperError('attributeName is required.');
  586. }
  587. if (!defined(attributeHighName)) {
  588. throw new DeveloperError('attributeHighName is required.');
  589. }
  590. if (!defined(attributeLowName)) {
  591. throw new DeveloperError('attributeLowName is required.');
  592. }
  593. if (!defined(geometry.attributes[attributeName])) {
  594. throw new DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.');
  595. }
  596. if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.DOUBLE) {
  597. throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.');
  598. }
  599. //>>includeEnd('debug');
  600. var attribute = geometry.attributes[attributeName];
  601. var values = attribute.values;
  602. var length = values.length;
  603. var highValues = new Float32Array(length);
  604. var lowValues = new Float32Array(length);
  605. for (var i = 0; i < length; ++i) {
  606. EncodedCartesian3.encode(values[i], encodedResult);
  607. highValues[i] = encodedResult.high;
  608. lowValues[i] = encodedResult.low;
  609. }
  610. var componentsPerAttribute = attribute.componentsPerAttribute;
  611. geometry.attributes[attributeHighName] = new GeometryAttribute({
  612. componentDatatype : ComponentDatatype.FLOAT,
  613. componentsPerAttribute : componentsPerAttribute,
  614. values : highValues
  615. });
  616. geometry.attributes[attributeLowName] = new GeometryAttribute({
  617. componentDatatype : ComponentDatatype.FLOAT,
  618. componentsPerAttribute : componentsPerAttribute,
  619. values : lowValues
  620. });
  621. delete geometry.attributes[attributeName];
  622. return geometry;
  623. };
  624. var scratchCartesian3 = new Cartesian3();
  625. function transformPoint(matrix, attribute) {
  626. if (defined(attribute)) {
  627. var values = attribute.values;
  628. var length = values.length;
  629. for (var i = 0; i < length; i += 3) {
  630. Cartesian3.unpack(values, i, scratchCartesian3);
  631. Matrix4.multiplyByPoint(matrix, scratchCartesian3, scratchCartesian3);
  632. Cartesian3.pack(scratchCartesian3, values, i);
  633. }
  634. }
  635. }
  636. function transformVector(matrix, attribute) {
  637. if (defined(attribute)) {
  638. var values = attribute.values;
  639. var length = values.length;
  640. for (var i = 0; i < length; i += 3) {
  641. Cartesian3.unpack(values, i, scratchCartesian3);
  642. Matrix3.multiplyByVector(matrix, scratchCartesian3, scratchCartesian3);
  643. scratchCartesian3 = Cartesian3.normalize(scratchCartesian3, scratchCartesian3);
  644. Cartesian3.pack(scratchCartesian3, values, i);
  645. }
  646. }
  647. }
  648. var inverseTranspose = new Matrix4();
  649. var normalMatrix = new Matrix3();
  650. /**
  651. * Transforms a geometry instance to world coordinates. This changes
  652. * the instance's <code>modelMatrix</code> to {@link Matrix4.IDENTITY} and transforms the
  653. * following attributes if they are present: <code>position</code>, <code>normal</code>,
  654. * <code>tangent</code>, and <code>bitangent</code>.
  655. *
  656. * @param {GeometryInstance} instance The geometry instance to modify.
  657. * @returns {GeometryInstance} The modified <code>instance</code> argument, with its attributes transforms to world coordinates.
  658. *
  659. * @example
  660. * Cesium.GeometryPipeline.transformToWorldCoordinates(instance);
  661. */
  662. GeometryPipeline.transformToWorldCoordinates = function(instance) {
  663. //>>includeStart('debug', pragmas.debug);
  664. if (!defined(instance)) {
  665. throw new DeveloperError('instance is required.');
  666. }
  667. //>>includeEnd('debug');
  668. var modelMatrix = instance.modelMatrix;
  669. if (Matrix4.equals(modelMatrix, Matrix4.IDENTITY)) {
  670. // Already in world coordinates
  671. return instance;
  672. }
  673. var attributes = instance.geometry.attributes;
  674. // Transform attributes in known vertex formats
  675. transformPoint(modelMatrix, attributes.position);
  676. transformPoint(modelMatrix, attributes.prevPosition);
  677. transformPoint(modelMatrix, attributes.nextPosition);
  678. if ((defined(attributes.normal)) ||
  679. (defined(attributes.tangent)) ||
  680. (defined(attributes.bitangent))) {
  681. Matrix4.inverse(modelMatrix, inverseTranspose);
  682. Matrix4.transpose(inverseTranspose, inverseTranspose);
  683. Matrix4.getMatrix3(inverseTranspose, normalMatrix);
  684. transformVector(normalMatrix, attributes.normal);
  685. transformVector(normalMatrix, attributes.tangent);
  686. transformVector(normalMatrix, attributes.bitangent);
  687. }
  688. var boundingSphere = instance.geometry.boundingSphere;
  689. if (defined(boundingSphere)) {
  690. instance.geometry.boundingSphere = BoundingSphere.transform(boundingSphere, modelMatrix, boundingSphere);
  691. }
  692. instance.modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  693. return instance;
  694. };
  695. function findAttributesInAllGeometries(instances, propertyName) {
  696. var length = instances.length;
  697. var attributesInAllGeometries = {};
  698. var attributes0 = instances[0][propertyName].attributes;
  699. var name;
  700. for (name in attributes0) {
  701. if (attributes0.hasOwnProperty(name) &&
  702. defined(attributes0[name]) &&
  703. defined(attributes0[name].values)) {
  704. var attribute = attributes0[name];
  705. var numberOfComponents = attribute.values.length;
  706. var inAllGeometries = true;
  707. // Does this same attribute exist in all geometries?
  708. for (var i = 1; i < length; ++i) {
  709. var otherAttribute = instances[i][propertyName].attributes[name];
  710. if ((!defined(otherAttribute)) ||
  711. (attribute.componentDatatype !== otherAttribute.componentDatatype) ||
  712. (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) ||
  713. (attribute.normalize !== otherAttribute.normalize)) {
  714. inAllGeometries = false;
  715. break;
  716. }
  717. numberOfComponents += otherAttribute.values.length;
  718. }
  719. if (inAllGeometries) {
  720. attributesInAllGeometries[name] = new GeometryAttribute({
  721. componentDatatype : attribute.componentDatatype,
  722. componentsPerAttribute : attribute.componentsPerAttribute,
  723. normalize : attribute.normalize,
  724. values : ComponentDatatype.createTypedArray(attribute.componentDatatype, numberOfComponents)
  725. });
  726. }
  727. }
  728. }
  729. return attributesInAllGeometries;
  730. }
  731. var tempScratch = new Cartesian3();
  732. function combineGeometries(instances, propertyName) {
  733. var length = instances.length;
  734. var name;
  735. var i;
  736. var j;
  737. var k;
  738. var m = instances[0].modelMatrix;
  739. var haveIndices = (defined(instances[0][propertyName].indices));
  740. var primitiveType = instances[0][propertyName].primitiveType;
  741. //>>includeStart('debug', pragmas.debug);
  742. for (i = 1; i < length; ++i) {
  743. if (!Matrix4.equals(instances[i].modelMatrix, m)) {
  744. throw new DeveloperError('All instances must have the same modelMatrix.');
  745. }
  746. if ((defined(instances[i][propertyName].indices)) !== haveIndices) {
  747. throw new DeveloperError('All instance geometries must have an indices or not have one.');
  748. }
  749. if (instances[i][propertyName].primitiveType !== primitiveType) {
  750. throw new DeveloperError('All instance geometries must have the same primitiveType.');
  751. }
  752. }
  753. //>>includeEnd('debug');
  754. // Find subset of attributes in all geometries
  755. var attributes = findAttributesInAllGeometries(instances, propertyName);
  756. var values;
  757. var sourceValues;
  758. var sourceValuesLength;
  759. // Combine attributes from each geometry into a single typed array
  760. for (name in attributes) {
  761. if (attributes.hasOwnProperty(name)) {
  762. values = attributes[name].values;
  763. k = 0;
  764. for (i = 0; i < length; ++i) {
  765. sourceValues = instances[i][propertyName].attributes[name].values;
  766. sourceValuesLength = sourceValues.length;
  767. for (j = 0; j < sourceValuesLength; ++j) {
  768. values[k++] = sourceValues[j];
  769. }
  770. }
  771. }
  772. }
  773. // Combine index lists
  774. var indices;
  775. if (haveIndices) {
  776. var numberOfIndices = 0;
  777. for (i = 0; i < length; ++i) {
  778. numberOfIndices += instances[i][propertyName].indices.length;
  779. }
  780. var numberOfVertices = Geometry.computeNumberOfVertices(new Geometry({
  781. attributes : attributes,
  782. primitiveType : PrimitiveType.POINTS
  783. }));
  784. var destIndices = IndexDatatype.createTypedArray(numberOfVertices, numberOfIndices);
  785. var destOffset = 0;
  786. var offset = 0;
  787. for (i = 0; i < length; ++i) {
  788. var sourceIndices = instances[i][propertyName].indices;
  789. var sourceIndicesLen = sourceIndices.length;
  790. for (k = 0; k < sourceIndicesLen; ++k) {
  791. destIndices[destOffset++] = offset + sourceIndices[k];
  792. }
  793. offset += Geometry.computeNumberOfVertices(instances[i][propertyName]);
  794. }
  795. indices = destIndices;
  796. }
  797. // Create bounding sphere that includes all instances
  798. var center = new Cartesian3();
  799. var radius = 0.0;
  800. var bs;
  801. for (i = 0; i < length; ++i) {
  802. bs = instances[i][propertyName].boundingSphere;
  803. if (!defined(bs)) {
  804. // If any geometries have an undefined bounding sphere, then so does the combined geometry
  805. center = undefined;
  806. break;
  807. }
  808. Cartesian3.add(bs.center, center, center);
  809. }
  810. if (defined(center)) {
  811. Cartesian3.divideByScalar(center, length, center);
  812. for (i = 0; i < length; ++i) {
  813. bs = instances[i][propertyName].boundingSphere;
  814. var tempRadius = Cartesian3.magnitude(Cartesian3.subtract(bs.center, center, tempScratch)) + bs.radius;
  815. if (tempRadius > radius) {
  816. radius = tempRadius;
  817. }
  818. }
  819. }
  820. return new Geometry({
  821. attributes : attributes,
  822. indices : indices,
  823. primitiveType : primitiveType,
  824. boundingSphere : (defined(center)) ? new BoundingSphere(center, radius) : undefined
  825. });
  826. }
  827. /**
  828. * Combines geometry from several {@link GeometryInstance} objects into one geometry.
  829. * This concatenates the attributes, concatenates and adjusts the indices, and creates
  830. * a bounding sphere encompassing all instances.
  831. * <p>
  832. * If the instances do not have the same attributes, a subset of attributes common
  833. * to all instances is used, and the others are ignored.
  834. * </p>
  835. * <p>
  836. * This is used by {@link Primitive} to efficiently render a large amount of static data.
  837. * </p>
  838. *
  839. * @private
  840. *
  841. * @param {GeometryInstance[]} [instances] The array of {@link GeometryInstance} objects whose geometry will be combined.
  842. * @returns {Geometry} A single geometry created from the provided geometry instances.
  843. *
  844. * @exception {DeveloperError} All instances must have the same modelMatrix.
  845. * @exception {DeveloperError} All instance geometries must have an indices or not have one.
  846. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  847. *
  848. *
  849. * @example
  850. * for (var i = 0; i < instances.length; ++i) {
  851. * Cesium.GeometryPipeline.transformToWorldCoordinates(instances[i]);
  852. * }
  853. * var geometries = Cesium.GeometryPipeline.combineInstances(instances);
  854. *
  855. * @see GeometryPipeline.transformToWorldCoordinates
  856. */
  857. GeometryPipeline.combineInstances = function(instances) {
  858. //>>includeStart('debug', pragmas.debug);
  859. if ((!defined(instances)) || (instances.length < 1)) {
  860. throw new DeveloperError('instances is required and must have length greater than zero.');
  861. }
  862. //>>includeEnd('debug');
  863. var instanceGeometry = [];
  864. var instanceSplitGeometry = [];
  865. var length = instances.length;
  866. for (var i = 0; i < length; ++i) {
  867. var instance = instances[i];
  868. if (defined(instance.geometry)) {
  869. instanceGeometry.push(instance);
  870. } else if (defined(instance.westHemisphereGeometry) && defined(instance.eastHemisphereGeometry)) {
  871. instanceSplitGeometry.push(instance);
  872. }
  873. }
  874. var geometries = [];
  875. if (instanceGeometry.length > 0) {
  876. geometries.push(combineGeometries(instanceGeometry, 'geometry'));
  877. }
  878. if (instanceSplitGeometry.length > 0) {
  879. geometries.push(combineGeometries(instanceSplitGeometry, 'westHemisphereGeometry'));
  880. geometries.push(combineGeometries(instanceSplitGeometry, 'eastHemisphereGeometry'));
  881. }
  882. return geometries;
  883. };
  884. var normal = new Cartesian3();
  885. var v0 = new Cartesian3();
  886. var v1 = new Cartesian3();
  887. var v2 = new Cartesian3();
  888. /**
  889. * Computes per-vertex normals for a geometry containing <code>TRIANGLES</code> by averaging the normals of
  890. * all triangles incident to the vertex. The result is a new <code>normal</code> attribute added to the geometry.
  891. * This assumes a counter-clockwise winding order.
  892. *
  893. * @param {Geometry} geometry The geometry to modify.
  894. * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>normal</code> attribute.
  895. *
  896. * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
  897. * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
  898. *
  899. * @example
  900. * Cesium.GeometryPipeline.computeNormal(geometry);
  901. */
  902. GeometryPipeline.computeNormal = function(geometry) {
  903. //>>includeStart('debug', pragmas.debug);
  904. if (!defined(geometry)) {
  905. throw new DeveloperError('geometry is required.');
  906. }
  907. if (!defined(geometry.attributes.position) || !defined(geometry.attributes.position.values)) {
  908. throw new DeveloperError('geometry.attributes.position.values is required.');
  909. }
  910. if (!defined(geometry.indices)) {
  911. throw new DeveloperError('geometry.indices is required.');
  912. }
  913. if (geometry.indices.length < 2 || geometry.indices.length % 3 !== 0) {
  914. throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
  915. }
  916. if (geometry.primitiveType !== PrimitiveType.TRIANGLES) {
  917. throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
  918. }
  919. //>>includeEnd('debug');
  920. var indices = geometry.indices;
  921. var attributes = geometry.attributes;
  922. var vertices = attributes.position.values;
  923. var numVertices = attributes.position.values.length / 3;
  924. var numIndices = indices.length;
  925. var normalsPerVertex = new Array(numVertices);
  926. var normalsPerTriangle = new Array(numIndices / 3);
  927. var normalIndices = new Array(numIndices);
  928. var i;
  929. for ( i = 0; i < numVertices; i++) {
  930. normalsPerVertex[i] = {
  931. indexOffset : 0,
  932. count : 0,
  933. currentCount : 0
  934. };
  935. }
  936. var j = 0;
  937. for (i = 0; i < numIndices; i += 3) {
  938. var i0 = indices[i];
  939. var i1 = indices[i + 1];
  940. var i2 = indices[i + 2];
  941. var i03 = i0 * 3;
  942. var i13 = i1 * 3;
  943. var i23 = i2 * 3;
  944. v0.x = vertices[i03];
  945. v0.y = vertices[i03 + 1];
  946. v0.z = vertices[i03 + 2];
  947. v1.x = vertices[i13];
  948. v1.y = vertices[i13 + 1];
  949. v1.z = vertices[i13 + 2];
  950. v2.x = vertices[i23];
  951. v2.y = vertices[i23 + 1];
  952. v2.z = vertices[i23 + 2];
  953. normalsPerVertex[i0].count++;
  954. normalsPerVertex[i1].count++;
  955. normalsPerVertex[i2].count++;
  956. Cartesian3.subtract(v1, v0, v1);
  957. Cartesian3.subtract(v2, v0, v2);
  958. normalsPerTriangle[j] = Cartesian3.cross(v1, v2, new Cartesian3());
  959. j++;
  960. }
  961. var indexOffset = 0;
  962. for (i = 0; i < numVertices; i++) {
  963. normalsPerVertex[i].indexOffset += indexOffset;
  964. indexOffset += normalsPerVertex[i].count;
  965. }
  966. j = 0;
  967. var vertexNormalData;
  968. for (i = 0; i < numIndices; i += 3) {
  969. vertexNormalData = normalsPerVertex[indices[i]];
  970. var index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
  971. normalIndices[index] = j;
  972. vertexNormalData.currentCount++;
  973. vertexNormalData = normalsPerVertex[indices[i + 1]];
  974. index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
  975. normalIndices[index] = j;
  976. vertexNormalData.currentCount++;
  977. vertexNormalData = normalsPerVertex[indices[i + 2]];
  978. index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
  979. normalIndices[index] = j;
  980. vertexNormalData.currentCount++;
  981. j++;
  982. }
  983. var normalValues = new Float32Array(numVertices * 3);
  984. for (i = 0; i < numVertices; i++) {
  985. var i3 = i * 3;
  986. vertexNormalData = normalsPerVertex[i];
  987. Cartesian3.clone(Cartesian3.ZERO, normal);
  988. if (vertexNormalData.count > 0) {
  989. for (j = 0; j < vertexNormalData.count; j++) {
  990. Cartesian3.add(normal, normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal);
  991. }
  992. // We can run into an issue where a vertex is used with 2 primitives that have opposite winding order.
  993. if (Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10)) {
  994. Cartesian3.clone(normalsPerTriangle[normalIndices[vertexNormalData.indexOffset]], normal);
  995. }
  996. }
  997. // We end up with a zero vector probably because of a degenerate triangle
  998. if (Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10)) {
  999. // Default to (0,0,1)
  1000. normal.z = 1.0;
  1001. }
  1002. Cartesian3.normalize(normal, normal);
  1003. normalValues[i3] = normal.x;
  1004. normalValues[i3 + 1] = normal.y;
  1005. normalValues[i3 + 2] = normal.z;
  1006. }
  1007. geometry.attributes.normal = new GeometryAttribute({
  1008. componentDatatype : ComponentDatatype.FLOAT,
  1009. componentsPerAttribute : 3,
  1010. values : normalValues
  1011. });
  1012. return geometry;
  1013. };
  1014. var normalScratch = new Cartesian3();
  1015. var normalScale = new Cartesian3();
  1016. var tScratch = new Cartesian3();
  1017. /**
  1018. * Computes per-vertex tangents and bitangents for a geometry containing <code>TRIANGLES</code>.
  1019. * The result is new <code>tangent</code> and <code>bitangent</code> attributes added to the geometry.
  1020. * This assumes a counter-clockwise winding order.
  1021. * <p>
  1022. * Based on <a href="http://www.terathon.com/code/tangent.html">Computing Tangent Space Basis Vectors
  1023. * for an Arbitrary Mesh</a> by Eric Lengyel.
  1024. * </p>
  1025. *
  1026. * @param {Geometry} geometry The geometry to modify.
  1027. * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>tangent</code> and <code>bitangent</code> attributes.
  1028. *
  1029. * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
  1030. * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
  1031. *
  1032. * @example
  1033. * Cesium.GeometryPipeline.computeTangentAndBiTangent(geometry);
  1034. */
  1035. GeometryPipeline.computeTangentAndBitangent = function(geometry) {
  1036. //>>includeStart('debug', pragmas.debug);
  1037. if (!defined(geometry)) {
  1038. throw new DeveloperError('geometry is required.');
  1039. }
  1040. //>>includeEnd('debug');
  1041. var attributes = geometry.attributes;
  1042. var indices = geometry.indices;
  1043. //>>includeStart('debug', pragmas.debug);
  1044. if (!defined(attributes.position) || !defined(attributes.position.values)) {
  1045. throw new DeveloperError('geometry.attributes.position.values is required.');
  1046. }
  1047. if (!defined(attributes.normal) || !defined(attributes.normal.values)) {
  1048. throw new DeveloperError('geometry.attributes.normal.values is required.');
  1049. }
  1050. if (!defined(attributes.st) || !defined(attributes.st.values)) {
  1051. throw new DeveloperError('geometry.attributes.st.values is required.');
  1052. }
  1053. if (!defined(indices)) {
  1054. throw new DeveloperError('geometry.indices is required.');
  1055. }
  1056. if (indices.length < 2 || indices.length % 3 !== 0) {
  1057. throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
  1058. }
  1059. if (geometry.primitiveType !== PrimitiveType.TRIANGLES) {
  1060. throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
  1061. }
  1062. //>>includeEnd('debug');
  1063. var vertices = geometry.attributes.position.values;
  1064. var normals = geometry.attributes.normal.values;
  1065. var st = geometry.attributes.st.values;
  1066. var numVertices = geometry.attributes.position.values.length / 3;
  1067. var numIndices = indices.length;
  1068. var tan1 = new Array(numVertices * 3);
  1069. var i;
  1070. for ( i = 0; i < tan1.length; i++) {
  1071. tan1[i] = 0;
  1072. }
  1073. var i03;
  1074. var i13;
  1075. var i23;
  1076. for (i = 0; i < numIndices; i += 3) {
  1077. var i0 = indices[i];
  1078. var i1 = indices[i + 1];
  1079. var i2 = indices[i + 2];
  1080. i03 = i0 * 3;
  1081. i13 = i1 * 3;
  1082. i23 = i2 * 3;
  1083. var i02 = i0 * 2;
  1084. var i12 = i1 * 2;
  1085. var i22 = i2 * 2;
  1086. var ux = vertices[i03];
  1087. var uy = vertices[i03 + 1];
  1088. var uz = vertices[i03 + 2];
  1089. var wx = st[i02];
  1090. var wy = st[i02 + 1];
  1091. var t1 = st[i12 + 1] - wy;
  1092. var t2 = st[i22 + 1] - wy;
  1093. var r = 1.0 / ((st[i12] - wx) * t2 - (st[i22] - wx) * t1);
  1094. var sdirx = (t2 * (vertices[i13] - ux) - t1 * (vertices[i23] - ux)) * r;
  1095. var sdiry = (t2 * (vertices[i13 + 1] - uy) - t1 * (vertices[i23 + 1] - uy)) * r;
  1096. var sdirz = (t2 * (vertices[i13 + 2] - uz) - t1 * (vertices[i23 + 2] - uz)) * r;
  1097. tan1[i03] += sdirx;
  1098. tan1[i03 + 1] += sdiry;
  1099. tan1[i03 + 2] += sdirz;
  1100. tan1[i13] += sdirx;
  1101. tan1[i13 + 1] += sdiry;
  1102. tan1[i13 + 2] += sdirz;
  1103. tan1[i23] += sdirx;
  1104. tan1[i23 + 1] += sdiry;
  1105. tan1[i23 + 2] += sdirz;
  1106. }
  1107. var tangentValues = new Float32Array(numVertices * 3);
  1108. var bitangentValues = new Float32Array(numVertices * 3);
  1109. for (i = 0; i < numVertices; i++) {
  1110. i03 = i * 3;
  1111. i13 = i03 + 1;
  1112. i23 = i03 + 2;
  1113. var n = Cartesian3.fromArray(normals, i03, normalScratch);
  1114. var t = Cartesian3.fromArray(tan1, i03, tScratch);
  1115. var scalar = Cartesian3.dot(n, t);
  1116. Cartesian3.multiplyByScalar(n, scalar, normalScale);
  1117. Cartesian3.normalize(Cartesian3.subtract(t, normalScale, t), t);
  1118. tangentValues[i03] = t.x;
  1119. tangentValues[i13] = t.y;
  1120. tangentValues[i23] = t.z;
  1121. Cartesian3.normalize(Cartesian3.cross(n, t, t), t);
  1122. bitangentValues[i03] = t.x;
  1123. bitangentValues[i13] = t.y;
  1124. bitangentValues[i23] = t.z;
  1125. }
  1126. geometry.attributes.tangent = new GeometryAttribute({
  1127. componentDatatype : ComponentDatatype.FLOAT,
  1128. componentsPerAttribute : 3,
  1129. values : tangentValues
  1130. });
  1131. geometry.attributes.bitangent = new GeometryAttribute({
  1132. componentDatatype : ComponentDatatype.FLOAT,
  1133. componentsPerAttribute : 3,
  1134. values : bitangentValues
  1135. });
  1136. return geometry;
  1137. };
  1138. var scratchCartesian2 = new Cartesian2();
  1139. var toEncode1 = new Cartesian3();
  1140. var toEncode2 = new Cartesian3();
  1141. var toEncode3 = new Cartesian3();
  1142. var encodeResult2 = new Cartesian2();
  1143. /**
  1144. * Compresses and packs geometry normal attribute values to save memory.
  1145. *
  1146. * @param {Geometry} geometry The geometry to modify.
  1147. * @returns {Geometry} The modified <code>geometry</code> argument, with its normals compressed and packed.
  1148. *
  1149. * @example
  1150. * geometry = Cesium.GeometryPipeline.compressVertices(geometry);
  1151. */
  1152. GeometryPipeline.compressVertices = function(geometry) {
  1153. //>>includeStart('debug', pragmas.debug);
  1154. if (!defined(geometry)) {
  1155. throw new DeveloperError('geometry is required.');
  1156. }
  1157. //>>includeEnd('debug');
  1158. var extrudeAttribute = geometry.attributes.extrudeDirection;
  1159. var i;
  1160. var numVertices;
  1161. if (defined(extrudeAttribute)) {
  1162. //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes
  1163. var extrudeDirections = extrudeAttribute.values;
  1164. numVertices = extrudeDirections.length / 3.0;
  1165. var compressedDirections = new Float32Array(numVertices * 2);
  1166. var i2 = 0;
  1167. for (i = 0; i < numVertices; ++i) {
  1168. Cartesian3.fromArray(extrudeDirections, i * 3.0, toEncode1);
  1169. if (Cartesian3.equals(toEncode1, Cartesian3.ZERO)) {
  1170. i2 += 2;
  1171. continue;
  1172. }
  1173. encodeResult2 = AttributeCompression.octEncodeInRange(toEncode1, 65535, encodeResult2);
  1174. compressedDirections[i2++] = encodeResult2.x;
  1175. compressedDirections[i2++] = encodeResult2.y;
  1176. }
  1177. geometry.attributes.compressedAttributes = new GeometryAttribute({
  1178. componentDatatype : ComponentDatatype.FLOAT,
  1179. componentsPerAttribute : 2,
  1180. values : compressedDirections
  1181. });
  1182. delete geometry.attributes.extrudeDirection;
  1183. return geometry;
  1184. }
  1185. var normalAttribute = geometry.attributes.normal;
  1186. var stAttribute = geometry.attributes.st;
  1187. var hasNormal = defined(normalAttribute);
  1188. var hasSt = defined(stAttribute);
  1189. if (!hasNormal && !hasSt) {
  1190. return geometry;
  1191. }
  1192. var tangentAttribute = geometry.attributes.tangent;
  1193. var bitangentAttribute = geometry.attributes.bitangent;
  1194. var hasTangent = defined(tangentAttribute);
  1195. var hasBitangent = defined(bitangentAttribute);
  1196. var normals;
  1197. var st;
  1198. var tangents;
  1199. var bitangents;
  1200. if (hasNormal) {
  1201. normals = normalAttribute.values;
  1202. }
  1203. if (hasSt) {
  1204. st = stAttribute.values;
  1205. }
  1206. if (hasTangent) {
  1207. tangents = tangentAttribute.values;
  1208. }
  1209. if (hasBitangent) {
  1210. bitangents = bitangentAttribute.values;
  1211. }
  1212. var length = hasNormal ? normals.length : st.length;
  1213. var numComponents = hasNormal ? 3.0 : 2.0;
  1214. numVertices = length / numComponents;
  1215. var compressedLength = numVertices;
  1216. var numCompressedComponents = hasSt && hasNormal ? 2.0 : 1.0;
  1217. numCompressedComponents += hasTangent || hasBitangent ? 1.0 : 0.0;
  1218. compressedLength *= numCompressedComponents;
  1219. var compressedAttributes = new Float32Array(compressedLength);
  1220. var normalIndex = 0;
  1221. for (i = 0; i < numVertices; ++i) {
  1222. if (hasSt) {
  1223. Cartesian2.fromArray(st, i * 2.0, scratchCartesian2);
  1224. compressedAttributes[normalIndex++] = AttributeCompression.compressTextureCoordinates(scratchCartesian2);
  1225. }
  1226. var index = i * 3.0;
  1227. if (hasNormal && defined(tangents) && defined(bitangents)) {
  1228. Cartesian3.fromArray(normals, index, toEncode1);
  1229. Cartesian3.fromArray(tangents, index, toEncode2);
  1230. Cartesian3.fromArray(bitangents, index, toEncode3);
  1231. AttributeCompression.octPack(toEncode1, toEncode2, toEncode3, scratchCartesian2);
  1232. compressedAttributes[normalIndex++] = scratchCartesian2.x;
  1233. compressedAttributes[normalIndex++] = scratchCartesian2.y;
  1234. } else {
  1235. if (hasNormal) {
  1236. Cartesian3.fromArray(normals, index, toEncode1);
  1237. compressedAttributes[normalIndex++] = AttributeCompression.octEncodeFloat(toEncode1);
  1238. }
  1239. if (hasTangent) {
  1240. Cartesian3.fromArray(tangents, index, toEncode1);
  1241. compressedAttributes[normalIndex++] = AttributeCompression.octEncodeFloat(toEncode1);
  1242. }
  1243. if (hasBitangent) {
  1244. Cartesian3.fromArray(bitangents, index, toEncode1);
  1245. compressedAttributes[normalIndex++] = AttributeCompression.octEncodeFloat(toEncode1);
  1246. }
  1247. }
  1248. }
  1249. geometry.attributes.compressedAttributes = new GeometryAttribute({
  1250. componentDatatype : ComponentDatatype.FLOAT,
  1251. componentsPerAttribute : numCompressedComponents,
  1252. values : compressedAttributes
  1253. });
  1254. if (hasNormal) {
  1255. delete geometry.attributes.normal;
  1256. }
  1257. if (hasSt) {
  1258. delete geometry.attributes.st;
  1259. }
  1260. if (hasBitangent) {
  1261. delete geometry.attributes.bitangent;
  1262. }
  1263. if (hasTangent) {
  1264. delete geometry.attributes.tangent;
  1265. }
  1266. return geometry;
  1267. };
  1268. function indexTriangles(geometry) {
  1269. if (defined(geometry.indices)) {
  1270. return geometry;
  1271. }
  1272. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1273. //>>includeStart('debug', pragmas.debug);
  1274. if (numberOfVertices < 3) {
  1275. throw new DeveloperError('The number of vertices must be at least three.');
  1276. }
  1277. if (numberOfVertices % 3 !== 0) {
  1278. throw new DeveloperError('The number of vertices must be a multiple of three.');
  1279. }
  1280. //>>includeEnd('debug');
  1281. var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
  1282. for (var i = 0; i < numberOfVertices; ++i) {
  1283. indices[i] = i;
  1284. }
  1285. geometry.indices = indices;
  1286. return geometry;
  1287. }
  1288. function indexTriangleFan(geometry) {
  1289. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1290. //>>includeStart('debug', pragmas.debug);
  1291. if (numberOfVertices < 3) {
  1292. throw new DeveloperError('The number of vertices must be at least three.');
  1293. }
  1294. //>>includeEnd('debug');
  1295. var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
  1296. indices[0] = 1;
  1297. indices[1] = 0;
  1298. indices[2] = 2;
  1299. var indicesIndex = 3;
  1300. for (var i = 3; i < numberOfVertices; ++i) {
  1301. indices[indicesIndex++] = i - 1;
  1302. indices[indicesIndex++] = 0;
  1303. indices[indicesIndex++] = i;
  1304. }
  1305. geometry.indices = indices;
  1306. geometry.primitiveType = PrimitiveType.TRIANGLES;
  1307. return geometry;
  1308. }
  1309. function indexTriangleStrip(geometry) {
  1310. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1311. //>>includeStart('debug', pragmas.debug);
  1312. if (numberOfVertices < 3) {
  1313. throw new DeveloperError('The number of vertices must be at least 3.');
  1314. }
  1315. //>>includeEnd('debug');
  1316. var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
  1317. indices[0] = 0;
  1318. indices[1] = 1;
  1319. indices[2] = 2;
  1320. if (numberOfVertices > 3) {
  1321. indices[3] = 0;
  1322. indices[4] = 2;
  1323. indices[5] = 3;
  1324. }
  1325. var indicesIndex = 6;
  1326. for (var i = 3; i < numberOfVertices - 1; i += 2) {
  1327. indices[indicesIndex++] = i;
  1328. indices[indicesIndex++] = i - 1;
  1329. indices[indicesIndex++] = i + 1;
  1330. if (i + 2 < numberOfVertices) {
  1331. indices[indicesIndex++] = i;
  1332. indices[indicesIndex++] = i + 1;
  1333. indices[indicesIndex++] = i + 2;
  1334. }
  1335. }
  1336. geometry.indices = indices;
  1337. geometry.primitiveType = PrimitiveType.TRIANGLES;
  1338. return geometry;
  1339. }
  1340. function indexLines(geometry) {
  1341. if (defined(geometry.indices)) {
  1342. return geometry;
  1343. }
  1344. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1345. //>>includeStart('debug', pragmas.debug);
  1346. if (numberOfVertices < 2) {
  1347. throw new DeveloperError('The number of vertices must be at least two.');
  1348. }
  1349. if (numberOfVertices % 2 !== 0) {
  1350. throw new DeveloperError('The number of vertices must be a multiple of 2.');
  1351. }
  1352. //>>includeEnd('debug');
  1353. var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
  1354. for (var i = 0; i < numberOfVertices; ++i) {
  1355. indices[i] = i;
  1356. }
  1357. geometry.indices = indices;
  1358. return geometry;
  1359. }
  1360. function indexLineStrip(geometry) {
  1361. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1362. //>>includeStart('debug', pragmas.debug);
  1363. if (numberOfVertices < 2) {
  1364. throw new DeveloperError('The number of vertices must be at least two.');
  1365. }
  1366. //>>includeEnd('debug');
  1367. var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 1) * 2);
  1368. indices[0] = 0;
  1369. indices[1] = 1;
  1370. var indicesIndex = 2;
  1371. for (var i = 2; i < numberOfVertices; ++i) {
  1372. indices[indicesIndex++] = i - 1;
  1373. indices[indicesIndex++] = i;
  1374. }
  1375. geometry.indices = indices;
  1376. geometry.primitiveType = PrimitiveType.LINES;
  1377. return geometry;
  1378. }
  1379. function indexLineLoop(geometry) {
  1380. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1381. //>>includeStart('debug', pragmas.debug);
  1382. if (numberOfVertices < 2) {
  1383. throw new DeveloperError('The number of vertices must be at least two.');
  1384. }
  1385. //>>includeEnd('debug');
  1386. var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices * 2);
  1387. indices[0] = 0;
  1388. indices[1] = 1;
  1389. var indicesIndex = 2;
  1390. for (var i = 2; i < numberOfVertices; ++i) {
  1391. indices[indicesIndex++] = i - 1;
  1392. indices[indicesIndex++] = i;
  1393. }
  1394. indices[indicesIndex++] = numberOfVertices - 1;
  1395. indices[indicesIndex] = 0;
  1396. geometry.indices = indices;
  1397. geometry.primitiveType = PrimitiveType.LINES;
  1398. return geometry;
  1399. }
  1400. function indexPrimitive(geometry) {
  1401. switch (geometry.primitiveType) {
  1402. case PrimitiveType.TRIANGLE_FAN:
  1403. return indexTriangleFan(geometry);
  1404. case PrimitiveType.TRIANGLE_STRIP:
  1405. return indexTriangleStrip(geometry);
  1406. case PrimitiveType.TRIANGLES:
  1407. return indexTriangles(geometry);
  1408. case PrimitiveType.LINE_STRIP:
  1409. return indexLineStrip(geometry);
  1410. case PrimitiveType.LINE_LOOP:
  1411. return indexLineLoop(geometry);
  1412. case PrimitiveType.LINES:
  1413. return indexLines(geometry);
  1414. }
  1415. return geometry;
  1416. }
  1417. function offsetPointFromXZPlane(p, isBehind) {
  1418. if (Math.abs(p.y) < CesiumMath.EPSILON6){
  1419. if (isBehind) {
  1420. p.y = -CesiumMath.EPSILON6;
  1421. } else {
  1422. p.y = CesiumMath.EPSILON6;
  1423. }
  1424. }
  1425. }
  1426. function offsetTriangleFromXZPlane(p0, p1, p2) {
  1427. if (p0.y !== 0.0 && p1.y !== 0.0 && p2.y !== 0.0) {
  1428. offsetPointFromXZPlane(p0, p0.y < 0.0);
  1429. offsetPointFromXZPlane(p1, p1.y < 0.0);
  1430. offsetPointFromXZPlane(p2, p2.y < 0.0);
  1431. return;
  1432. }
  1433. var p0y = Math.abs(p0.y);
  1434. var p1y = Math.abs(p1.y);
  1435. var p2y = Math.abs(p2.y);
  1436. var sign;
  1437. if (p0y > p1y) {
  1438. if (p0y > p2y) {
  1439. sign = CesiumMath.sign(p0.y);
  1440. } else {
  1441. sign = CesiumMath.sign(p2.y);
  1442. }
  1443. } else if (p1y > p2y) {
  1444. sign = CesiumMath.sign(p1.y);
  1445. } else {
  1446. sign = CesiumMath.sign(p2.y);
  1447. }
  1448. var isBehind = sign < 0.0;
  1449. offsetPointFromXZPlane(p0, isBehind);
  1450. offsetPointFromXZPlane(p1, isBehind);
  1451. offsetPointFromXZPlane(p2, isBehind);
  1452. }
  1453. var c3 = new Cartesian3();
  1454. function getXZIntersectionOffsetPoints(p, p1, u1, v1) {
  1455. Cartesian3.add(p, Cartesian3.multiplyByScalar(Cartesian3.subtract(p1, p, c3), p.y/(p.y-p1.y), c3), u1);
  1456. Cartesian3.clone(u1, v1);
  1457. offsetPointFromXZPlane(u1, true);
  1458. offsetPointFromXZPlane(v1, false);
  1459. }
  1460. var u1 = new Cartesian3();
  1461. var u2 = new Cartesian3();
  1462. var q1 = new Cartesian3();
  1463. var q2 = new Cartesian3();
  1464. var splitTriangleResult = {
  1465. positions : new Array(7),
  1466. indices : new Array(3 * 3)
  1467. };
  1468. function splitTriangle(p0, p1, p2) {
  1469. // In WGS84 coordinates, for a triangle approximately on the
  1470. // ellipsoid to cross the IDL, first it needs to be on the
  1471. // negative side of the plane x = 0.
  1472. if ((p0.x >= 0.0) || (p1.x >= 0.0) || (p2.x >= 0.0)) {
  1473. return undefined;
  1474. }
  1475. offsetTriangleFromXZPlane(p0, p1, p2);
  1476. var p0Behind = p0.y < 0.0;
  1477. var p1Behind = p1.y < 0.0;
  1478. var p2Behind = p2.y < 0.0;
  1479. var numBehind = 0;
  1480. numBehind += p0Behind ? 1 : 0;
  1481. numBehind += p1Behind ? 1 : 0;
  1482. numBehind += p2Behind ? 1 : 0;
  1483. var indices = splitTriangleResult.indices;
  1484. if (numBehind === 1) {
  1485. indices[1] = 3;
  1486. indices[2] = 4;
  1487. indices[5] = 6;
  1488. indices[7] = 6;
  1489. indices[8] = 5;
  1490. if (p0Behind) {
  1491. getXZIntersectionOffsetPoints(p0, p1, u1, q1);
  1492. getXZIntersectionOffsetPoints(p0, p2, u2, q2);
  1493. indices[0] = 0;
  1494. indices[3] = 1;
  1495. indices[4] = 2;
  1496. indices[6] = 1;
  1497. } else if (p1Behind) {
  1498. getXZIntersectionOffsetPoints(p1, p2, u1, q1);
  1499. getXZIntersectionOffsetPoints(p1, p0, u2, q2);
  1500. indices[0] = 1;
  1501. indices[3] = 2;
  1502. indices[4] = 0;
  1503. indices[6] = 2;
  1504. } else if (p2Behind) {
  1505. getXZIntersectionOffsetPoints(p2, p0, u1, q1);
  1506. getXZIntersectionOffsetPoints(p2, p1, u2, q2);
  1507. indices[0] = 2;
  1508. indices[3] = 0;
  1509. indices[4] = 1;
  1510. indices[6] = 0;
  1511. }
  1512. } else if (numBehind === 2) {
  1513. indices[2] = 4;
  1514. indices[4] = 4;
  1515. indices[5] = 3;
  1516. indices[7] = 5;
  1517. indices[8] = 6;
  1518. if (!p0Behind) {
  1519. getXZIntersectionOffsetPoints(p0, p1, u1, q1);
  1520. getXZIntersectionOffsetPoints(p0, p2, u2, q2);
  1521. indices[0] = 1;
  1522. indices[1] = 2;
  1523. indices[3] = 1;
  1524. indices[6] = 0;
  1525. } else if (!p1Behind) {
  1526. getXZIntersectionOffsetPoints(p1, p2, u1, q1);
  1527. getXZIntersectionOffsetPoints(p1, p0, u2, q2);
  1528. indices[0] = 2;
  1529. indices[1] = 0;
  1530. indices[3] = 2;
  1531. indices[6] = 1;
  1532. } else if (!p2Behind) {
  1533. getXZIntersectionOffsetPoints(p2, p0, u1, q1);
  1534. getXZIntersectionOffsetPoints(p2, p1, u2, q2);
  1535. indices[0] = 0;
  1536. indices[1] = 1;
  1537. indices[3] = 0;
  1538. indices[6] = 2;
  1539. }
  1540. }
  1541. var positions = splitTriangleResult.positions;
  1542. positions[0] = p0;
  1543. positions[1] = p1;
  1544. positions[2] = p2;
  1545. positions.length = 3;
  1546. if (numBehind === 1 || numBehind === 2) {
  1547. positions[3] = u1;
  1548. positions[4] = u2;
  1549. positions[5] = q1;
  1550. positions[6] = q2;
  1551. positions.length = 7;
  1552. }
  1553. return splitTriangleResult;
  1554. }
  1555. function updateGeometryAfterSplit(geometry, computeBoundingSphere) {
  1556. var attributes = geometry.attributes;
  1557. if (attributes.position.values.length === 0) {
  1558. return undefined;
  1559. }
  1560. for (var property in attributes) {
  1561. if (attributes.hasOwnProperty(property) &&
  1562. defined(attributes[property]) &&
  1563. defined(attributes[property].values)) {
  1564. var attribute = attributes[property];
  1565. attribute.values = ComponentDatatype.createTypedArray(attribute.componentDatatype, attribute.values);
  1566. }
  1567. }
  1568. var numberOfVertices = Geometry.computeNumberOfVertices(geometry);
  1569. geometry.indices = IndexDatatype.createTypedArray(numberOfVertices, geometry.indices);
  1570. if (computeBoundingSphere) {
  1571. geometry.boundingSphere = BoundingSphere.fromVertices(attributes.position.values);
  1572. }
  1573. return geometry;
  1574. }
  1575. function copyGeometryForSplit(geometry) {
  1576. var attributes = geometry.attributes;
  1577. var copiedAttributes = {};
  1578. for (var property in attributes) {
  1579. if (attributes.hasOwnProperty(property) &&
  1580. defined(attributes[property]) &&
  1581. defined(attributes[property].values)) {
  1582. var attribute = attributes[property];
  1583. copiedAttributes[property] = new GeometryAttribute({
  1584. componentDatatype : attribute.componentDatatype,
  1585. componentsPerAttribute : attribute.componentsPerAttribute,
  1586. normalize : attribute.normalize,
  1587. values : []
  1588. });
  1589. }
  1590. }
  1591. return new Geometry({
  1592. attributes : copiedAttributes,
  1593. indices : [],
  1594. primitiveType : geometry.primitiveType
  1595. });
  1596. }
  1597. function updateInstanceAfterSplit(instance, westGeometry, eastGeometry) {
  1598. var computeBoundingSphere = defined(instance.geometry.boundingSphere);
  1599. westGeometry = updateGeometryAfterSplit(westGeometry, computeBoundingSphere);
  1600. eastGeometry = updateGeometryAfterSplit(eastGeometry, computeBoundingSphere);
  1601. if (defined(eastGeometry) && !defined(westGeometry)) {
  1602. instance.geometry = eastGeometry;
  1603. } else if (!defined(eastGeometry) && defined(westGeometry)) {
  1604. instance.geometry = westGeometry;
  1605. } else {
  1606. instance.westHemisphereGeometry = westGeometry;
  1607. instance.eastHemisphereGeometry = eastGeometry;
  1608. instance.geometry = undefined;
  1609. }
  1610. }
  1611. function generateBarycentricInterpolateFunction(CartesianType, numberOfComponents) {
  1612. var v0Scratch = new CartesianType();
  1613. var v1Scratch = new CartesianType();
  1614. var v2Scratch = new CartesianType();
  1615. return function(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, normalize) {
  1616. var v0 = CartesianType.fromArray(sourceValues, i0 * numberOfComponents, v0Scratch);
  1617. var v1 = CartesianType.fromArray(sourceValues, i1 * numberOfComponents, v1Scratch);
  1618. var v2 = CartesianType.fromArray(sourceValues, i2 * numberOfComponents, v2Scratch);
  1619. CartesianType.multiplyByScalar(v0, coords.x, v0);
  1620. CartesianType.multiplyByScalar(v1, coords.y, v1);
  1621. CartesianType.multiplyByScalar(v2, coords.z, v2);
  1622. var value = CartesianType.add(v0, v1, v0);
  1623. CartesianType.add(value, v2, value);
  1624. if (normalize) {
  1625. CartesianType.normalize(value, value);
  1626. }
  1627. CartesianType.pack(value, currentValues, insertedIndex * numberOfComponents);
  1628. };
  1629. }
  1630. var interpolateAndPackCartesian4 = generateBarycentricInterpolateFunction(Cartesian4, 4);
  1631. var interpolateAndPackCartesian3 = generateBarycentricInterpolateFunction(Cartesian3, 3);
  1632. var interpolateAndPackCartesian2 = generateBarycentricInterpolateFunction(Cartesian2, 2);
  1633. var interpolateAndPackBoolean = function(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex) {
  1634. var v1 = sourceValues[i0] * coords.x;
  1635. var v2 = sourceValues[i1] * coords.y;
  1636. var v3 = sourceValues[i2] * coords.z;
  1637. currentValues[insertedIndex] = (v1 + v2 + v3) > CesiumMath.EPSILON6 ? 1 : 0;
  1638. };
  1639. var p0Scratch = new Cartesian3();
  1640. var p1Scratch = new Cartesian3();
  1641. var p2Scratch = new Cartesian3();
  1642. var barycentricScratch = new Cartesian3();
  1643. function computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, allAttributes, insertedIndex) {
  1644. if (!defined(normals) && !defined(tangents) && !defined(bitangents) && !defined(texCoords) && !defined(extrudeDirections) && customAttributesLength === 0) {
  1645. return;
  1646. }
  1647. var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
  1648. var p1 = Cartesian3.fromArray(positions, i1 * 3, p1Scratch);
  1649. var p2 = Cartesian3.fromArray(positions, i2 * 3, p2Scratch);
  1650. var coords = barycentricCoordinates(point, p0, p1, p2, barycentricScratch);
  1651. if (defined(normals)) {
  1652. interpolateAndPackCartesian3(i0, i1, i2, coords, normals, currentAttributes.normal.values, insertedIndex, true);
  1653. }
  1654. if (defined(extrudeDirections)) {
  1655. var d0 = Cartesian3.fromArray(extrudeDirections, i0 * 3, p0Scratch);
  1656. var d1 = Cartesian3.fromArray(extrudeDirections, i1 * 3, p1Scratch);
  1657. var d2 = Cartesian3.fromArray(extrudeDirections, i2 * 3, p2Scratch);
  1658. Cartesian3.multiplyByScalar(d0, coords.x, d0);
  1659. Cartesian3.multiplyByScalar(d1, coords.y, d1);
  1660. Cartesian3.multiplyByScalar(d2, coords.z, d2);
  1661. var direction;
  1662. if (!Cartesian3.equals(d0, Cartesian3.ZERO) || !Cartesian3.equals(d1, Cartesian3.ZERO) || !Cartesian3.equals(d2, Cartesian3.ZERO)) {
  1663. direction = Cartesian3.add(d0, d1, d0);
  1664. Cartesian3.add(direction, d2, direction);
  1665. Cartesian3.normalize(direction, direction);
  1666. } else {
  1667. direction = p0Scratch;
  1668. direction.x = 0;
  1669. direction.y = 0;
  1670. direction.z = 0;
  1671. }
  1672. Cartesian3.pack(direction, currentAttributes.extrudeDirection.values, insertedIndex * 3);
  1673. }
  1674. if (defined(applyOffset)) {
  1675. interpolateAndPackBoolean(i0, i1, i2, coords, applyOffset, currentAttributes.applyOffset.values, insertedIndex);
  1676. }
  1677. if (defined(tangents)) {
  1678. interpolateAndPackCartesian3(i0, i1, i2, coords, tangents, currentAttributes.tangent.values, insertedIndex, true);
  1679. }
  1680. if (defined(bitangents)) {
  1681. interpolateAndPackCartesian3(i0, i1, i2, coords, bitangents, currentAttributes.bitangent.values, insertedIndex, true);
  1682. }
  1683. if (defined(texCoords)) {
  1684. interpolateAndPackCartesian2(i0, i1, i2, coords, texCoords, currentAttributes.st.values, insertedIndex);
  1685. }
  1686. if (customAttributesLength > 0) {
  1687. for (var i = 0; i < customAttributesLength; i++) {
  1688. var attributeName = customAttributeNames[i];
  1689. genericInterpolate(i0, i1, i2, coords, insertedIndex, allAttributes[attributeName], currentAttributes[attributeName]);
  1690. }
  1691. }
  1692. }
  1693. function genericInterpolate(i0, i1, i2, coords, insertedIndex, sourceAttribute, currentAttribute) {
  1694. var componentsPerAttribute = sourceAttribute.componentsPerAttribute;
  1695. var sourceValues = sourceAttribute.values;
  1696. var currentValues = currentAttribute.values;
  1697. switch(componentsPerAttribute) {
  1698. case 4:
  1699. interpolateAndPackCartesian4(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
  1700. break;
  1701. case 3:
  1702. interpolateAndPackCartesian3(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
  1703. break;
  1704. case 2:
  1705. interpolateAndPackCartesian2(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
  1706. break;
  1707. default:
  1708. currentValues[insertedIndex] = sourceValues[i0] * coords.x + sourceValues[i1] * coords.y + sourceValues[i2] * coords.z;
  1709. }
  1710. }
  1711. function insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, currentIndex, point) {
  1712. var insertIndex = currentAttributes.position.values.length / 3;
  1713. if (currentIndex !== -1) {
  1714. var prevIndex = indices[currentIndex];
  1715. var newIndex = currentIndexMap[prevIndex];
  1716. if (newIndex === -1) {
  1717. currentIndexMap[prevIndex] = insertIndex;
  1718. currentAttributes.position.values.push(point.x, point.y, point.z);
  1719. currentIndices.push(insertIndex);
  1720. return insertIndex;
  1721. }
  1722. currentIndices.push(newIndex);
  1723. return newIndex;
  1724. }
  1725. currentAttributes.position.values.push(point.x, point.y, point.z);
  1726. currentIndices.push(insertIndex);
  1727. return insertIndex;
  1728. }
  1729. var NAMED_ATTRIBUTES = {
  1730. position : true,
  1731. normal : true,
  1732. bitangent : true,
  1733. tangent : true,
  1734. st : true,
  1735. extrudeDirection : true,
  1736. applyOffset: true
  1737. };
  1738. function splitLongitudeTriangles(instance) {
  1739. var geometry = instance.geometry;
  1740. var attributes = geometry.attributes;
  1741. var positions = attributes.position.values;
  1742. var normals = (defined(attributes.normal)) ? attributes.normal.values : undefined;
  1743. var bitangents = (defined(attributes.bitangent)) ? attributes.bitangent.values : undefined;
  1744. var tangents = (defined(attributes.tangent)) ? attributes.tangent.values : undefined;
  1745. var texCoords = (defined(attributes.st)) ? attributes.st.values : undefined;
  1746. var extrudeDirections = (defined(attributes.extrudeDirection)) ? attributes.extrudeDirection.values : undefined;
  1747. var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined;
  1748. var indices = geometry.indices;
  1749. var customAttributeNames = [];
  1750. for (var attributeName in attributes) {
  1751. if (attributes.hasOwnProperty(attributeName) && !NAMED_ATTRIBUTES[attributeName] && defined(attributes[attributeName])) {
  1752. customAttributeNames.push(attributeName);
  1753. }
  1754. }
  1755. var customAttributesLength = customAttributeNames.length;
  1756. var eastGeometry = copyGeometryForSplit(geometry);
  1757. var westGeometry = copyGeometryForSplit(geometry);
  1758. var currentAttributes;
  1759. var currentIndices;
  1760. var currentIndexMap;
  1761. var insertedIndex;
  1762. var i;
  1763. var westGeometryIndexMap = [];
  1764. westGeometryIndexMap.length = positions.length / 3;
  1765. var eastGeometryIndexMap = [];
  1766. eastGeometryIndexMap.length = positions.length / 3;
  1767. for (i = 0; i < westGeometryIndexMap.length; ++i) {
  1768. westGeometryIndexMap[i] = -1;
  1769. eastGeometryIndexMap[i] = -1;
  1770. }
  1771. var len = indices.length;
  1772. for (i = 0; i < len; i += 3) {
  1773. var i0 = indices[i];
  1774. var i1 = indices[i + 1];
  1775. var i2 = indices[i + 2];
  1776. var p0 = Cartesian3.fromArray(positions, i0 * 3);
  1777. var p1 = Cartesian3.fromArray(positions, i1 * 3);
  1778. var p2 = Cartesian3.fromArray(positions, i2 * 3);
  1779. var result = splitTriangle(p0, p1, p2);
  1780. if (defined(result) && result.positions.length > 3) {
  1781. var resultPositions = result.positions;
  1782. var resultIndices = result.indices;
  1783. var resultLength = resultIndices.length;
  1784. for (var j = 0; j < resultLength; ++j) {
  1785. var resultIndex = resultIndices[j];
  1786. var point = resultPositions[resultIndex];
  1787. if (point.y < 0.0) {
  1788. currentAttributes = westGeometry.attributes;
  1789. currentIndices = westGeometry.indices;
  1790. currentIndexMap = westGeometryIndexMap;
  1791. } else {
  1792. currentAttributes = eastGeometry.attributes;
  1793. currentIndices = eastGeometry.indices;
  1794. currentIndexMap = eastGeometryIndexMap;
  1795. }
  1796. insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, resultIndex < 3 ? i + resultIndex : -1, point);
  1797. computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
  1798. }
  1799. } else {
  1800. if (defined(result)) {
  1801. p0 = result.positions[0];
  1802. p1 = result.positions[1];
  1803. p2 = result.positions[2];
  1804. }
  1805. if (p0.y < 0.0) {
  1806. currentAttributes = westGeometry.attributes;
  1807. currentIndices = westGeometry.indices;
  1808. currentIndexMap = westGeometryIndexMap;
  1809. } else {
  1810. currentAttributes = eastGeometry.attributes;
  1811. currentIndices = eastGeometry.indices;
  1812. currentIndexMap = eastGeometryIndexMap;
  1813. }
  1814. insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0);
  1815. computeTriangleAttributes(i0, i1, i2, p0, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
  1816. insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1);
  1817. computeTriangleAttributes(i0, i1, i2, p1, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
  1818. insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 2, p2);
  1819. computeTriangleAttributes(i0, i1, i2, p2, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
  1820. }
  1821. }
  1822. updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
  1823. }
  1824. var xzPlane = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Y);
  1825. var offsetScratch = new Cartesian3();
  1826. var offsetPointScratch = new Cartesian3();
  1827. function computeLineAttributes(i0, i1, point, positions, insertIndex, currentAttributes, applyOffset) {
  1828. if (!defined(applyOffset)) {
  1829. return;
  1830. }
  1831. var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
  1832. if (Cartesian3.equalsEpsilon(p0, point, CesiumMath.EPSILON10)) {
  1833. currentAttributes.applyOffset.values[insertIndex] = applyOffset[i0];
  1834. } else {
  1835. currentAttributes.applyOffset.values[insertIndex] = applyOffset[i1];
  1836. }
  1837. }
  1838. function splitLongitudeLines(instance) {
  1839. var geometry = instance.geometry;
  1840. var attributes = geometry.attributes;
  1841. var positions = attributes.position.values;
  1842. var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined;
  1843. var indices = geometry.indices;
  1844. var eastGeometry = copyGeometryForSplit(geometry);
  1845. var westGeometry = copyGeometryForSplit(geometry);
  1846. var i;
  1847. var length = indices.length;
  1848. var westGeometryIndexMap = [];
  1849. westGeometryIndexMap.length = positions.length / 3;
  1850. var eastGeometryIndexMap = [];
  1851. eastGeometryIndexMap.length = positions.length / 3;
  1852. for (i = 0; i < westGeometryIndexMap.length; ++i) {
  1853. westGeometryIndexMap[i] = -1;
  1854. eastGeometryIndexMap[i] = -1;
  1855. }
  1856. for (i = 0; i < length; i += 2) {
  1857. var i0 = indices[i];
  1858. var i1 = indices[i + 1];
  1859. var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
  1860. var p1 = Cartesian3.fromArray(positions, i1 * 3, p1Scratch);
  1861. var insertIndex;
  1862. if (Math.abs(p0.y) < CesiumMath.EPSILON6){
  1863. if (p0.y < 0.0) {
  1864. p0.y = -CesiumMath.EPSILON6;
  1865. } else {
  1866. p0.y = CesiumMath.EPSILON6;
  1867. }
  1868. }
  1869. if (Math.abs(p1.y) < CesiumMath.EPSILON6){
  1870. if (p1.y < 0.0) {
  1871. p1.y = -CesiumMath.EPSILON6;
  1872. } else {
  1873. p1.y = CesiumMath.EPSILON6;
  1874. }
  1875. }
  1876. var p0Attributes = eastGeometry.attributes;
  1877. var p0Indices = eastGeometry.indices;
  1878. var p0IndexMap = eastGeometryIndexMap;
  1879. var p1Attributes = westGeometry.attributes;
  1880. var p1Indices = westGeometry.indices;
  1881. var p1IndexMap = westGeometryIndexMap;
  1882. var intersection = IntersectionTests.lineSegmentPlane(p0, p1, xzPlane, p2Scratch);
  1883. if (defined(intersection)) {
  1884. // move point on the xz-plane slightly away from the plane
  1885. var offset = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Y, 5.0 * CesiumMath.EPSILON9, offsetScratch);
  1886. if (p0.y < 0.0) {
  1887. Cartesian3.negate(offset, offset);
  1888. p0Attributes = westGeometry.attributes;
  1889. p0Indices = westGeometry.indices;
  1890. p0IndexMap = westGeometryIndexMap;
  1891. p1Attributes = eastGeometry.attributes;
  1892. p1Indices = eastGeometry.indices;
  1893. p1IndexMap = eastGeometryIndexMap;
  1894. }
  1895. var offsetPoint = Cartesian3.add(intersection, offset, offsetPointScratch);
  1896. insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, i, p0);
  1897. computeLineAttributes(i0, i1, p0, positions, insertIndex, p0Attributes, applyOffset);
  1898. insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, -1, offsetPoint);
  1899. computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p0Attributes, applyOffset);
  1900. Cartesian3.negate(offset, offset);
  1901. Cartesian3.add(intersection, offset, offsetPoint);
  1902. insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, -1, offsetPoint);
  1903. computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p1Attributes, applyOffset);
  1904. insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, i + 1, p1);
  1905. computeLineAttributes(i0, i1, p1, positions, insertIndex, p1Attributes, applyOffset);
  1906. } else {
  1907. var currentAttributes;
  1908. var currentIndices;
  1909. var currentIndexMap;
  1910. if (p0.y < 0.0) {
  1911. currentAttributes = westGeometry.attributes;
  1912. currentIndices = westGeometry.indices;
  1913. currentIndexMap = westGeometryIndexMap;
  1914. } else {
  1915. currentAttributes = eastGeometry.attributes;
  1916. currentIndices = eastGeometry.indices;
  1917. currentIndexMap = eastGeometryIndexMap;
  1918. }
  1919. insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0);
  1920. computeLineAttributes(i0, i1, p0, positions, insertIndex, currentAttributes, applyOffset);
  1921. insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1);
  1922. computeLineAttributes(i0, i1, p1, positions, insertIndex, currentAttributes, applyOffset);
  1923. }
  1924. }
  1925. updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
  1926. }
  1927. var cartesian2Scratch0 = new Cartesian2();
  1928. var cartesian2Scratch1 = new Cartesian2();
  1929. var cartesian3Scratch0 = new Cartesian3();
  1930. var cartesian3Scratch2 = new Cartesian3();
  1931. var cartesian3Scratch3 = new Cartesian3();
  1932. var cartesian3Scratch4 = new Cartesian3();
  1933. var cartesian3Scratch5 = new Cartesian3();
  1934. var cartesian3Scratch6 = new Cartesian3();
  1935. var cartesian4Scratch0 = new Cartesian4();
  1936. function updateAdjacencyAfterSplit(geometry) {
  1937. var attributes = geometry.attributes;
  1938. var positions = attributes.position.values;
  1939. var prevPositions = attributes.prevPosition.values;
  1940. var nextPositions = attributes.nextPosition.values;
  1941. var length = positions.length;
  1942. for (var j = 0; j < length; j += 3) {
  1943. var position = Cartesian3.unpack(positions, j, cartesian3Scratch0);
  1944. if (position.x > 0.0) {
  1945. continue;
  1946. }
  1947. var prevPosition = Cartesian3.unpack(prevPositions, j, cartesian3Scratch2);
  1948. if ((position.y < 0.0 && prevPosition.y > 0.0) || (position.y > 0.0 && prevPosition.y < 0.0)) {
  1949. if (j - 3 > 0) {
  1950. prevPositions[j] = positions[j - 3];
  1951. prevPositions[j + 1] = positions[j - 2];
  1952. prevPositions[j + 2] = positions[j - 1];
  1953. } else {
  1954. Cartesian3.pack(position, prevPositions, j);
  1955. }
  1956. }
  1957. var nextPosition = Cartesian3.unpack(nextPositions, j, cartesian3Scratch3);
  1958. if ((position.y < 0.0 && nextPosition.y > 0.0) || (position.y > 0.0 && nextPosition.y < 0.0)) {
  1959. if (j + 3 < length) {
  1960. nextPositions[j] = positions[j + 3];
  1961. nextPositions[j + 1] = positions[j + 4];
  1962. nextPositions[j + 2] = positions[j + 5];
  1963. } else {
  1964. Cartesian3.pack(position, nextPositions, j);
  1965. }
  1966. }
  1967. }
  1968. }
  1969. var offsetScalar = 5.0 * CesiumMath.EPSILON9;
  1970. var coplanarOffset = CesiumMath.EPSILON6;
  1971. function splitLongitudePolyline(instance) {
  1972. var geometry = instance.geometry;
  1973. var attributes = geometry.attributes;
  1974. var positions = attributes.position.values;
  1975. var prevPositions = attributes.prevPosition.values;
  1976. var nextPositions = attributes.nextPosition.values;
  1977. var expandAndWidths = attributes.expandAndWidth.values;
  1978. var texCoords = (defined(attributes.st)) ? attributes.st.values : undefined;
  1979. var colors = (defined(attributes.color)) ? attributes.color.values : undefined;
  1980. var eastGeometry = copyGeometryForSplit(geometry);
  1981. var westGeometry = copyGeometryForSplit(geometry);
  1982. var i;
  1983. var j;
  1984. var index;
  1985. var intersectionFound = false;
  1986. var length = positions.length / 3;
  1987. for (i = 0; i < length; i += 4) {
  1988. var i0 = i;
  1989. var i2 = i + 2;
  1990. var p0 = Cartesian3.fromArray(positions, i0 * 3, cartesian3Scratch0);
  1991. var p2 = Cartesian3.fromArray(positions, i2 * 3, cartesian3Scratch2);
  1992. // Offset points that are close to the 180 longitude and change the previous/next point
  1993. // to be the same offset point so it can be projected to 2D. There is special handling in the
  1994. // shader for when position == prevPosition || position == nextPosition.
  1995. if (Math.abs(p0.y) < coplanarOffset) {
  1996. p0.y = coplanarOffset * (p2.y < 0.0 ? -1.0 : 1.0);
  1997. positions[i * 3 + 1] = p0.y;
  1998. positions[(i + 1) * 3 + 1] = p0.y;
  1999. for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) {
  2000. prevPositions[j] = positions[i * 3];
  2001. prevPositions[j + 1] = positions[i * 3 + 1];
  2002. prevPositions[j + 2] = positions[i * 3 + 2];
  2003. }
  2004. }
  2005. // Do the same but for when the line crosses 180 longitude in the opposite direction.
  2006. if (Math.abs(p2.y) < coplanarOffset) {
  2007. p2.y = coplanarOffset * (p0.y < 0.0 ? -1.0 : 1.0);
  2008. positions[(i + 2) * 3 + 1] = p2.y;
  2009. positions[(i + 3) * 3 + 1] = p2.y;
  2010. for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) {
  2011. nextPositions[j] = positions[(i + 2) * 3];
  2012. nextPositions[j + 1] = positions[(i + 2) * 3 + 1];
  2013. nextPositions[j + 2] = positions[(i + 2) * 3 + 2];
  2014. }
  2015. }
  2016. var p0Attributes = eastGeometry.attributes;
  2017. var p0Indices = eastGeometry.indices;
  2018. var p2Attributes = westGeometry.attributes;
  2019. var p2Indices = westGeometry.indices;
  2020. var intersection = IntersectionTests.lineSegmentPlane(p0, p2, xzPlane, cartesian3Scratch4);
  2021. if (defined(intersection)) {
  2022. intersectionFound = true;
  2023. // move point on the xz-plane slightly away from the plane
  2024. var offset = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Y, offsetScalar, cartesian3Scratch5);
  2025. if (p0.y < 0.0) {
  2026. Cartesian3.negate(offset, offset);
  2027. p0Attributes = westGeometry.attributes;
  2028. p0Indices = westGeometry.indices;
  2029. p2Attributes = eastGeometry.attributes;
  2030. p2Indices = eastGeometry.indices;
  2031. }
  2032. var offsetPoint = Cartesian3.add(intersection, offset, cartesian3Scratch6);
  2033. p0Attributes.position.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z);
  2034. p0Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2035. p0Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2036. p0Attributes.prevPosition.values.push(prevPositions[i0 * 3], prevPositions[i0 * 3 + 1], prevPositions[i0 * 3 + 2]);
  2037. p0Attributes.prevPosition.values.push(prevPositions[i0 * 3 + 3], prevPositions[i0 * 3 + 4], prevPositions[i0 * 3 + 5]);
  2038. p0Attributes.prevPosition.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z);
  2039. p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2040. p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2041. p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2042. p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2043. Cartesian3.negate(offset, offset);
  2044. Cartesian3.add(intersection, offset, offsetPoint);
  2045. p2Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2046. p2Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2047. p2Attributes.position.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z);
  2048. p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2049. p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2050. p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2051. p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
  2052. p2Attributes.nextPosition.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z);
  2053. p2Attributes.nextPosition.values.push(nextPositions[i2 * 3], nextPositions[i2 * 3 + 1], nextPositions[i2 * 3 + 2]);
  2054. p2Attributes.nextPosition.values.push(nextPositions[i2 * 3 + 3], nextPositions[i2 * 3 + 4], nextPositions[i2 * 3 + 5]);
  2055. var ew0 = Cartesian2.fromArray(expandAndWidths, i0 * 2, cartesian2Scratch0);
  2056. var width = Math.abs(ew0.y);
  2057. p0Attributes.expandAndWidth.values.push(-1, width, 1, width);
  2058. p0Attributes.expandAndWidth.values.push(-1, -width, 1, -width);
  2059. p2Attributes.expandAndWidth.values.push(-1, width, 1, width);
  2060. p2Attributes.expandAndWidth.values.push(-1, -width, 1, -width);
  2061. var t = Cartesian3.magnitudeSquared(Cartesian3.subtract(intersection, p0, cartesian3Scratch3));
  2062. t /= Cartesian3.magnitudeSquared(Cartesian3.subtract(p2, p0, cartesian3Scratch3));
  2063. if (defined(colors)) {
  2064. var c0 = Cartesian4.fromArray(colors, i0 * 4, cartesian4Scratch0);
  2065. var c2 = Cartesian4.fromArray(colors, i2 * 4, cartesian4Scratch0);
  2066. var r = CesiumMath.lerp(c0.x, c2.x, t);
  2067. var g = CesiumMath.lerp(c0.y, c2.y, t);
  2068. var b = CesiumMath.lerp(c0.z, c2.z, t);
  2069. var a = CesiumMath.lerp(c0.w, c2.w, t);
  2070. for (j = i0 * 4; j < i0 * 4 + 2 * 4; ++j) {
  2071. p0Attributes.color.values.push(colors[j]);
  2072. }
  2073. p0Attributes.color.values.push(r, g, b, a);
  2074. p0Attributes.color.values.push(r, g, b, a);
  2075. p2Attributes.color.values.push(r, g, b, a);
  2076. p2Attributes.color.values.push(r, g, b, a);
  2077. for (j = i2 * 4; j < i2 * 4 + 2 * 4; ++j) {
  2078. p2Attributes.color.values.push(colors[j]);
  2079. }
  2080. }
  2081. if (defined(texCoords)) {
  2082. var s0 = Cartesian2.fromArray(texCoords, i0 * 2, cartesian2Scratch0);
  2083. var s3 = Cartesian2.fromArray(texCoords, (i + 3) * 2, cartesian2Scratch1);
  2084. var sx = CesiumMath.lerp(s0.x, s3.x, t);
  2085. for (j = i0 * 2; j < i0 * 2 + 2 * 2; ++j) {
  2086. p0Attributes.st.values.push(texCoords[j]);
  2087. }
  2088. p0Attributes.st.values.push(sx, s0.y);
  2089. p0Attributes.st.values.push(sx, s3.y);
  2090. p2Attributes.st.values.push(sx, s0.y);
  2091. p2Attributes.st.values.push(sx, s3.y);
  2092. for (j = i2 * 2; j < i2 * 2 + 2 * 2; ++j) {
  2093. p2Attributes.st.values.push(texCoords[j]);
  2094. }
  2095. }
  2096. index = p0Attributes.position.values.length / 3 - 4;
  2097. p0Indices.push(index, index + 2, index + 1);
  2098. p0Indices.push(index + 1, index + 2, index + 3);
  2099. index = p2Attributes.position.values.length / 3 - 4;
  2100. p2Indices.push(index, index + 2, index + 1);
  2101. p2Indices.push(index + 1, index + 2, index + 3);
  2102. } else {
  2103. var currentAttributes;
  2104. var currentIndices;
  2105. if (p0.y < 0.0) {
  2106. currentAttributes = westGeometry.attributes;
  2107. currentIndices = westGeometry.indices;
  2108. } else {
  2109. currentAttributes = eastGeometry.attributes;
  2110. currentIndices = eastGeometry.indices;
  2111. }
  2112. currentAttributes.position.values.push(p0.x, p0.y, p0.z);
  2113. currentAttributes.position.values.push(p0.x, p0.y, p0.z);
  2114. currentAttributes.position.values.push(p2.x, p2.y, p2.z);
  2115. currentAttributes.position.values.push(p2.x, p2.y, p2.z);
  2116. for (j = i * 3; j < i * 3 + 4 * 3; ++j) {
  2117. currentAttributes.prevPosition.values.push(prevPositions[j]);
  2118. currentAttributes.nextPosition.values.push(nextPositions[j]);
  2119. }
  2120. for (j = i * 2; j < i * 2 + 4 * 2; ++j) {
  2121. currentAttributes.expandAndWidth.values.push(expandAndWidths[j]);
  2122. if (defined(texCoords)) {
  2123. currentAttributes.st.values.push(texCoords[j]);
  2124. }
  2125. }
  2126. if (defined(colors)) {
  2127. for (j = i * 4; j < i * 4 + 4 * 4; ++j) {
  2128. currentAttributes.color.values.push(colors[j]);
  2129. }
  2130. }
  2131. index = currentAttributes.position.values.length / 3 - 4;
  2132. currentIndices.push(index, index + 2, index + 1);
  2133. currentIndices.push(index + 1, index + 2, index + 3);
  2134. }
  2135. }
  2136. if (intersectionFound) {
  2137. updateAdjacencyAfterSplit(westGeometry);
  2138. updateAdjacencyAfterSplit(eastGeometry);
  2139. }
  2140. updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
  2141. }
  2142. /**
  2143. * Splits the instances's geometry, by introducing new vertices and indices,that
  2144. * intersect the International Date Line and Prime Meridian so that no primitives cross longitude
  2145. * -180/180 degrees. This is not required for 3D drawing, but is required for
  2146. * correcting drawing in 2D and Columbus view.
  2147. *
  2148. * @private
  2149. *
  2150. * @param {GeometryInstance} instance The instance to modify.
  2151. * @returns {GeometryInstance} The modified <code>instance</code> argument, with it's geometry split at the International Date Line.
  2152. *
  2153. * @example
  2154. * instance = Cesium.GeometryPipeline.splitLongitude(instance);
  2155. */
  2156. GeometryPipeline.splitLongitude = function(instance) {
  2157. //>>includeStart('debug', pragmas.debug);
  2158. if (!defined(instance)) {
  2159. throw new DeveloperError('instance is required.');
  2160. }
  2161. //>>includeEnd('debug');
  2162. var geometry = instance.geometry;
  2163. var boundingSphere = geometry.boundingSphere;
  2164. if (defined(boundingSphere)) {
  2165. var minX = boundingSphere.center.x - boundingSphere.radius;
  2166. if (minX > 0 || BoundingSphere.intersectPlane(boundingSphere, Plane.ORIGIN_ZX_PLANE) !== Intersect.INTERSECTING) {
  2167. return instance;
  2168. }
  2169. }
  2170. if (geometry.geometryType !== GeometryType.NONE) {
  2171. switch (geometry.geometryType) {
  2172. case GeometryType.POLYLINES:
  2173. splitLongitudePolyline(instance);
  2174. break;
  2175. case GeometryType.TRIANGLES:
  2176. splitLongitudeTriangles(instance);
  2177. break;
  2178. case GeometryType.LINES:
  2179. splitLongitudeLines(instance);
  2180. break;
  2181. }
  2182. } else {
  2183. indexPrimitive(geometry);
  2184. if (geometry.primitiveType === PrimitiveType.TRIANGLES) {
  2185. splitLongitudeTriangles(instance);
  2186. } else if (geometry.primitiveType === PrimitiveType.LINES) {
  2187. splitLongitudeLines(instance);
  2188. }
  2189. }
  2190. return instance;
  2191. };
  2192. export default GeometryPipeline;