Box3 plDistribComponent_old::GetFade() { Point3 pmin; if( fCompPB->GetInt(kFadeInActive) ) { pmin.Set(fCompPB->GetFloat(kFadeInTran), fCompPB->GetFloat(kFadeInOpaq), 0); if( pmin[0] == pmin[1] ) pmin[2] = 0; else if( pmin[0] < pmin[1] ) pmin[2] = -1.f; else pmin[2] = 1.f; } else { pmin.Set(0.f,0.f,0.f); } Point3 pmax; pmax.Set(fCompPB->GetFloat(kFadeOutTran), fCompPB->GetFloat(kFadeOutOpaq), 0); if( pmax[0] == pmax[1] ) pmax[2] = 0; else if( pmax[0] < pmax[1] ) pmax[2] = -1.f; else pmax[2] = 1.f; return Box3(pmin, pmax); }
/** * @brief * Returns the position, rotation and scale of the scene node at a given time */ void PLSceneNode::GetPosRotScale(Point3 &vPos, Quat &qRot, Point3 &vScale, TimeValue nTime) { if (m_pIGameNode) { // Get the position, rotation and scale - relative to the parent node GMatrix mParentMatrix; IGameNode *pIGameNodeParent = m_pIGameNode->GetNodeParent(); if (pIGameNodeParent) mParentMatrix = pIGameNodeParent->GetWorldTM(nTime); PLTools::GetPosRotScale(m_pIGameNode->GetWorldTM(nTime)*PLTools::Inverse(mParentMatrix), vPos, qRot, vScale, IsRotationFlipped()); // Get the scale (NOT done for special nodes!) if (m_nType != TypeContainer && m_nType != TypeScene && m_nType != TypeCell && m_nType != TypeCamera && m_nType != TypeLight) { // [TODO] Do we still need this hint? // Check for none uniform scale // if (m_vScale.x != m_vScale.y || m_vScale.x != m_vScale.z || m_vScale.y != m_vScale.z) { // We have to use '%e' because else we may get output like '(1 1 1) is no uniform scale' // g_pLog->LogFLine(PLLog::Hint, "Node '%s' has a none uniform scale. (%e %e %e) This 'may' cause problems in special situations...", m_sName.c_str(), m_vScale.x, m_vScale.y, m_vScale.z); // } } else { // Set scale to 1 vScale.Set(1.0f, 1.0f, 1.0f); } } }
// ///////////////////////////////////////////////////////////////// // // ///////////////////////////////////////////////////////////////// bool SetPoint3FromLua(const LuaPlus::LuaObject &posData, Point3 &position) { if(!posData.IsTable()) { return (false); } LuaPlus::LuaObject xData = posData["x"]; LuaPlus::LuaObject yData = posData["y"]; LuaPlus::LuaObject zData = posData["z"]; if(!xData.IsNumber() || !yData.IsNumber() || !zData.IsNumber()) { return (false); } F32 x = static_cast<F32>(xData.GetNumber()); F32 y = static_cast<F32>(yData.GetNumber()); F32 z = static_cast<F32>(zData.GetNumber()); position.Set(x, y, z); return (true); }
Color MtlBlinn::Shade(const Cone &ray, const HitInfo &hInfo, const LightList &lights, int bounceCount) const{ float bias = BIAS_SHADING; Color shade; Color rShade = Color(0,0,0); Color tShade = Color(0,0,0); const Material *mat; mat = hInfo.node->GetMaterial(); const MtlBlinn* mb =static_cast<const MtlBlinn*>(mat); // cout<<"HInfo front: "<<hInfo.front<<endl; /* local copy */ Point3 P; P.Set(hInfo.p.x,hInfo.p.y,hInfo.p.z); Cone iRay = ray; Color ambInt = mb->diffuse.Sample(hInfo.uvw, hInfo.duvw); Color allOther = Color(0,0,0); Color diffuse = mb->diffuse.Sample(hInfo.uvw, hInfo.duvw); Color ambComponent = Color(0,0,0); Point3 newN = hInfo.N; for ( unsigned int i=0; i<lights.size(); i++ ) { if(lights[i]->IsAmbient()){ // cout<<"ambient "<<endl; Color intensity = lights[i]->Illuminate(hInfo.p); ambComponent += (ambInt * intensity); continue; } else{ // cout<<"other lighting "<<endl; Point3 L = -lights[i]->Direction(P); L.Normalize(); Point3 V = ray.p - P; V.Normalize(); Point3 LplusV = L + V; Point3 H = (L+V)/LplusV.Length(); H.Normalize(); float alpha = mb->glossiness; // Point3 N = hInfo.N; Point3 N = newN; float S = H.Dot(N); S = pow(S,alpha); float costheta = L.Dot(N)/(L.Length() * N.Length()); Color intensity = lights[i]->Illuminate(P); // cout<<"costheta "<<endl; allOther += intensity * (costheta>0?costheta:0) * (diffuse + S * (mb->specular.Sample(hInfo.uvw, hInfo.duvw))) ; } /* finally add inta*cola + intall*costheta*(cold + s* colS)*/ shade = ambComponent + allOther; } /* Calculate refraction */ if(refraction.GetColor().r>0 && bounceCount>0){ //compute new jittered normal float gloss = refractionGlossiness; if(gloss){ float random = rand()/(float)RAND_MAX; float rRadius = sqrtf(random) * gloss; random = rand()/(float)RAND_MAX; float rAngle = random * 2.0 * M_PI; float x = rRadius * cos(rAngle); float y = rRadius * sin(rAngle); Point3 xAxis(1,0,0), yAxis(0,1,0), v1, v2, normalDir; normalDir = hInfo.N; // normalDir.Normalize(); if(normalDir.Dot(xAxis) > 0.7) v1 = normalDir.Cross(yAxis); else v1 = normalDir.Cross(xAxis); v2 = v1.Cross(normalDir); v1.Normalize(); v2.Normalize(); v1 *= x; v2 *= y; newN = hInfo.N + v1.Length() + v2.Length(); newN.Normalize(); } else{ newN = hInfo.N; } //------------------------------------- Color reflShade = Color(0,0,0); float R0, Refl = 0.0f, Trans = 0.0f; HitInfo temp; temp.Init(); // Point3 N = hInfo.N; Point3 N = newN; // Point3 V = Point3(iRay.p.x - hInfo.p.x, iRay.p.y - hInfo.p.y, iRay.p.z - hInfo.p.z); Point3 V = Point3(hInfo.p.x - iRay.p.x, hInfo.p.y - iRay.p.y, hInfo.p.z - iRay.p.z); V.Normalize(); float n1 = 1, n2 = 1; if(hInfo.front){ /* Hitting from outside */ // temp.front = false; n2 = ior; // cout<<"outside "<<endl; } else if(!hInfo.front){ /* Transmission from the inside */ // temp.front = true; n1 = ior; // cout<<"intside... "<<endl; // N = -hInfo.N; N *= -1; } float ratio_n = n1 / n2; float costheta_v = -V.Dot(N); /* refer: http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf */ float sin2theta_t = ratio_n * ratio_n * (1 - costheta_v * costheta_v); Point3 T = ratio_n * V + (ratio_n * costheta_v - sqrtf(1 - sin2theta_t)) * N ; // cout<<ratio_n<<" "<<"cos_v "<<costheta_v<<" sin2theta_t "<<sin2theta_t<<endl; Cone tRay = Cone(hInfo.p,T); //tRay.dir.Normalize(); tRay.p.x = tRay.p.x + bias *tRay.dir.x; /* add bias */ tRay.p.y = tRay.p.y + bias *tRay.dir.y; tRay.p.z = tRay.p.z + bias *tRay.dir.z; // cout<<"B temp front: "<< temp.front<<endl; temp.timeInstance = hInfo.timeInstance; if(sin2theta_t <= 1){ if(RayTrace_2(tRay, temp)){ /* ray tracing after refraction */ // bounceCount--; tShade = temp.node->GetMaterial()->Shade(tRay,temp,lights,bounceCount); } else{ /* no hit after refraction */ tShade = environment.SampleEnvironment(tRay.dir); } /* Calculate Schlick's approximation */ R0 = (n1 - n2)/(n1 + n2); R0 *= R0; double X = 0.0; // if(n1 > n2){ // X = 1.0 - sqrtf(1.0 - sin2theta_t); // } // else{ X = 1.0 - costheta_v; } X = 1.0 - costheta_v; Refl = R0 + (1.0 - R0) * X * X * X * X * X; Trans = 1.0 - Refl; tShade.r *= exp(-absorption.r * temp.z); tShade.g *= exp(-absorption.g * temp.z); tShade.b *= exp(-absorption.b * temp.z); } else {/* Total internal reflection */ Refl = 1.0f; } /* Calculate reflection due to reflectance */ if(bounceCount >0){ // N = hInfo.N; N = newN; Point3 V = Point3(iRay.p.x - P.x, iRay.p.y - P.y, iRay.p.z - P.z); //V.Normalize(); Point3 VR = 2 * V.Dot(N) * N - V; //VR.Normalize(); Cone rRay = Cone(P + BIAS_SHADING * VR, VR); rRay.dir.Normalize(); HitInfo temp1; temp1.Init(); temp.timeInstance = hInfo.timeInstance; if(rootNode.GetNumChild()>0){ if(RayTrace_2(rRay, temp1)){ bounceCount --; reflShade = temp1.node->GetMaterial()->Shade(rRay, temp1, lights, bounceCount); } else{ reflShade = environment.SampleEnvironment(rRay.dir); // reflShade = Color(1,100,1); } } } // cout<<"Refl: "<<Refl<<"Trans "<<Trans<<endl; tShade = refraction.GetColor().r * (Trans * tShade + Refl * reflShade); } /* calculate reflection*/ if(reflection.GetColor().r>0 && bounceCount > 0){ //compute new jittered normal float gloss = reflectionGlossiness; if(gloss){ float random = rand()/(float)RAND_MAX; float rRadius = sqrtf(random) * gloss; random = rand()/(float)RAND_MAX; float rAngle = random * 2.0 * M_PI; float x = rRadius * cos(rAngle); float y = rRadius * sin(rAngle); Point3 xAxis(1,0,0), yAxis(0,1,0), v1, v2, normalDir; normalDir = hInfo.N; // normalDir.Normalize(); if(normalDir.Dot(xAxis) > 0.7) v1 = normalDir.Cross(yAxis); else v1 = normalDir.Cross(xAxis); v2 = v1.Cross(normalDir); v1.Normalize(); v2.Normalize(); v1 *= x; v2 *= y; newN = hInfo.N + v1+ v2; newN.Normalize(); } else{ newN = hInfo.N; } //------------------------------------- Point3 N = newN;//hInfo.N; Point3 V = Point3(iRay.p.x - P.x, iRay.p.y - P.y, iRay.p.z - P.z); // V.Normalize(); Point3 VR = 2 * V.Dot(N) * N - V; Cone rRay = Cone(P + BIAS_SHADING * VR, VR); rRay.dir.Normalize(); HitInfo temp; temp.Init(); temp.timeInstance=hInfo.timeInstance; if(rootNode.GetNumChild()>0){ if(RayTrace_2(rRay, temp)){ bounceCount--; rShade = reflection.GetColor().r * temp.node->GetMaterial()->Shade(rRay, temp, lights, bounceCount); } else{ rShade = reflection.GetColor().r *environment.SampleEnvironment(rRay.dir); // rShade = Color(1,111,1); } } } /* Add shade with reflected and refracted colors */ shade += (rShade + tShade); return shade; };
int ExportQuake3Model(const TCHAR *filename, ExpInterface *ei, Interface *gi, int start_time, std::list<ExportNode> lTags, std::list<ExportNode> lMeshes) { FILE *file; int i, j, totalTags, totalMeshes, current_time = 0; long pos_current, totalTris = 0, totalVerts = 0; std::list<FrameRange>::iterator range_i; std::vector<Point3> lFrameBBoxMin; std::vector<Point3> lFrameBBoxMax; long pos_tagstart; long pos_tagend; long pos_filesize; long pos_framestart; int lazynamesfixed = 0; const Point3 x_axis(1, 0, 0); const Point3 z_axis(0, 0, 1); SceneEnumProc checkScene(ei->theScene, start_time, gi); totalTags = (int)lTags.size(); if (g_tag_for_pivot) totalTags++; totalMeshes = (int)lMeshes.size(); // open file file = _tfopen(filename, _T("wb")); if (!file) { ExportError("Cannot open file '%s'.", filename); return FALSE; } ExportDebug("%s:", filename); // sync pattern and version putChars("IDP3", 4, file); put32(15, file); putChars("Darkplaces MD3 Exporter", 64, file); put32(0, file); // flags // MD3 header ExportState("Writing MD3 header"); put32(g_total_frames, file); // how many frames put32(totalTags, file); // tagsnum put32(totalMeshes, file); // meshnum put32(1, file); // maxskinnum put32(108, file); // headersize pos_tagstart = ftell(file); put32(0, file); // tagstart pos_tagend = ftell(file); put32(256, file); // tagend pos_filesize = ftell(file); put32(512, file); // filesize ExportDebug(" %i frames, %i tags, %i meshes", g_total_frames, totalTags, totalMeshes); // frame info // bbox arrays get filled while exported mesh and written back then ExportState("Writing frame info"); pos_framestart = ftell(file); lFrameBBoxMin.resize(g_total_frames); lFrameBBoxMax.resize(g_total_frames); for (i = 0; i < g_total_frames; i++) { // init frame data lFrameBBoxMin[i].Set(0, 0, 0); lFrameBBoxMax[i].Set(0, 0, 0); // put data putFloat(-1.0f, file); // bbox min vector putFloat(-1.0f, file); putFloat(-1.0f, file); putFloat( 1.0f, file); // bbox max vector putFloat(1.0f, file); putFloat(1.0f, file); putFloat(0.0f, file); // local origin (usually 0 0 0) putFloat(0.0f, file); putFloat(0.0f, file); putFloat(1.0f, file); // radius of bounding sphere putChars("", 16, file); } // tags pos_current = ftell(file); fseek(file, pos_tagstart, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); // for each frame range cycle all frames and write out each tag long pos_tags = pos_current; if (totalTags) { long current_frame = 0; ExportState("Writing %i tags", totalTags); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi); current_time = current_scene.time; // write out tags if (lTags.size()) { for (std::list<ExportNode>::iterator tag_i = lTags.begin(); tag_i != lTags.end(); tag_i++) { INode *node = current_scene[tag_i->i]->node; Matrix3 tm = node->GetObjTMAfterWSM(current_time); ExportState("Writing '%s' frame %i of %i", tag_i->name, i, g_total_frames); // tagname putChars(tag_i->name, 64, file); // origin, rotation matrix Point3 row = tm.GetRow(3); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(0); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(1); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(2); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); } } // write the center of mass tag_pivot which is avg of all objects's pivots if (g_tag_for_pivot) { ExportState("Writing 'tag_pivot' frame %i of %i", i, g_total_frames); // write the null data as tag_pivot need to be written after actual geometry // (it needs information on frame bound boxes to get proper blendings) putChars("tag_pivot", 64, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); } } } } // write the tag object offsets pos_current = ftell(file); fseek(file, pos_tagend, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); // allocate the structs used to calculate tag_pivot std::vector<Point3> tag_pivot_origin; std::vector<double> tag_pivot_volume; if (g_tag_for_pivot) { tag_pivot_origin.resize(g_total_frames); tag_pivot_volume.resize(g_total_frames); } // mesh objects // for each mesh object write uv and frames SceneEnumProc scratch(ei->theScene, start_time, gi); ExportState("Writing %i meshes", (int)lMeshes.size()); for (std::list<ExportNode>::iterator mesh_i = lMeshes.begin(); mesh_i != lMeshes.end(); mesh_i++) { bool needsDel; ExportState("Start mesh #%i", mesh_i); INode *node = checkScene[mesh_i->i]->node; Matrix3 tm = node->GetObjTMAfterWSM(start_time); TriObject *tri = GetTriObjectFromNode(node, start_time, needsDel); if (!tri) continue; // get mesh, compute normals Mesh &mesh = tri->GetMesh(); MeshNormalSpec *meshNormalSpec = mesh.GetSpecifiedNormals(); if (meshNormalSpec) { if (!meshNormalSpec->GetNumFaces()) meshNormalSpec = NULL; else { meshNormalSpec->SetParent(&mesh); meshNormalSpec->CheckNormals(); } } mesh.checkNormals(TRUE); // fix lazy object names ExportState("Attempt to fix mesh name '%s'", mesh_i->name); char meshname[64]; size_t meshnamelen = min(63, strlen(mesh_i->name)); memset(meshname, 0, 64); strncpy(meshname, mesh_i->name, meshnamelen); meshname[meshnamelen] = 0; if (!strncmp("Box", meshname, 3) || !strncmp("Sphere", meshname, 6) || !strncmp("Cylinder", meshname, 8) || !strncmp("Torus", meshname, 5) || !strncmp("Cone", meshname, 4) || !strncmp("GeoSphere", meshname, 9) || !strncmp("Tube", meshname, 4) || !strncmp("Pyramid", meshname, 7) || !strncmp("Plane", meshname, 5) || !strncmp("Teapot", meshname, 6) || !strncmp("Object", meshname, 6)) { name_conflict: lazynamesfixed++; if (lazynamesfixed == 1) strcpy(meshname, "base"); else sprintf(meshname, "base%i", lazynamesfixed); // check if it's not used by another mesh for (std::list<ExportNode>::iterator m_i = lMeshes.begin(); m_i != lMeshes.end(); m_i++) if (!strncmp(m_i->name, meshname, strlen(meshname))) goto name_conflict; // approve name ExportWarning("Lazy object name '%s' (mesh renamed to '%s').", node->GetName(), meshname); } // special mesh check bool shadow_or_collision = false; if (g_mesh_special) if (!strncmp("collision", meshname, 9) || !strncmp("shadow", meshname, 6)) shadow_or_collision = true; // get material const char *shadername = NULL; Texmap *tex = 0; Mtl *mtl = 0; if (!shadow_or_collision) { mtl = node->GetMtl(); if (mtl) { // check for multi-material if (mtl->IsMultiMtl()) { // check if it's truly multi material // we do support multi-material with only one texture (some importers set it) bool multi_material = false; MtlID matId = mesh.faces[0].getMatID(); for (i = 1; i < mesh.getNumFaces(); i++) if (mesh.faces[i].getMatID() != matId) multi_material = true; if (multi_material) if (g_mesh_multimaterials == MULTIMATERIALS_NONE) ExportWarning("Object '%s' is multimaterial and using multiple materials on its faces, that case is not yet supported (truncating to first submaterial).", node->GetName()); // switch to submaterial mtl = mtl->GetSubMtl(matId); } // get shader from material if supplied char *materialname = GetChar(mtl->GetName()); if (g_mesh_materialasshader && (strstr(materialname, "/") != NULL || strstr(materialname, "\\") != NULL)) shadername = GetChar(mtl->GetName()); else { // get texture tex = mtl->GetSubTexmap(ID_DI); if (tex) { if (tex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00)) { shadername = GetChar(((BitmapTex *)tex)->GetMapName()); if (shadername == NULL || !shadername[0]) ExportWarning("Object '%s' material '%s' has no bitmap.", tex->GetName(), node->GetName()); } else { tex = NULL; ExportWarning("Object '%s' has material with wrong texture type (only Bitmap are supported).", node->GetName()); } } else ExportWarning("Object '%s' has material but no texture.", node->GetName()); } } else ExportWarning("Object '%s' has no material.", node->GetName()); } long pos_meshstart = ftell(file); // surface object ExportState("Writing mesh '%s' header", meshname); putChars("IDP3", 4, file); putChars(meshname, 64, file); put32(0, file); // flags put32(g_total_frames, file); // framecount put32(1, file); // skincount long pos_vertexnum = ftell(file); put32(0, file); // vertexcount put32(mesh.getNumFaces(), file); // trianglecount long pos_trianglestart = ftell(file); put32(0, file); // start triangles put32(108, file); // header size long pos_texvecstart = ftell(file); put32(0, file); // texvecstart long pos_vertexstart = ftell(file); put32(16, file); // vertexstart long pos_meshsize = ftell(file); put32(32, file); // meshsize // write out a single 'skin' ExportState("Writing mesh %s texture", meshname); if (shadow_or_collision) putChars(meshname, 64, file); else if (shadername) putMaterial(shadername, mtl, tex, file); else putChars("noshader", 64, file); put32(0, file); // flags // build geometry ExportState("Building vertexes/triangles"); std::vector<ExportVertex>vVertexes; std::vector<ExportTriangle>vTriangles; vVertexes.resize(mesh.getNumVerts()); int vExtraVerts = mesh.getNumVerts(); for (i = 0; i < mesh.getNumVerts(); i++) { vVertexes[i].vert = i; vVertexes[i].normalfilled = false; // todo: check for coincident verts } int vNumExtraVerts = 0; // check normals if (!mesh.normalsBuilt && !shadow_or_collision) ExportWarning("Object '%s' does not have normals contructed.", node->GetName()); // get info for triangles const float normal_epsilon = 0.01f; vTriangles.resize(mesh.getNumFaces()); for (i = 0; i < mesh.getNumFaces(); i++) { DWORD smGroup = mesh.faces[i].getSmGroup(); ExportState("Mesh %s: checking normals for face %i of %i", meshname, i, mesh.getNumFaces()); for (j = 0; j < 3; j++) { int vert = mesh.faces[i].getVert(j); vTriangles[i].e[j] = vert; // find a right normal for this vertex and save its 'address' int vni; Point3 vn; if (!mesh.normalsBuilt || shadow_or_collision) { vn.Set(0, 0, 0); vni = 0; } else { int numNormals; RVertex *rv = mesh.getRVertPtr(vert); if (meshNormalSpec) { ExportState("face %i vert %i have normal specified", i, j); // mesh have explicit normals (i.e. Edit Normals modifier) vn = meshNormalSpec->GetNormal(i, j); vni = meshNormalSpec->GetNormalIndex(i, j); } else if (rv && rv->rFlags & SPECIFIED_NORMAL) { ExportState("face %i vert %i have SPECIFIED_NORMAL flag", i, j); // SPECIFIED_NORMAL flag vn = rv->rn.getNormal(); vni = 0; } else if (rv && (numNormals = rv->rFlags & NORCT_MASK) && smGroup) { // If there is only one vertex is found in the rn member. if (numNormals == 1) { ExportState("face %i vert %i have solid smooth group", i, j); vn = rv->rn.getNormal(); vni = 0; } else { ExportState("face %i vert %i have mixed smoothing groups", i, j); // If two or more vertices are there you need to step through them // and find the vertex with the same smoothing group as the current face. // You will find multiple normals in the ern member. for (int k = 0; k < numNormals; k++) { if (rv->ern[k].getSmGroup() & smGroup) { vn = rv->ern[k].getNormal(); vni = 1 + k; } } } } else { ExportState("face %i vert %i flat shaded", i, j); // Get the normal from the Face if no smoothing groups are there vn = mesh.getFaceNormal(i); vni = 0 - (i + 1); } } // subdivide to get all normals right if (!vVertexes[vert].normalfilled) { vVertexes[vert].normal = vn; vVertexes[vert].normalindex = vni; vVertexes[vert].normalfilled = true; } else if ((vVertexes[vert].normal - vn).Length() >= normal_epsilon) { // current vertex not matching normal - it was already filled by different smoothing group // find a vert in extra verts in case it was already created bool vert_found = false; for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++) { if (vVertexes[ev].vert == vert && (vVertexes[ev].normal - vn).Length() < normal_epsilon) { vert_found = true; vTriangles[i].e[j] = ev; break; } } // we havent found a vertex, create new if (!vert_found) { ExportVertex NewVert; NewVert.vert = vVertexes[vert].vert; NewVert.normal = vn; NewVert.normalindex = vni; NewVert.normalfilled = true; vTriangles[i].e[j] = (int)vVertexes.size(); vVertexes.push_back(NewVert); vNumExtraVerts++; } } } } int vNumExtraVertsForSmoothGroups = vNumExtraVerts; // generate UV map // VorteX: use direct maps reading since getNumTVerts()/getTVert is deprecated // max sets two default mesh maps: 0 - vertex color, 1 : UVW, 2 & up are custom ones ExportState("Building UV map"); std::vector<ExportUV>vUVMap; vUVMap.resize(vVertexes.size()); int meshMap = 1; if (!mesh.mapSupport(meshMap) || !mesh.getNumMapVerts(meshMap) || shadow_or_collision) { for (i = 0; i < mesh.getNumVerts(); i++) { vUVMap[i].u = 0.5; vUVMap[i].v = 0.5; } if (!shadow_or_collision) ExportWarning("No UV mapping was found on object '%s'.", node->GetName()); } else { UVVert *meshUV = mesh.mapVerts(meshMap); for (i = 0; i < (int)vTriangles.size(); i++) { ExportState("Mesh %s: converting tvert for face %i of %i", meshname, i, (int)vTriangles.size()); // for 3 face vertexes for (j = 0; j < 3; j++) { int vert = vTriangles[i].e[j]; int tv = mesh.tvFace[i].t[j]; UVVert &UV = meshUV[tv]; if (!vUVMap[vert].filled) { // fill uvMap vertex vUVMap[vert].u = UV.x; vUVMap[vert].v = UV.y; vUVMap[vert].filled = true; vUVMap[vert].tvert = tv; } else if (tv != vUVMap[vert].tvert) { // uvMap slot for this vertex has been filled // we should arrange triangle to other vertex, which not filled and having same shading and uv // check if any of the extra vertices can fit bool vert_found = false; for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++) { if (vVertexes[ev].vert == vert && vUVMap[vert].u == UV.x &&vUVMap[vert].v == UV.y && (vVertexes[ev].normal - vVertexes[vert].normal).Length() < normal_epsilon) { vert_found = true; vTriangles[i].e[j] = vVertexes[ev].vert; break; } } if (!vert_found) { // create new vert ExportVertex NewVert; NewVert.vert = vVertexes[vert].vert; NewVert.normal = vVertexes[vert].normal; NewVert.normalindex = vVertexes[vert].normalindex; NewVert.normalfilled = vVertexes[vert].normalfilled; vTriangles[i].e[j] = (int)vVertexes.size(); vVertexes.push_back(NewVert); vNumExtraVerts++; // create new TVert ExportUV newUV; newUV.filled = true; newUV.u = UV.x; newUV.v = UV.y; newUV.tvert = tv; vUVMap.push_back(newUV); } } } } } int vNumExtraVertsForUV = (vNumExtraVerts - vNumExtraVertsForSmoothGroups); // print some debug stats ExportDebug(" mesh %s: %i vertexes +%i %s +%i UV, %i triangles", meshname, ((int)vVertexes.size() - vNumExtraVerts), vNumExtraVertsForSmoothGroups, meshNormalSpec ? "EditNormals" : "SmoothGroups", vNumExtraVertsForUV, (int)vTriangles.size()); // fill in triangle start pos_current = ftell(file); fseek(file, pos_trianglestart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // detect if object have negative scale (mirrored) // in this canse we should rearrange triangles counterclockwise // so stuff will not be inverted ExportState("Mesh %s: writing %i triangles", meshname, (int)vTriangles.size()); if (DotProd(CrossProd(tm.GetRow(0), tm.GetRow(1)), tm.GetRow(2)) < 0.0) { ExportWarning("Object '%s' is mirrored (having negative scale on it's transformation)", node->GetName()); for (i = 0; i < (int)vTriangles.size(); i++) { put32(vTriangles[i].b, file); // vertex index put32(vTriangles[i].c, file); // for 3 vertices put32(vTriangles[i].a, file); // of triangle } } else { for (i = 0; i < (int)vTriangles.size(); i++) { put32(vTriangles[i].a, file); // vertex index put32(vTriangles[i].c, file); // for 3 vertices put32(vTriangles[i].b, file); // of triangle } } // fill in texvecstart // write out UV mapping coords. ExportState("Mesh %s: writing %i UV vertexes", meshname, (int)vUVMap.size()); pos_current = ftell(file); fseek(file, pos_texvecstart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); for (i = 0; i < (int)vUVMap.size(); i++) { putFloat(vUVMap[i].u, file); // texture coord u,v putFloat(1.0f - vUVMap[i].v, file); // for vertex } vUVMap.clear(); // fill in vertexstart pos_current = ftell(file); fseek(file, pos_vertexstart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // fill in vertexnum pos_current = ftell(file); fseek(file, pos_vertexnum, SEEK_SET); put32((int)vVertexes.size(), file); fseek(file, pos_current, SEEK_SET); // write out for each frame the position of each vertex long current_frame = 0; ExportState("Mesh %s: writing %i frames", meshname, g_total_frames); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { bool _needsDel; // get triobject for current frame SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi); current_time = current_scene.time; INode *_node = current_scene[mesh_i->i]->node; TriObject *_tri = GetTriObjectFromNode(_node, current_time, _needsDel); if (!_tri) continue; // get mesh, compute normals Mesh &_mesh = _tri->GetMesh(); MeshNormalSpec *_meshNormalSpec = _mesh.GetSpecifiedNormals(); if (_meshNormalSpec) { if (!_meshNormalSpec->GetNumFaces()) _meshNormalSpec = NULL; else { _meshNormalSpec->SetParent(&_mesh); _meshNormalSpec->CheckNormals(); } } _mesh.checkNormals(TRUE); // get transformations for current frame Matrix3 _tm = _node->GetObjTMAfterWSM(current_time); ExportState("Mesh %s: writing frame %i of %i", meshname, current_frame, g_total_frames); Point3 BoxMin(0, 0, 0); Point3 BoxMax(0, 0, 0); for (j = 0; j < (int)vVertexes.size(); j++) // number of vertices { ExportState("Mesh %s: transform vertex %i of %i", meshname, j, (int)vVertexes.size()); int vert = vVertexes[j].vert; Point3 &v = _tm.PointTransform(_mesh.getVert(vert)); // populate bbox data if (!shadow_or_collision) { BoxMin.x = min(BoxMin.x, v.x); BoxMin.y = min(BoxMin.y, v.y); BoxMin.z = min(BoxMin.z, v.z); BoxMax.x = max(BoxMax.x, v.x); BoxMax.y = max(BoxMax.y, v.y); BoxMax.z = max(BoxMax.z, v.z); } // write vertex double f; f = v.x * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); f = v.y * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); f = v.z * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); // get normal ExportState("Mesh %s: transform vertex normal %i of %i", meshname, j, (int)vVertexes.size()); Point3 n; if (_meshNormalSpec) // mesh have explicit normals (i.e. Edit Normals modifier) n = _meshNormalSpec->Normal(vVertexes[j].normalindex); else if (!vVertexes[j].normalfilled || !_mesh.normalsBuilt) n = _mesh.getNormal(vert); else { RVertex *rv = _mesh.getRVertPtr(vert); if (vVertexes[j].normalindex < 0) n = _mesh.getFaceNormal((0 - vVertexes[j].normalindex) - 1); else if (vVertexes[j].normalindex == 0) n = rv->rn.getNormal(); else n = rv->ern[vVertexes[j].normalindex - 1].getNormal(); } // transform normal Point3 &nt = _tm.VectorTransform(n).Normalize(); // encode a normal vector into a 16-bit latitude-longitude value double lng = acos(nt.z) * 255 / (2 * pi); double lat = atan2(nt.y, nt.x) * 255 / (2 * pi); put16((((int)lat & 0xFF) << 8) | ((int)lng & 0xFF), file); } // blend the pivot positions for tag_pivot using mesh's volumes for blending power if (g_tag_for_pivot && !shadow_or_collision) { ExportState("Mesh %s: writing tag_pivot", meshname); Point3 Size = BoxMax - BoxMin; double BoxVolume = pow(Size.x * Size.y * Size.z, 0.333f); // blend matrices float blend = (float)(BoxVolume / (BoxVolume + tag_pivot_volume[current_frame])); float iblend = 1 - blend; tag_pivot_volume[current_frame] = tag_pivot_volume[current_frame] + BoxVolume; Point3 row = _tm.GetRow(3) - _node->GetObjOffsetPos(); tag_pivot_origin[current_frame].x = tag_pivot_origin[current_frame].x * iblend + row.x * blend; tag_pivot_origin[current_frame].y = tag_pivot_origin[current_frame].y * iblend + row.y * blend; tag_pivot_origin[current_frame].z = tag_pivot_origin[current_frame].z * iblend + row.z * blend; } // populate bbox data for frames lFrameBBoxMin[current_frame].x = min(lFrameBBoxMin[current_frame].x, BoxMin.x); lFrameBBoxMin[current_frame].y = min(lFrameBBoxMin[current_frame].y, BoxMin.y); lFrameBBoxMin[current_frame].z = min(lFrameBBoxMin[current_frame].z, BoxMin.z); lFrameBBoxMax[current_frame].x = max(lFrameBBoxMax[current_frame].x, BoxMax.x); lFrameBBoxMax[current_frame].y = max(lFrameBBoxMax[current_frame].y, BoxMax.y); lFrameBBoxMax[current_frame].z = max(lFrameBBoxMax[current_frame].z, BoxMax.z); // delete the working object, if necessary. if (_needsDel) delete _tri; } } // delete if necessary if (needsDel) delete tri; // fill in meshsize pos_current = ftell(file); fseek(file, pos_meshsize, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // reset back to first frame SceneEnumProc scratch(ei->theScene, start_time, gi); totalTris += (long)vTriangles.size(); totalVerts += (long)vVertexes.size(); vTriangles.clear(); vVertexes.clear(); } // write tag_pivot ExportState("Writing tag_pivot positions"); if (g_tag_for_pivot) { pos_current = ftell(file); long current_frame = 0; for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { fseek(file, pos_tags + totalTags*112*current_frame + (int)lTags.size()*112 + 64, SEEK_SET); // origin putFloat(tag_pivot_origin[current_frame].x, file); putFloat(tag_pivot_origin[current_frame].y, file); putFloat(tag_pivot_origin[current_frame].z, file); } } fseek(file, pos_current, SEEK_SET); } tag_pivot_volume.clear(); tag_pivot_origin.clear(); // write frame data ExportState("Writing culling info"); long current_frame = 0; pos_current = ftell(file); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { fseek(file, pos_framestart + current_frame*56, SEEK_SET); putFloat(lFrameBBoxMin[current_frame].x, file); // bbox min vector putFloat(lFrameBBoxMin[current_frame].y, file); putFloat(lFrameBBoxMin[current_frame].z, file); putFloat(lFrameBBoxMax[current_frame].x, file); // bbox max vector putFloat(lFrameBBoxMax[current_frame].y, file); putFloat(lFrameBBoxMax[current_frame].z, file); putFloat(0, file); // local origin (usually 0 0 0) putFloat(0, file); putFloat(0, file); putFloat(max(lFrameBBoxMin[current_frame].Length(), lFrameBBoxMax[current_frame].Length()) , file); // radius of bounding sphere } } fseek(file, pos_current, SEEK_SET); lFrameBBoxMin.clear(); lFrameBBoxMax.clear(); // fill in filesize pos_current = ftell(file); fseek(file, pos_filesize, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); fclose(file); ExportDebug(" total: %i vertexes, %i triangles", totalVerts, totalTris); return TRUE; }
Color MtlBlinn::Shade(const Ray &ray, const HitInfo &hInfo, const LightList &lights, int bounceCount) const{ float bias = BIAS_SHADING; Color shade; Color rShade = Color(0,0,0); Color tShade = Color(0,0,0); const Material *mat; mat = hInfo.node->GetMaterial(); const MtlBlinn* mb =static_cast<const MtlBlinn*>(mat); // cout<<"HInfo front: "<<hInfo.front<<endl; /* local copy */ Point3 P; P.Set(hInfo.p.x,hInfo.p.y,hInfo.p.z); Ray iRay = ray; Color ambInt = mb->diffuse; Color allOther = Color(0,0,0); Color diffuse = mb->diffuse;; Color ambComponent = Color(0,0,0); for ( unsigned int i=0; i<lights.size(); i++ ) { if(lights[i]->IsAmbient()){ // cout<<"ambient "<<endl; Color intensity = lights[i]->Illuminate(hInfo.p); ambComponent += (ambInt * intensity); continue; } else{ // cout<<"other lighting "<<endl; Point3 L = -lights[i]->Direction(P); L.Normalize(); Point3 V = ray.p - P; V.Normalize(); Point3 LplusV = L + V; Point3 H = (L+V)/LplusV.Length(); H.Normalize(); float alpha = mb->glossiness; Point3 N = hInfo.N; float S = H.Dot(N); S = pow(S,alpha); float costheta = L.Dot(N)/(L.Length() * N.Length()); Color intensity = lights[i]->Illuminate(P); // cout<<"costheta "<<endl; allOther += intensity * (costheta>0?costheta:0) * (diffuse + S * (mb->specular)) ; } /* finally add inta*cola + intall*costheta*(cold + s* colS)*/ shade = ambComponent + allOther; } /* Calculate refraction */ if(refraction.Grey()>0 && bounceCount>0){ Color reflShade = Color(0,0,0); float R0, Refl = 0.0f, Trans = 0.0f; HitInfo temp; temp.Init(); Point3 N = hInfo.N; // Point3 V = Point3(iRay.p.x - hInfo.p.x, iRay.p.y - hInfo.p.y, iRay.p.z - hInfo.p.z); Point3 V = Point3(hInfo.p.x - iRay.p.x, hInfo.p.y - iRay.p.y, hInfo.p.z - iRay.p.z); V.Normalize(); float n1 = 1, n2 = 1; if(hInfo.front){ /* Hitting from outside */ // temp.front = false; n2 = ior; // cout<<"outside "<<endl; } else if(!hInfo.front){ /* Transmission from the inside */ // temp.front = true; n1 = ior; // cout<<"intside... "<<endl; N = -hInfo.N; } float ratio_n = n1 / n2; float costheta_v = -V.Dot(N); /* refer: http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf */ float sin2theta_t = ratio_n * ratio_n * (1 - costheta_v * costheta_v); Point3 T = ratio_n * V + (ratio_n * costheta_v - sqrtf(1 - sin2theta_t)) * N ; // cout<<ratio_n<<" "<<"cos_v "<<costheta_v<<" sin2theta_t "<<sin2theta_t<<endl; Ray tRay = Ray(hInfo.p,T); //tRay.dir.Normalize(); tRay.p.x = tRay.p.x + bias *tRay.dir.x; /* add bias */ tRay.p.y = tRay.p.y + bias *tRay.dir.y; tRay.p.z = tRay.p.z + bias *tRay.dir.z; // cout<<"B temp front: "<< temp.front<<endl; if(sin2theta_t <= 1){ if(RayTrace_2(tRay, temp)){ // bounceCount--; // cout<<"A temp front: "<< temp.front<<endl; tShade = temp.node->GetMaterial()->Shade(tRay,temp,lights,bounceCount); tShade.r *= exp(-absorption.r * temp.z); tShade.g *= exp(-absorption.g * temp.z); tShade.b *= exp(-absorption.b * temp.z); // shade = tShade; /* remove later */ // return shade; /* Calculate Schlick's approximation */ R0 = (n1 - n2)/(n1 + n2); R0 *= R0; double X = 0.0; // if(n1 > n2){ // X = 1.0 - sqrtf(1.0 - sin2theta_t); // } // else{ X = 1.0 - costheta_v; } X = 1.0 - costheta_v; Refl = R0 + (1.0 - R0) * X * X * X * X * X; Trans = 1.0 - Refl; } } else {/* Total internal reflection */ Refl = 1.0f; } /* Calculate reflection due to reflectance */ if(bounceCount >0){ N = hInfo.N; Point3 V = Point3(iRay.p.x - P.x, iRay.p.y - P.y, iRay.p.z - P.z); //V.Normalize(); Point3 VR = 2 * V.Dot(N) * N - V; //VR.Normalize(); Ray rRay = Ray(P, VR); //rRay.dir.Normalize(); rRay.p.x = rRay.p.x + bias *rRay.dir.x; rRay.p.y = rRay.p.y + bias *rRay.dir.y; rRay.p.z = rRay.p.z + bias *rRay.dir.z; HitInfo temp1; temp1.Init(); if(rootNode.GetNumChild()>0){ if(RayTrace_2(rRay, temp1)){ bounceCount --; reflShade = temp1.node->GetMaterial()->Shade(rRay, temp1, lights, bounceCount); } } } // cout<<"Refl: "<<Refl<<"Trans "<<Trans<<endl; tShade = refraction * (Trans * tShade + Refl * reflShade); } /* calculate reflection*/ if(reflection.Grey()>0 && bounceCount > 0){ Point3 N = hInfo.N; Point3 V = Point3(iRay.p.x - P.x, iRay.p.y - P.y, iRay.p.z - P.z); // V.Normalize(); Point3 VR = 2 * V.Dot(N) * N - V; Ray rRay = Ray(hInfo.p, VR); //rRay.dir.Normalize(); rRay.p.x = rRay.p.x + bias *rRay.dir.x; rRay.p.y = rRay.p.y + bias *rRay.dir.y; rRay.p.z = rRay.p.z + bias *rRay.dir.z; HitInfo temp; temp.Init(); if(rootNode.GetNumChild()>0){ if(RayTrace_2(rRay, temp)){ bounceCount--; rShade = reflection * temp.node->GetMaterial()->Shade(rRay, temp, lights, bounceCount); } } } /* Add shade with reflected and refracted colors */ shade += (rShade + tShade); return shade; };
void VectorOpsTester::Test () { stringstream ssError; try { Point3<float> p; Point3<float> q; p.Set( 1, 2, 3 ); q.Set( 4, 5, 6 ); Assert( !(p == q), "== didn't work." ); Assert( (p != q), "!= didn't work." ); p.Set( 1, 2, 3 ); q.Set( 1, 2, 3 ); Assert( (p == q), "== didn't work." ); Assert( !(p != q), "!= didn't work." ); const int cVals = 10; float vals[cVals]; for ( int n = 0; n < cVals; n++ ) { vals[n] = (float)random() / (float)random(); } p.Set( vals[0], vals[1], vals[2] ); q.Set( vals[3], vals[4], vals[5] ); float dot = VectorOps::Dot( p, q ); Assert( (fabs( dot - (p[0] * q[0] + p[1] * q[1] + p[2] * q[2])) < 0.0001), "dot didn't work." ); p.Set( 0, 0, 0 ); q.Set( 0, 0, 3 ); Point3<float> plane( 0, 0, 2 ); Point3<float> n( 0, 0, 1 ); Point3<float> x; VectorOps::IntersectionResult rIntersect = VectorOps::SegmentIntersectsPlane( p, q, plane, n, x ); Assert( (VectorOps::intersect == rIntersect), "SegmentIntersectsPlane failed, incorrect result" ); Assert( (x[0] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[1] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[2] == 2), "SegmentIntersectsPlane failed, x was wrong" ); p.Set( 0, 0, 0 ); q.Set( 0, 0, 3 ); plane.Set( 0, 0, 4 ); n.Set( 0, 0, 1 ); rIntersect = VectorOps::SegmentIntersectsPlane( p, q, plane, n, x ); Assert( (VectorOps::dontIntersect == rIntersect), "SegmentIntersectsPlane failed, incorrect result" ); Assert( (x[0] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[1] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[2] == 2), "SegmentIntersectsPlane failed, x was wrong" ); p.Set( 0, 0, 0 ); q.Set( 0, 0, 1 ); plane.Set( 0, 0, 1 ); n.Set( 0, 1, 0 ); rIntersect = VectorOps::SegmentIntersectsPlane( p, q, plane, n, x ); Assert( (VectorOps::segmentInPlane == rIntersect), "SegmentIntersectsPlane failed, incorrect result" ); Assert( (x[0] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[1] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[2] == 2), "SegmentIntersectsPlane failed, x was wrong" ); p.Set( 0, 0, 0 ); q.Set( 0, 0, 1 ); plane.Set( 0, 1, 0 ); n.Set( 0, 1, 0 ); rIntersect = VectorOps::SegmentIntersectsPlane( p, q, plane, n, x ); Assert( (VectorOps::segmentParallelToPlane == rIntersect), "SegmentIntersectsPlane failed, incorrect result" ); Assert( (x[0] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[1] == 0), "SegmentIntersectsPlane failed, x was wrong" ); Assert( (x[2] == 2), "SegmentIntersectsPlane failed, x was wrong" ); p.Set( 1, 0, 0 ); q.Set( 0, 1, 0 ); double rads = VectorOps::RadsBetweenVectors( p, q ); Assert( fabs(rads - M_PI/2.0) < 0.0001, "RadsBetweenVectors was wrong" ); Point3<float> sq[4]; sq[0].Set( 0, 0, 0 ); sq[1].Set( 4, 0, 0 ); sq[2].Set( 4, 4, 0 ); sq[3].Set( 0, 4, 0 ); p.Set( 1.402, 3.2, 0 ); Point3<float> v[4]; v[0] = sq[0] - p; v[1] = sq[1] - p; v[2] = sq[2] - p; v[3] = sq[3] - p; double angle[4]; angle[0] = VectorOps::RadsBetweenVectors( v[0], v[1] ); angle[1] = VectorOps::RadsBetweenVectors( v[1], v[2] ); angle[2] = VectorOps::RadsBetweenVectors( v[2], v[3] ); angle[3] = VectorOps::RadsBetweenVectors( v[3], v[0] ); double sum = angle[0] + angle[1] + angle[2] + angle[3]; Assert( fabs(sum - M_PI*2.0) < 0.0001, "angle sum was wrong" ); Point3<float> p1, p2, q1, q2; p1.Set( 0, 0, 0 ); p2.Set( 0, 0, 2 ); q1.Set( -1, 0, 1 ); q2.Set( 1, 0, 1 ); rIntersect = VectorOps::SegmentIntersectsSegment( p1, p2, q1, q2, x ); Assert( (rIntersect == VectorOps::intersect), "SegmentIntersectsSegment didn't intersect" ); { stringstream ssError; ssError << "SegmentIntersectsSegment returned incorrect intersect: " << x << endl; Assert( (x[0] == 0 && x[1] == 0 && x[2] == 1), ssError.str() ); } p1.Set( 0, 0, 0 ); p2.Set( 0, 0, 2 ); q1.Set( 0, 0, 2 ); q2.Set( 0, 0, 4 ); rIntersect = VectorOps::SegmentIntersectsSegment( p1, p2, q1, q2, x ); Assert( (rIntersect == VectorOps::intersect), "SegmentIntersectsSegment didn't intersect" ); { stringstream ssError; ssError << "SegmentIntersectsSegment returned incorrect intersect: " << x << endl; Assert( (x[0] == 0 && x[1] == 0 && x[2] == 2), ssError.str() ); } p1.Set( 0, 0, 0 ); p2.Set( 0, 0, 2 ); q1.Set( 0, 0, 2 ); q2.Set( 0, 0, 2 ); rIntersect = VectorOps::SegmentIntersectsSegment( p1, p2, q1, q2, x ); Assert( (rIntersect == VectorOps::intersect), "SegmentIntersectsSegment didn't intersect" ); { stringstream ssError; ssError << "SegmentIntersectsSegment returned incorrect intersect: " << x << endl; Assert( (x[0] == 0 && x[1] == 0 && x[2] == 2), ssError.str() ); } p1.Set( 0, 0, 2 ); p2.Set( 0, 0, 2 ); q1.Set( 0, 0, 2 ); q2.Set( 0, 0, 2 ); rIntersect = VectorOps::SegmentIntersectsSegment( p1, p2, q1, q2, x ); Assert( (rIntersect == VectorOps::intersect), "SegmentIntersectsSegment didn't intersect" ); { stringstream ssError; ssError << "SegmentIntersectsSegment returned incorrect intersect: " << x << endl; Assert( (x[0] == 0 && x[1] == 0 && x[2] == 2), ssError.str() ); } } catch ( runtime_error& e ) { cerr << "failed with exception: " << e.what() << endl; exit( 1 ); } catch (...) { cerr << "failed" << endl; exit( 1 ); } }
BOOL objFileRead(const TCHAR *filename, ImpInterface *imp){ FILE *fp; int i; tLump *idxtable; if((fp = fopen(filename, "rb")) == NULL) return 0; fseek(fp, 0, SEEK_END); int fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); char *data = new char[fileSize]; fread(data, fileSize, 1, fp); tHeader *header = (tHeader *)data; if(*(int*)header->strID != CMAPHEADER_ID) return 0; if(header->version != CMAPHEADER_VERSION) return 0; idxtable = (tLump *)header + 1; tVertex *Verts = (tVertex *)(data + idxtable[kVertices].offset); int NumVerts = idxtable[kVertices].length / sizeof(tVertex); tFace *faces = (tFace *)(data + idxtable[kLeafFaces].offset); int NumFaces = idxtable[kLeafFaces].length / sizeof(tFace); int *elems = (int *)(data + idxtable[kElements].offset); int NumElems = idxtable[kElements].length / sizeof(int); tArea *mAreas = (tArea *)(data + idxtable[kAreas].offset); int NumAreas = idxtable[kAreas].length / sizeof(tArea); Point3 p; TriObject *object = CreateNewTriObject(); if(!object) return 0; Mesh *msh = &object->GetMesh(); msh->setNumVerts(NumVerts); for(i = 0 ; i < NumVerts ; i++){ msh->setVert(i, Verts[i].point[0], -Verts[i].point[2], Verts[i].point[1]); p.Set(Verts[i].norm[0], -Verts[i].norm[2], Verts[i].norm[1]); msh->setNormal(i, p); } int face = 0; for(int k = 1 ; k < NumAreas ; k++){ for(i = mAreas[k].startFace ; i < mAreas[k].startFace + mAreas[k].numOfFaces ; i++){ for(int j = faces[i].startElement ; j < faces[i].startElement + faces[i].elementsSize ; j++){ int k = j * 3; if(elems[k] < 0){ elems[k] = 0; elems[k+1] = 1; elems[k+2] = 2; faces[i].vertexIndex = 0; } msh->setNumFaces(face + 1, TRUE); msh->faces[face].setVerts(faces[i].vertexIndex + elems[k + 0], faces[i].vertexIndex + elems[k + 1], faces[i].vertexIndex + elems[k + 2]); msh->faces[face].setEdgeVisFlags(1, 1, 1); face++; } } } delete [] data; fclose(fp); msh->buildNormals(); msh->buildBoundingBox(); msh->InvalidateEdgeList(); ImpNode *node = imp->CreateNode(); if(!node) { delete object; return 0; } Matrix3 tm; tm.IdentityMatrix(); node->Reference(object); node->SetTransform(0,tm); imp->AddNodeToScene(node); node->SetName(_T("Sylphis map")); return TRUE; }
void VolumeCollectionTester::Test ( Tcl_Interp* iInterp ) { stringstream ssError; try { string fnMRI = "test_data/bertT1.mgz"; VolumeCollection* vol = new VolumeCollection(); vol->SetFileName( fnMRI ); MRI* mri = const_cast<MRI*>(vol->GetMRI()); Assert( (vol->GetTypeDescription() == "Volume"), "GetTypeDescription didn't return Volume" ); DataManager dataMgr = DataManager::GetManager(); MRILoader mriLoader = dataMgr.GetMRILoader(); Assert( 1 == mriLoader.CountLoaded(), "CountLoaded didn't return 1" ); Assert( 1 == mriLoader.CountReferences(mri), "CountReferences didn't return 1" ); char* fnMRIC = strdup( fnMRI.c_str() ); MRI* mriComp = MRIread( fnMRIC ); Assert( (MRImatch( mriComp, mri )), "MRImatch failed for load" ); MRIfree( &mriComp ); // Save it in /tmp, load it, and match it again. string fnSave( "/tmp/test.mgz" ); vol->Save( fnSave ); VolumeCollection* testVol = new VolumeCollection(); testVol->SetFileName( fnSave ); MRI* testMri = const_cast<MRI*>(testVol->GetMRI()); Assert( (MRImatch( testMri, mri )), "MRImatch failed for load after save"); // Make an ROI and make sure it's a volume ROI. try { int roiID = vol->NewROI(); ScubaROIVolume* roi = dynamic_cast<ScubaROIVolume*>(&ScubaROI::FindByID( roiID )); roi = NULL; } catch (...) { throw( runtime_error("typecast failed for NewROI") ); } // Try our conversions. Point3<float> world; Point3<float> data; Point3<int> index; world.Set( -50, 0, -80 ); vol->RASToMRIIndex( world.xyz(), index.xyz() ); { stringstream ssError; ssError << "RASToMRIIndex failed. world " << world << " index " << index; Assert( (index.x() == 178 && index.y() == 208 && index.z() == 128), ssError.str() ); } // Set a transform that scales the volume up by 2x in the world. ScubaTransform dataTransform; dataTransform.SetMainTransform( 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1 ); vol->SetDataToWorldTransform( dataTransform.GetID() ); world.Set( -50, 0, -80 ); vol->RASToDataRAS( world.xyz(), data.xyz() ); { stringstream ssError; ssError << "RASToDataRAS failed. world " << world << " data " << data; Assert( ((FEQUAL(data.x(),-25)) && (FEQUAL(data.y(),0)) && (FEQUAL(data.z(),-40))), ssError.str() ); } vol->RASToMRIIndex( world.xyz(), index.xyz() ); if ( index.x() != 153 || index.y() != 168 || index.z() != 128 ) { cerr << "RASToMRIIndex with data transform failed. world " << world << " index " << index << endl; throw( runtime_error( "failed" ) ); } world.Set( -50, 0, -80 ); VolumeLocation loc( vol->MakeVolumeLocationFromRAS( world.xyz() ) ); if ( !vol->IsInBounds( loc ) ) { stringstream ssError; ssError << "IsInBounds failed. world " << world; throw( runtime_error( ssError.str() ) ); } world.Set( -1000, 0, 0 ); VolumeLocation loc2( vol->MakeVolumeLocationFromRAS( world.xyz() ) ); if ( vol->IsInBounds( loc2 ) ) { stringstream ssError; ssError << "IsInBounds failed. world " << world; throw( runtime_error( ssError.str() ) ); } dataTransform.SetMainTransform( 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1 ); vol->SetDataToWorldTransform( dataTransform.GetID() ); world.Set( 0, -1000, -254 ); VolumeLocation loc3( vol->MakeVolumeLocationFromRAS( world.xyz() ) ); if ( vol->IsInBounds( loc3 ) ) { stringstream ssError; vol->RASToMRIIndex( world.xyz(), index.xyz() ); ssError << "IsRASInMRIBounds failed. world " << world << " index " << index; throw( runtime_error( ssError.str() ) ); } { // Create a new one from template. VolumeCollection* vol2 = new VolumeCollection(); vol2->MakeUsingTemplate( vol->GetID(), -1 ); Assert( (vol->mVoxelSize[0] == vol2->mVoxelSize[0] && vol->mVoxelSize[1] == vol2->mVoxelSize[1] && vol->mVoxelSize[2] == vol2->mVoxelSize[2]), "NewUsingTemplate failed, vol2 didn't match vol's voxelsize" ); Assert( (vol->GetDataType() == vol2->GetDataType()), "NewUsingTemplate(-1) failed, vol2 didn't match vol's data type" ); delete vol2; } { VolumeCollection* vol2 = new VolumeCollection(); vol2->MakeUsingTemplate( vol->GetID(), MRI_FLOAT ); Assert( (vol->mVoxelSize[0] == vol2->mVoxelSize[0] && vol->mVoxelSize[1] == vol2->mVoxelSize[1] && vol->mVoxelSize[2] == vol2->mVoxelSize[2]), "NewUsingTemplate failed, vol2 didn't match vol's voxelsize" ); Assert( (MRI_FLOAT == vol2->GetDataType()), "NewUsingTemplate(float) failed, vol2 wasn't a float" ); int idx[3] = { 0, 0, 0 }; VolumeLocation loc( vol2->MakeVolumeLocationFromIndex( idx ) ); vol2->SetMRIValue( loc, 0.6789 ); float value = vol2->GetMRINearestValue(loc); stringstream ssError; ssError << "NewUsingTemplate(float) failed value comparison, " << "was expecting 0.6789 but got " << value; Assert( (fabs(0.6789 - value) < 0.00001), ssError.str() ); delete vol2; } { VolumeCollection* vol2 = NULL; try { vol2 = new VolumeCollection(); vol2->MakeUsingTemplate( vol->GetID(), 10000 ); throw runtime_error( "MakeUsingTemplate(10000) didn't throw an error"); } catch(...) {} delete vol2; } dataTransform.SetMainTransform( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); vol->SetDataToWorldTransform( dataTransform.GetID() ); // FindRASPointsInSquare { Point3<float> sqRAS[4], cRAS; sqRAS[0].Set( 0, -3, 71 ); sqRAS[1].Set( 0, 1, 71 ); sqRAS[2].Set( 0, 1, 67 ); sqRAS[3].Set( 0, -3, 67 ); cRAS.Set( 0, -1, 69 ); list<Point3<float> > points; vol->FindRASPointsInSquare( cRAS.xyz(), sqRAS[0].xyz(), sqRAS[1].xyz(), sqRAS[2].xyz(), sqRAS[3].xyz(), 0, points ); list<Point3<float> >::iterator tPoint; for ( tPoint = points.begin(); tPoint != points.end(); ++tPoint ) { Point3<float> pRAS = *tPoint; stringstream ssMsg; ssMsg << "Failed: " << pRAS << " outside of square"; Assert( ( pRAS[0] == 0 && (pRAS[1] >= -3 && pRAS[1] <= 1) && (pRAS[2] >= 67 && pRAS[2] <= 71) ), ssMsg.str() ); } } // VoxelIntersectsSegment { Point3<int> idx; Point3<float> segIdxA, segIdxB; Point3<float> intIdx; idx.Set( 5, 5, 5 ); float aSegments[6][3] = { {3, 5.5, 5.5}, {6, 5.5, 5.5}, {5.5, 3, 5.5}, {5.5, 6, 5.5}, {5.5, 5.5, 3}, {5.5, 5.5, 6} }; for ( int nSegment = 0; nSegment < 6; nSegment += 2 ) { segIdxA.Set( aSegments[nSegment] ); segIdxB.Set( aSegments[nSegment+1] ); VectorOps::IntersectionResult rInt = vol->VoxelIntersectsSegment( idx, segIdxA, segIdxB, intIdx ); if ( VectorOps::intersect != rInt ) { cerr << "Failed VoxelIntersectsSegment test: idx " << idx << ", seg " << segIdxA << ", " << segIdxB << endl << "\tDidn't intersect" << endl; throw runtime_error("failed"); } } segIdxA.Set( 0, 5.5, 5.5 ); segIdxB.Set( 4, 5.5, 5.5 ); VectorOps::IntersectionResult rInt = vol->VoxelIntersectsSegment( idx, segIdxA, segIdxB, intIdx ); if ( VectorOps::dontIntersect != rInt ) { cerr << "Failed VoxelIntersectsSegment test: idx " << idx << ", seg " << segIdxA << ", " << segIdxB << endl << "\tIntersected" << endl; throw runtime_error("failed"); } } // FindRASPointsOnSegment { } // GetVoxelsWithValue { // This is a 5cubed volume whose values are set to the x // coordinate. So for x=3,y=0..4,z=0..4, value = 3. string fnVol = "test_data/testVolumeCollection-GetVoxelsWithValue.mgh"; ifstream fVol( fnMRI.c_str(), ios::in ); if ( !fVol ) { throw runtime_error("Couldn't find necessary test data."); } fVol.close(); VolumeCollection* vol = new VolumeCollection(); vol->SetFileName( fnVol ); vol->LoadVolume(); // Get the values 0-4 and make sure we got the right voxels. for ( int nStructure = 0; nStructure < 5; nStructure++ ) { list<VolumeLocation> lLocations; vol->GetVoxelsWithValue( nStructure, lLocations ); Volume3<bool> bGot( 5, 5, 5, false ); list<VolumeLocation>::iterator tLocation; for ( tLocation = lLocations.begin(); tLocation != lLocations.end(); ++tLocation ) { VolumeLocation loc = *(tLocation); bGot.Set( loc.Index()[0], loc.Index()[1], loc.Index()[2], true ); } for ( int nZ = 0; nZ < 5; nZ++ ) { for ( int nY = 0; nY < 5; nY++ ) { for ( int nX = 0; nX < 5; nX++ ) { if ( nX == nStructure && !bGot.Get( nX, nY, nZ ) ) { stringstream ssErr; ssErr << "Failed GetVoxelsWithValue test: " << " nStructure = " << nStructure << " index " << Point3<int>(nX,nY,nZ) << " - was supposed to get voxel but didn't"; throw runtime_error(ssErr.str()); } if ( nX != nStructure && bGot.Get( nX, nY, nZ ) ) { stringstream ssErr; ssErr << "Failed GetVoxelsWithValue test: " << " nStructure = " << nStructure << " index " << Point3<int>(nX,nY,nZ) << " - wasn't supposed to get voxel but did"; throw runtime_error(ssErr.str()); } } } } } delete vol; } // GetAverageValue { // We'll use the same volume as with GetVoxelsWithValue. string fnVol = "test_data/testVolumeCollection-GetVoxelsWithValue.mgh"; ifstream fVol( fnMRI.c_str(), ios::in ); if ( !fVol ) { throw runtime_error("Couldn't find necessary test data."); } fVol.close(); VolumeCollection* vol = new VolumeCollection(); vol->SetFileName( fnVol ); vol->LoadVolume(); // Get values of all voxels in plane x=3 and make sure it's 3. list<VolumeLocation> lVoxels; for ( int nZ = 0; nZ < 5; nZ++ ) { for ( int nY = 0; nY < 5; nY++ ) { int index[3] = { 3, nY, nZ }; VolumeLocation loc( vol->MakeVolumeLocationFromIndex( index ) ); lVoxels.push_back( loc ); } } float average = vol->GetAverageValue( lVoxels ); if ( average != 3.0 ) { stringstream ssErr; ssErr << "Failed GetAverageValue: Getting all voxels in x=3, " << "average should have been 3, but was " << average; throw runtime_error( ssErr.str() ); } // Get values of 5 voxels, one from each x plane // (val=0,1,2,3,4), and make sure it's 2. lVoxels.clear(); for ( int nX = 0; nX < 5; nX++ ) { int index[3] = { nX, 3, 3 }; VolumeLocation loc( vol->MakeVolumeLocationFromIndex( index ) ); lVoxels.push_back( loc ); } average = vol->GetAverageValue( lVoxels ); if ( average != 2.0 ) { stringstream ssErr; ssErr << "Failed GetAverageValue: Getting voxels in different planes, " << "average should have been 2, but was " << average; throw runtime_error( ssErr.str() ); } // Make sure we get an error for no voxels. lVoxels.clear(); bool bDidntThrow = false; try { average = vol->GetAverageValue( lVoxels ); bDidntThrow = true; } catch ( exception& e ) {} if ( bDidntThrow ) { stringstream ssErr; ssErr << "Failed GetAverageValue: Didn't throw with empty list"; throw runtime_error( ssErr.str() ); } delete vol; } // Check the tcl commands. char sCommand[1024]; int rTcl; int id = vol->GetID(); string fnTest = "test-name"; sprintf( sCommand, "SetVolumeCollectionFileName %d test-name", id ); rTcl = Tcl_Eval( iInterp, sCommand ); AssertTclOK( rTcl ); Assert( (vol->mfnMRI == fnTest), "Setting file name via tcl didn't work" ); vol->SetDataToWorldTransform( 0 ); delete vol; delete testVol; } catch ( exception& e ) { cerr << "failed with exception: " << e.what() << endl; exit( 1 ); } catch (...) { cerr << "failed." << endl; exit( 1 ); } }