/** * Constructor * * @param state */ public Grid(GridState state) { super(state.name); this.state = state; this.cellSize = state.size; this.color = state.color; this.lineWidth = state.lineWidth; offset = new Vector3(); setLocation(state.location, false); lattice = new HiddenLine("_lattice", IndexMode.Lines); SpatialUtil.setPickHost(lattice, this); lattice.setColor(color); lattice.setModelBound(new BoundingBox()); MaterialState ms = new MaterialState(); ms.setColorMaterial(ColorMaterial.Emissive); ms.setColorMaterialFace(MaterialState.MaterialFace.FrontAndBack); ms.setEnabled(true); text = new Node("_text"); text.getSceneHints().setLightCombineMode(LightCombineMode.Off); text.getSceneHints().setPickingHint(PickingHint.Pickable, false); text.setRenderState(ms); setLabelVisible(state.labelVisible); attachChild(lattice); attachChild(text); setVisible(state.visible); setPinned(state.pinned); state.setMapElement(this); }
/** * Set the color * * @param color */ public void setColor(ReadOnlyColorRGBA color) { this.color.set(color); MaterialState ms = new MaterialState(); this.color.setAlpha(0.3f); ms.setDiffuse(MaterialState.MaterialFace.FrontAndBack, this.color); ms.setAmbient(MaterialState.MaterialFace.FrontAndBack, ColorRGBA.BLACK); ms.setEmissive(MaterialState.MaterialFace.FrontAndBack, this.color); ms.setEnabled(true); setRenderState(ms); }
private void addMesh(final Spatial spatial) { spatial.setTranslation( (index % wrapCount) * 8 - wrapCount * 4, (index / wrapCount) * 8 - wrapCount * 4, -50); if (spatial instanceof Mesh) { ((Mesh) spatial).updateModelBound(); } final MaterialState ms = new MaterialState(); ms.setAmbient(ColorRGBA.DARK_GRAY); spatial.setRenderState(ms); _root.attachChild(spatial); index++; }
/** * Constructor * * @param color */ public SimpleCrosshair(ReadOnlyColorRGBA color) { super("Crosshair"); ReadOnlyColorRGBA[] crosshairColor = {color, color, color, color}; getMeshData().setIndexMode(IndexMode.Lines); getMeshData().setVertexBuffer(BufferUtils.createFloatBuffer(crosshairVertex)); FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(crosshairColor); colorBuffer.rewind(); getMeshData().setColorBuffer(colorBuffer); getMeshData().setIndexBuffer(BufferUtils.createIntBuffer(crosshairIndex)); getMeshData().getIndexBuffer().limit(4); getMeshData().getIndexBuffer().rewind(); getSceneHints().setAllPickingHints(false); setModelBound(new BoundingBox()); updateModelBound(); MaterialState crosshairMaterialState = new MaterialState(); crosshairMaterialState.setColorMaterial(MaterialState.ColorMaterial.Emissive); crosshairMaterialState.setEnabled(true); getSceneHints().setLightCombineMode(LightCombineMode.Off); setRenderState(crosshairMaterialState); updateGeometricState(0, true); }
/** * Find and apply the given material to the given Mesh. * * @param materialName our material name * @param mesh the mesh to apply material to. */ public void applyMaterial(final String materialName, final Mesh mesh) { if (materialName == null) { logger.warning("materialName is null"); return; } Element mat = _dataCache.getBoundMaterial(materialName); if (mat == null) { logger.warning("material not bound: " + materialName + ", trying search with id."); mat = _colladaDOMUtil.findTargetWithId(materialName); } if (mat == null || !"material".equals(mat.getName())) { logger.warning("material not found: " + materialName); return; } final String originalMaterial = mat.getAttributeValue("id"); MaterialInfo mInfo = null; if (!_dataCache.getMaterialInfoMap().containsKey(originalMaterial)) { mInfo = new MaterialInfo(); mInfo.setMaterialName(originalMaterial); _dataCache.getMaterialInfoMap().put(originalMaterial, mInfo); } _dataCache.getMeshMaterialMap().put(mesh, originalMaterial); final Element child = mat.getChild("instance_effect"); final Element effectNode = _colladaDOMUtil.findTargetWithId(child.getAttributeValue("url")); if (effectNode == null) { logger.warning( "material effect not found: " + mat.getChild("instance_material").getAttributeValue("url")); return; } if ("effect".equals(effectNode.getName())) { /* * temp cache for textures, we do not want to add textures twice (for example, transparant map might point * to diffuse texture) */ final HashMap<String, Texture> loadedTextures = new HashMap<String, Texture>(); final Element effect = effectNode; // XXX: For now, just grab the common technique: if (effect.getChild("profile_COMMON") != null) { if (mInfo != null) { mInfo.setProfile("COMMON"); } final Element technique = effect.getChild("profile_COMMON").getChild("technique"); String type = "blinn"; if (technique.getChild(type) == null) { type = "phong"; if (technique.getChild(type) == null) { type = "lambert"; if (technique.getChild(type) == null) { type = "constant"; if (technique.getChild(type) == null) { ColladaMaterialUtils.logger.warning( "COMMON material has unusuable techinque. " + child.getAttributeValue("url")); return; } } } } final Element blinnPhongLambert = technique.getChild(type); if (mInfo != null) { mInfo.setTechnique(type); } final MaterialState mState = new MaterialState(); // TODO: implement proper transparency handling Texture diffuseTexture = null; ColorRGBA transparent = new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); float transparency = 1.0f; boolean useTransparency = false; String opaqueMode = "A_ONE"; /* * place holder for current property, we import material properties in fixed order (for texture order) */ Element property = null; /* Diffuse property */ property = blinnPhongLambert.getChild("diffuse"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("color".equals(propertyValue.getName())) { final ColorRGBA color = _colladaDOMUtil.getColor(propertyValue.getText()); mState.setDiffuse(MaterialFace.FrontAndBack, color); } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { diffuseTexture = populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "diffuse"); } } /* Ambient property */ property = blinnPhongLambert.getChild("ambient"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("color".equals(propertyValue.getName())) { final ColorRGBA color = _colladaDOMUtil.getColor(propertyValue.getText()); mState.setAmbient(MaterialFace.FrontAndBack, color); } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "ambient"); } } /* Transparent property */ property = blinnPhongLambert.getChild("transparent"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("color".equals(propertyValue.getName())) { transparent = _colladaDOMUtil.getColor(propertyValue.getText()); // TODO: use this useTransparency = true; } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "transparent"); } opaqueMode = property.getAttributeValue("opaque", "A_ONE"); } /* Transparency property */ property = blinnPhongLambert.getChild("transparency"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("float".equals(propertyValue.getName())) { transparency = Float.parseFloat(propertyValue.getText().replace(",", ".")); // TODO: use this if (_flipTransparency) { transparency = 1f - transparency; } useTransparency = true; } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState( mesh, propertyValue, effect, loadedTextures, mInfo, "transparency"); } } /* Emission property */ property = blinnPhongLambert.getChild("emission"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("color".equals(propertyValue.getName())) { mState.setEmissive( MaterialFace.FrontAndBack, _colladaDOMUtil.getColor(propertyValue.getText())); } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "emissive"); } } /* Specular property */ property = blinnPhongLambert.getChild("specular"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("color".equals(propertyValue.getName())) { mState.setSpecular( MaterialFace.FrontAndBack, _colladaDOMUtil.getColor(propertyValue.getText())); } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "specular"); } } /* Shininess property */ property = blinnPhongLambert.getChild("shininess"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("float".equals(propertyValue.getName())) { float shininess = Float.parseFloat(propertyValue.getText().replace(",", ".")); if (shininess >= 0.0f && shininess <= 1.0f) { final float oldShininess = shininess; shininess *= 128; logger.finest( "Shininess - " + oldShininess + " - was in the [0,1] range. Scaling to [0, 128] - " + shininess); } else if (shininess < 0 || shininess > 128) { final float oldShininess = shininess; shininess = MathUtils.clamp(shininess, 0, 128); logger.warning( "Shininess must be between 0 and 128. Shininess " + oldShininess + " was clamped to " + shininess); } mState.setShininess(MaterialFace.FrontAndBack, shininess); } else if ("texture".equals(propertyValue.getName()) && _loadTextures) { populateTextureState(mesh, propertyValue, effect, loadedTextures, mInfo, "shininess"); } } /* Reflectivity property */ float reflectivity = 1.0f; property = blinnPhongLambert.getChild("reflectivity"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("float".equals(propertyValue.getName())) { reflectivity = Float.parseFloat(propertyValue.getText().replace(",", ".")); } } /* Reflective property. Texture only */ property = blinnPhongLambert.getChild("reflective"); if (property != null) { final Element propertyValue = (Element) property.getChildren().get(0); if ("texture".equals(propertyValue.getName()) && _loadTextures) { final Texture reflectiveTexture = populateTextureState( mesh, propertyValue, effect, loadedTextures, mInfo, "reflective"); reflectiveTexture.setEnvironmentalMapMode(Texture.EnvironmentalMapMode.SphereMap); reflectiveTexture.setApply(ApplyMode.Combine); reflectiveTexture.setCombineFuncRGB(CombinerFunctionRGB.Interpolate); // color 1 reflectiveTexture.setCombineSrc0RGB(CombinerSource.CurrentTexture); reflectiveTexture.setCombineOp0RGB(CombinerOperandRGB.SourceColor); // color 2 reflectiveTexture.setCombineSrc1RGB(CombinerSource.Previous); reflectiveTexture.setCombineOp1RGB(CombinerOperandRGB.SourceColor); // interpolate param will come from alpha of constant color reflectiveTexture.setCombineSrc2RGB(CombinerSource.Constant); reflectiveTexture.setCombineOp2RGB(CombinerOperandRGB.SourceAlpha); reflectiveTexture.setConstantColor(new ColorRGBA(1, 1, 1, reflectivity)); } } /* * An extra tag defines some materials not part of the collada standard. Since we're not able to parse * we simply extract the textures from the element (such that shaders etc can at least pick up on them) */ property = technique.getChild("extra"); if (property != null) { getTexturesFromElement(mesh, property, effect, loadedTextures, mInfo); } // XXX: There are some issues with clarity on how to use alpha blending in OpenGL FFP. // The best interpretation I have seen is that if transparent has a texture == diffuse, // Turn on alpha blending and use diffuse alpha. // check to make sure we actually need this. // testing separately for a transparency of 0.0 is to hack around erroneous exports, since // usually // there is no use in exporting something with 100% transparency. if ("A_ONE".equals(opaqueMode) && ColorRGBA.WHITE.equals(transparent) && transparency == 1.0 || transparency == 0.0) { useTransparency = false; } if (useTransparency) { if (diffuseTexture != null) { final BlendState blend = new BlendState(); blend.setBlendEnabled(true); blend.setTestEnabled(true); blend.setSourceFunction(BlendState.SourceFunction.SourceAlpha); blend.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha); mesh.setRenderState(blend); } else { final BlendState blend = new BlendState(); blend.setBlendEnabled(true); blend.setTestEnabled(true); transparent.setAlpha(transparent.getAlpha() * transparency); blend.setConstantColor(transparent); blend.setSourceFunction(BlendState.SourceFunction.ConstantAlpha); blend.setDestinationFunction(BlendState.DestinationFunction.OneMinusConstantAlpha); mesh.setRenderState(blend); } mesh.getSceneHints().setRenderBucketType(RenderBucketType.Transparent); } if (mInfo != null) { if (useTransparency) { mInfo.setUseTransparency(useTransparency); if (diffuseTexture == null) { mInfo.setTransparency(transparent.getAlpha() * transparency); } } mInfo.setMaterialState(mState); } mesh.setRenderState(mState); } } else { ColladaMaterialUtils.logger.warning( "material effect not found: " + mat.getChild("instance_material").getAttributeValue("url")); } }
@Override protected void initExample() { _canvas.setTitle("Various size imposters - Example"); _canvas.getCanvasRenderer().getCamera().setLocation(new Vector3(0, 60, 80)); _canvas.getCanvasRenderer().getCamera().lookAt(new Vector3(), Vector3.UNIT_Y); final BasicText keyText = BasicText.createDefaultTextLabel("Text", "[SPACE] Switch imposters off"); keyText.getSceneHints().setRenderBucketType(RenderBucketType.Ortho); keyText.getSceneHints().setLightCombineMode(LightCombineMode.Off); keyText.setTranslation(new Vector3(0, 20, 0)); _root.attachChild(keyText); final Box box = new Box("Box", new Vector3(), 150, 1, 150); box.setModelBound(new BoundingBox()); box.setTranslation(new Vector3(0, -10, 0)); _root.attachChild(box); final QuadImposterNode imposter0 = new QuadImposterNode( "Imposter1", 256, 256, _settings.getDepthBits(), _settings.getSamples(), _timer); imposter0.setRedrawRate(0.0); // No timed update imposter0.setCameraAngleThreshold(1.0 * MathUtils.DEG_TO_RAD); imposter0.setCameraDistanceThreshold(0.1); _root.attachChild(imposter0); final Node scene1 = createModel(); scene1.setTranslation(0, 0, 0); imposter0.attachChild(scene1); final QuadImposterNode imposter1 = new QuadImposterNode( "Imposter1", 128, 128, _settings.getDepthBits(), _settings.getSamples(), _timer); imposter1.setRedrawRate(0.0); // No timed update imposter1.setCameraAngleThreshold(1.0 * MathUtils.DEG_TO_RAD); imposter1.setCameraDistanceThreshold(0.1); _root.attachChild(imposter1); final Node scene2 = createModel(); scene2.setTranslation(-15, 0, -25); imposter1.attachChild(scene2); final QuadImposterNode imposter2 = new QuadImposterNode( "Imposter2", 64, 64, _settings.getDepthBits(), _settings.getSamples(), _timer); imposter2.setRedrawRate(0.0); // No timed update imposter2.setCameraAngleThreshold(1.0 * MathUtils.DEG_TO_RAD); imposter2.setCameraDistanceThreshold(0.1); _root.attachChild(imposter2); final Node scene3 = createModel(); scene3.setTranslation(15, 0, -25); imposter2.attachChild(scene3); _logicalLayer.registerTrigger( new InputTrigger( new KeyPressedCondition(Key.SPACE), new TriggerAction() { public void perform( final Canvas source, final TwoInputStates inputStates, final double tpf) { showImposter = !showImposter; if (showImposter) { _root.detachChild(scene1); _root.detachChild(scene2); _root.detachChild(scene3); imposter0.attachChild(scene1); imposter1.attachChild(scene2); imposter2.attachChild(scene3); _root.attachChild(imposter0); _root.attachChild(imposter1); _root.attachChild(imposter2); keyText.setText("[SPACE] Switch imposters off"); } else { _root.detachChild(imposter0); _root.detachChild(imposter1); _root.detachChild(imposter2); _root.attachChild(scene1); _root.attachChild(scene2); _root.attachChild(scene3); keyText.setText("[SPACE] Switch imposters on"); } } })); final TextureState ts = new TextureState(); ts.setEnabled(true); ts.setTexture( TextureManager.load( "images/ardor3d_white_256.jpg", Texture.MinificationFilter.Trilinear, true)); final MaterialState ms = new MaterialState(); ms.setColorMaterial(ColorMaterial.Diffuse); _root.setRenderState(ms); _root.setRenderState(ts); _root.acceptVisitor(new UpdateModelBoundVisitor(), false); }
/** * Save a mesh to the given files. * * @param mesh mesh to export * @param objFile WaveFront OBJ file * @param mtlFile material file, optional * @param append indicates whether the data are written to the end of the OBJ file * @param firstVertexIndex first vertex index used for this mesh during the export * @param firstFiles indicates whether the couple of files is used for the first time, i.e there * is nothing to append despite the value of <code>append</code> * @param materialList list of materials already exported in this material file * @param customTextureName texture name that overrides the one of the mesh, optional * @throws IOException */ protected void save( final Mesh mesh, final File objFile, final File mtlFile, final boolean append, final int firstVertexIndex, final boolean firstFiles, final List<ObjMaterial> materialList, final String customTextureName) throws IOException { File parentDirectory = objFile.getParentFile(); if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } if (mtlFile != null) { parentDirectory = mtlFile.getParentFile(); if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } } PrintWriter objPw = null, mtlPw = null; try { // fills the MTL file final String mtlName; if (mtlFile != null) { final FileOutputStream mtlOs = new FileOutputStream(mtlFile, append); mtlPw = new PrintWriter(new BufferedOutputStream(mtlOs)); // writes some comments if (firstFiles) { mtlPw.println("# Ardor3D 1.0 MTL file"); } final ObjMaterial currentMtl = new ObjMaterial(null); final MaterialState mtlState = (MaterialState) mesh.getLocalRenderState(StateType.Material); if (mtlState != null) { final ReadOnlyColorRGBA ambientColor = mtlState.getAmbient(); if (ambientColor != null) { currentMtl.d = ambientColor.getAlpha(); currentMtl.Ka = new float[] { ambientColor.getRed(), ambientColor.getGreen(), ambientColor.getBlue(), ambientColor.getAlpha() }; } final ReadOnlyColorRGBA diffuseColor = mtlState.getDiffuse(); if (diffuseColor != null) { currentMtl.Kd = new float[] { diffuseColor.getRed(), diffuseColor.getGreen(), diffuseColor.getBlue(), diffuseColor.getAlpha() }; } final ReadOnlyColorRGBA specularColor = mtlState.getSpecular(); if (specularColor != null) { currentMtl.Ks = new float[] { specularColor.getRed(), specularColor.getGreen(), specularColor.getBlue(), specularColor.getAlpha() }; } currentMtl.Ns = mtlState.getShininess(); } if (customTextureName == null) { currentMtl.textureName = getLocalMeshTextureName(mesh); } else { currentMtl.textureName = customTextureName; } if (mesh.getSceneHints().getLightCombineMode() == LightCombineMode.Off) { // Color on and Ambient off currentMtl.illumType = 0; } else { // Color on and Ambient on currentMtl.illumType = 1; } ObjMaterial sameObjMtl = null; if (materialList != null && !materialList.isEmpty()) { for (final ObjMaterial mtl : materialList) { if (mtl.illumType == currentMtl.illumType && mtl.Ns == currentMtl.Ns && mtl.forceBlend == currentMtl.forceBlend && mtl.d == currentMtl.d && Arrays.equals(mtl.Ka, currentMtl.Ka) && Arrays.equals(mtl.Kd, currentMtl.Kd) && Arrays.equals(mtl.Ks, currentMtl.Ks) // && Objects.equals(mtl.textureName, currentMtl.textureName)) { && mtl.textureName.equals(currentMtl.textureName)) { sameObjMtl = mtl; break; } } } if (sameObjMtl == null) { // writes the new material library mtlName = mtlFile.getName().trim().replaceAll(" ", "") + "_" + (materialList == null ? 1 : materialList.size() + 1); if (materialList != null) { final ObjMaterial mtl = new ObjMaterial(mtlName); mtl.illumType = currentMtl.illumType; mtl.textureName = currentMtl.textureName; materialList.add(mtl); } mtlPw.println("newmtl " + mtlName); if (currentMtl.Ns != -1) { mtlPw.println("Ns " + currentMtl.Ns); } if (currentMtl.Ka != null) { mtlPw.print("Ka"); for (final float KaCoef : currentMtl.Ka) { mtlPw.print(" " + KaCoef); } mtlPw.println(); } if (currentMtl.Kd != null) { mtlPw.print("Kd"); for (final float KdCoef : currentMtl.Kd) { mtlPw.print(" " + KdCoef); } mtlPw.println(); } if (currentMtl.Ks != null) { mtlPw.print("Ks"); for (final float KsCoef : currentMtl.Ks) { mtlPw.print(" " + KsCoef); } mtlPw.println(); } if (currentMtl.d != -1) { mtlPw.println("d " + currentMtl.d); } mtlPw.println("illum " + currentMtl.illumType); if (currentMtl.textureName != null) { mtlPw.println("map_Kd " + currentMtl.textureName); } } else { mtlName = sameObjMtl.getName(); } } else { mtlName = null; } final FileOutputStream objOs = new FileOutputStream(objFile, append); objPw = new PrintWriter(new BufferedOutputStream(objOs)); // writes some comments if (firstFiles) { objPw.println("# Ardor3D 1.0 OBJ file"); objPw.println("# www.ardor3d.com"); // writes the material file name if any if (mtlFile != null) { final String mtlLibFilename = mtlFile.getName(); objPw.println("mtllib " + mtlLibFilename); } } // writes the object name final String objName; String meshName = mesh.getName(); // removes all spaces from the mesh name if (meshName != null && !meshName.isEmpty()) { meshName = meshName.trim().replaceAll(" ", ""); } if (meshName != null && !meshName.isEmpty()) { objName = meshName; } else { objName = "obj_mesh" + mesh.hashCode(); } objPw.println("o " + objName); final MeshData meshData = mesh.getMeshData(); // writes the coordinates final FloatBufferData verticesData = meshData.getVertexCoords(); if (verticesData == null) { throw new IllegalArgumentException("cannot export a mesh with no vertices"); } final int expectedTupleCount = verticesData.getTupleCount(); saveFloatBufferData(verticesData, objPw, "v", expectedTupleCount); final FloatBufferData texCoordsData = meshData.getTextureCoords(0); saveFloatBufferData(texCoordsData, objPw, "vt", expectedTupleCount); final FloatBufferData normalsData = meshData.getNormalCoords(); saveFloatBufferData(normalsData, objPw, "vn", expectedTupleCount); // writes the used material library if (mtlFile != null) { objPw.println("usemtl " + mtlName); } // writes the faces for (int sectionIndex = 0; sectionIndex < meshData.getSectionCount(); sectionIndex++) { final IndexMode indexMode = meshData.getIndexMode(sectionIndex); final int[] indices = new int[indexMode.getVertexCount()]; switch (indexMode) { case TriangleFan: case Triangles: case TriangleStrip: case Quads: for (int primIndex = 0, primCount = meshData.getPrimitiveCount(sectionIndex); primIndex < primCount; primIndex++) { meshData.getPrimitiveIndices(primIndex, sectionIndex, indices); objPw.print("f"); for (int vertexIndex = 0; vertexIndex < indices.length; vertexIndex++) { // indices start at 1 in the WaveFront OBJ format whereas indices start at 0 in // Ardor3D final int shiftedIndex = indices[vertexIndex] + 1 + firstVertexIndex; // vertex index objPw.print(" " + shiftedIndex); // texture coordinate index if (texCoordsData != null) { objPw.print("/" + shiftedIndex); } // normal coordinate index if (normalsData != null) { objPw.print("/" + shiftedIndex); } } objPw.println(); } break; default: throw new IllegalArgumentException("index mode " + indexMode + " not supported"); } } } catch (final Throwable t) { throw new Error("Unable to save the mesh into an obj", t); } finally { if (objPw != null) { objPw.flush(); objPw.close(); } if (mtlPw != null) { mtlPw.flush(); mtlPw.close(); } } }