Material.js 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import clone from "../Core/clone.js";
  3. import Color from "../Core/Color.js";
  4. import combine from "../Core/combine.js";
  5. import createGuid from "../Core/createGuid.js";
  6. import defaultValue from "../Core/defaultValue.js";
  7. import defined from "../Core/defined.js";
  8. import destroyObject from "../Core/destroyObject.js";
  9. import DeveloperError from "../Core/DeveloperError.js";
  10. import loadKTX2 from "../Core/loadKTX2.js";
  11. import Matrix2 from "../Core/Matrix2.js";
  12. import Matrix3 from "../Core/Matrix3.js";
  13. import Matrix4 from "../Core/Matrix4.js";
  14. import Resource from "../Core/Resource.js";
  15. import CubeMap from "../Renderer/CubeMap.js";
  16. import Texture from "../Renderer/Texture.js";
  17. import AspectRampMaterial from "../Shaders/Materials/AspectRampMaterial.js";
  18. import BumpMapMaterial from "../Shaders/Materials/BumpMapMaterial.js";
  19. import CheckerboardMaterial from "../Shaders/Materials/CheckerboardMaterial.js";
  20. import DotMaterial from "../Shaders/Materials/DotMaterial.js";
  21. import ElevationBandMaterial from "../Shaders/Materials/ElevationBandMaterial.js";
  22. import ElevationContourMaterial from "../Shaders/Materials/ElevationContourMaterial.js";
  23. import ElevationRampMaterial from "../Shaders/Materials/ElevationRampMaterial.js";
  24. import FadeMaterial from "../Shaders/Materials/FadeMaterial.js";
  25. import GridMaterial from "../Shaders/Materials/GridMaterial.js";
  26. import NormalMapMaterial from "../Shaders/Materials/NormalMapMaterial.js";
  27. import PolylineArrowMaterial from "../Shaders/Materials/PolylineArrowMaterial.js";
  28. import PolylineDashMaterial from "../Shaders/Materials/PolylineDashMaterial.js";
  29. import PolylineGlowMaterial from "../Shaders/Materials/PolylineGlowMaterial.js";
  30. import PolylineOutlineMaterial from "../Shaders/Materials/PolylineOutlineMaterial.js";
  31. import RimLightingMaterial from "../Shaders/Materials/RimLightingMaterial.js";
  32. import Sampler from "../Renderer/Sampler.js";
  33. import SlopeRampMaterial from "../Shaders/Materials/SlopeRampMaterial.js";
  34. import StripeMaterial from "../Shaders/Materials/StripeMaterial.js";
  35. import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
  36. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  37. import WaterMaterial from "../Shaders/Materials/Water.js";
  38. /**
  39. * A Material defines surface appearance through a combination of diffuse, specular,
  40. * normal, emission, and alpha components. These values are specified using a
  41. * JSON schema called Fabric which gets parsed and assembled into glsl shader code
  42. * behind-the-scenes. Check out the {@link https://github.com/CesiumGS/cesium/wiki/Fabric|wiki page}
  43. * for more details on Fabric.
  44. * <br /><br />
  45. * <style type="text/css">
  46. * #materialDescriptions code {
  47. * font-weight: normal;
  48. * font-family: Consolas, 'Lucida Console', Monaco, monospace;
  49. * color: #A35A00;
  50. * }
  51. * #materialDescriptions ul, #materialDescriptions ul ul {
  52. * list-style-type: none;
  53. * }
  54. * #materialDescriptions ul ul {
  55. * margin-bottom: 10px;
  56. * }
  57. * #materialDescriptions ul ul li {
  58. * font-weight: normal;
  59. * color: #000000;
  60. * text-indent: -2em;
  61. * margin-left: 2em;
  62. * }
  63. * #materialDescriptions ul li {
  64. * font-weight: bold;
  65. * color: #0053CF;
  66. * }
  67. * </style>
  68. *
  69. * Base material types and their uniforms:
  70. * <div id='materialDescriptions'>
  71. * <ul>
  72. * <li>Color</li>
  73. * <ul>
  74. * <li><code>color</code>: rgba color object.</li>
  75. * </ul>
  76. * <li>Image</li>
  77. * <ul>
  78. * <li><code>image</code>: path to image.</li>
  79. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  80. * </ul>
  81. * <li>DiffuseMap</li>
  82. * <ul>
  83. * <li><code>image</code>: path to image.</li>
  84. * <li><code>channels</code>: Three character string containing any combination of r, g, b, and a for selecting the desired image channels.</li>
  85. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  86. * </ul>
  87. * <li>AlphaMap</li>
  88. * <ul>
  89. * <li><code>image</code>: path to image.</li>
  90. * <li><code>channel</code>: One character string containing r, g, b, or a for selecting the desired image channel. </li>
  91. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  92. * </ul>
  93. * <li>SpecularMap</li>
  94. * <ul>
  95. * <li><code>image</code>: path to image.</li>
  96. * <li><code>channel</code>: One character string containing r, g, b, or a for selecting the desired image channel. </li>
  97. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  98. * </ul>
  99. * <li>EmissionMap</li>
  100. * <ul>
  101. * <li><code>image</code>: path to image.</li>
  102. * <li><code>channels</code>: Three character string containing any combination of r, g, b, and a for selecting the desired image channels. </li>
  103. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  104. * </ul>
  105. * <li>BumpMap</li>
  106. * <ul>
  107. * <li><code>image</code>: path to image.</li>
  108. * <li><code>channel</code>: One character string containing r, g, b, or a for selecting the desired image channel. </li>
  109. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  110. * <li><code>strength</code>: Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.</li>
  111. * </ul>
  112. * <li>NormalMap</li>
  113. * <ul>
  114. * <li><code>image</code>: path to image.</li>
  115. * <li><code>channels</code>: Three character string containing any combination of r, g, b, and a for selecting the desired image channels. </li>
  116. * <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
  117. * <li><code>strength</code>: Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.</li>
  118. * </ul>
  119. * <li>Grid</li>
  120. * <ul>
  121. * <li><code>color</code>: rgba color object for the whole material.</li>
  122. * <li><code>cellAlpha</code>: Alpha value for the cells between grid lines. This will be combined with color.alpha.</li>
  123. * <li><code>lineCount</code>: Object with x and y values specifying the number of columns and rows respectively.</li>
  124. * <li><code>lineThickness</code>: Object with x and y values specifying the thickness of grid lines (in pixels where available).</li>
  125. * <li><code>lineOffset</code>: Object with x and y values specifying the offset of grid lines (range is 0 to 1).</li>
  126. * </ul>
  127. * <li>Stripe</li>
  128. * <ul>
  129. * <li><code>horizontal</code>: Boolean that determines if the stripes are horizontal or vertical.</li>
  130. * <li><code>evenColor</code>: rgba color object for the stripe's first color.</li>
  131. * <li><code>oddColor</code>: rgba color object for the stripe's second color.</li>
  132. * <li><code>offset</code>: Number that controls at which point into the pattern to begin drawing; with 0.0 being the beginning of the even color, 1.0 the beginning of the odd color, 2.0 being the even color again, and any multiple or fractional values being in between.</li>
  133. * <li><code>repeat</code>: Number that controls the total number of stripes, half light and half dark.</li>
  134. * </ul>
  135. * <li>Checkerboard</li>
  136. * <ul>
  137. * <li><code>lightColor</code>: rgba color object for the checkerboard's light alternating color.</li>
  138. * <li><code>darkColor</code>: rgba color object for the checkerboard's dark alternating color.</li>
  139. * <li><code>repeat</code>: Object with x and y values specifying the number of columns and rows respectively.</li>
  140. * </ul>
  141. * <li>Dot</li>
  142. * <ul>
  143. * <li><code>lightColor</code>: rgba color object for the dot color.</li>
  144. * <li><code>darkColor</code>: rgba color object for the background color.</li>
  145. * <li><code>repeat</code>: Object with x and y values specifying the number of columns and rows of dots respectively.</li>
  146. * </ul>
  147. * <li>Water</li>
  148. * <ul>
  149. * <li><code>baseWaterColor</code>: rgba color object base color of the water.</li>
  150. * <li><code>blendColor</code>: rgba color object used when blending from water to non-water areas.</li>
  151. * <li><code>specularMap</code>: Single channel texture used to indicate areas of water.</li>
  152. * <li><code>normalMap</code>: Normal map for water normal perturbation.</li>
  153. * <li><code>frequency</code>: Number that controls the number of waves.</li>
  154. * <li><code>animationSpeed</code>: Number that controls the animations speed of the water.</li>
  155. * <li><code>amplitude</code>: Number that controls the amplitude of water waves.</li>
  156. * <li><code>specularIntensity</code>: Number that controls the intensity of specular reflections.</li>
  157. * </ul>
  158. * <li>RimLighting</li>
  159. * <ul>
  160. * <li><code>color</code>: diffuse color and alpha.</li>
  161. * <li><code>rimColor</code>: diffuse color and alpha of the rim.</li>
  162. * <li><code>width</code>: Number that determines the rim's width.</li>
  163. * </ul>
  164. * <li>Fade</li>
  165. * <ul>
  166. * <li><code>fadeInColor</code>: diffuse color and alpha at <code>time</code></li>
  167. * <li><code>fadeOutColor</code>: diffuse color and alpha at <code>maximumDistance</code> from <code>time</code></li>
  168. * <li><code>maximumDistance</code>: Number between 0.0 and 1.0 where the <code>fadeInColor</code> becomes the <code>fadeOutColor</code>. A value of 0.0 gives the entire material a color of <code>fadeOutColor</code> and a value of 1.0 gives the the entire material a color of <code>fadeInColor</code></li>
  169. * <li><code>repeat</code>: true if the fade should wrap around the texture coodinates.</li>
  170. * <li><code>fadeDirection</code>: Object with x and y values specifying if the fade should be in the x and y directions.</li>
  171. * <li><code>time</code>: Object with x and y values between 0.0 and 1.0 of the <code>fadeInColor</code> position</li>
  172. * </ul>
  173. * <li>PolylineArrow</li>
  174. * <ul>
  175. * <li><code>color</code>: diffuse color and alpha.</li>
  176. * </ul>
  177. * <li>PolylineDash</li>
  178. * <ul>
  179. * <li><code>color</code>: color for the line.</li>
  180. * <li><code>gapColor</code>: color for the gaps in the line.</li>
  181. * <li><code>dashLength</code>: Dash length in pixels.</li>
  182. * <li><code>dashPattern</code>: The 16 bit stipple pattern for the line..</li>
  183. * </ul>
  184. * <li>PolylineGlow</li>
  185. * <ul>
  186. * <li><code>color</code>: color and maximum alpha for the glow on the line.</li>
  187. * <li><code>glowPower</code>: strength of the glow, as a percentage of the total line width (less than 1.0).</li>
  188. * <li><code>taperPower</code>: strength of the tapering effect, as a percentage of the total line length. If 1.0 or higher, no taper effect is used.</li>
  189. * </ul>
  190. * <li>PolylineOutline</li>
  191. * <ul>
  192. * <li><code>color</code>: diffuse color and alpha for the interior of the line.</li>
  193. * <li><code>outlineColor</code>: diffuse color and alpha for the outline.</li>
  194. * <li><code>outlineWidth</code>: width of the outline in pixels.</li>
  195. * </ul>
  196. * <li>ElevationContour</li>
  197. * <ul>
  198. * <li><code>color</code>: color and alpha for the contour line.</li>
  199. * <li><code>spacing</code>: spacing for contour lines in meters.</li>
  200. * <li><code>width</code>: Number specifying the width of the grid lines in pixels.</li>
  201. * </ul>
  202. * <li>ElevationRamp</li>
  203. * <ul>
  204. * <li><code>image</code>: color ramp image to use for coloring the terrain.</li>
  205. * <li><code>minimumHeight</code>: minimum height for the ramp.</li>
  206. * <li><code>maximumHeight</code>: maximum height for the ramp.</li>
  207. * </ul>
  208. * <li>SlopeRamp</li>
  209. * <ul>
  210. * <li><code>image</code>: color ramp image to use for coloring the terrain by slope.</li>
  211. * </ul>
  212. * <li>AspectRamp</li>
  213. * <ul>
  214. * <li><code>image</code>: color ramp image to use for color the terrain by aspect.</li>
  215. * </ul>
  216. * <li>ElevationBand</li>
  217. * <ul>
  218. * <li><code>heights</code>: image of heights sorted from lowest to highest.</li>
  219. * <li><code>colors</code>: image of colors at the corresponding heights.</li>
  220. * </ul>
  221. * </ul>
  222. * </ul>
  223. * </div>
  224. *
  225. * @alias Material
  226. *
  227. * @param {Object} [options] Object with the following properties:
  228. * @param {Boolean} [options.strict=false] Throws errors for issues that would normally be ignored, including unused uniforms or materials.
  229. * @param {Boolean|Function} [options.translucent=true] When <code>true</code> or a function that returns <code>true</code>, the geometry
  230. * with this material is expected to appear translucent.
  231. * @param {TextureMinificationFilter} [options.minificationFilter=TextureMinificationFilter.LINEAR] The {@link TextureMinificationFilter} to apply to this material's textures.
  232. * @param {TextureMagnificationFilter} [options.magnificationFilter=TextureMagnificationFilter.LINEAR] The {@link TextureMagnificationFilter} to apply to this material's textures.
  233. * @param {Object} options.fabric The fabric JSON used to generate the material.
  234. *
  235. * @constructor
  236. *
  237. * @exception {DeveloperError} fabric: uniform has invalid type.
  238. * @exception {DeveloperError} fabric: uniforms and materials cannot share the same property.
  239. * @exception {DeveloperError} fabric: cannot have source and components in the same section.
  240. * @exception {DeveloperError} fabric: property name is not valid. It should be 'type', 'materials', 'uniforms', 'components', or 'source'.
  241. * @exception {DeveloperError} fabric: property name is not valid. It should be 'diffuse', 'specular', 'shininess', 'normal', 'emission', or 'alpha'.
  242. * @exception {DeveloperError} strict: shader source does not use string.
  243. * @exception {DeveloperError} strict: shader source does not use uniform.
  244. * @exception {DeveloperError} strict: shader source does not use material.
  245. *
  246. * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric wiki page} for a more detailed options of Fabric.
  247. *
  248. * @demo {@link https://sandcastle.cesium.com/index.html?src=Materials.html|Cesium Sandcastle Materials Demo}
  249. *
  250. * @example
  251. * // Create a color material with fromType:
  252. * polygon.material = Cesium.Material.fromType('Color');
  253. * polygon.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
  254. *
  255. * // Create the default material:
  256. * polygon.material = new Cesium.Material();
  257. *
  258. * // Create a color material with full Fabric notation:
  259. * polygon.material = new Cesium.Material({
  260. * fabric : {
  261. * type : 'Color',
  262. * uniforms : {
  263. * color : new Cesium.Color(1.0, 1.0, 0.0, 1.0)
  264. * }
  265. * }
  266. * });
  267. */
  268. function Material(options) {
  269. /**
  270. * The material type. Can be an existing type or a new type. If no type is specified in fabric, type is a GUID.
  271. * @type {String}
  272. * @default undefined
  273. */
  274. this.type = undefined;
  275. /**
  276. * The glsl shader source for this material.
  277. * @type {String}
  278. * @default undefined
  279. */
  280. this.shaderSource = undefined;
  281. /**
  282. * Maps sub-material names to Material objects.
  283. * @type {Object}
  284. * @default undefined
  285. */
  286. this.materials = undefined;
  287. /**
  288. * Maps uniform names to their values.
  289. * @type {Object}
  290. * @default undefined
  291. */
  292. this.uniforms = undefined;
  293. this._uniforms = undefined;
  294. /**
  295. * When <code>true</code> or a function that returns <code>true</code>,
  296. * the geometry is expected to appear translucent.
  297. * @type {Boolean|Function}
  298. * @default undefined
  299. */
  300. this.translucent = undefined;
  301. this._minificationFilter = defaultValue(
  302. options.minificationFilter,
  303. TextureMinificationFilter.LINEAR
  304. );
  305. this._magnificationFilter = defaultValue(
  306. options.magnificationFilter,
  307. TextureMagnificationFilter.LINEAR
  308. );
  309. this._strict = undefined;
  310. this._template = undefined;
  311. this._count = undefined;
  312. this._texturePaths = {};
  313. this._loadedImages = [];
  314. this._loadedCubeMaps = [];
  315. this._textures = {};
  316. this._updateFunctions = [];
  317. this._defaultTexture = undefined;
  318. initializeMaterial(options, this);
  319. Object.defineProperties(this, {
  320. type: {
  321. value: this.type,
  322. writable: false,
  323. },
  324. });
  325. if (!defined(Material._uniformList[this.type])) {
  326. Material._uniformList[this.type] = Object.keys(this._uniforms);
  327. }
  328. }
  329. // Cached list of combined uniform names indexed by type.
  330. // Used to get the list of uniforms in the same order.
  331. Material._uniformList = {};
  332. /**
  333. * Creates a new material using an existing material type.
  334. * <br /><br />
  335. * Shorthand for: new Material({fabric : {type : type}});
  336. *
  337. * @param {String} type The base material type.
  338. * @param {Object} [uniforms] Overrides for the default uniforms.
  339. * @returns {Material} New material object.
  340. *
  341. * @exception {DeveloperError} material with that type does not exist.
  342. *
  343. * @example
  344. * const material = Cesium.Material.fromType('Color', {
  345. * color : new Cesium.Color(1.0, 0.0, 0.0, 1.0)
  346. * });
  347. */
  348. Material.fromType = function (type, uniforms) {
  349. //>>includeStart('debug', pragmas.debug);
  350. if (!defined(Material._materialCache.getMaterial(type))) {
  351. throw new DeveloperError(`material with type '${type}' does not exist.`);
  352. }
  353. //>>includeEnd('debug');
  354. const material = new Material({
  355. fabric: {
  356. type: type,
  357. },
  358. });
  359. if (defined(uniforms)) {
  360. for (const name in uniforms) {
  361. if (uniforms.hasOwnProperty(name)) {
  362. material.uniforms[name] = uniforms[name];
  363. }
  364. }
  365. }
  366. return material;
  367. };
  368. /**
  369. * Gets whether or not this material is translucent.
  370. * @returns {Boolean} <code>true</code> if this material is translucent, <code>false</code> otherwise.
  371. */
  372. Material.prototype.isTranslucent = function () {
  373. if (defined(this.translucent)) {
  374. if (typeof this.translucent === "function") {
  375. return this.translucent();
  376. }
  377. return this.translucent;
  378. }
  379. let translucent = true;
  380. const funcs = this._translucentFunctions;
  381. const length = funcs.length;
  382. for (let i = 0; i < length; ++i) {
  383. const func = funcs[i];
  384. if (typeof func === "function") {
  385. translucent = translucent && func();
  386. } else {
  387. translucent = translucent && func;
  388. }
  389. if (!translucent) {
  390. break;
  391. }
  392. }
  393. return translucent;
  394. };
  395. /**
  396. * @private
  397. */
  398. Material.prototype.update = function (context) {
  399. this._defaultTexture = context.defaultTexture;
  400. let i;
  401. let uniformId;
  402. const loadedImages = this._loadedImages;
  403. let length = loadedImages.length;
  404. for (i = 0; i < length; ++i) {
  405. const loadedImage = loadedImages[i];
  406. uniformId = loadedImage.id;
  407. let image = loadedImage.image;
  408. // Images transcoded from KTX2 can contain multiple mip levels:
  409. // https://github.khronos.org/KTX-Specification/#_mip_level_array
  410. let mipLevels;
  411. if (Array.isArray(image)) {
  412. // highest detail mip should be level 0
  413. mipLevels = image.slice(1, image.length).map(function (mipLevel) {
  414. return mipLevel.bufferView;
  415. });
  416. image = image[0];
  417. }
  418. const sampler = new Sampler({
  419. minificationFilter: this._minificationFilter,
  420. magnificationFilter: this._magnificationFilter,
  421. });
  422. let texture;
  423. if (defined(image.internalFormat)) {
  424. texture = new Texture({
  425. context: context,
  426. pixelFormat: image.internalFormat,
  427. width: image.width,
  428. height: image.height,
  429. source: {
  430. arrayBufferView: image.bufferView,
  431. mipLevels: mipLevels,
  432. },
  433. sampler: sampler,
  434. });
  435. } else {
  436. texture = new Texture({
  437. context: context,
  438. source: image,
  439. sampler: sampler,
  440. });
  441. }
  442. // The material destroys its old texture only after the new one has been loaded.
  443. // This will ensure a smooth swap of textures and prevent the default texture
  444. // from appearing for a few frames.
  445. const oldTexture = this._textures[uniformId];
  446. if (defined(oldTexture) && oldTexture !== this._defaultTexture) {
  447. oldTexture.destroy();
  448. }
  449. this._textures[uniformId] = texture;
  450. const uniformDimensionsName = `${uniformId}Dimensions`;
  451. if (this.uniforms.hasOwnProperty(uniformDimensionsName)) {
  452. const uniformDimensions = this.uniforms[uniformDimensionsName];
  453. uniformDimensions.x = texture._width;
  454. uniformDimensions.y = texture._height;
  455. }
  456. }
  457. loadedImages.length = 0;
  458. const loadedCubeMaps = this._loadedCubeMaps;
  459. length = loadedCubeMaps.length;
  460. for (i = 0; i < length; ++i) {
  461. const loadedCubeMap = loadedCubeMaps[i];
  462. uniformId = loadedCubeMap.id;
  463. const images = loadedCubeMap.images;
  464. const cubeMap = new CubeMap({
  465. context: context,
  466. source: {
  467. positiveX: images[0],
  468. negativeX: images[1],
  469. positiveY: images[2],
  470. negativeY: images[3],
  471. positiveZ: images[4],
  472. negativeZ: images[5],
  473. },
  474. sampler: new Sampler({
  475. minificationFilter: this._minificationFilter,
  476. magnificationFilter: this._magnificationFilter,
  477. }),
  478. });
  479. this._textures[uniformId] = cubeMap;
  480. }
  481. loadedCubeMaps.length = 0;
  482. const updateFunctions = this._updateFunctions;
  483. length = updateFunctions.length;
  484. for (i = 0; i < length; ++i) {
  485. updateFunctions[i](this, context);
  486. }
  487. const subMaterials = this.materials;
  488. for (const name in subMaterials) {
  489. if (subMaterials.hasOwnProperty(name)) {
  490. subMaterials[name].update(context);
  491. }
  492. }
  493. };
  494. /**
  495. * Returns true if this object was destroyed; otherwise, false.
  496. * <br /><br />
  497. * If this object was destroyed, it should not be used; calling any function other than
  498. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  499. *
  500. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  501. *
  502. * @see Material#destroy
  503. */
  504. Material.prototype.isDestroyed = function () {
  505. return false;
  506. };
  507. /**
  508. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  509. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  510. * <br /><br />
  511. * Once an object is destroyed, it should not be used; calling any function other than
  512. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  513. * assign the return value (<code>undefined</code>) to the object as done in the example.
  514. *
  515. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  516. *
  517. *
  518. * @example
  519. * material = material && material.destroy();
  520. *
  521. * @see Material#isDestroyed
  522. */
  523. Material.prototype.destroy = function () {
  524. const textures = this._textures;
  525. for (const texture in textures) {
  526. if (textures.hasOwnProperty(texture)) {
  527. const instance = textures[texture];
  528. if (instance !== this._defaultTexture) {
  529. instance.destroy();
  530. }
  531. }
  532. }
  533. const materials = this.materials;
  534. for (const material in materials) {
  535. if (materials.hasOwnProperty(material)) {
  536. materials[material].destroy();
  537. }
  538. }
  539. return destroyObject(this);
  540. };
  541. function initializeMaterial(options, result) {
  542. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  543. result._strict = defaultValue(options.strict, false);
  544. result._count = defaultValue(options.count, 0);
  545. result._template = clone(
  546. defaultValue(options.fabric, defaultValue.EMPTY_OBJECT)
  547. );
  548. result._template.uniforms = clone(
  549. defaultValue(result._template.uniforms, defaultValue.EMPTY_OBJECT)
  550. );
  551. result._template.materials = clone(
  552. defaultValue(result._template.materials, defaultValue.EMPTY_OBJECT)
  553. );
  554. result.type = defined(result._template.type)
  555. ? result._template.type
  556. : createGuid();
  557. result.shaderSource = "";
  558. result.materials = {};
  559. result.uniforms = {};
  560. result._uniforms = {};
  561. result._translucentFunctions = [];
  562. let translucent;
  563. // If the cache contains this material type, build the material template off of the stored template.
  564. const cachedMaterial = Material._materialCache.getMaterial(result.type);
  565. if (defined(cachedMaterial)) {
  566. const template = clone(cachedMaterial.fabric, true);
  567. result._template = combine(result._template, template, true);
  568. translucent = cachedMaterial.translucent;
  569. }
  570. // Make sure the template has no obvious errors. More error checking happens later.
  571. checkForTemplateErrors(result);
  572. // If the material has a new type, add it to the cache.
  573. if (!defined(cachedMaterial)) {
  574. Material._materialCache.addMaterial(result.type, result);
  575. }
  576. createMethodDefinition(result);
  577. createUniforms(result);
  578. createSubMaterials(result);
  579. const defaultTranslucent =
  580. result._translucentFunctions.length === 0 ? true : undefined;
  581. translucent = defaultValue(translucent, defaultTranslucent);
  582. translucent = defaultValue(options.translucent, translucent);
  583. if (defined(translucent)) {
  584. if (typeof translucent === "function") {
  585. const wrappedTranslucent = function () {
  586. return translucent(result);
  587. };
  588. result._translucentFunctions.push(wrappedTranslucent);
  589. } else {
  590. result._translucentFunctions.push(translucent);
  591. }
  592. }
  593. }
  594. function checkForValidProperties(object, properties, result, throwNotFound) {
  595. if (defined(object)) {
  596. for (const property in object) {
  597. if (object.hasOwnProperty(property)) {
  598. const hasProperty = properties.indexOf(property) !== -1;
  599. if (
  600. (throwNotFound && !hasProperty) ||
  601. (!throwNotFound && hasProperty)
  602. ) {
  603. result(property, properties);
  604. }
  605. }
  606. }
  607. }
  608. }
  609. function invalidNameError(property, properties) {
  610. //>>includeStart('debug', pragmas.debug);
  611. let errorString = `fabric: property name '${property}' is not valid. It should be `;
  612. for (let i = 0; i < properties.length; i++) {
  613. const propertyName = `'${properties[i]}'`;
  614. errorString +=
  615. i === properties.length - 1 ? `or ${propertyName}.` : `${propertyName}, `;
  616. }
  617. throw new DeveloperError(errorString);
  618. //>>includeEnd('debug');
  619. }
  620. function duplicateNameError(property, properties) {
  621. //>>includeStart('debug', pragmas.debug);
  622. const errorString = `fabric: uniforms and materials cannot share the same property '${property}'`;
  623. throw new DeveloperError(errorString);
  624. //>>includeEnd('debug');
  625. }
  626. const templateProperties = [
  627. "type",
  628. "materials",
  629. "uniforms",
  630. "components",
  631. "source",
  632. ];
  633. const componentProperties = [
  634. "diffuse",
  635. "specular",
  636. "shininess",
  637. "normal",
  638. "emission",
  639. "alpha",
  640. ];
  641. function checkForTemplateErrors(material) {
  642. const template = material._template;
  643. const uniforms = template.uniforms;
  644. const materials = template.materials;
  645. const components = template.components;
  646. // Make sure source and components do not exist in the same template.
  647. //>>includeStart('debug', pragmas.debug);
  648. if (defined(components) && defined(template.source)) {
  649. throw new DeveloperError(
  650. "fabric: cannot have source and components in the same template."
  651. );
  652. }
  653. //>>includeEnd('debug');
  654. // Make sure all template and components properties are valid.
  655. checkForValidProperties(template, templateProperties, invalidNameError, true);
  656. checkForValidProperties(
  657. components,
  658. componentProperties,
  659. invalidNameError,
  660. true
  661. );
  662. // Make sure uniforms and materials do not share any of the same names.
  663. const materialNames = [];
  664. for (const property in materials) {
  665. if (materials.hasOwnProperty(property)) {
  666. materialNames.push(property);
  667. }
  668. }
  669. checkForValidProperties(uniforms, materialNames, duplicateNameError, false);
  670. }
  671. function isMaterialFused(shaderComponent, material) {
  672. const materials = material._template.materials;
  673. for (const subMaterialId in materials) {
  674. if (materials.hasOwnProperty(subMaterialId)) {
  675. if (shaderComponent.indexOf(subMaterialId) > -1) {
  676. return true;
  677. }
  678. }
  679. }
  680. return false;
  681. }
  682. // Create the czm_getMaterial method body using source or components.
  683. function createMethodDefinition(material) {
  684. const components = material._template.components;
  685. const source = material._template.source;
  686. if (defined(source)) {
  687. material.shaderSource += `${source}\n`;
  688. } else {
  689. material.shaderSource +=
  690. "czm_material czm_getMaterial(czm_materialInput materialInput)\n{\n";
  691. material.shaderSource +=
  692. "czm_material material = czm_getDefaultMaterial(materialInput);\n";
  693. if (defined(components)) {
  694. const isMultiMaterial =
  695. Object.keys(material._template.materials).length > 0;
  696. for (const component in components) {
  697. if (components.hasOwnProperty(component)) {
  698. if (component === "diffuse" || component === "emission") {
  699. const isFusion =
  700. isMultiMaterial &&
  701. isMaterialFused(components[component], material);
  702. const componentSource = isFusion
  703. ? components[component]
  704. : `czm_gammaCorrect(${components[component]})`;
  705. material.shaderSource += `material.${component} = ${componentSource}; \n`;
  706. } else if (component === "alpha") {
  707. material.shaderSource += `material.alpha = ${components.alpha}; \n`;
  708. } else {
  709. material.shaderSource += `material.${component} = ${components[component]};\n`;
  710. }
  711. }
  712. }
  713. }
  714. material.shaderSource += "return material;\n}\n";
  715. }
  716. }
  717. const matrixMap = {
  718. mat2: Matrix2,
  719. mat3: Matrix3,
  720. mat4: Matrix4,
  721. };
  722. const ktx2Regex = /\.ktx2$/i;
  723. function createTexture2DUpdateFunction(uniformId) {
  724. let oldUniformValue;
  725. return function (material, context) {
  726. const uniforms = material.uniforms;
  727. const uniformValue = uniforms[uniformId];
  728. const uniformChanged = oldUniformValue !== uniformValue;
  729. const uniformValueIsDefaultImage =
  730. !defined(uniformValue) || uniformValue === Material.DefaultImageId;
  731. oldUniformValue = uniformValue;
  732. let texture = material._textures[uniformId];
  733. let uniformDimensionsName;
  734. let uniformDimensions;
  735. if (uniformValue instanceof HTMLVideoElement) {
  736. // HTMLVideoElement.readyState >=2 means we have enough data for the current frame.
  737. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
  738. if (uniformValue.readyState >= 2) {
  739. if (uniformChanged && defined(texture)) {
  740. if (texture !== context.defaultTexture) {
  741. texture.destroy();
  742. }
  743. texture = undefined;
  744. }
  745. if (!defined(texture) || texture === context.defaultTexture) {
  746. const sampler = new Sampler({
  747. minificationFilter: material._minificationFilter,
  748. magnificationFilter: material._magnificationFilter,
  749. });
  750. texture = new Texture({
  751. context: context,
  752. source: uniformValue,
  753. sampler: sampler,
  754. });
  755. material._textures[uniformId] = texture;
  756. return;
  757. }
  758. texture.copyFrom({
  759. source: uniformValue,
  760. });
  761. } else if (!defined(texture)) {
  762. material._textures[uniformId] = context.defaultTexture;
  763. }
  764. return;
  765. }
  766. if (uniformValue instanceof Texture && uniformValue !== texture) {
  767. material._texturePaths[uniformId] = undefined;
  768. const tmp = material._textures[uniformId];
  769. if (defined(tmp) && tmp !== material._defaultTexture) {
  770. tmp.destroy();
  771. }
  772. material._textures[uniformId] = uniformValue;
  773. uniformDimensionsName = `${uniformId}Dimensions`;
  774. if (uniforms.hasOwnProperty(uniformDimensionsName)) {
  775. uniformDimensions = uniforms[uniformDimensionsName];
  776. uniformDimensions.x = uniformValue._width;
  777. uniformDimensions.y = uniformValue._height;
  778. }
  779. return;
  780. }
  781. if (uniformChanged && defined(texture) && uniformValueIsDefaultImage) {
  782. // If the newly-assigned texture is the default texture,
  783. // we don't need to wait for a new image to load before destroying
  784. // the old texture.
  785. if (texture !== material._defaultTexture) {
  786. texture.destroy();
  787. }
  788. texture = undefined;
  789. }
  790. if (!defined(texture)) {
  791. material._texturePaths[uniformId] = undefined;
  792. texture = material._textures[uniformId] = material._defaultTexture;
  793. uniformDimensionsName = `${uniformId}Dimensions`;
  794. if (uniforms.hasOwnProperty(uniformDimensionsName)) {
  795. uniformDimensions = uniforms[uniformDimensionsName];
  796. uniformDimensions.x = texture._width;
  797. uniformDimensions.y = texture._height;
  798. }
  799. }
  800. if (uniformValueIsDefaultImage) {
  801. return;
  802. }
  803. // When using the entity layer, the Resource objects get recreated on getValue because
  804. // they are clonable. That's why we check the url property for Resources
  805. // because the instances aren't the same and we keep trying to load the same
  806. // image if it fails to load.
  807. const isResource = uniformValue instanceof Resource;
  808. if (
  809. !defined(material._texturePaths[uniformId]) ||
  810. (isResource &&
  811. uniformValue.url !== material._texturePaths[uniformId].url) ||
  812. (!isResource && uniformValue !== material._texturePaths[uniformId])
  813. ) {
  814. if (typeof uniformValue === "string" || isResource) {
  815. const resource = isResource
  816. ? uniformValue
  817. : Resource.createIfNeeded(uniformValue);
  818. let promise;
  819. if (ktx2Regex.test(resource.url)) {
  820. promise = loadKTX2(resource.url);
  821. } else {
  822. promise = resource.fetchImage();
  823. }
  824. Promise.resolve(promise)
  825. .then(function (image) {
  826. material._loadedImages.push({
  827. id: uniformId,
  828. image: image,
  829. });
  830. })
  831. .catch(function () {
  832. if (defined(texture) && texture !== material._defaultTexture) {
  833. texture.destroy();
  834. }
  835. material._textures[uniformId] = material._defaultTexture;
  836. });
  837. } else if (
  838. uniformValue instanceof HTMLCanvasElement ||
  839. uniformValue instanceof HTMLImageElement
  840. ) {
  841. material._loadedImages.push({
  842. id: uniformId,
  843. image: uniformValue,
  844. });
  845. }
  846. material._texturePaths[uniformId] = uniformValue;
  847. }
  848. };
  849. }
  850. function createCubeMapUpdateFunction(uniformId) {
  851. return function (material, context) {
  852. const uniformValue = material.uniforms[uniformId];
  853. if (uniformValue instanceof CubeMap) {
  854. const tmp = material._textures[uniformId];
  855. if (tmp !== material._defaultTexture) {
  856. tmp.destroy();
  857. }
  858. material._texturePaths[uniformId] = undefined;
  859. material._textures[uniformId] = uniformValue;
  860. return;
  861. }
  862. if (!defined(material._textures[uniformId])) {
  863. material._texturePaths[uniformId] = undefined;
  864. material._textures[uniformId] = context.defaultCubeMap;
  865. }
  866. if (uniformValue === Material.DefaultCubeMapId) {
  867. return;
  868. }
  869. const path =
  870. uniformValue.positiveX +
  871. uniformValue.negativeX +
  872. uniformValue.positiveY +
  873. uniformValue.negativeY +
  874. uniformValue.positiveZ +
  875. uniformValue.negativeZ;
  876. if (path !== material._texturePaths[uniformId]) {
  877. const promises = [
  878. Resource.createIfNeeded(uniformValue.positiveX).fetchImage(),
  879. Resource.createIfNeeded(uniformValue.negativeX).fetchImage(),
  880. Resource.createIfNeeded(uniformValue.positiveY).fetchImage(),
  881. Resource.createIfNeeded(uniformValue.negativeY).fetchImage(),
  882. Resource.createIfNeeded(uniformValue.positiveZ).fetchImage(),
  883. Resource.createIfNeeded(uniformValue.negativeZ).fetchImage(),
  884. ];
  885. Promise.all(promises).then(function (images) {
  886. material._loadedCubeMaps.push({
  887. id: uniformId,
  888. images: images,
  889. });
  890. });
  891. material._texturePaths[uniformId] = path;
  892. }
  893. };
  894. }
  895. function createUniforms(material) {
  896. const uniforms = material._template.uniforms;
  897. for (const uniformId in uniforms) {
  898. if (uniforms.hasOwnProperty(uniformId)) {
  899. createUniform(material, uniformId);
  900. }
  901. }
  902. }
  903. // Writes uniform declarations to the shader file and connects uniform values with
  904. // corresponding material properties through the returnUniforms function.
  905. function createUniform(material, uniformId) {
  906. const strict = material._strict;
  907. const materialUniforms = material._template.uniforms;
  908. const uniformValue = materialUniforms[uniformId];
  909. const uniformType = getUniformType(uniformValue);
  910. //>>includeStart('debug', pragmas.debug);
  911. if (!defined(uniformType)) {
  912. throw new DeveloperError(
  913. `fabric: uniform '${uniformId}' has invalid type.`
  914. );
  915. }
  916. //>>includeEnd('debug');
  917. let replacedTokenCount;
  918. if (uniformType === "channels") {
  919. replacedTokenCount = replaceToken(material, uniformId, uniformValue, false);
  920. //>>includeStart('debug', pragmas.debug);
  921. if (replacedTokenCount === 0 && strict) {
  922. throw new DeveloperError(
  923. `strict: shader source does not use channels '${uniformId}'.`
  924. );
  925. }
  926. //>>includeEnd('debug');
  927. } else {
  928. // Since webgl doesn't allow texture dimension queries in glsl, create a uniform to do it.
  929. // Check if the shader source actually uses texture dimensions before creating the uniform.
  930. if (uniformType === "sampler2D") {
  931. const imageDimensionsUniformName = `${uniformId}Dimensions`;
  932. if (getNumberOfTokens(material, imageDimensionsUniformName) > 0) {
  933. materialUniforms[imageDimensionsUniformName] = {
  934. type: "ivec3",
  935. x: 1,
  936. y: 1,
  937. };
  938. createUniform(material, imageDimensionsUniformName);
  939. }
  940. }
  941. // Add uniform declaration to source code.
  942. const uniformDeclarationRegex = new RegExp(
  943. `uniform\\s+${uniformType}\\s+${uniformId}\\s*;`
  944. );
  945. if (!uniformDeclarationRegex.test(material.shaderSource)) {
  946. const uniformDeclaration = `uniform ${uniformType} ${uniformId};`;
  947. material.shaderSource = uniformDeclaration + material.shaderSource;
  948. }
  949. const newUniformId = `${uniformId}_${material._count++}`;
  950. replacedTokenCount = replaceToken(material, uniformId, newUniformId);
  951. //>>includeStart('debug', pragmas.debug);
  952. if (replacedTokenCount === 1 && strict) {
  953. throw new DeveloperError(
  954. `strict: shader source does not use uniform '${uniformId}'.`
  955. );
  956. }
  957. //>>includeEnd('debug');
  958. // Set uniform value
  959. material.uniforms[uniformId] = uniformValue;
  960. if (uniformType === "sampler2D") {
  961. material._uniforms[newUniformId] = function () {
  962. return material._textures[uniformId];
  963. };
  964. material._updateFunctions.push(createTexture2DUpdateFunction(uniformId));
  965. } else if (uniformType === "samplerCube") {
  966. material._uniforms[newUniformId] = function () {
  967. return material._textures[uniformId];
  968. };
  969. material._updateFunctions.push(createCubeMapUpdateFunction(uniformId));
  970. } else if (uniformType.indexOf("mat") !== -1) {
  971. const scratchMatrix = new matrixMap[uniformType]();
  972. material._uniforms[newUniformId] = function () {
  973. return matrixMap[uniformType].fromColumnMajorArray(
  974. material.uniforms[uniformId],
  975. scratchMatrix
  976. );
  977. };
  978. } else {
  979. material._uniforms[newUniformId] = function () {
  980. return material.uniforms[uniformId];
  981. };
  982. }
  983. }
  984. }
  985. // Determines the uniform type based on the uniform in the template.
  986. function getUniformType(uniformValue) {
  987. let uniformType = uniformValue.type;
  988. if (!defined(uniformType)) {
  989. const type = typeof uniformValue;
  990. if (type === "number") {
  991. uniformType = "float";
  992. } else if (type === "boolean") {
  993. uniformType = "bool";
  994. } else if (
  995. type === "string" ||
  996. uniformValue instanceof Resource ||
  997. uniformValue instanceof HTMLCanvasElement ||
  998. uniformValue instanceof HTMLImageElement
  999. ) {
  1000. if (/^([rgba]){1,4}$/i.test(uniformValue)) {
  1001. uniformType = "channels";
  1002. } else if (uniformValue === Material.DefaultCubeMapId) {
  1003. uniformType = "samplerCube";
  1004. } else {
  1005. uniformType = "sampler2D";
  1006. }
  1007. } else if (type === "object") {
  1008. if (Array.isArray(uniformValue)) {
  1009. if (
  1010. uniformValue.length === 4 ||
  1011. uniformValue.length === 9 ||
  1012. uniformValue.length === 16
  1013. ) {
  1014. uniformType = `mat${Math.sqrt(uniformValue.length)}`;
  1015. }
  1016. } else {
  1017. let numAttributes = 0;
  1018. for (const attribute in uniformValue) {
  1019. if (uniformValue.hasOwnProperty(attribute)) {
  1020. numAttributes += 1;
  1021. }
  1022. }
  1023. if (numAttributes >= 2 && numAttributes <= 4) {
  1024. uniformType = `vec${numAttributes}`;
  1025. } else if (numAttributes === 6) {
  1026. uniformType = "samplerCube";
  1027. }
  1028. }
  1029. }
  1030. }
  1031. return uniformType;
  1032. }
  1033. // Create all sub-materials by combining source and uniforms together.
  1034. function createSubMaterials(material) {
  1035. const strict = material._strict;
  1036. const subMaterialTemplates = material._template.materials;
  1037. for (const subMaterialId in subMaterialTemplates) {
  1038. if (subMaterialTemplates.hasOwnProperty(subMaterialId)) {
  1039. // Construct the sub-material.
  1040. const subMaterial = new Material({
  1041. strict: strict,
  1042. fabric: subMaterialTemplates[subMaterialId],
  1043. count: material._count,
  1044. });
  1045. material._count = subMaterial._count;
  1046. material._uniforms = combine(
  1047. material._uniforms,
  1048. subMaterial._uniforms,
  1049. true
  1050. );
  1051. material.materials[subMaterialId] = subMaterial;
  1052. material._translucentFunctions = material._translucentFunctions.concat(
  1053. subMaterial._translucentFunctions
  1054. );
  1055. // Make the material's czm_getMaterial unique by appending the sub-material type.
  1056. const originalMethodName = "czm_getMaterial";
  1057. const newMethodName = `${originalMethodName}_${material._count++}`;
  1058. replaceToken(subMaterial, originalMethodName, newMethodName);
  1059. material.shaderSource = subMaterial.shaderSource + material.shaderSource;
  1060. // Replace each material id with an czm_getMaterial method call.
  1061. const materialMethodCall = `${newMethodName}(materialInput)`;
  1062. const tokensReplacedCount = replaceToken(
  1063. material,
  1064. subMaterialId,
  1065. materialMethodCall
  1066. );
  1067. //>>includeStart('debug', pragmas.debug);
  1068. if (tokensReplacedCount === 0 && strict) {
  1069. throw new DeveloperError(
  1070. `strict: shader source does not use material '${subMaterialId}'.`
  1071. );
  1072. }
  1073. //>>includeEnd('debug');
  1074. }
  1075. }
  1076. }
  1077. // Used for searching or replacing a token in a material's shader source with something else.
  1078. // If excludePeriod is true, do not accept tokens that are preceded by periods.
  1079. // http://stackoverflow.com/questions/641407/javascript-negative-lookbehind-equivalent
  1080. function replaceToken(material, token, newToken, excludePeriod) {
  1081. excludePeriod = defaultValue(excludePeriod, true);
  1082. let count = 0;
  1083. const suffixChars = "([\\w])?";
  1084. const prefixChars = `([\\w${excludePeriod ? "." : ""}])?`;
  1085. const regExp = new RegExp(prefixChars + token + suffixChars, "g");
  1086. material.shaderSource = material.shaderSource.replace(regExp, function (
  1087. $0,
  1088. $1,
  1089. $2
  1090. ) {
  1091. if ($1 || $2) {
  1092. return $0;
  1093. }
  1094. count += 1;
  1095. return newToken;
  1096. });
  1097. return count;
  1098. }
  1099. function getNumberOfTokens(material, token, excludePeriod) {
  1100. return replaceToken(material, token, token, excludePeriod);
  1101. }
  1102. Material._materialCache = {
  1103. _materials: {},
  1104. addMaterial: function (type, materialTemplate) {
  1105. this._materials[type] = materialTemplate;
  1106. },
  1107. getMaterial: function (type) {
  1108. return this._materials[type];
  1109. },
  1110. };
  1111. /**
  1112. * Gets or sets the default texture uniform value.
  1113. * @type {String}
  1114. */
  1115. Material.DefaultImageId = "czm_defaultImage";
  1116. /**
  1117. * Gets or sets the default cube map texture uniform value.
  1118. * @type {String}
  1119. */
  1120. Material.DefaultCubeMapId = "czm_defaultCubeMap";
  1121. /**
  1122. * Gets the name of the color material.
  1123. * @type {String}
  1124. * @readonly
  1125. */
  1126. Material.ColorType = "Color";
  1127. Material._materialCache.addMaterial(Material.ColorType, {
  1128. fabric: {
  1129. type: Material.ColorType,
  1130. uniforms: {
  1131. color: new Color(1.0, 0.0, 0.0, 0.5),
  1132. },
  1133. components: {
  1134. diffuse: "color.rgb",
  1135. alpha: "color.a",
  1136. },
  1137. },
  1138. translucent: function (material) {
  1139. return material.uniforms.color.alpha < 1.0;
  1140. },
  1141. });
  1142. /**
  1143. * Gets the name of the image material.
  1144. * @type {String}
  1145. * @readonly
  1146. */
  1147. Material.ImageType = "Image";
  1148. Material._materialCache.addMaterial(Material.ImageType, {
  1149. fabric: {
  1150. type: Material.ImageType,
  1151. uniforms: {
  1152. image: Material.DefaultImageId,
  1153. repeat: new Cartesian2(1.0, 1.0),
  1154. color: new Color(1.0, 1.0, 1.0, 1.0),
  1155. },
  1156. components: {
  1157. diffuse:
  1158. "texture2D(image, fract(repeat * materialInput.st)).rgb * color.rgb",
  1159. alpha: "texture2D(image, fract(repeat * materialInput.st)).a * color.a",
  1160. },
  1161. },
  1162. translucent: function (material) {
  1163. return material.uniforms.color.alpha < 1.0;
  1164. },
  1165. });
  1166. /**
  1167. * Gets the name of the diffuce map material.
  1168. * @type {String}
  1169. * @readonly
  1170. */
  1171. Material.DiffuseMapType = "DiffuseMap";
  1172. Material._materialCache.addMaterial(Material.DiffuseMapType, {
  1173. fabric: {
  1174. type: Material.DiffuseMapType,
  1175. uniforms: {
  1176. image: Material.DefaultImageId,
  1177. channels: "rgb",
  1178. repeat: new Cartesian2(1.0, 1.0),
  1179. },
  1180. components: {
  1181. diffuse: "texture2D(image, fract(repeat * materialInput.st)).channels",
  1182. },
  1183. },
  1184. translucent: false,
  1185. });
  1186. /**
  1187. * Gets the name of the alpha map material.
  1188. * @type {String}
  1189. * @readonly
  1190. */
  1191. Material.AlphaMapType = "AlphaMap";
  1192. Material._materialCache.addMaterial(Material.AlphaMapType, {
  1193. fabric: {
  1194. type: Material.AlphaMapType,
  1195. uniforms: {
  1196. image: Material.DefaultImageId,
  1197. channel: "a",
  1198. repeat: new Cartesian2(1.0, 1.0),
  1199. },
  1200. components: {
  1201. alpha: "texture2D(image, fract(repeat * materialInput.st)).channel",
  1202. },
  1203. },
  1204. translucent: true,
  1205. });
  1206. /**
  1207. * Gets the name of the specular map material.
  1208. * @type {String}
  1209. * @readonly
  1210. */
  1211. Material.SpecularMapType = "SpecularMap";
  1212. Material._materialCache.addMaterial(Material.SpecularMapType, {
  1213. fabric: {
  1214. type: Material.SpecularMapType,
  1215. uniforms: {
  1216. image: Material.DefaultImageId,
  1217. channel: "r",
  1218. repeat: new Cartesian2(1.0, 1.0),
  1219. },
  1220. components: {
  1221. specular: "texture2D(image, fract(repeat * materialInput.st)).channel",
  1222. },
  1223. },
  1224. translucent: false,
  1225. });
  1226. /**
  1227. * Gets the name of the emmision map material.
  1228. * @type {String}
  1229. * @readonly
  1230. */
  1231. Material.EmissionMapType = "EmissionMap";
  1232. Material._materialCache.addMaterial(Material.EmissionMapType, {
  1233. fabric: {
  1234. type: Material.EmissionMapType,
  1235. uniforms: {
  1236. image: Material.DefaultImageId,
  1237. channels: "rgb",
  1238. repeat: new Cartesian2(1.0, 1.0),
  1239. },
  1240. components: {
  1241. emission: "texture2D(image, fract(repeat * materialInput.st)).channels",
  1242. },
  1243. },
  1244. translucent: false,
  1245. });
  1246. /**
  1247. * Gets the name of the bump map material.
  1248. * @type {String}
  1249. * @readonly
  1250. */
  1251. Material.BumpMapType = "BumpMap";
  1252. Material._materialCache.addMaterial(Material.BumpMapType, {
  1253. fabric: {
  1254. type: Material.BumpMapType,
  1255. uniforms: {
  1256. image: Material.DefaultImageId,
  1257. channel: "r",
  1258. strength: 0.8,
  1259. repeat: new Cartesian2(1.0, 1.0),
  1260. },
  1261. source: BumpMapMaterial,
  1262. },
  1263. translucent: false,
  1264. });
  1265. /**
  1266. * Gets the name of the normal map material.
  1267. * @type {String}
  1268. * @readonly
  1269. */
  1270. Material.NormalMapType = "NormalMap";
  1271. Material._materialCache.addMaterial(Material.NormalMapType, {
  1272. fabric: {
  1273. type: Material.NormalMapType,
  1274. uniforms: {
  1275. image: Material.DefaultImageId,
  1276. channels: "rgb",
  1277. strength: 0.8,
  1278. repeat: new Cartesian2(1.0, 1.0),
  1279. },
  1280. source: NormalMapMaterial,
  1281. },
  1282. translucent: false,
  1283. });
  1284. /**
  1285. * Gets the name of the grid material.
  1286. * @type {String}
  1287. * @readonly
  1288. */
  1289. Material.GridType = "Grid";
  1290. Material._materialCache.addMaterial(Material.GridType, {
  1291. fabric: {
  1292. type: Material.GridType,
  1293. uniforms: {
  1294. color: new Color(0.0, 1.0, 0.0, 1.0),
  1295. cellAlpha: 0.1,
  1296. lineCount: new Cartesian2(8.0, 8.0),
  1297. lineThickness: new Cartesian2(1.0, 1.0),
  1298. lineOffset: new Cartesian2(0.0, 0.0),
  1299. },
  1300. source: GridMaterial,
  1301. },
  1302. translucent: function (material) {
  1303. const uniforms = material.uniforms;
  1304. return uniforms.color.alpha < 1.0 || uniforms.cellAlpha < 1.0;
  1305. },
  1306. });
  1307. /**
  1308. * Gets the name of the stripe material.
  1309. * @type {String}
  1310. * @readonly
  1311. */
  1312. Material.StripeType = "Stripe";
  1313. Material._materialCache.addMaterial(Material.StripeType, {
  1314. fabric: {
  1315. type: Material.StripeType,
  1316. uniforms: {
  1317. horizontal: true,
  1318. evenColor: new Color(1.0, 1.0, 1.0, 0.5),
  1319. oddColor: new Color(0.0, 0.0, 1.0, 0.5),
  1320. offset: 0.0,
  1321. repeat: 5.0,
  1322. },
  1323. source: StripeMaterial,
  1324. },
  1325. translucent: function (material) {
  1326. const uniforms = material.uniforms;
  1327. return uniforms.evenColor.alpha < 1.0 || uniforms.oddColor.alpha < 1.0;
  1328. },
  1329. });
  1330. /**
  1331. * Gets the name of the checkerboard material.
  1332. * @type {String}
  1333. * @readonly
  1334. */
  1335. Material.CheckerboardType = "Checkerboard";
  1336. Material._materialCache.addMaterial(Material.CheckerboardType, {
  1337. fabric: {
  1338. type: Material.CheckerboardType,
  1339. uniforms: {
  1340. lightColor: new Color(1.0, 1.0, 1.0, 0.5),
  1341. darkColor: new Color(0.0, 0.0, 0.0, 0.5),
  1342. repeat: new Cartesian2(5.0, 5.0),
  1343. },
  1344. source: CheckerboardMaterial,
  1345. },
  1346. translucent: function (material) {
  1347. const uniforms = material.uniforms;
  1348. return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 1.0;
  1349. },
  1350. });
  1351. /**
  1352. * Gets the name of the dot material.
  1353. * @type {String}
  1354. * @readonly
  1355. */
  1356. Material.DotType = "Dot";
  1357. Material._materialCache.addMaterial(Material.DotType, {
  1358. fabric: {
  1359. type: Material.DotType,
  1360. uniforms: {
  1361. lightColor: new Color(1.0, 1.0, 0.0, 0.75),
  1362. darkColor: new Color(0.0, 1.0, 1.0, 0.75),
  1363. repeat: new Cartesian2(5.0, 5.0),
  1364. },
  1365. source: DotMaterial,
  1366. },
  1367. translucent: function (material) {
  1368. const uniforms = material.uniforms;
  1369. return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 1.0;
  1370. },
  1371. });
  1372. /**
  1373. * Gets the name of the water material.
  1374. * @type {String}
  1375. * @readonly
  1376. */
  1377. Material.WaterType = "Water";
  1378. Material._materialCache.addMaterial(Material.WaterType, {
  1379. fabric: {
  1380. type: Material.WaterType,
  1381. uniforms: {
  1382. baseWaterColor: new Color(0.2, 0.3, 0.6, 1.0),
  1383. blendColor: new Color(0.0, 1.0, 0.699, 1.0),
  1384. specularMap: Material.DefaultImageId,
  1385. normalMap: Material.DefaultImageId,
  1386. frequency: 10.0,
  1387. animationSpeed: 0.01,
  1388. amplitude: 1.0,
  1389. specularIntensity: 0.5,
  1390. fadeFactor: 1.0,
  1391. },
  1392. source: WaterMaterial,
  1393. },
  1394. translucent: function (material) {
  1395. const uniforms = material.uniforms;
  1396. return (
  1397. uniforms.baseWaterColor.alpha < 1.0 || uniforms.blendColor.alpha < 1.0
  1398. );
  1399. },
  1400. });
  1401. /**
  1402. * Gets the name of the rim lighting material.
  1403. * @type {String}
  1404. * @readonly
  1405. */
  1406. Material.RimLightingType = "RimLighting";
  1407. Material._materialCache.addMaterial(Material.RimLightingType, {
  1408. fabric: {
  1409. type: Material.RimLightingType,
  1410. uniforms: {
  1411. color: new Color(1.0, 0.0, 0.0, 0.7),
  1412. rimColor: new Color(1.0, 1.0, 1.0, 0.4),
  1413. width: 0.3,
  1414. },
  1415. source: RimLightingMaterial,
  1416. },
  1417. translucent: function (material) {
  1418. const uniforms = material.uniforms;
  1419. return uniforms.color.alpha < 1.0 || uniforms.rimColor.alpha < 1.0;
  1420. },
  1421. });
  1422. /**
  1423. * Gets the name of the fade material.
  1424. * @type {String}
  1425. * @readonly
  1426. */
  1427. Material.FadeType = "Fade";
  1428. Material._materialCache.addMaterial(Material.FadeType, {
  1429. fabric: {
  1430. type: Material.FadeType,
  1431. uniforms: {
  1432. fadeInColor: new Color(1.0, 0.0, 0.0, 1.0),
  1433. fadeOutColor: new Color(0.0, 0.0, 0.0, 0.0),
  1434. maximumDistance: 0.5,
  1435. repeat: true,
  1436. fadeDirection: {
  1437. x: true,
  1438. y: true,
  1439. },
  1440. time: new Cartesian2(0.5, 0.5),
  1441. },
  1442. source: FadeMaterial,
  1443. },
  1444. translucent: function (material) {
  1445. const uniforms = material.uniforms;
  1446. return (
  1447. uniforms.fadeInColor.alpha < 1.0 || uniforms.fadeOutColor.alpha < 1.0
  1448. );
  1449. },
  1450. });
  1451. /**
  1452. * Gets the name of the polyline arrow material.
  1453. * @type {String}
  1454. * @readonly
  1455. */
  1456. Material.PolylineArrowType = "PolylineArrow";
  1457. Material._materialCache.addMaterial(Material.PolylineArrowType, {
  1458. fabric: {
  1459. type: Material.PolylineArrowType,
  1460. uniforms: {
  1461. color: new Color(1.0, 1.0, 1.0, 1.0),
  1462. },
  1463. source: PolylineArrowMaterial,
  1464. },
  1465. translucent: true,
  1466. });
  1467. /**
  1468. * Gets the name of the polyline glow material.
  1469. * @type {String}
  1470. * @readonly
  1471. */
  1472. Material.PolylineDashType = "PolylineDash";
  1473. Material._materialCache.addMaterial(Material.PolylineDashType, {
  1474. fabric: {
  1475. type: Material.PolylineDashType,
  1476. uniforms: {
  1477. color: new Color(1.0, 0.0, 1.0, 1.0),
  1478. gapColor: new Color(0.0, 0.0, 0.0, 0.0),
  1479. dashLength: 16.0,
  1480. dashPattern: 255.0,
  1481. },
  1482. source: PolylineDashMaterial,
  1483. },
  1484. translucent: true,
  1485. });
  1486. /**
  1487. * Gets the name of the polyline glow material.
  1488. * @type {String}
  1489. * @readonly
  1490. */
  1491. Material.PolylineGlowType = "PolylineGlow";
  1492. Material._materialCache.addMaterial(Material.PolylineGlowType, {
  1493. fabric: {
  1494. type: Material.PolylineGlowType,
  1495. uniforms: {
  1496. color: new Color(0.0, 0.5, 1.0, 1.0),
  1497. glowPower: 0.25,
  1498. taperPower: 1.0,
  1499. },
  1500. source: PolylineGlowMaterial,
  1501. },
  1502. translucent: true,
  1503. });
  1504. /**
  1505. * Gets the name of the polyline outline material.
  1506. * @type {String}
  1507. * @readonly
  1508. */
  1509. Material.PolylineOutlineType = "PolylineOutline";
  1510. Material._materialCache.addMaterial(Material.PolylineOutlineType, {
  1511. fabric: {
  1512. type: Material.PolylineOutlineType,
  1513. uniforms: {
  1514. color: new Color(1.0, 1.0, 1.0, 1.0),
  1515. outlineColor: new Color(1.0, 0.0, 0.0, 1.0),
  1516. outlineWidth: 1.0,
  1517. },
  1518. source: PolylineOutlineMaterial,
  1519. },
  1520. translucent: function (material) {
  1521. const uniforms = material.uniforms;
  1522. return uniforms.color.alpha < 1.0 || uniforms.outlineColor.alpha < 1.0;
  1523. },
  1524. });
  1525. /**
  1526. * Gets the name of the elevation contour material.
  1527. * @type {String}
  1528. * @readonly
  1529. */
  1530. Material.ElevationContourType = "ElevationContour";
  1531. Material._materialCache.addMaterial(Material.ElevationContourType, {
  1532. fabric: {
  1533. type: Material.ElevationContourType,
  1534. uniforms: {
  1535. spacing: 100.0,
  1536. color: new Color(1.0, 0.0, 0.0, 1.0),
  1537. width: 1.0,
  1538. },
  1539. source: ElevationContourMaterial,
  1540. },
  1541. translucent: false,
  1542. });
  1543. /**
  1544. * Gets the name of the elevation contour material.
  1545. * @type {String}
  1546. * @readonly
  1547. */
  1548. Material.ElevationRampType = "ElevationRamp";
  1549. Material._materialCache.addMaterial(Material.ElevationRampType, {
  1550. fabric: {
  1551. type: Material.ElevationRampType,
  1552. uniforms: {
  1553. image: Material.DefaultImageId,
  1554. minimumHeight: 0.0,
  1555. maximumHeight: 10000.0,
  1556. },
  1557. source: ElevationRampMaterial,
  1558. },
  1559. translucent: false,
  1560. });
  1561. /**
  1562. * Gets the name of the slope ramp material.
  1563. * @type {String}
  1564. * @readonly
  1565. */
  1566. Material.SlopeRampMaterialType = "SlopeRamp";
  1567. Material._materialCache.addMaterial(Material.SlopeRampMaterialType, {
  1568. fabric: {
  1569. type: Material.SlopeRampMaterialType,
  1570. uniforms: {
  1571. image: Material.DefaultImageId,
  1572. },
  1573. source: SlopeRampMaterial,
  1574. },
  1575. translucent: false,
  1576. });
  1577. /**
  1578. * Gets the name of the aspect ramp material.
  1579. * @type {String}
  1580. * @readonly
  1581. */
  1582. Material.AspectRampMaterialType = "AspectRamp";
  1583. Material._materialCache.addMaterial(Material.AspectRampMaterialType, {
  1584. fabric: {
  1585. type: Material.AspectRampMaterialType,
  1586. uniforms: {
  1587. image: Material.DefaultImageId,
  1588. },
  1589. source: AspectRampMaterial,
  1590. },
  1591. translucent: false,
  1592. });
  1593. /**
  1594. * Gets the name of the elevation band material.
  1595. * @type {String}
  1596. * @readonly
  1597. */
  1598. Material.ElevationBandType = "ElevationBand";
  1599. Material._materialCache.addMaterial(Material.ElevationBandType, {
  1600. fabric: {
  1601. type: Material.ElevationBandType,
  1602. uniforms: {
  1603. heights: Material.DefaultImageId,
  1604. colors: Material.DefaultImageId,
  1605. },
  1606. source: ElevationBandMaterial,
  1607. },
  1608. translucent: true,
  1609. });
  1610. export default Material;