//--------------------------------------------------------------------------- // IsFrontFace() // Check whether a direction of the face is front side in the scene. // 面の向きが表かどうか調べる //--------------------------------------------------------------------------- bool IsFrontFace(MQScene scene, MQObject obj, int face_index) { int num = obj->GetFacePointCount(face_index); int vert_index[4]; obj->GetFacePointArray(face_index, vert_index); MQPoint sp[4]; for (int i=0; i<num; i++){ sp[i] = scene->Convert3DToScreen(obj->GetVertex(vert_index[i])); // 視野より手前にあれば表とみなさない if (sp[i].z <= 0) return false; } // 法線の向きで調べる if(num >= 3){ if((sp[1].x-sp[0].x) * (sp[2].y-sp[1].y) - (sp[1].y-sp[0].y) * (sp[2].x-sp[1].x) < 0) { return true; }else if(num >= 4){ if((sp[2].x-sp[0].x) * (sp[3].y-sp[2].y) - (sp[2].y-sp[0].y) * (sp[3].x-sp[2].x) < 0) { return true; } } } else if (num > 0){ return true; } return false; }
MQPoint GetPoint(MQDocument doc) { MQPoint p(0,0,0); if(IsEmpty()) return p; MQObject obj = doc->GetObject(index_o); int indices[5]; int pcount; if(obj == NULL) return p; switch(type) { case SELEL_FACE: obj->GetFacePointArray(index_1,indices); pcount = obj->GetFacePointCount(index_1); for(int i=0;i<pcount;i++) p += obj->GetVertex(indices[i]); p /= (float)pcount; break; case SELEL_LINE: obj->GetFacePointArray(index_1,indices); pcount = obj->GetFacePointCount(index_1); p += obj->GetVertex(indices[index_2]); p += obj->GetVertex(indices[(index_2+1)%pcount]); p /= 2.0f; break; case SELEL_VERTEX: p = obj->GetVertex(index_1); break; } return p; }
static void get_selection(MQDocument doc,MQScene scene,std::vector<MQSelectVertex>& out) { int indices[4]; int obj_count = doc->GetObjectCount(); std::set<MQSelectVertex> tmp; // for each objects ObjectEnumerator objenum(doc); for(MQObject obj = NULL; (obj = objenum.next()) != NULL;) { int o = objenum.GetIndex(); int face_count = obj->GetFaceCount(); for(int f = 0; f < face_count; f++) { // pass through if the face is invalid (count of vertex refer is 0) int ptcount = obj->GetFacePointCount(f); if(ptcount == 0) continue; obj->GetFacePointArray(f,indices); // selected is face if(doc->IsSelectFace(o,f)) { for(int p = 0; p < ptcount; p++) tmp.insert(MQSelectVertex(o,indices[p])); continue; } for(int p = 0; p < ptcount; p++) { if(doc->IsSelectLine(o,f,p)) { tmp.insert(MQSelectVertex(o,indices[p])); tmp.insert(MQSelectVertex(o,indices[(p+1)%ptcount])); } if(doc->IsSelectVertex(o,indices[p])) { tmp.insert(MQSelectVertex(o,indices[p])); } } } } for(std::set<MQSelectVertex>::iterator it = tmp.begin(); it != tmp.end(); ++it) { out.push_back(*it); } }
static void find_faces_contains_vertex(MQObject obj, int vindex, std::vector<int>& out) { out.reserve(8); int fcount = obj->GetFaceCount(); for(int f = 0; f < fcount; f++) { int indices[5] = {-1,-1,-1,-1,-1}; obj->GetFacePointArray(f,indices); for(int i = 0; indices[i] != -1; i++) { if(vindex == indices[i]) { out.push_back(f); break; } } } }
MQObject next() { while(1) { m_cur++; if(m_cur >= m_objcount) return NULL; MQObject obj = m_doc->GetObject(m_cur); if(obj == NULL) continue; if(obj->GetVertexCount() == 0) continue; if((m_option & OE_SKIPLOCKED) && obj->GetLocking()) continue; if((m_option & OE_SKIPHIDDEN) && !obj->GetVisible()) continue; if((m_option & OE_APPLYEDITOPTION) && s_editoption.CurrentObjectOnly && m_cur != m_doc->GetCurrentObjectIndex()) continue; return obj; } }
// DXF形式を入力 BOOL LoadSTL(const char *filename, MQDocument doc) { int index[3]; MQPoint p; std::ifstream ifs(filename, std::ios::in | std::ios::binary); if (ifs.fail()) { return FALSE; } // オブジェクトの作成 MQObject obj = MQ_CreateObject(); //MQObj_SetName(obj, ""); //このサンプルでは名前は付けないのでコメントアウト char header[80]; ifs.read(header, sizeof(header)); int triangleCount = 0; ifs.read((char*)&triangleCount, sizeof(int)); float tmpNormal[3]; MQPoint tmpVertices[3]; for (int i = 0; i < triangleCount; i++) { ifs.read((char*)tmpNormal, sizeof(float) * 3); ifs.read((char*)tmpVertices, sizeof(float) * 9); char dummy[2]; ifs.read(dummy, 2); index[0] = obj->AddVertex(tmpVertices[0]); index[1] = obj->AddVertex(tmpVertices[2]); index[2] = obj->AddVertex(tmpVertices[1]); obj->AddFace(3, index); } // もし面がまったく登録されていないならFALSEを返す if(obj->GetFaceCount() == 0) { obj->DeleteThis(); return FALSE; } // 各ポリゴンはバラバラであるため、同じ位置(実際には小数点誤差を考慮して // 0.0001以下の距離)にあるすべて頂点を結合する obj->OptimizeVertex(0.0001f, NULL); // オブジェクトをドキュメントへ登録 doc->AddObject(obj); return TRUE; }
//--------------------------------------------------------------------------- // SearchInvertedFace() // Check whether a face exists in the opposite direction of the specified one. // It returns an index of the face if it exists, or returns -1 if not exist. // 反対向きの面が存在するかどうかを調べる // 存在していればその面のインデックスを、なければ-1を返す //--------------------------------------------------------------------------- int SearchInvertedFace(MQObject obj, int faceindex, int start, int end) { int i,j,k; int cvidx[4], dvidx[4]; int face_count = obj->GetFaceCount(); if(faceindex >= face_count) return -1; int pt_count = obj->GetFacePointCount(faceindex); if(pt_count == 0) return -1; obj->GetFacePointArray(faceindex, dvidx); if(start < 0) start = 0; if(end < 0) end = face_count-1; for(i=start; i<=end; i++) { if(pt_count != obj->GetFacePointCount(i)) continue; obj->GetFacePointArray(i, cvidx); for(j=0; j<pt_count; j++) { if(cvidx[j] != dvidx[0]) continue; for(k=1; k<pt_count; k++) { if(cvidx[(j+k)%pt_count] != dvidx[pt_count-k]) break; } if(k == pt_count) return i; } } return -1; }
//--------------------------------------------------------------------------- // class MQObjEdge // オブジェクト中のエッジ対を管理する // 以下のようにしてface_indexで示される面中のline_indexの辺を共有して // いる面のインデックスがpfに、辺のインデックスがplに得られる。 // 使用例は以下。 // // int pf,pl; // MQObjEdge *edge = new MQObjEdge(obj); // edge->getPair(face_index, line_index, pf, pl); // delete edge; // //--------------------------------------------------------------------------- MQObjEdge::MQObjEdge(MQObject obj) { int vert_count = obj->GetVertexCount(); int face_count = obj->GetFaceCount(); m_obj = obj; m_face = face_count; m_pair = new MQObjEdgePairFace[face_count]; class MRelTree { public: MQObjEdgePair pair; int next; inline MRelTree() { next = -1; } inline MRelTree& operator = (const MRelTree& r) {pair=r.pair; next=r.next; return *this;} }; MRelTree *tree = new MRelTree[vert_count + face_count*4]; int regvc = vert_count; for(int cf=0; cf<face_count; cf++) { int cvi[4]; int cfc = obj->GetFacePointCount(cf); if(cfc < 3) continue; obj->GetFacePointArray(cf, cvi); cfc--; // decrement to be faster for(int j=0; j<=cfc; j++) { // check tree int v1 = cvi[j]; int v2 = cvi[j<cfc? j+1 : 0]; for(MRelTree drel=tree[v2]; drel.pair.face>=0; drel=tree[drel.next]) { int dvi[4]; int df = drel.pair.face; int dfc = obj->GetFacePointCount(df); obj->GetFacePointArray(df, dvi); if(m_pair[df][drel.pair.line].face < 0) { if(/*v2 == dvi[drel.pair.line] &&*/ v1 == dvi[drel.pair.line<dfc-1 ? drel.pair.line+1 : 0]) { // set relation m_pair[df][drel.pair.line] = MQObjEdgePair(cf,j); m_pair[cf][j] = drel.pair; goto CR_REG_END; } } if(drel.next < 0) break; } // not find in tree, so regist vertex1 MRelTree *ctr; for(ctr=&tree[v1]; ctr->pair.face>=0; ctr=&tree[ctr->next]){ if(ctr->next < 0){ ctr->next = regvc; ctr = &tree[regvc++]; break; } } ctr->pair = MQObjEdgePair(cf,j); CR_REG_END:; } } delete[] tree; }
MQObjNormal::MQObjNormal(MQObject obj) { int i,j; int face_count, vert_count; int vi[4]; face_count = obj->GetFaceCount(); vert_count = obj->GetVertexCount(); MQPoint *face_n = new MQPoint[face_count]; normal = new MQPoint[face_count * 4]; // 面ごとに法線を計算 for(i=0; i<face_count; i++) { int count = obj->GetFacePointCount(i); // 三角形と四角形では面に対する法線の計算法は異なる switch(count) { case 3: obj->GetFacePointArray(i, vi); face_n[i] = GetNormal( obj->GetVertex(vi[0]), obj->GetVertex(vi[1]), obj->GetVertex(vi[2])); break; case 4: obj->GetFacePointArray(i, vi); face_n[i] = GetQuadNormal( obj->GetVertex(vi[0]), obj->GetVertex(vi[1]), obj->GetVertex(vi[2]), obj->GetVertex(vi[3])); break; default: face_n[i].zero(); break; } } switch(obj->GetShading()) { case MQOBJECT_SHADE_FLAT: for(i=0; i<face_count; i++) { int count = obj->GetFacePointCount(i); for(j=0; j<count; j++) normal[i*4+j] = face_n[i]; for(; j<4; j++) normal[i*4+j].zero(); } break; case MQOBJECT_SHADE_GOURAUD: { MQGouraudHashTable *vtbl, *cvt; MQGouraudHash *hash, *chs; // スムージング角度の取得 float facet = cosf( RAD(obj->GetSmoothAngle()) ); // ハッシュの初期化 vtbl = new MQGouraudHashTable[face_count]; hash = new MQGouraudHash[vert_count + face_count*4]; int hash_count = vert_count; // 面ごとにハッシュに法線ベクトルをセット for(i=0,cvt=vtbl; i<face_count; i++,cvt++) { int count = obj->GetFacePointCount(i); if(count < 3) continue; obj->GetFacePointArray(i, vi); // 面中の各頂点ごとに法線ベクトルをハッシュへ格納してやる for(j=0; j<count; j++) { // 注目する頂点に対してのハッシュを得る chs = &hash[vi[j]]; // ハッシュがまだ空ならそこに情報を格納 if(chs->count == 0) { chs->nv = face_n[i]; chs->count++; (*cvt)[j] = chs; continue; } // ハッシュが空でないなら、既に格納されている法線とのスムージング // 角度をチェックする必要がある。 // アルゴリズムとしては不完全かもしれないが、とりあえず実用程度に // はなると思う。 const MQPoint& pa = face_n[i]; float da = pa.norm(); for(; ; chs=chs->next) { // 2面の角度をチェック float c = 0.0f; if(da > 0.0f) { MQPoint& pb = chs->nv; float db = pb.norm(); if(db > 0.0f) c = GetInnerProduct(pa, pb) / sqrtf(da*db); } // スムージング角度以内か? if(c >= facet) { // 注目する頂点に対して面の法線ベクトルをそのまま加算する。 // 本来なら、注目する頂点に属する面内の2辺の角度によって // ベクトルの加算量を変えるべきだが、とりあえずパス。 chs->nv += pa; chs->count++; (*cvt)[j] = chs; break; } // スムージングは行われないので、次のハッシュをチェック。 // 次のハッシュデータがない場合は新規作成。 if(chs->next == NULL) { (*cvt)[j] = chs->next = &hash[hash_count++]; chs = chs->next; chs->nv = pa; chs->count = 1; chs->next = NULL; break; } } } } // ハッシュ中の法線ベクトルの正規化 for(i=0,chs=hash; i<hash_count; i++,chs++) { if(chs->count > 1) chs->nv.normalize(); } // 法線をバッファにセット for(i=0,cvt=vtbl; i<face_count; i++,cvt++) { int count = obj->GetFacePointCount(i); if(count < 3) continue; for(j=0; j<count; j++) normal[i*4+j] = (*cvt)[j]->nv; for(; j<4; j++) normal[i*4+j].zero(); } // ハッシュを解放 delete[] vtbl; delete[] hash; } break; } delete[] face_n; }