Material.java 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. /*
  2. * Copyright (c) 2009-2012 jMonkeyEngine
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package com.jme3.material;
  33. import com.jme3.asset.AssetKey;
  34. import com.jme3.asset.AssetManager;
  35. import com.jme3.asset.CloneableSmartAsset;
  36. import com.jme3.export.*;
  37. import com.jme3.light.*;
  38. import com.jme3.material.RenderState.BlendMode;
  39. import com.jme3.material.RenderState.FaceCullMode;
  40. import com.jme3.material.TechniqueDef.LightMode;
  41. import com.jme3.material.TechniqueDef.ShadowMode;
  42. import com.jme3.math.*;
  43. import com.jme3.renderer.Caps;
  44. import com.jme3.renderer.GL1Renderer;
  45. import com.jme3.renderer.RenderManager;
  46. import com.jme3.renderer.Renderer;
  47. import com.jme3.renderer.queue.RenderQueue.Bucket;
  48. import com.jme3.scene.Geometry;
  49. import com.jme3.shader.Shader;
  50. import com.jme3.shader.Uniform;
  51. import com.jme3.shader.UniformBindingManager;
  52. import com.jme3.shader.VarType;
  53. import com.jme3.texture.Texture;
  54. import com.jme3.util.ListMap;
  55. import com.jme3.util.TempVars;
  56. import java.io.IOException;
  57. import java.util.*;
  58. import java.util.logging.Level;
  59. import java.util.logging.Logger;
  60. /**
  61. * <code>Material</code> describes the rendering style for a given
  62. * {@link Geometry}.
  63. * <p>A material is essentially a list of {@link MatParam parameters},
  64. * those parameters map to uniforms which are defined in a shader.
  65. * Setting the parameters can modify the behavior of a
  66. * shader.
  67. * <p/>
  68. *
  69. * @author Kirill Vainer
  70. */
  71. public class Material implements CloneableSmartAsset, Cloneable, Savable {
  72. // Version #2: Fixed issue with RenderState.apply*** flags not getting exported
  73. public static final int SAVABLE_VERSION = 2;
  74. private static final Logger logger = Logger.getLogger(Material.class.getName());
  75. private static final RenderState additiveLight = new RenderState();
  76. private static final RenderState depthOnly = new RenderState();
  77. private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
  78. static {
  79. depthOnly.setDepthTest(true);
  80. depthOnly.setDepthWrite(true);
  81. depthOnly.setFaceCullMode(RenderState.FaceCullMode.Back);
  82. depthOnly.setColorWrite(false);
  83. additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
  84. additiveLight.setDepthWrite(false);
  85. }
  86. private AssetKey key;
  87. private String name;
  88. private MaterialDef def;
  89. private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
  90. private Technique technique;
  91. private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
  92. private int nextTexUnit = 0;
  93. private RenderState additionalState = null;
  94. private RenderState mergedRenderState = new RenderState();
  95. private boolean transparent = false;
  96. private boolean receivesShadows = false;
  97. private int sortingId = -1;
  98. private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
  99. public Material(MaterialDef def) {
  100. if (def == null) {
  101. throw new NullPointerException("Material definition cannot be null");
  102. }
  103. this.def = def;
  104. // Load default values from definition (if any)
  105. for (MatParam param : def.getMaterialParams()) {
  106. if (param.getValue() != null) {
  107. setParam(param.getName(), param.getVarType(), param.getValue());
  108. }
  109. }
  110. }
  111. public Material(AssetManager contentMan, String defName) {
  112. this((MaterialDef) contentMan.loadAsset(new AssetKey(defName)));
  113. }
  114. /**
  115. * Do not use this constructor. Serialization purposes only.
  116. */
  117. public Material() {
  118. }
  119. /**
  120. * Returns the asset key name of the asset from which this material was loaded.
  121. *
  122. * <p>This value will be <code>null</code> unless this material was loaded
  123. * from a .j3m file.
  124. *
  125. * @return Asset key name of the j3m file
  126. */
  127. public String getAssetName() {
  128. return key != null ? key.getName() : null;
  129. }
  130. /**
  131. * @return the name of the material (not the same as the asset name), the returned value can be null
  132. */
  133. public String getName() {
  134. return name;
  135. }
  136. /**
  137. * This method sets the name of the material.
  138. * The name is not the same as the asset name.
  139. * It can be null and there is no guarantee of its uniqness.
  140. * @param name the name of the material
  141. */
  142. public void setName(String name) {
  143. this.name = name;
  144. }
  145. public void setKey(AssetKey key) {
  146. this.key = key;
  147. }
  148. public AssetKey getKey() {
  149. return key;
  150. }
  151. /**
  152. * Returns the sorting ID or sorting index for this material.
  153. *
  154. * <p>The sorting ID is used internally by the system to sort rendering
  155. * of geometries. It sorted to reduce shader switches, if the shaders
  156. * are equal, then it is sorted by textures.
  157. *
  158. * @return The sorting ID used for sorting geometries for rendering.
  159. */
  160. public int getSortId() {
  161. Technique t = getActiveTechnique();
  162. if (sortingId == -1 && t != null && t.getShader() != null) {
  163. int texId = -1;
  164. for (int i = 0; i < paramValues.size(); i++) {
  165. MatParam param = paramValues.getValue(i);
  166. if (param instanceof MatParamTexture) {
  167. MatParamTexture tex = (MatParamTexture) param;
  168. if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
  169. if (texId == -1) {
  170. texId = 0;
  171. }
  172. texId += tex.getTextureValue().getImage().getId() % 0xff;
  173. }
  174. }
  175. }
  176. sortingId = texId + t.getShader().getId() * 1000;
  177. }
  178. return sortingId;
  179. }
  180. /**
  181. * Clones this material. The result is returned.
  182. */
  183. @Override
  184. public Material clone() {
  185. try {
  186. Material mat = (Material) super.clone();
  187. if (additionalState != null) {
  188. mat.additionalState = additionalState.clone();
  189. }
  190. mat.technique = null;
  191. mat.techniques = new HashMap<String, Technique>();
  192. mat.paramValues = new ListMap<String, MatParam>();
  193. for (int i = 0; i < paramValues.size(); i++) {
  194. Map.Entry<String, MatParam> entry = paramValues.getEntry(i);
  195. mat.paramValues.put(entry.getKey(), entry.getValue().clone());
  196. }
  197. return mat;
  198. } catch (CloneNotSupportedException ex) {
  199. throw new AssertionError(ex);
  200. }
  201. }
  202. /**
  203. * Compares two materials and returns true if they are equal.
  204. * This methods compare definition, parameters, additional render states.
  205. * Since materials are mutable objects, implementing equals() properly is not possible,
  206. * hence the name contentEquals().
  207. *
  208. * @param otherObj the material to compare to this material
  209. * @return true if the materials are equal.
  210. */
  211. public boolean contentEquals(Object otherObj) {
  212. if (!(otherObj instanceof Material)) {
  213. return false;
  214. }
  215. Material other = (Material) otherObj;
  216. // Early exit if the material are the same object
  217. if (this == other) {
  218. return true;
  219. }
  220. // Check material definition
  221. if (this.getMaterialDef() != other.getMaterialDef()) {
  222. return false;
  223. }
  224. // Early exit if the size of the params is different
  225. if (this.paramValues.size() != other.paramValues.size()) {
  226. return false;
  227. }
  228. // Checking technique
  229. if (this.technique != null || other.technique != null) {
  230. // Techniques are considered equal if their names are the same
  231. // E.g. if user chose custom technique for one material but
  232. // uses default technique for other material, the materials
  233. // are not equal.
  234. String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default";
  235. String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default";
  236. if (!thisDefName.equals(otherDefName)) {
  237. return false;
  238. }
  239. }
  240. // Comparing parameters
  241. for (String paramKey : paramValues.keySet()) {
  242. MatParam thisParam = this.getParam(paramKey);
  243. MatParam otherParam = other.getParam(paramKey);
  244. // This param does not exist in compared mat
  245. if (otherParam == null) {
  246. return false;
  247. }
  248. if (!otherParam.equals(thisParam)) {
  249. return false;
  250. }
  251. }
  252. // Comparing additional render states
  253. if (additionalState == null) {
  254. if (other.additionalState != null) {
  255. return false;
  256. }
  257. } else {
  258. if (!additionalState.equals(other.additionalState)) {
  259. return false;
  260. }
  261. }
  262. return true;
  263. }
  264. /**
  265. * Works like {@link Object#hashCode() } except it may change together with the material as the material is mutable by definition.
  266. */
  267. public int contentHashCode() {
  268. int hash = 7;
  269. hash = 29 * hash + (this.def != null ? this.def.hashCode() : 0);
  270. hash = 29 * hash + (this.paramValues != null ? this.paramValues.hashCode() : 0);
  271. hash = 29 * hash + (this.technique != null ? this.technique.getDef().getName().hashCode() : 0);
  272. hash = 29 * hash + (this.additionalState != null ? this.additionalState.contentHashCode() : 0);
  273. return hash;
  274. }
  275. /**
  276. * Returns the currently active technique.
  277. * <p>
  278. * The technique is selected automatically by the {@link RenderManager}
  279. * based on system capabilities. Users may select their own
  280. * technique by using
  281. * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }.
  282. *
  283. * @return the currently active technique.
  284. *
  285. * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
  286. */
  287. public Technique getActiveTechnique() {
  288. return technique;
  289. }
  290. /**
  291. * Check if the transparent value marker is set on this material.
  292. * @return True if the transparent value marker is set on this material.
  293. * @see #setTransparent(boolean)
  294. */
  295. public boolean isTransparent() {
  296. return transparent;
  297. }
  298. /**
  299. * Set the transparent value marker.
  300. *
  301. * <p>This value is merely a marker, by itself it does nothing.
  302. * Generally model loaders will use this marker to indicate further
  303. * up that the material is transparent and therefore any geometries
  304. * using it should be put into the {@link Bucket#Transparent transparent
  305. * bucket}.
  306. *
  307. * @param transparent the transparent value marker.
  308. */
  309. public void setTransparent(boolean transparent) {
  310. this.transparent = transparent;
  311. }
  312. /**
  313. * Check if the material should receive shadows or not.
  314. *
  315. * @return True if the material should receive shadows.
  316. *
  317. * @see Material#setReceivesShadows(boolean)
  318. */
  319. public boolean isReceivesShadows() {
  320. return receivesShadows;
  321. }
  322. /**
  323. * Set if the material should receive shadows or not.
  324. *
  325. * <p>This value is merely a marker, by itself it does nothing.
  326. * Generally model loaders will use this marker to indicate
  327. * the material should receive shadows and therefore any
  328. * geometries using it should have the {@link ShadowMode#Receive} set
  329. * on them.
  330. *
  331. * @param receivesShadows if the material should receive shadows or not.
  332. */
  333. public void setReceivesShadows(boolean receivesShadows) {
  334. this.receivesShadows = receivesShadows;
  335. }
  336. /**
  337. * Acquire the additional {@link RenderState render state} to apply
  338. * for this material.
  339. *
  340. * <p>The first call to this method will create an additional render
  341. * state which can be modified by the user to apply any render
  342. * states in addition to the ones used by the renderer. Only render
  343. * states which are modified in the additional render state will be applied.
  344. *
  345. * @return The additional render state.
  346. */
  347. public RenderState getAdditionalRenderState() {
  348. if (additionalState == null) {
  349. additionalState = RenderState.ADDITIONAL.clone();
  350. }
  351. return additionalState;
  352. }
  353. /**
  354. * Get the material definition (j3md file info) that <code>this</code>
  355. * material is implementing.
  356. *
  357. * @return the material definition this material implements.
  358. */
  359. public MaterialDef getMaterialDef() {
  360. return def;
  361. }
  362. /**
  363. * Returns the parameter set on this material with the given name,
  364. * returns <code>null</code> if the parameter is not set.
  365. *
  366. * @param name The parameter name to look up.
  367. * @return The MatParam if set, or null if not set.
  368. */
  369. public MatParam getParam(String name) {
  370. return paramValues.get(name);
  371. }
  372. /**
  373. * Returns the texture parameter set on this material with the given name,
  374. * returns <code>null</code> if the parameter is not set.
  375. *
  376. * @param name The parameter name to look up.
  377. * @return The MatParamTexture if set, or null if not set.
  378. */
  379. public MatParamTexture getTextureParam(String name) {
  380. MatParam param = paramValues.get(name);
  381. if (param instanceof MatParamTexture) {
  382. return (MatParamTexture) param;
  383. }
  384. return null;
  385. }
  386. /**
  387. * Returns a collection of all parameters set on this material.
  388. *
  389. * @return a collection of all parameters set on this material.
  390. *
  391. * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
  392. */
  393. public Collection<MatParam> getParams() {
  394. return paramValues.values();
  395. }
  396. /**
  397. * Check if setting the parameter given the type and name is allowed.
  398. * @param type The type that the "set" function is designed to set
  399. * @param name The name of the parameter
  400. */
  401. private void checkSetParam(VarType type, String name) {
  402. MatParam paramDef = def.getMaterialParam(name);
  403. if (paramDef == null) {
  404. throw new IllegalArgumentException("Material parameter is not defined: " + name);
  405. }
  406. if (type != null && paramDef.getVarType() != type) {
  407. logger.log(Level.WARNING, "Material parameter being set: {0} with "
  408. + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()});
  409. }
  410. }
  411. /**
  412. * Pass a parameter to the material shader.
  413. *
  414. * @param name the name of the parameter defined in the material definition (j3md)
  415. * @param type the type of the parameter {@link VarType}
  416. * @param value the value of the parameter
  417. */
  418. public void setParam(String name, VarType type, Object value) {
  419. checkSetParam(type, name);
  420. if (type.isTextureType()) {
  421. setTextureParam(name, type, (Texture)value);
  422. } else {
  423. MatParam val = getParam(name);
  424. if (val == null) {
  425. MatParam paramDef = def.getMaterialParam(name);
  426. paramValues.put(name, new MatParam(type, name, value, paramDef.getFixedFuncBinding()));
  427. } else {
  428. val.setValue(value);
  429. }
  430. if (technique != null) {
  431. technique.notifyParamChanged(name, type, value);
  432. }
  433. }
  434. }
  435. /**
  436. * Clear a parameter from this material. The parameter must exist
  437. * @param name the name of the parameter to clear
  438. */
  439. public void clearParam(String name) {
  440. checkSetParam(null, name);
  441. MatParam matParam = getParam(name);
  442. if (matParam == null) {
  443. return;
  444. }
  445. paramValues.remove(name);
  446. if (matParam instanceof MatParamTexture) {
  447. int texUnit = ((MatParamTexture) matParam).getUnit();
  448. nextTexUnit--;
  449. for (MatParam param : paramValues.values()) {
  450. if (param instanceof MatParamTexture) {
  451. MatParamTexture texParam = (MatParamTexture) param;
  452. if (texParam.getUnit() > texUnit) {
  453. texParam.setUnit(texParam.getUnit() - 1);
  454. }
  455. }
  456. }
  457. sortingId = -1;
  458. }
  459. if (technique != null) {
  460. technique.notifyParamChanged(name, null, null);
  461. }
  462. }
  463. /**
  464. * Set a texture parameter.
  465. *
  466. * @param name The name of the parameter
  467. * @param type The variable type {@link VarType}
  468. * @param value The texture value of the parameter.
  469. *
  470. * @throws IllegalArgumentException is value is null
  471. */
  472. public void setTextureParam(String name, VarType type, Texture value) {
  473. if (value == null) {
  474. throw new IllegalArgumentException();
  475. }
  476. checkSetParam(type, name);
  477. MatParamTexture val = getTextureParam(name);
  478. if (val == null) {
  479. paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++));
  480. } else {
  481. val.setTextureValue(value);
  482. }
  483. if (technique != null) {
  484. technique.notifyParamChanged(name, type, nextTexUnit - 1);
  485. }
  486. // need to recompute sort ID
  487. sortingId = -1;
  488. }
  489. /**
  490. * Pass a texture to the material shader.
  491. *
  492. * @param name the name of the texture defined in the material definition
  493. * (j3md) (for example Texture for Lighting.j3md)
  494. * @param value the Texture object previously loaded by the asset manager
  495. */
  496. public void setTexture(String name, Texture value) {
  497. if (value == null) {
  498. // clear it
  499. clearParam(name);
  500. return;
  501. }
  502. VarType paramType = null;
  503. switch (value.getType()) {
  504. case TwoDimensional:
  505. paramType = VarType.Texture2D;
  506. break;
  507. case TwoDimensionalArray:
  508. paramType = VarType.TextureArray;
  509. break;
  510. case ThreeDimensional:
  511. paramType = VarType.Texture3D;
  512. break;
  513. case CubeMap:
  514. paramType = VarType.TextureCubeMap;
  515. break;
  516. default:
  517. throw new UnsupportedOperationException("Unknown texture type: " + value.getType());
  518. }
  519. setTextureParam(name, paramType, value);
  520. }
  521. /**
  522. * Pass a Matrix4f to the material shader.
  523. *
  524. * @param name the name of the matrix defined in the material definition (j3md)
  525. * @param value the Matrix4f object
  526. */
  527. public void setMatrix4(String name, Matrix4f value) {
  528. setParam(name, VarType.Matrix4, value);
  529. }
  530. /**
  531. * Pass a boolean to the material shader.
  532. *
  533. * @param name the name of the boolean defined in the material definition (j3md)
  534. * @param value the boolean value
  535. */
  536. public void setBoolean(String name, boolean value) {
  537. setParam(name, VarType.Boolean, value);
  538. }
  539. /**
  540. * Pass a float to the material shader.
  541. *
  542. * @param name the name of the float defined in the material definition (j3md)
  543. * @param value the float value
  544. */
  545. public void setFloat(String name, float value) {
  546. setParam(name, VarType.Float, value);
  547. }
  548. /**
  549. * Pass an int to the material shader.
  550. *
  551. * @param name the name of the int defined in the material definition (j3md)
  552. * @param value the int value
  553. */
  554. public void setInt(String name, int value) {
  555. setParam(name, VarType.Int, value);
  556. }
  557. /**
  558. * Pass a Color to the material shader.
  559. *
  560. * @param name the name of the color defined in the material definition (j3md)
  561. * @param value the ColorRGBA value
  562. */
  563. public void setColor(String name, ColorRGBA value) {
  564. setParam(name, VarType.Vector4, value);
  565. }
  566. /**
  567. * Pass a Vector2f to the material shader.
  568. *
  569. * @param name the name of the Vector2f defined in the material definition (j3md)
  570. * @param value the Vector2f value
  571. */
  572. public void setVector2(String name, Vector2f value) {
  573. setParam(name, VarType.Vector2, value);
  574. }
  575. /**
  576. * Pass a Vector3f to the material shader.
  577. *
  578. * @param name the name of the Vector3f defined in the material definition (j3md)
  579. * @param value the Vector3f value
  580. */
  581. public void setVector3(String name, Vector3f value) {
  582. setParam(name, VarType.Vector3, value);
  583. }
  584. /**
  585. * Pass a Vector4f to the material shader.
  586. *
  587. * @param name the name of the Vector4f defined in the material definition (j3md)
  588. * @param value the Vector4f value
  589. */
  590. public void setVector4(String name, Vector4f value) {
  591. setParam(name, VarType.Vector4, value);
  592. }
  593. private ColorRGBA getAmbientColor(LightList lightList) {
  594. ambientLightColor.set(0, 0, 0, 1);
  595. for (int j = 0; j < lightList.size(); j++) {
  596. Light l = lightList.get(j);
  597. if (l instanceof AmbientLight) {
  598. ambientLightColor.addLocal(l.getColor());
  599. }
  600. }
  601. ambientLightColor.a = 1.0f;
  602. return ambientLightColor;
  603. }
  604. /**
  605. * Uploads the lights in the light list as two uniform arrays.<br/><br/> *
  606. * <p>
  607. * <code>uniform vec4 g_LightColor[numLights];</code><br/> //
  608. * g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
  609. * g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
  610. * 2 = Spot. <br/> <br/>
  611. * <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
  612. * g_LightPosition.xyz is the position of the light (for point lights)<br/>
  613. * // or the direction of the light (for directional lights).<br/> //
  614. * g_LightPosition.w is the inverse radius (1/r) of the light (for
  615. * attenuation) <br/> </p>
  616. */
  617. protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
  618. if (numLights == 0) { // this shader does not do lighting, ignore.
  619. return;
  620. }
  621. LightList lightList = g.getWorldLightList();
  622. Uniform lightColor = shader.getUniform("g_LightColor");
  623. Uniform lightPos = shader.getUniform("g_LightPosition");
  624. Uniform lightDir = shader.getUniform("g_LightDirection");
  625. lightColor.setVector4Length(numLights);
  626. lightPos.setVector4Length(numLights);
  627. lightDir.setVector4Length(numLights);
  628. Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
  629. ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
  630. int lightIndex = 0;
  631. for (int i = 0; i < numLights; i++) {
  632. if (lightList.size() <= i) {
  633. lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
  634. lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
  635. } else {
  636. Light l = lightList.get(i);
  637. ColorRGBA color = l.getColor();
  638. lightColor.setVector4InArray(color.getRed(),
  639. color.getGreen(),
  640. color.getBlue(),
  641. l.getType().getId(),
  642. i);
  643. switch (l.getType()) {
  644. case Directional:
  645. DirectionalLight dl = (DirectionalLight) l;
  646. Vector3f dir = dl.getDirection();
  647. lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);
  648. break;
  649. case Point:
  650. PointLight pl = (PointLight) l;
  651. Vector3f pos = pl.getPosition();
  652. float invRadius = pl.getInvRadius();
  653. lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
  654. break;
  655. case Spot:
  656. SpotLight sl = (SpotLight) l;
  657. Vector3f pos2 = sl.getPosition();
  658. Vector3f dir2 = sl.getDirection();
  659. float invRange = sl.getInvSpotRange();
  660. float spotAngleCos = sl.getPackedAngleCos();
  661. lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);
  662. lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);
  663. break;
  664. case Ambient:
  665. // skip this light. Does not increase lightIndex
  666. continue;
  667. default:
  668. throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
  669. }
  670. }
  671. lightIndex++;
  672. }
  673. while (lightIndex < numLights) {
  674. lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
  675. lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
  676. lightIndex++;
  677. }
  678. }
  679. protected void renderMultipassLighting(Shader shader, Geometry g, RenderManager rm) {
  680. Renderer r = rm.getRenderer();
  681. LightList lightList = g.getWorldLightList();
  682. Uniform lightDir = shader.getUniform("g_LightDirection");
  683. Uniform lightColor = shader.getUniform("g_LightColor");
  684. Uniform lightPos = shader.getUniform("g_LightPosition");
  685. Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
  686. boolean isFirstLight = true;
  687. boolean isSecondLight = false;
  688. for (int i = 0; i < lightList.size(); i++) {
  689. Light l = lightList.get(i);
  690. if (l instanceof AmbientLight) {
  691. continue;
  692. }
  693. if (isFirstLight) {
  694. // set ambient color for first light only
  695. ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
  696. isFirstLight = false;
  697. isSecondLight = true;
  698. } else if (isSecondLight) {
  699. ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
  700. // apply additive blending for 2nd and future lights
  701. r.applyRenderState(additiveLight);
  702. isSecondLight = false;
  703. }
  704. TempVars vars = TempVars.get();
  705. Quaternion tmpLightDirection = vars.quat1;
  706. Quaternion tmpLightPosition = vars.quat2;
  707. ColorRGBA tmpLightColor = vars.color;
  708. Vector4f tmpVec = vars.vect4f;
  709. ColorRGBA color = l.getColor();
  710. tmpLightColor.set(color);
  711. tmpLightColor.a = l.getType().getId();
  712. lightColor.setValue(VarType.Vector4, tmpLightColor);
  713. switch (l.getType()) {
  714. case Directional:
  715. DirectionalLight dl = (DirectionalLight) l;
  716. Vector3f dir = dl.getDirection();
  717. //FIXME : there is an inconstencie here due to backward
  718. //compatibility of the lighting shader.
  719. //The directional light direction is passed in the
  720. //LightPosition uniform. The lightinf shader needs to be
  721. //reworked though in order to fix this.
  722. tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
  723. lightPos.setValue(VarType.Vector4, tmpLightPosition);
  724. tmpLightDirection.set(0, 0, 0, 0);
  725. lightDir.setValue(VarType.Vector4, tmpLightDirection);
  726. break;
  727. case Point:
  728. PointLight pl = (PointLight) l;
  729. Vector3f pos = pl.getPosition();
  730. float invRadius = pl.getInvRadius();
  731. tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
  732. lightPos.setValue(VarType.Vector4, tmpLightPosition);
  733. tmpLightDirection.set(0, 0, 0, 0);
  734. lightDir.setValue(VarType.Vector4, tmpLightDirection);
  735. break;
  736. case Spot:
  737. SpotLight sl = (SpotLight) l;
  738. Vector3f pos2 = sl.getPosition();
  739. Vector3f dir2 = sl.getDirection();
  740. float invRange = sl.getInvSpotRange();
  741. float spotAngleCos = sl.getPackedAngleCos();
  742. tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
  743. lightPos.setValue(VarType.Vector4, tmpLightPosition);
  744. //We transform the spot directoin in view space here to save 5 varying later in the lighting shader
  745. //one vec4 less and a vec4 that becomes a vec3
  746. //the downside is that spotAngleCos decoding happen now in the frag shader.
  747. tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
  748. rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
  749. tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
  750. lightDir.setValue(VarType.Vector4, tmpLightDirection);
  751. break;
  752. default:
  753. throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
  754. }
  755. vars.release();
  756. r.setShader(shader);
  757. r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
  758. }
  759. if (isFirstLight && lightList.size() > 0) {
  760. // There are only ambient lights in the scene. Render
  761. // a dummy "normal light" so we can see the ambient
  762. ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
  763. lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
  764. lightPos.setValue(VarType.Vector4, nullDirLight);
  765. r.setShader(shader);
  766. r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
  767. }
  768. }
  769. /**
  770. * Select the technique to use for rendering this material.
  771. * <p>
  772. * If <code>name</code> is "Default", then one of the
  773. * {@link MaterialDef#getDefaultTechniques() default techniques}
  774. * on the material will be selected. Otherwise, the named technique
  775. * will be found in the material definition.
  776. * <p>
  777. * Any candidate technique for selection (either default or named)
  778. * must be verified to be compatible with the system, for that, the
  779. * <code>renderManager</code> is queried for capabilities.
  780. *
  781. * @param name The name of the technique to select, pass "Default" to
  782. * select one of the default techniques.
  783. * @param renderManager The {@link RenderManager render manager}
  784. * to query for capabilities.
  785. *
  786. * @throws IllegalArgumentException If "Default" is passed and no default
  787. * techniques are available on the material definition, or if a name
  788. * is passed but there's no technique by that name.
  789. * @throws UnsupportedOperationException If no candidate technique supports
  790. * the system capabilities.
  791. */
  792. public void selectTechnique(String name, RenderManager renderManager) {
  793. // check if already created
  794. Technique tech = techniques.get(name);
  795. // When choosing technique, we choose one that
  796. // supports all the caps.
  797. EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
  798. if (tech == null) {
  799. if (name.equals("Default")) {
  800. List<TechniqueDef> techDefs = def.getDefaultTechniques();
  801. if (techDefs == null || techDefs.isEmpty()) {
  802. throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
  803. }
  804. TechniqueDef lastTech = null;
  805. for (TechniqueDef techDef : techDefs) {
  806. if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
  807. // use the first one that supports all the caps
  808. tech = new Technique(this, techDef);
  809. techniques.put(name, tech);
  810. break;
  811. }
  812. lastTech = techDef;
  813. }
  814. if (tech == null) {
  815. throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n"
  816. + " is supported by the video hardware. The caps "
  817. + lastTech.getRequiredCaps() + " are required.");
  818. }
  819. } else {
  820. // create "special" technique instance
  821. TechniqueDef techDef = def.getTechniqueDef(name);
  822. if (techDef == null) {
  823. throw new IllegalArgumentException("For material " + def.getName() + ", technique not found: " + name);
  824. }
  825. if (!rendererCaps.containsAll(techDef.getRequiredCaps())) {
  826. throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n"
  827. + "requires caps " + techDef.getRequiredCaps() + " which are not "
  828. + "supported by the video renderer");
  829. }
  830. tech = new Technique(this, techDef);
  831. techniques.put(name, tech);
  832. }
  833. } else if (technique == tech) {
  834. // attempting to switch to an already
  835. // active technique.
  836. return;
  837. }
  838. technique = tech;
  839. tech.makeCurrent(def.getAssetManager(), true, rendererCaps);
  840. // shader was changed
  841. sortingId = -1;
  842. }
  843. private void autoSelectTechnique(RenderManager rm) {
  844. if (technique == null) {
  845. selectTechnique("Default", rm);
  846. } else {
  847. technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps());
  848. }
  849. }
  850. /**
  851. * Preloads this material for the given render manager.
  852. * <p>
  853. * Preloading the material can ensure that when the material is first
  854. * used for rendering, there won't be any delay since the material has
  855. * been already been setup for rendering.
  856. *
  857. * @param rm The render manager to preload for
  858. */
  859. public void preload(RenderManager rm) {
  860. autoSelectTechnique(rm);
  861. Renderer r = rm.getRenderer();
  862. TechniqueDef techDef = technique.getDef();
  863. Collection<MatParam> params = paramValues.values();
  864. for (MatParam param : params) {
  865. if (param instanceof MatParamTexture) {
  866. MatParamTexture texParam = (MatParamTexture) param;
  867. r.setTexture(0, texParam.getTextureValue());
  868. } else {
  869. if (!techDef.isUsingShaders()) {
  870. continue;
  871. }
  872. technique.updateUniformParam(param.getName(), param.getVarType(), param.getValue());
  873. }
  874. }
  875. Shader shader = technique.getShader();
  876. if (techDef.isUsingShaders()) {
  877. r.setShader(shader);
  878. }
  879. }
  880. private void clearUniformsSetByCurrent(Shader shader) {
  881. ListMap<String, Uniform> uniforms = shader.getUniformMap();
  882. int size = uniforms.size();
  883. for (int i = 0; i < size; i++) {
  884. Uniform u = uniforms.getValue(i);
  885. u.clearSetByCurrentMaterial();
  886. }
  887. }
  888. private void resetUniformsNotSetByCurrent(Shader shader) {
  889. ListMap<String, Uniform> uniforms = shader.getUniformMap();
  890. int size = uniforms.size();
  891. for (int i = 0; i < size; i++) {
  892. Uniform u = uniforms.getValue(i);
  893. if (!u.isSetByCurrentMaterial()) {
  894. u.clearValue();
  895. }
  896. }
  897. }
  898. /**
  899. * Called by {@link RenderManager} to render the geometry by
  900. * using this material.
  901. * <p>
  902. * The material is rendered as follows:
  903. * <ul>
  904. * <li>Determine which technique to use to render the material -
  905. * either what the user selected via
  906. * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
  907. * Material.selectTechnique()},
  908. * or the first default technique that the renderer supports
  909. * (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering capabilities})<ul>
  910. * <li>If the technique has been changed since the last frame, then it is notified via
  911. * {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean, java.util.EnumSet)
  912. * Technique.makeCurrent()}.
  913. * If the technique wants to use a shader to render the model, it should load it at this part -
  914. * the shader should have all the proper defines as declared in the technique definition,
  915. * including those that are bound to material parameters.
  916. * The technique can re-use the shader from the last frame if
  917. * no changes to the defines occurred.</li></ul>
  918. * <li>Set the {@link RenderState} to use for rendering. The render states are
  919. * applied in this order (later RenderStates override earlier RenderStates):<ol>
  920. * <li>{@link TechniqueDef#getRenderState() Technique Definition's RenderState}
  921. * - i.e. specific renderstate that is required for the shader.</li>
  922. * <li>{@link #getAdditionalRenderState() Material Instance Additional RenderState}
  923. * - i.e. ad-hoc renderstate set per model</li>
  924. * <li>{@link RenderManager#getForcedRenderState() RenderManager's Forced RenderState}
  925. * - i.e. renderstate requested by a {@link com.jme3.post.SceneProcessor} or
  926. * post-processing filter.</li></ol>
  927. * <li>If the technique {@link TechniqueDef#isUsingShaders() uses a shader}, then the uniforms of the shader must be updated.<ul>
  928. * <li>Uniforms bound to material parameters are updated based on the current material parameter values.</li>
  929. * <li>Uniforms bound to world parameters are updated from the RenderManager.
  930. * Internally {@link UniformBindingManager} is used for this task.</li>
  931. * <li>Uniforms bound to textures will cause the texture to be uploaded as necessary.
  932. * The uniform is set to the texture unit where the texture is bound.</li></ul>
  933. * <li>If the technique uses a shader, the model is then rendered according
  934. * to the lighting mode specified on the technique definition.<ul>
  935. * <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays
  936. * with the first 4 lights and renders the model once.</li>
  937. * <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times,
  938. * for the first light it is rendered opaque, on subsequent lights it is
  939. * rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth writing disabled.</li>
  940. * </ul>
  941. * <li>For techniques that do not use shaders,
  942. * fixed function OpenGL is used to render the model (see {@link GL1Renderer} interface):<ul>
  943. * <li>OpenGL state ({@link FixedFuncBinding}) that is bound to material parameters is updated. </li>
  944. * <li>The texture set on the material is uploaded and bound.
  945. * Currently only 1 texture is supported for fixed function techniques.</li>
  946. * <li>If the technique uses lighting, then OpenGL lighting state is updated
  947. * based on the light list on the geometry, otherwise OpenGL lighting is disabled.</li>
  948. * <li>The mesh is uploaded and rendered.</li>
  949. * </ul>
  950. * </ul>
  951. *
  952. * @param geom The geometry to render
  953. * @param rm The render manager requesting the rendering
  954. */
  955. public void render(Geometry geom, RenderManager rm) {
  956. autoSelectTechnique(rm);
  957. Renderer r = rm.getRenderer();
  958. TechniqueDef techDef = technique.getDef();
  959. if (techDef.getLightMode() == LightMode.MultiPass
  960. && geom.getWorldLightList().size() == 0) {
  961. return;
  962. }
  963. if (rm.getForcedRenderState() != null) {
  964. r.applyRenderState(rm.getForcedRenderState());
  965. } else {
  966. if (techDef.getRenderState() != null) {
  967. r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
  968. } else {
  969. r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
  970. }
  971. }
  972. // update camera and world matrices
  973. // NOTE: setWorldTransform should have been called already
  974. if (techDef.isUsingShaders()) {
  975. // reset unchanged uniform flag
  976. clearUniformsSetByCurrent(technique.getShader());
  977. rm.updateUniformBindings(technique.getWorldBindUniforms());
  978. }
  979. // setup textures and uniforms
  980. for (int i = 0; i < paramValues.size(); i++) {
  981. MatParam param = paramValues.getValue(i);
  982. param.apply(r, technique);
  983. }
  984. Shader shader = technique.getShader();
  985. // send lighting information, if needed
  986. switch (techDef.getLightMode()) {
  987. case Disable:
  988. r.setLighting(null);
  989. break;
  990. case SinglePass:
  991. updateLightListUniforms(shader, geom, 4);
  992. break;
  993. case FixedPipeline:
  994. r.setLighting(geom.getWorldLightList());
  995. break;
  996. case MultiPass:
  997. // NOTE: Special case!
  998. resetUniformsNotSetByCurrent(shader);
  999. renderMultipassLighting(shader, geom, rm);
  1000. // very important, notice the return statement!
  1001. return;
  1002. }
  1003. // upload and bind shader
  1004. if (techDef.isUsingShaders()) {
  1005. // any unset uniforms will be set to 0
  1006. resetUniformsNotSetByCurrent(shader);
  1007. r.setShader(shader);
  1008. }
  1009. r.renderMesh(geom.getMesh(), geom.getLodLevel(), 1);
  1010. }
  1011. public void write(JmeExporter ex) throws IOException {
  1012. OutputCapsule oc = ex.getCapsule(this);
  1013. oc.write(def.getAssetName(), "material_def", null);
  1014. oc.write(additionalState, "render_state", null);
  1015. oc.write(transparent, "is_transparent", false);
  1016. oc.writeStringSavableMap(paramValues, "parameters", null);
  1017. }
  1018. public void read(JmeImporter im) throws IOException {
  1019. InputCapsule ic = im.getCapsule(this);
  1020. additionalState = (RenderState) ic.readSavable("render_state", null);
  1021. transparent = ic.readBoolean("is_transparent", false);
  1022. // Load the material def
  1023. String defName = ic.readString("material_def", null);
  1024. HashMap<String, MatParam> params = (HashMap<String, MatParam>) ic.readStringSavableMap("parameters", null);
  1025. boolean enableVcolor = false;
  1026. boolean separateTexCoord = false;
  1027. boolean applyDefaultValues = false;
  1028. boolean guessRenderStateApply = false;
  1029. int ver = ic.getSavableVersion(Material.class);
  1030. if (ver < 1) {
  1031. applyDefaultValues = true;
  1032. }
  1033. if (ver < 2) {
  1034. guessRenderStateApply = true;
  1035. }
  1036. if (im.getFormatVersion() == 0) {
  1037. // Enable compatibility with old models
  1038. if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) {
  1039. // Using VertexColor, switch to Unshaded and set VertexColor=true
  1040. enableVcolor = true;
  1041. defName = "Common/MatDefs/Misc/Unshaded.j3md";
  1042. } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
  1043. || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) {
  1044. // Using SimpleTextured/SolidColor, just switch to Unshaded
  1045. defName = "Common/MatDefs/Misc/Unshaded.j3md";
  1046. } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) {
  1047. // Using WireColor, set wireframe renderstate = true and use Unshaded
  1048. getAdditionalRenderState().setWireframe(true);
  1049. defName = "Common/MatDefs/Misc/Unshaded.j3md";
  1050. } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) {
  1051. // Uses unshaded, ensure that the proper param is set
  1052. MatParam value = params.get("SeperateTexCoord");
  1053. if (value != null && ((Boolean) value.getValue()) == true) {
  1054. params.remove("SeperateTexCoord");
  1055. separateTexCoord = true;
  1056. }
  1057. }
  1058. assert applyDefaultValues && guessRenderStateApply;
  1059. }
  1060. def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
  1061. paramValues = new ListMap<String, MatParam>();
  1062. // load the textures and update nextTexUnit
  1063. for (Map.Entry<String, MatParam> entry : params.entrySet()) {
  1064. MatParam param = entry.getValue();
  1065. if (param instanceof MatParamTexture) {
  1066. MatParamTexture texVal = (MatParamTexture) param;
  1067. if (nextTexUnit < texVal.getUnit() + 1) {
  1068. nextTexUnit = texVal.getUnit() + 1;
  1069. }
  1070. // the texture failed to load for this param
  1071. // do not add to param values
  1072. if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
  1073. continue;
  1074. }
  1075. }
  1076. if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) {
  1077. // Ancient version of jME3 ...
  1078. param.setName(param.getName().substring(2));
  1079. }
  1080. checkSetParam(param.getVarType(), param.getName());
  1081. paramValues.put(param.getName(), param);
  1082. }
  1083. if (applyDefaultValues) {
  1084. // compatability with old versions where default vars were
  1085. // not available
  1086. for (MatParam param : def.getMaterialParams()) {
  1087. if (param.getValue() != null && paramValues.get(param.getName()) == null) {
  1088. setParam(param.getName(), param.getVarType(), param.getValue());
  1089. }
  1090. }
  1091. }
  1092. if (guessRenderStateApply && additionalState != null) {
  1093. // Try to guess values of "apply" render state based on defaults
  1094. // if value != default then set apply to true
  1095. additionalState.applyPolyOffset = additionalState.offsetEnabled;
  1096. additionalState.applyAlphaFallOff = additionalState.alphaTest;
  1097. additionalState.applyAlphaTest = additionalState.alphaTest;
  1098. additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
  1099. additionalState.applyColorWrite = !additionalState.colorWrite;
  1100. additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
  1101. additionalState.applyDepthTest = !additionalState.depthTest;
  1102. additionalState.applyDepthWrite = !additionalState.depthWrite;
  1103. additionalState.applyPointSprite = additionalState.pointSprite;
  1104. additionalState.applyStencilTest = additionalState.stencilTest;
  1105. additionalState.applyWireFrame = additionalState.wireframe;
  1106. }
  1107. if (enableVcolor) {
  1108. setBoolean("VertexColor", true);
  1109. }
  1110. if (separateTexCoord) {
  1111. setBoolean("SeparateTexCoord", true);
  1112. }
  1113. }
  1114. }