1 2 module ws.gl.model; 3 4 import file = std.file, std.parallelism; 5 6 import 7 std.path, 8 std.file, 9 std.algorithm, 10 std.conv, 11 ws.io, 12 ws.exception, 13 ws.thread.loader, 14 ws.gl.gl, 15 ws.gl.batch, 16 ws.gl.texture, 17 ws.gl.material, 18 ws.file.obj, 19 ws.file.bbatch, 20 ws.log, 21 ws.string, 22 ws.math.vector; 23 24 25 __gshared: 26 27 28 class Model: Loadable { 29 30 string path; 31 BatchMaterial[] data; 32 33 alias Ptr=void*; 34 bool[Ptr] alreadyAdded; 35 36 package Loader batchFinisher; 37 package Loader materialFinisher; 38 package Loader glFinisher; 39 40 this(string p, Loader glFinisher=null, Loader batchFinisher=null, Loader materialFinisher=null){ 41 path = p; 42 this.batchFinisher = batchFinisher; 43 this.materialFinisher = materialFinisher; 44 this.glFinisher = glFinisher; 45 if(glFinisher) 46 glFinisher.run(&finish); 47 else 48 finish; 49 } 50 51 override protected void finish(){ 52 loadState = Loading; 53 if(path.extension == ".obj"){ 54 auto mdl = new OBJ("models/" ~ path); 55 long count=0; 56 foreach(object; mdl.objects){ 57 object.materials.values.each!((material){ 58 count++; 59 auto b = new BatchMaterial; 60 if(batchFinisher) 61 batchFinisher.run({ 62 b.batch = modelBatch(material); 63 }); 64 else if(!glFinisher) 65 b.batch = modelBatch(material); 66 else 67 assert(0, "Running in glFinisher but no way to finish batch in main"); 68 try 69 b.material = modelMaterial(mdl, material.name, materialFinisher); 70 catch(Exception e) 71 Log.warning(e.toString()); 72 data ~= b; 73 }); 74 } 75 }else if(path.extension == ".bb"){ 76 BinaryBatch mdl; 77 if(!exists("models/" ~ path)){ 78 mdl = BinaryBatch.fromObj(path.setExtension("obj")); 79 mdl.save; 80 }else 81 mdl = new BinaryBatch(path); 82 foreach(vm; mdl.data){ 83 auto b = new BatchMaterial; 84 data ~= b; 85 batchFinisher.run({ 86 b.batch = modelBatch(vm); 87 }); 88 } 89 }else{ 90 assert(0, "Unknown extension in " ~ path); 91 } 92 loadState = Loaded; 93 94 } 95 96 } 97 98 99 class BatchMaterial { 100 Batch batch; 101 DeferredMaterial material; 102 } 103 104 105 auto modelMaterial(OBJ mdl, string material_name, Loader materialFinisher=null){ 106 auto material = new DeferredMaterial(mdl.path ~ ':' ~ material_name); 107 float[3] col = [1,1,1]; 108 material.addUniform("diffuseColor", col); 109 bool hasDiffuse = false; 110 bool hasNormal = false; 111 foreach(mtllib; mdl.mtllibs){ 112 foreach(mtl; mtllib.mtls){ 113 if(mtl.name == material_name){ 114 if(mtl.mapDiffuse.length){ 115 material.linkVertex("diffuse_tex", "forwardTexCoords"); 116 material.linkFragment("diffuse_tex"); 117 material.addTexture("diffuse", mtl.mapDiffuse); 118 hasDiffuse = true; 119 } 120 if(mtl.illum == 0) 121 material.linkFragment("unlit"); 122 else 123 material.linkFragment("lit"); 124 if(mtl.mapBump.length){ 125 //material.linkVertex("normal_bump"); 126 material.linkFragment("normal_bump"); 127 material.addTexture("normal_bump", mtl.mapBump); 128 hasNormal = true; 129 } 130 break; 131 } 132 } 133 } 134 if(!hasDiffuse){ 135 material.linkFragment("diffuse_default"); 136 } 137 if(!hasNormal){ 138 material.linkVertex("normal_default", "forwardNormal"); 139 material.linkFragment("normal_default"); 140 } 141 if(materialFinisher) 142 materialFinisher.run(&material.finish); 143 else 144 material.finish; 145 return material; 146 } 147 148 149 auto modelBatch(OBJ.Material material){ 150 auto batch = new Batch; 151 batch.begin(material.vertcount); 152 foreach(polygon; material.polygons){ 153 foreach(vertex; polygon.vertices){ 154 float[2] uvw = vertex.uvw.data[0..2]; 155 batch.addPoint(vertex.pos, vertex.normal, uvw); 156 } 157 } 158 batch.finish; 159 return batch; 160 } 161 162 auto modelBatch(VertMat vm){ 163 auto batch = new Batch; 164 batch.begin(cast(int)(vm.vertices.length)); 165 foreach(vert; vm.vertices) 166 batch.addPoint(vert[0..3], vert[3..6], vert[6..8]); 167 batch.finish; 168 return batch; 169 } 170 171 172