Hey, i was been studying how to create a real time game environment from Environment Modeling and Texturing DVD by CG Master Christopher Plush and him use Stencil Maps for Splatting, but i don’t know if jme use stencil maps for splatting, because i seen the
texture splatting doc in wiki. So i would like to know if is possible i create my terrains in blender with stencil maps and load them in jmp ( because this method is very useful ;P). Thanks in advance ;P.
no there is no built in way of doing this.
The only way to do this would be to modify the lighting shader to support stencilMap from blender.
Hmm. Yes it’s true. I found another way easier to do it, by baking the mesh’s textures with stencil map, using the “Textures” bake mode, generating so a new color texture ( perfectly equals the result of stencil map ) . It’s very useful, do you agree?
Yes can be good for small areas or models, but the problem in baking is that you will loose resolution on your color map.
when using stencil maps in blender the idea is that the associated color map is a relatively small map, but repeated n time.
For example, if you use a 512 x 512 color map repeated 10 times, you virtually have a 5120 x 5120 color map
If you bake the resulting color map after the stencil map has been applied, you’ll have to bake it in a smaller map, usually 1024 x 1024 or 2048 x 2048. this will result in a loss of resolution.
Also usually stencil map can be 1024 x 1024 maps, because you don’t need much details. and in the end a 512 map + a 1024 map use a lot less memory than a 2048 map.
On a side note, a similar idea is used for the terrain material in jME3, but instead of stencil map an alpha map is used.
Stencil map are black and white, so using an rbga image to store the data is a waste because it could be store in a single channel instead of 4.
The alpha map, store a different stencil map on each of its channel. So an alpha map can control up to 4 colormaps.
What could be nice, would be to make a blender script that would combine 4 stencil maps in an alpha map that could be used with the terrain material…
Hm it should be possible with a shader am i wrong? Input the stencil map + the two textures you want to splat, and the rest is done by the shader.
→ I assume currently you use the whole texture once none repeating ?
The shader would allow you to repeat it (if written correctly) and makes your texture resolution independent of the terrain/object size.
nehon said:
What could be nice, would be to make a blender script that would combine 4 stencil maps in an alpha map that could be used with the terrain material...
Yes, this realy could be nice. I haven't experience with terrains in jme yet, and I still didn't used alpha map combined with color maps in jme. So when I learn to do it, will try to create a blender script as you said ;P. I know i can do it, i have about 60 gb of blender tutorials ;P.
@glaucomardano @nehon Is there actually an easy way to do that?, I would like to use blender to apply more than one texture to the same mesh (making terrains). The only way I can see is using the stencil mode, is there another way?, or is that script you talk about already made?
@NemesisMate said: @glaucomardano @nehon Is there actually an easy way to do that?, I would like to use blender to apply more than one texture to the same mesh (making terrains). The only way I can see is using the stencil mode, is there another way?, or is that script you talk about already made?This topic is quite old by now...
You should be able to achieve splatting in Blender by using their new material node system, however you will still need to create the material manually when you import into jME3.
@Momoko_Fan said: This topic is quite old by now...You should be able to achieve splatting in Blender by using their new material node system, however you will still need to create the material manually when you import into jME3.
That’s just what I wanted to avoid, the need of creating manually the material in JME3.
@NemesisMate said: That's just what I wanted to avoid, the need of creating manually the material in JME3.You would be hard pressed to find any game engine that can seamlessly import materials from modelling tools. The material systems in those tools are designed for non real-time ray traced rendering used for CG pictures and movies, so they are vastly more complex than what is typically found in a game engine.
@Momoko_Fan said: You would be hard pressed to find any game engine that can seamlessly import materials from modelling tools. The material systems in those tools are designed for non real-time ray traced rendering used for CG pictures and movies, so they are vastly more complex than what is typically found in a game engine.
I know that but it’s also possible to make an importer adapt some functions to the engine system what is not implemented yet in this case in JME3 as I can see. I’ll manage to get it work just like you said, creating the material in JME3, till I find an automated external way (I’m trying to get a “just import” to have a scene working).
Thanks for the info.
Maybe I can provide a headstart for you. I have a script which will setup a node based material for splatting.
The splat map can then be painted on by the artist by changing the rgba of the splat map within blender.
it is based on an old jme shader written aeons ago for 4x splatting.
I have also a script somewhere which writes the node setup back to a jme material file.
I’ll post the stuff when I get back home.
some documentation on what you can do can be found here: https://code.google.com/p/l2jserver-client/wiki/Painting_terrain_in_blender
this one is the file to add the splat material to an object
#---------------------------------------------------------- # File addSplattMaterial.py #---------------------------------------------------------- bl_info = { 'name': 'Add Splatt Materials', 'author': 'tom [et] 3d-inferno [point] com', 'version': (0, 0, 1), 'blender': (2, 6, 5), "location": "View3D > Specials > Add 4x splatting", 'description': 'Add Shadernodes for 4x texture splatting to a mesh, be sure to switch to glsl display', 'warning': '', 'wiki_url': '', 'tracker_url': '', "support": 'COMMUNITY', "category": "Object"} import bpy def add_image_texture(name): tex = bpy.data.textures.new(name, type = 'IMAGE') return tex def add_splat_material(name, texture, x, y, z) : mat = bpy.data.materials.new(name) mat.specular_intensity = 0 mat.use_mist = False ts = mat.texture_slots.add() ts.texture = texture ts.texture_coords = 'UV' #ts.specular_factor = 0 ts.scale = x, y, z return mat #make sure the display of the 3d view is set to glsl def do_4x_splatting(ob) : #new material based on nodes mat = bpy.data.materials.new('SplattMaterial') mat.specular_hardness = 0 mat.use_mist = False mat.use_nodes = True #Get the shaderNodeTree tree = mat.node_tree links = tree.links # clear default nodes for n in tree.nodes: tree.nodes.remove(n) #splat material splat = tree.nodes.new('MATERIAL') splat.name = 'Splat' splat.location = 216.3437, 781.6590 dif1 = tree.nodes.new('MATERIAL') dif1.name = 'SplatDif01' dif1.location = 358.4370, 504.2595 dif2 = tree.nodes.new('MATERIAL') dif2.name = 'SplatDif02' dif2.location = 643.1497, 505.2844 dif3 = tree.nodes.new('MATERIAL') dif3.name = 'SplatDif03' dif3.location = 901.6075, 511.2451 dif4 = tree.nodes.new('MATERIAL') dif4.name = 'SplatDif04' dif4.location = 1087.5452, 504.4561 out = tree.nodes.new('OUTPUT') out.name = 'Output' out.location = 1213.7106, 663.7264 mix1 = tree.nodes.new('MIX_RGB') mix1.name = 'Mix01' mix1.location = 790.5494, 618.7881 mix1.blend_type = 'MIX' mix2 = tree.nodes.new('MIX_RGB') mix2.name = 'Mix03' mix2.location = 960.7827, 716.9671 mix2.blend_type = 'MIX' mix3 = tree.nodes.new('MIX_RGB') mix3.name = 'Mix03' mix3.location = 1088.2360, 837.9338 mix3.blend_type = 'MIX' val = tree.nodes.new('VALUE') val.name = 'Value' val.location = 460.1999, 850.9328 val.outputs[0].default_value = -1.0 sep = tree.nodes.new('SEPRGB') sep.name = 'Separate RGB' sep.location = 373.5712, 714.5419 mult = tree.nodes.new('MIX_RGB') mult.name = 'Mult' mult.location = 529.8510, 585.7188 mult.blend_type = 'MULTIPLY' mult.inputs[0].default_value = 1.0 add = tree.nodes.new('MIX_RGB') add.name = 'Add' add.location = 698.0180, 849.9446 add.blend_type = 'ADD' add.inputs[0].default_value = 0.5 #link splat links.new(splat.outputs[0], sep.inputs[0]) # color - sep image links.new(splat.outputs[1], add.inputs[2]) # alpha - color 2 #link value links.new(val.outputs[0], add.inputs[1]) # value - color 1 #link seperate rgb links.new(sep.outputs[0], mult.inputs[1]) # R - color 1 links.new(sep.outputs[1], mix1.inputs[0]) # G - factor links.new(sep.outputs[2], mix2.inputs[0]) # B - factor #link add links.new(add.outputs[0], mix3.inputs[0]) # color - factor #link material 1 links.new(dif1.outputs[0], mult.inputs[2]) # color - color 2 #link material 2 links.new(dif2.outputs[0], mix1.inputs[2]) # color - color 2 #link material 3 links.new(dif3.outputs[0], mix2.inputs[2]) # color - color 2 #link material 4 links.new(dif4.outputs[0], mix3.inputs[2]) # color - color 2 #link mult links.new(mult.outputs[0], mix1.inputs[1]) # color - color 1 #link mix 1 links.new(mix1.outputs[0], mix2.inputs[1]) # color - color 1 #link mix 2 links.new(mix2.outputs[0], mix3.inputs[1]) # color - color 1 #link mix 3 links.new(mix3.outputs[0], out.inputs[0]) # color - color 1 # #add 4 diffuse textures and 1 base splat texture, based on uv sb = add_image_texture('SplatBlendMask') s1 = add_image_texture('Splat01') s2 = add_image_texture('Splat02') s3 = add_image_texture('Splat03') s4 = add_image_texture('Splat04') #add 4 diffuse mats and 1 base splat mat, with removed specular and removed mist #these mats are not added to the object, just to the nodes sx = sy = sz = 5 splat.material = add_splat_material('SplatBlendMask', sb, 1, 1, 1) splat.use_diffuse = True splat.use_specular = False dif1.material = add_splat_material('Splat01', s1, sx, sy, sz) dif1.use_diffuse = True dif1.use_specular = False dif2.material = add_splat_material('Splat02', s2, sx, sy, sz) dif2.use_diffuse = True dif2.use_specular = False dif3.material = add_splat_material('Splat03', s3, sx, sy, sz) dif3.use_diffuse = True dif3.use_specular = False dif4.material = add_splat_material('Splat04', s4, sx, sy, sz) dif4.use_diffuse = True dif4.use_specular = False #do this at the end ob.data.materials.append(mat) return ob class Splatt(bpy.types.Operator): """Adds a ShaderNode material for game texture splatting """ \ """based on a blending mask texture, render in GLSL """ bl_idname = 'mesh.splatt' bl_label = 'Splatt' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): obj = context.active_object return (obj and obj.type == 'MESH') def execute(self, context): do_4x_splatting(context.active_object) return {'FINISHED'} def menu_func(self, context): self.layout.operator(Splatt.bl_idname, text="Add 4x splatt") def register(): bpy.utils.register_module(__name__) #bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func) #bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func) def unregister(): bpy.utils.unregister_module(__name__) #bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func) #bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func) bpy.types.VIEW3D_MT_object.remove(menu_func) if __name__ == "__main__": register()
this one is the file to export a splat material to a j3m file, should be also able to create std. lighting material and unlit
#---------------------------------------------------------- # File exportSplattMaterial.py #---------------------------------------------------------- bl_info = { 'name': 'Export Splatt Material', 'author': 'tom [et] 3d-inferno [point] com', 'version': (0, 0, 1), 'blender': (2, 6, 5), "location": "View3D > Specials > Export Splatt Material", 'description': 'Export Shadernodes to jme material', 'warning': '', 'wiki_url': '', 'tracker_url': '', "support": 'COMMUNITY', "category": "Object"} import bpy def mapSplatNode(node) : #Texture2D DiffuseMap_0 #Float DiffuseMap_0_scale #Texture2D NormalMap_0 # #Texture2D AlphaMap if node.name == "Splat": return "AlphaMap" if node.name == "SplatDif01" : return "DiffuseMap_0" if node.name == "SplatDif02" : return "DiffuseMap_1" if node.name == "SplatDif03" : return "DiffuseMap_2" if node.name == "SplatDif04" : return "DiffuseMap_3" else : #TODO throw a warning return "" def writeSplatMaterial(mat, filename, oname) : #print (str(path) + "," + str(oname)) with open(filename, "wt") as f: f.write("Material "+oname+" : Common/MatDefs/Misc/Splatt4x.j3md {\n") f.write("\tMaterialParameters {\n") #Diffuse maps aand Blendmask tree = mat.node_tree #print(str(tree)) for n in tree.nodes : #print(str(n.type)) if n.type == 'MATERIAL': if n.material.texture_slots[0] != None : #if n.material and len(n.material.texture_slots) > 0 : #this will always be true, check if a texture is set then move on! #print("ts >0, "+str(n.material.texture_slots[0].texture)) if n.material.texture_slots[0].texture and n.material.texture_slots[0].texture.type == 'IMAGE' : mapname = mapSplatNode(n) f.write("\t"+mapname+" : "+ bpy.path.abspath(n.material.texture_slots[0].texture.image.filepath) +"\n") #print("abs: "+ bpy.path.abspath(n.material.texture_slots[0].texture.image.filepath) ) #next line will blow up if textures would be on a different drive than the blend files :-( # so we use abspath and correct this later in the pipeline #print("rel: "+ (bpy.path.relpath(n.material.texture_slots[0].texture.image.filepath)[2:]) ) if mapname != "AlphaMap" : #ts.scale only one value f.write("\t"+mapname+"_scale : %f\n" % max(n.material.texture_slots[0].scale.x, n.material.texture_slots[0].scale.y, n.material.texture_slots[0].scale.z) ) #we assume second texture is normal map in splatting (not used so far), only write out if diffuse was written #if n.material and len(n.material.texture_slots) > 1 : #this is always the case belnder has 18 texture slots if n.material.texture_slots[1] != None and n.material.texture_slots[1].texture != None: #print("slots: "+str(len(n.material.texture_slots))) #print("type: "+n.material.texture_slots[1].type ) if n.material.texture_slots[1].texture.type == 'IMAGE' : f.write("\tNormalMap"+mapname[0:2]+" : "+ bpy.path.abspath(n.material.texture_slots[1].texture.image.filepath) +"\n") f.write("\tShininess : %f\n" % mat.specular_hardness) f.write("\tAmbient : 0.5 0.5 0.5 %f\n" % mat.ambient) f.write("\tDiffuse : %f %f %f %f\n" % (mat.diffuse_color.r, mat.diffuse_color.g, mat.diffuse_color.b, mat.alpha) ) f.write("\tSpecular : %f %f %f %f\n" % (mat.specular_color.r, mat.specular_color.g, mat.specular_color.b, mat.specular_alpha) ) f.write("\t}\n") f.write("}\n") f.close() # f.write("\tDiffuseMap : Models/Buggy/buggy_diffuse.jpg\n") # f.write("\tGlowMap : Models/Buggy/buggy_glow.jpg\n") # f.write("\tSpecularMap : Models/Buggy/buggy_specular.jpg\n") # f.write("\tNormalMap : Models/Buggy/buggy_normals.png\n") def mapLightingParam(name) : lname = lowercase(name) if lname == "diffuse" or lname == "difuse" : return "DiffuseMap" if lname == "glow" : return "GlowMap" if lname == "specular" : return "SpecularMap" if lname == "normal" : return "NormalMap" else : #TODO what is the dummy texture? throw a warning return "" #write out a j3m material based on lighted values #example from jme3 #Material My Material : Common/MatDefs/Light/Lighting.j3md { # MaterialParameters { # DiffuseMap : Models/Buggy/buggy_diffuse.jpg # GlowMap : Models/Buggy/buggy_glow.jpg # SpecularMap : Models/Buggy/buggy_specular.jpg # NormalMap : Models/Buggy/buggy_normals.png # Shininess : 10 # # UseMaterialColors : true # Ambient : 0.5 0.5 0.5 1 # Diffuse : 1 1 1 1 # Specular : 1 1 1 1 # } #} #example end def writeLightingMaterial(mat, filename, oname): with open(filename, "wt") as f: f.write("Material "+oname+" : Common/MatDefs/Lighting/Lighting.j3md {\n") f.write("\tMaterialParameters {\n") useMatColor = "false" if len(mat.texture_slots) > 0 : for ts in mat.texture_slots : if ts != None and ts.texture != None and ts.texture.type == 'IMGAE' : useMatColor = "true" f.write("\t%s : %s\n" % (mapLightingParam(ts.texture.name),bpy.path.abspath(ts.texture.image.filepath)[2:]) ) f.write("\tShininess : "+ mat.specular_hardness +"\n") f.write("\n") f.write("\tUseMaterialColors : "+useMatColor+"\n") f.write("\tAmbient : 0.5 0.5 0.5 %f\n" % mat.ambient) f.write("\tDiffuse : %f %f %f %f\n" % (mat.diffuse_color.r, mat.diffuse_color.g, mat.diffuse_color.b, mat.alpha) ) f.write("\tSpecular : %f %f %f %f\n" % (mat.specular_color.r, mat.specular_color.g, mat.specular_color.b, mat.specular_alpha) ) f.write("\t}\n") f.write("}\n") f.close() #write out a j3m material based on diffuse color of material only #example from jme3 #Material Red Color : Common/MatDefs/Misc/Unshaded.j3md { # MaterialParameters { # Color : 1 0 0 1 # } #} #example end def writeUnlitMaterial(ob, filename) : #name of object/group oname = ob.name if ob.users_group : oname = ob.users_group with open(filename, "wt") as f: f.write("Material "+oname+" : Common/MatDefs/Misc/Unshaded.j3md {\n") f.write("\tMaterialParameters {\n") if ob.material_slots != None and len(ob.material_slots) > 0 and ob.material_slots[0].material != None : mat = ob.material_slots[0].material f.write("\tColor : %f %f %f %f\n" % (mat.diffuse_color.r, mat.diffuse_color.g, mat.diffuse_color.b, mat.alpha) ) else : f.write("\tColor : 0.8 0 0.45 1\n") f.write("\t}\n") f.write("}\n") f.close() def hasUVs(ob) : if len(ob.data.uv_layers) > 0 : return True else : return False def writeMaterial(ob, filename): #name of object/group oname = ob.name if len(ob.users_group) > 0 : oname = ob.users_group[0].name #nodes based material (splatting for terrain, etc) if hasUVs(ob) and ob.material_slots[0] != None and ob.material_slots[0].material != None : if ob.material_slots[0].material.use_nodes : writeSplatMaterial(ob.material_slots[0].material, filename, oname) else : #no its a simple material with up to 3 textures writeLightingMaterial(ob.material_slots[0].material, filename, oname) else : writeUnlitMaterial(ob, filename) #TODO copy textures over! #for each material of the object # stuff in parameters # for each texture in material # writeImageTexture(texture, filename) return {'FINISHED'} from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty class ExportSplatt(bpy.types.Operator, ExportHelper): """Export a ShaderNode material into a jme material """ bl_idname = 'mesh.exportsplatt' bl_label = 'ExportSplatt' bl_options = {'REGISTER', 'UNDO'} # ExportHelper mixin class uses this filename_ext = ".j3m" filepath = "" filter_glob = StringProperty(default="*.j3m",options={'HIDDEN'}) # @classmethod # def poll(cls, context): # obj = context.active_object # return (obj and obj.type == 'MESH') def execute(self, context): #return write_some_data(context, self.filepath, self.use_setting) return writeMaterial(context.active_object, self.filepath) #return {'FINISHED'} def menu_func(self, context): self.layout.operator(ExportSplatt.bl_idname, text="Export Splatt Material") def register(): bpy.utils.register_module(__name__) #bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func) #bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func) bpy.types.INFO_MT_file_export.append(menu_func) def unregister(): bpy.utils.unregister_module(__name__) #bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func) #bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func) bpy.types.VIEW3D_MT_object.remove(menu_func) bpy.types.INFO_MT_file_export.remove(menu_func) if __name__ == "__main__": register() # test call #bpy.ops.mesh.exportsplatt('INVOKE_DEFAULT')
just give me a pm where to send the files in case you get into trouble with the indents for the python scripts.
these files are addon files for blender and should be placed in the scripts addons folder, last edition tested with was blender 2.65, nodes were quite new, some things might have changed and will not work for sure in 2.7+ But it will give anybody willing to write an exporter/node setup a good head start.