//input vertex number is 2, constant inline void computePlanarRotationMatrix( const double *_Q, const int strideQ, const double *_Weight, const int strideW, double3x3 &rot) { Vector3d& X = *((Vector3d *)&rot.x[0]); Vector3d& Y = *((Vector3d *)&rot.x[3]); Vector3d& Z = *((Vector3d *)&rot.x[6]); Vector3d q1 = _getVertex3dUsingStride(_Q, 0, strideQ); Vector3d q2 = _getVertex3dUsingStride(_Q, 1, strideQ); const double w1 = _getDoubleUsingStride(_Weight, 0, strideW); const double w2 = _getDoubleUsingStride(_Weight, 1, strideW); Z = CrossProd(q1, q2); const double l2 = Magnitude2(Z); if (l2<1e-32){ X = q1; X.normalize(); const double nx = fabs(X.x); const double ny = fabs(X.y); const double nz = fabs(X.z); const double maxnxyz = _MAX3_(nx, ny, nz); if (nx==maxnxyz) Z = Vector3d(0,0,1); else if (ny==maxnxyz) Z = Vector3d(0,0,1); else Z = Vector3d(-1,0,0); Y = CrossProd(Z, X); Y.normalize(); Z = CrossProd(X, Y); } else{ Z *= 1.0/sqrt(l2); //normalize Z q1.normalize(); q2.normalize(); Y = w1*q1+w2*q2; Y.normalize(); X = CrossProd(Y, Z); } }
//use the center plane of the dihedral angle as the reference plane inline void getReferencePlanesForQuadPair( const Vector3d &p1, const Vector3d &p2, const Vector3d &p3, const Vector3d &p4, const Vector3d &p5, Vector3d &facenorm0, Vector3d &facenorm1, double3x3& mat, double &xlen) { Vector3d Y0, Y1; Vector3d &Z0 = facenorm0; Vector3d &Z1 = facenorm1; Vector3d &X = *((Vector3d*)(&mat.x[0])); Vector3d &Y = *((Vector3d*)(&mat.x[3])); Vector3d &Z = *((Vector3d*)(&mat.x[6])); const Vector3d p23 = (p2+p3)*0.5; //quad center const Vector3d p45 = (p4+p5)*0.5; //quad center X = p1; xlen = Magnitude(X); X /= xlen; //normalize Y0 = p23; Z0 = CrossProd(X, Y0); Y1 = p45; Z1 = CrossProd(Y1, X); Z0.Normalize(); Z1.Normalize(); Z = Z0+Z1; Z.Normalize(); Y = CrossProd(Z, X); }
void InterpolatorKNN::KollinearWithDistinctPositions(NNCandidate candidates[3], Vertex v) { // project v onto line <v_01, v_02> with hesse normal form D3DXVECTOR3 v_01 = candidates[1].vertex.pos-candidates[0].vertex.pos; D3DXVECTOR3 v_02 = candidates[2].vertex.pos-candidates[0].vertex.pos; D3DXVECTOR3 v_0v = v.pos-candidates[0].vertex.pos; D3DXVECTOR3 v_1v = v.pos-candidates[1].vertex.pos; D3DXVECTOR3 v_2v = v.pos-candidates[2].vertex.pos; D3DXVECTOR3 v_2 = CrossProd(v_01, v_0v); if(v_2.x != 0.0f || v_2.y != 0.0f || v_2.z != 0.0f) { D3DXVECTOR3 normal = Normalize(CrossProd(v_01, v_2)); D3DXVECTOR3 pointOnPlane = candidates[0].vertex.pos; float d = DotProd(normal, pointOnPlane); float distance = DotProd(normal, v.pos) - d; v.pos = v.pos + distance * (-normal); } if(DotProd(v_01, v_02) < -0.9f) { // 0 is in the middle if(DotProd(v_01, v_0v) > 0.9f){ // v on the same side as 1 InterpolateLinear(candidates[0], candidates[1], v); candidates[2].weight = 0.0f; } else{ // v on the same side as 2 InterpolateLinear(candidates[0], candidates[2], v); candidates[1].weight = 0.0f; } } if(Length(v_01) < Length(v_02)) { // 1 is in the middle if(DotProd(-v_01, v_1v) > 0.9f){ // v on the same side as 0 InterpolateLinear(candidates[1], candidates[0], v); candidates[2].weight = 0.0f; } else{ // v on the same side as 2 InterpolateLinear(candidates[1], candidates[2], v); candidates[0].weight = 0.0f; } } else{ // 2 is in the middle if(DotProd(-v_02, v_2v) > 0.9f){ // v on the same side as 0 InterpolateLinear(candidates[2], candidates[0], v); candidates[1].weight = 0.0f; } else{ // v on the same side as 1 InterpolateLinear(candidates[2], candidates[1], v); candidates[0].weight = 0.0f; } } }
void SplineData::AlignCrossSection(int splineIndex, int crossSectionIndex,Point3 vec) { if ((splineIndex >= 0) && (splineIndex < mSplineElementData.Count())) { int numCrossSections = NumberOfCrossSections(splineIndex); if ((crossSectionIndex >= 0) && (crossSectionIndex < numCrossSections)) { //put the vec in spline space SplineCrossSection *crossSection = GetCrossSection(splineIndex,crossSectionIndex); Matrix3 crossSectionTM = crossSection->mTM; crossSectionTM.NoScale(); crossSectionTM.NoTrans(); Matrix3 icrossSectionTM = Inverse(crossSectionTM); Point3 crossSectionZVec = crossSection->mTangentNormalized; Point3 yvec = Normalize(vec); Point3 xvec = Normalize(CrossProd(yvec,crossSectionZVec)); Point3 zvec = Normalize(CrossProd(xvec,yvec)); Matrix3 rtm(1); rtm.SetRow(0,xvec); rtm.SetRow(1,yvec); rtm.SetRow(2,zvec); rtm.SetRow(3,Point3(0.0f,0.0f,0.0f)); Matrix3 relativeTM = crossSection->mIBaseTM * rtm; Quat q(relativeTM); q = TransformQuat(crossSection->mIBaseTM,q); crossSection->mQuat = q; return; } } DbgAssert(0); }
hsBool plMaxNodeBase::Contains(const Point3& worldPt) { TimeValue currTime = 0;//hsConverterUtils::Instance().GetTime(GetInterface()); Object *obj = EvalWorldState(currTime).obj; if( !obj ) return false; Matrix3 l2w = GetObjectTM(currTime); Matrix3 w2l = Inverse(l2w); Point3 pt = w2l * worldPt; if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) { DummyObject* dummy = (DummyObject*)obj; Box3 bnd = dummy->GetBox(); return bnd.Contains(pt); } if( obj->CanConvertToType(triObjectClassID) ) { TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); if( !meshObj ) return false; Mesh& mesh = meshObj->mesh; Box3 bnd = mesh.getBoundingBox(); if( !bnd.Contains(pt) ) { if( meshObj != obj ) meshObj->DeleteThis(); return false; } hsBool retVal = true; int i; for( i = 0; i < mesh.getNumFaces(); i++ ) { Face& face = mesh.faces[i]; Point3 p0 = mesh.verts[face.v[0]]; Point3 p1 = mesh.verts[face.v[1]]; Point3 p2 = mesh.verts[face.v[2]]; Point3 n = CrossProd(p1 - p0, p2 - p0); if( DotProd(pt, n) > DotProd(p0, n) ) { retVal = false; break; } } if( meshObj != obj ) meshObj->DeleteThis(); return retVal; } // If we can't figure out what it is, the point isn't inside it. return false; }
void VNormal::Normalize() { VNormal *Ptr,*Prev; Matrix3 Mat; Ptr = m_Next; Prev = this; while(Ptr) { if(Ptr->m_Smooth & m_Smooth) { m_Normal += Ptr->m_Normal; m_S += Ptr->m_S; m_T += Ptr->m_T; Prev->m_Next = Ptr->m_Next; Ptr->m_Next = NULL; delete Ptr; Ptr = Prev->m_Next; } else { Prev = Ptr; Ptr = Ptr->m_Next; } } Point3Normalize(m_Normal); Point3Normalize(m_S); m_T = CrossProd(m_S,m_Normal); Point3Normalize(m_T); m_S = CrossProd(m_Normal,m_T); Point3Normalize(m_S); m_SxT = m_Normal; if(m_Next) { m_Next->Normalize(); } }
static inline void _computeLocalFrame(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, Vector3f& X, Vector3f& Y, Vector3f& Z, float& avg_edgelen) { const Vector3f d0 = v1 - v0; const Vector3f d1 = v2 - v1; const Vector3f d2 = v0 - v2; Z = CrossProd(d0, d1); X = d0; Y = CrossProd(Z, X); avg_edgelen = DotProd(d0, d0) + DotProd(d1, d1) + DotProd(d2, d2); X.normalize(); Y.normalize(); Z.normalize(); }
Point3 Gradient::EvalNormalPerturb(ShadeContext& sc) { Point3 dPdu, dPdv; if (!sc.doMaps) return Point3(0,0,0); if (gbufID) sc.SetGBufferID(gbufID); Point2 dM = uvGen->EvalDeriv(sc,&mysamp); uvGen->GetBumpDP(sc,dPdu,dPdv); #if 0 // Blinn's algorithm Point3 N = sc.Normal(); Point3 uVec = CrossProd(N,dPdv); Point3 vVec = CrossProd(N,dPdu); Point3 np = -dM.x*uVec+dM.y*vVec; #else // Lazy algorithm Point3 np = dM.x*dPdu+dM.y*dPdv; // return texout->Filter(dM.x*dPdu+dM.y*dPdv); #endif Texmap* sub[3]; for (int i=0; i<3; i++) sub[i] = mapOn[i]?subTex[i]:NULL; if (sub[0]||sub[1]||sub[2]) { // d((1-k)*a + k*b ) = dk*(b-a) + k*(db-da) + da float a,b,k; Point3 da,db; Point2 UV, dUV; uvGen->GetUV(sc, UV,dUV); k = gradFunc(UV.x,UV.y); if (k<=center) { k = k/center; EVALSUBPERTURB(a,da,2); EVALSUBPERTURB(b,db,1); } else { k = (k-center)/(1.0f-center); EVALSUBPERTURB(a,da,1); EVALSUBPERTURB(b,db,0); } np = (b-a)*np + k*(db-da) + da; } return texout->Filter(np); }
int direction(Point3 *v) { Point3 a = v[0]-v[2]; Point3 b = v[1]-v[0]; Point3 n = CrossProd(a,b); switch(MaxComponent(n)) { case 0: return (n.x<0)?NEGX:POSX; case 1: return (n.y<0)?NEGY:POSY; case 2: return (n.z<0)?NEGZ:POSZ; } return 0; }
inline void CThinshell2Element::_computeLocalTransformMatrix2Tagent( const Vector3d p[], const int stride, const Vector3d norm[], double3x3& mat) { Vector3d &X = *((Vector3d*)(&mat.x[0])); Vector3d &Y = *((Vector3d*)(&mat.x[3])); Vector3d &Z = *((Vector3d*)(&mat.x[6])); Vector3d p0 = _getVertexUsingStride(p, m_nCenterID, stride); const int ii = 0; Vector3d p1 = _getVertexUsingStride(p, m_nNodeID[ii], stride); const int len = m_nRod >> 1; //div by 2 for (int i=1; i<len; i++){ const int j = m_nNodeID[i]; p1 += _getVertexUsingStride(p, j, stride); } //Z = _computeAccurateVertexNormal(p, stride, m_nCenterID, m_1ringTri, m_n1RingPoly); Z = _computeAccurateVertexNormal2(norm, m_n1RingPolyID, m_n1RingPoly); X = p1 - p0; Y = CrossProd(Z, X); Y.Normalize(); X = CrossProd(Y, Z); }
//construct the two local coord sys. of the shell element using current nodal positions //the aixs of the coord sys are stored in two 3x3 matrices //static inline inline void getReferencePlanesForTrianglePair( const Vector3d &q1, const Vector3d &q2, const Vector3d &q3, Vector3d &facenorm0, Vector3d &facenorm1, double3x3& mat, double &xlen) { Vector3d Y0, Y1; Vector3d &Z0 = facenorm0; Vector3d &Z1 = facenorm1; Vector3d &X = *((Vector3d*)(&mat.x[0])); Vector3d &Y = *((Vector3d*)(&mat.x[3])); Vector3d &Z = *((Vector3d*)(&mat.x[6])); X = q1; xlen = Magnitude(X); X /= xlen; //normalize Y0 = q2; Z0 = CrossProd(X, Y0); Y1 = q3; Z1 = CrossProd(Y1, X); Z0.Normalize(); Z1.Normalize(); Z = Z0+Z1; Z.Normalize(); Y = CrossProd(Z, X); }
void TrackMouseCallBack::draw_marker(ViewExp& vpt, Point3 p, Point3 norm) { return; // sorry, this doesn't work yet - I'll post it later // set GW tm to orientation specified by norm and draw a circle Matrix3 tm; Point3 zdir, ydir, xdir; // compute the direction of the z axis to be. // the positive z axis points AWAY from the target. zdir = Normalize(norm); // compute direction of the X axis before roll. xdir = Normalize(CrossProd(zdir.x > 0 ? Point3(0, 0, 1) : Point3(0, 0, -1), zdir)); // compute direction of the Y axis before roll. ydir = Normalize(CrossProd(zdir, xdir)); tm.SetRow(0, xdir); tm.SetRow(1, ydir); tm.SetRow(2, zdir); vpt.getGW()->setTransform(tm); vpt.getGW()->setColor(LINE_COLOR, MARKER_COLOR); vpt.getGW()->marker(&p, CIRCLE_MRKR); }
void CGLWin::prepareMirrorMatrix(const Vector3d &x, const Vector3d & z, const Vector3d &p, Matrix &mat) { Vector3d X = Normalize(x); Vector3d Z = Normalize(z); Vector3d Y = CrossProd(Z, X); Y.normalize(); Matrix t0; SetTranslationMatrix(-p, t0); Matrix r0; GenRotationMatrix(X, Y, Z, r0); Matrix m0; MirrorZ(m0); Matrix r1 = r0; r1.transpose(); Matrix rr = r1*r0; Matrix t1; SetTranslationMatrix(p, t1); mat = t0*r0*m0*r1*t1; }
Point3 UVW_ChannelClass::GeomFaceNormal(int index) { if (index < 0) return Point3(0.0f,0.0f,1.0f); if (index >= f.Count()) return Point3(0.0f,0.0f,1.0f); if (f[index]->count < 3) return Point3(0.0f,0.0f,1.0f); Point3 vec1,vec2; if (f[index]->count == 3) { int a = f[index]->v[0]; int b = f[index]->v[1]; int c = f[index]->v[2]; vec1 = Normalize(geomPoints[b]-geomPoints[a]); vec2 = Normalize(geomPoints[c]-geomPoints[a]); Point3 norm = CrossProd(vec1,vec2); return Normalize(norm); } else { int i; Point3 tempC(0.0f,0.0f,0.0f); // Don't mess with f[fc].c. int deg = f[index]->count; for (i = 0; i < deg; i++) { int a = f[index]->v[i]; Point3 p = geomPoints[a]; tempC += p; } tempC = tempC/((float)deg); Point3 norm = Point3(0.0f,0.0f,0.0f); for (i=0; i< deg; i++) { int a = f[index]->v[i]; int b = f[index]->v[(i+1)%deg]; norm += (geomPoints[a] - tempC) ^ (geomPoints[b] - tempC); } norm = Normalize(norm); return norm; } }
// specular reflectivity, no colors yet, all vectors assumed normalized float GaussHighlight( float gloss, float aniso, float orient, Point3& N, Point3& V, Point3& L, Point3& T, float* pNL ) { float out = 0.0f; float asz = (1.0f - gloss) * ALPHA_SZ; float ax = ALPHA_MIN + asz; float ay = ALPHA_MIN + asz * (1.0f-aniso); // DbgAssert( ax >= 0.0f && ay >= 0.0f ); LBound( ax ); LBound( ay ); Point3 H = Normalize(L - V); // (L + -V)/2 float NH = DotProd(N, H); if (NH > 0.0f) { float axy = /* normalizeOn ? ax * ay : */ DEFAULT_GLOSS2; float norm = 1.0f / (4.0f * PI * axy ); float NV = -DotProd(N, V ); if ( NV <= 0.001f) NV = 0.001f; float NL = pNL ? *pNL : DotProd( N, L ); float g = 1.0f / (float)sqrt( NL * NV ); if ( g > 3.0f ) g = 3.0f; // Apply Orientation rotation here float or = orient * 180.0f; Point3 T1 = T; if ( or != 0.0f ) T1 = RotateVec( T, N, DegToRdn(or)); // get binormal Point3 B = CrossProd( T1, N ); float x = Dot( H, T1 ) / ax; float y = Dot( H, B ) / ay; float e = (float)exp( -2.0 * (x*x + y*y) / (1.0+NH) ); out = norm * g * e; } return SPEC_MAX * out; // does not have speclev or light color or kL }
Point3 UVW_ChannelClass::UVFaceNormal(int index) { if (index < 0) return Point3(0.0f,0.0f,1.0f); if (index >= f.Count()) return Point3(0.0f,0.0f,1.0f); if (f[index]->count < 3) return Point3(0.0f,0.0f,1.0f); Point3 vec1,vec2; if (f[index]->count == 3) { int a = f[index]->t[0]; int b = f[index]->t[1]; int c = f[index]->t[2]; vec1 = Normalize(v[b].GetP()-v[a].GetP()); vec2 = Normalize(v[c].GetP()-v[a].GetP()); } else { int a = f[index]->t[0]; int b = f[index]->t[1]; vec1 = Normalize(v[b].GetP()-v[a].GetP()); for (int i = 2; i < f[index]->count; i++) { b = f[index]->t[i]; vec2 = Normalize(v[b].GetP()-v[a].GetP()); float dot = DotProd(vec1,vec2); if (fabs(dot) != 1.0f) i = f[index]->count; } } Point3 norm = CrossProd(vec1,vec2); return Normalize(norm); }
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ // ÄÄÄÄÄ>> Normal Calculating Funtions <<ÄÄÄÄÄÄ //ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ void GetNormal(WORD v0, WORD v1, WORD v2, float *fvertbuf, float *nbuf) { dot3d w, n, normal; /* Get the Normal factors in order 0-1 2-1 */ w.x = fvertbuf[v0*3] - fvertbuf[v1*3]; w.y = fvertbuf[(v0*3)+1] - fvertbuf[(v1*3)+1]; w.z = fvertbuf[(v0*3)+2] - fvertbuf[(v1*3)+2]; n.x = fvertbuf[v2*3] - fvertbuf[v1*3]; n.y = fvertbuf[(v2*3)+1] - fvertbuf[(v1*3)+1]; n.z = fvertbuf[(v2*3)+2] - fvertbuf[(v1*3)+2]; /* Extract normal */ normal = CrossProd(&w, &n); nbuf[0] = normal.x; nbuf[1] = normal.y; nbuf[2] = normal.z; }
void InterpolatorKNN::CalculateBaryzentricCoordinates(NNCandidate candidates[3], Vertex v) { D3DXVECTOR3 v_01 = Normalize(candidates[1].vertex.pos-candidates[0].vertex.pos); D3DXVECTOR3 v_02 = Normalize(candidates[2].vertex.pos-candidates[0].vertex.pos); // project v onto plane <v_01, v_02> with hesse normal form D3DXVECTOR3 normal = Normalize(CrossProd(v_01, v_02)); D3DXVECTOR3 pointOnPlane = candidates[0].vertex.pos; float d = DotProd(normal, pointOnPlane); float distance = DotProd(normal, v.pos) - d; v.pos = v.pos + distance * (-normal); float translate = 0.0f; if(v.pos.x == 0 && v.pos.y == 0 && v.pos.z == 0) { translate = 10; } candidates[0].vertex.pos.x += translate; candidates[1].vertex.pos.x += translate; candidates[2].vertex.pos.x += translate; v.pos.x += translate; D3DXVECTOR3 res = SolveLES( candidates[0].vertex.pos, candidates[1].vertex.pos, candidates[2].vertex.pos, v.pos); candidates[0].weight = res.x; candidates[1].weight = res.y; candidates[2].weight = res.z; D3DXVECTOR3 check = candidates[0].weight * candidates[0].vertex.pos + candidates[1].weight * candidates[1].vertex.pos + candidates[2].weight * candidates[2].vertex.pos - v.pos; float error = abs(check.x) + abs(check.y) + abs(check.z); if(error > 0.00001) { PD(L"big error solving lgs: ", error); } }
Matrix3 UVW_ChannelClass::MatrixFromUVFace(int index) { Matrix3 tm(1); if (f[index]->count < 3) return Matrix3(1); Point3 xvec,yvec,zvec; zvec = UVFaceNormal(index); int a,b; a = f[index]->t[0]; b = f[index]->t[1]; xvec = Normalize(v[b].GetP()-v[a].GetP()); yvec = Normalize(CrossProd(xvec,zvec)); tm.SetRow(0,xvec); tm.SetRow(1,yvec); tm.SetRow(2,zvec); tm.SetRow(3,v[a].GetP()); return tm; }
Matrix3 UVW_ChannelClass::MatrixFromGeoFace(int index) { if (f[index]->count < 3) return Matrix3(1); Matrix3 tm(1); Point3 xvec,yvec,zvec; zvec = GeomFaceNormal(index); int a,b; a = f[index]->v[0]; b = f[index]->v[1]; xvec = Normalize(geomPoints[b]-geomPoints[a]); yvec = Normalize(CrossProd(xvec,zvec)); tm.SetRow(0,xvec); tm.SetRow(1,yvec); tm.SetRow(2,zvec); tm.SetRow(3,geomPoints[a]); return tm; }
//================================================================= // Methods for DumpNodesTEP // int DumpNodesTEP::callback(INode *pnode) { ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!"); if (::FNodeMarkedToSkip(pnode)) return TREE_CONTINUE; // Get node's parent INode *pnodeParent; pnodeParent = pnode->GetParentNode(); // The model's root is a child of the real "scene root" TSTR strNodeName(pnode->GetName()); BOOL fNodeIsRoot = pnodeParent->IsRootNode( ); int iNode = ::GetIndexOfINode(pnode); int iNodeParent = ::GetIndexOfINode(pnodeParent, !fNodeIsRoot/*fAssertPropExists*/); // Convenient time to cache this m_phec->m_rgmaxnode[iNode].imaxnodeParent = fNodeIsRoot ? SmdExportClass::UNDESIRABLE_NODE_MARKER : iNodeParent; // Root node has no parent, thus no translation if (fNodeIsRoot) iNodeParent = -1; // check to see if the matrix isn't right handed m_phec->m_rgmaxnode[iNode].isMirrored = DotProd( CrossProd( m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(0).Normalize(), m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(1).Normalize() ).Normalize(), m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(2).Normalize() ) < 0; // Dump node description fprintf(m_pfile, "%3d \"%s\" %3d\n", iNode, strNodeName, iNodeParent ); return TREE_CONTINUE; }
bool bgGlobalMax::CheckNegativeTM(Matrix3& m) { return (DotProd(CrossProd(m.GetRow(0), m.GetRow(1)), m.GetRow(2)) < 0.0) ? 1 : 0; }
bool PFOperatorSimpleSpeed::Proceed(IObject* pCont, PreciseTimeValue timeStart, PreciseTimeValue& timeEnd, Object* pSystem, INode* pNode, INode* actionNode, IPFIntegrator* integrator) { // acquire all necessary channels, create additional if needed IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont); if(chNew == NULL) return false; IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont); if(chTime == NULL) return false; IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont); if(chAmount == NULL) return false; // the position channel may not be present. For some option configurations it is okay IParticleChannelPoint3R* chPos = GetParticleChannelPositionRInterface(pCont); int iDir = _pblock()->GetInt(kSimpleSpeed_direction, timeStart); if ((chPos == NULL) && ((iDir == kSS_Icon_Center_Out) || (iDir == kSS_Icon_Arrow_Out))) return false; IChannelContainer* chCont; chCont = GetChannelContainerInterface(pCont); if (chCont == NULL) return false; // the channel of interest bool initSpeed = false; IParticleChannelPoint3W* chSpeed = (IParticleChannelPoint3W*)chCont->EnsureInterface(PARTICLECHANNELSPEEDW_INTERFACE, ParticleChannelPoint3_Class_ID, true, PARTICLECHANNELSPEEDR_INTERFACE, PARTICLECHANNELSPEEDW_INTERFACE, true, actionNode, (Object*)NULL, &initSpeed); IParticleChannelPoint3R* chSpeedR = GetParticleChannelSpeedRInterface(pCont); if ((chSpeed == NULL) || (chSpeedR == NULL)) return false; // there are no new particles if (chNew->IsAllOld()) return true; float fUPFScale = 1.0f/TIME_TICKSPERSEC; // conversion units per seconds to units per tick Point3 pt3SpeedVec; RandGenerator* prg = randLinker().GetRandGenerator(pCont); int iQuant = chAmount->Count(); bool wasIgnoringEmitterTMChange = IsIgnoringEmitterTMChange(); if (!wasIgnoringEmitterTMChange) SetIgnoreEmitterTMChange(); for(int i = 0; i < iQuant; i++) { if(chNew->IsNew(i)) { // apply only to new particles TimeValue tv = chTime->GetValue(i).TimeValue(); Matrix3 nodeTM = pNode->GetObjectTM(tv); float fSpeedParam = fUPFScale * GetPFFloat(pblock(), kSimpleSpeed_speed, tv); // change speed in user selected direction switch(iDir) { case kSS_Along_Icon_Arrow: { // icon arrow appears to be in the negative z direction pt3SpeedVec = -Normalize(nodeTM.GetRow(2)); } break; case kSS_Icon_Center_Out: { Point3 pt3IconCenter = nodeTM.GetTrans(); Point3 pt3PartPos = chPos->GetValue(i); pt3SpeedVec = Normalize(pt3PartPos - pt3IconCenter); } break; case kSS_Icon_Arrow_Out: { Point3 pt3PartPos = chPos->GetValue(i); Point3 pt3ArrowVec = nodeTM.GetRow(2); Point3 pt3Tmp = CrossProd(pt3PartPos - nodeTM.GetTrans(), pt3ArrowVec); pt3SpeedVec = Normalize(CrossProd(pt3ArrowVec, pt3Tmp)); } break; case kSS_Rand_3D: { pt3SpeedVec = RandSphereSurface(prg); } break; case kSS_Rand_Horiz: { float fAng = TWOPI * prg->Rand01(); // establish x, y coordinates of random angle, z component zero float x = cos(fAng); float y = sin(fAng); float z = 0.0f; pt3SpeedVec = Point3(x, y, z); } break; case kSS_Inherit_Prev: { if (initSpeed) pt3SpeedVec = Point3::Origin; else pt3SpeedVec = Normalize(chSpeedR->GetValue(i)); } break; } // account for reverse check box int iRev = _pblock()->GetInt(kSimpleSpeed_reverse, 0); float fDirMult = iRev > 0 ? -1.f : 1.f; // calculate variation float fVar = fUPFScale * GetPFFloat(pblock(), kSimpleSpeed_variation, tv); if(fVar > 0.f) fSpeedParam = fSpeedParam + fVar * prg->Rand11(); pt3SpeedVec = fDirMult * fSpeedParam * pt3SpeedVec; // calculate divergence float fDiv = GetPFFloat(pblock(), kSimpleSpeed_divergence, tv); pt3SpeedVec = DivergeVectorRandom(pt3SpeedVec, prg, fDiv); chSpeed->SetValue(i, pt3SpeedVec); } } if (!wasIgnoringEmitterTMChange) ClearIgnoreEmitterTMChange(); return true; }
void RenderMesh::ComputeVertexNormals(Mesh *aMesh, Tab<BasisVert> &FNorms, Tab<VNormal> &VNorms, bool NegScale) { Face *Face; Point3 *Verts; Point3 Vt0,Vt1,Vt2; Point3 Normal; int i,j,k,A,B,C; int NumUV,NumVert,NumFace; UVVert *UVVert; TVFace *UVFace; Vert3 Vert[3]; Point3 S,T; Point3 Edge01,Edge02; Point3 Cp; float U0,V0,U1,V1,U2,V2; int UVCount; unsigned long Sg; NumUV = aMesh->getNumMaps(); NumVert = aMesh->getNumVerts(); NumFace = aMesh->getNumFaces(); Face = aMesh->faces; Verts = aMesh->verts; VNorms.SetCount(NumVert); FNorms.SetCount(NumFace); if(NumUV > MAX_TMUS + 1) { NumUV = MAX_TMUS + 1; } for(i=0; i < NumVert; i++) { VNorms[i].Clear(); } for(i=0; i < NumFace; i++, Face++) { A = Face->v[gVIndex[0]]; B = Face->v[gVIndex[1]]; C = Face->v[gVIndex[2]]; Vt0 = Verts[A]; Vt1 = Verts[B]; Vt2 = Verts[C]; Normal = (Vt1 - Vt0) ^ (Vt2 - Vt0); Point3Normalize(Normal); for(j=0; j < 3; j++) { UVCount = 0; for(k=0;k<m_MapChannels.Count();k++) { int index = m_MapChannels[k]; if(aMesh->getNumMapVerts(index)) { UVVert = aMesh->mapVerts(index); UVFace = aMesh->mapFaces(index); Vert[j].m_UV[k].x = UVVert[UVFace[i].t[gVIndex[j]]].x; Vert[j].m_UV[k].y = UVVert[UVFace[i].t[gVIndex[j]]].y; } else { Vert[j].m_UV[k].x = 0.0f; Vert[j].m_UV[k].y = 0.0f; } } } S.Set(0.0f,0.0f,0.0f); T.Set(0.0f,0.0f,0.0f); U0 = -Vert[0].m_UV[NORMAL_UV].x; V0 = Vert[0].m_UV[NORMAL_UV].y; Rotate2DPoint(U0,V0,DEG_RAD(180.0f)); U1 = -Vert[1].m_UV[NORMAL_UV].x; V1 = Vert[1].m_UV[NORMAL_UV].y; Rotate2DPoint(U1,V1,DEG_RAD(180.0f)); U2 = -Vert[2].m_UV[NORMAL_UV].x; V2 = Vert[2].m_UV[NORMAL_UV].y; Rotate2DPoint(U2,V2,DEG_RAD(180.0f)); // x, s, t Edge01 = Point3(Vt1.x - Vt0.x, U1 - U0, V1 - V0); Edge02 = Point3(Vt2.x - Vt0.x, U2 - U0, V2 - V0); Cp = CrossProd(Edge01,Edge02); Point3Normalize(Cp); if(fabs(Cp.x) > 0.0001f) { S.x = -Cp.y / Cp.x; T.x = -Cp.z / Cp.x; } // y, s, t Edge01 = Point3(Vt1.y - Vt0.y, U1 - U0, V1 - V0); Edge02 = Point3(Vt2.y - Vt0.y, U2 - U0, V2 - V0); Cp = CrossProd(Edge01,Edge02); Point3Normalize(Cp); if(fabs(Cp.x) > 0.0001f) { S.y = -Cp.y / Cp.x; T.y = -Cp.z / Cp.x; } // z, s, t Edge01 = Point3(Vt1.z - Vt0.z, U1 - U0, V1 - V0); Edge02 = Point3(Vt2.z - Vt0.z, U2 - U0, V2 - V0); Cp = CrossProd(Edge01,Edge02); Point3Normalize(Cp); if(fabs(Cp.x) > 0.0001f) { S.z = -Cp.y / Cp.x; T.z = -Cp.z / Cp.x; } Point3Normalize(S); Point3Normalize(T); Sg = Face->smGroup; if(Sg) { VNorms[A].AddNormal(Normal,Sg,S,T); VNorms[B].AddNormal(Normal,Sg,S,T); VNorms[C].AddNormal(Normal,Sg,S,T); } else { T = CrossProd(S,Normal); Point3Normalize(T); S = CrossProd(Normal,T); Point3Normalize(S); FNorms[i].m_Normal = Normal; FNorms[i].m_S = S; FNorms[i].m_T = T; FNorms[i].m_SxT = Normal; } } for(i=0; i < NumVert; i++) { VNorms[i].Normalize(); } }
void ResetVert (PatchMesh *patch) { // Make a edge table // Static table to avoid alloc prb CVertexNeighborhood& edgeTab=vertexNeighborhoodGlobal; edgeTab.build (*patch); // For each vertices for (int nV=0; nV<patch->numVerts; nV++) { // Selected ? if (patch->vertSel[nV]) { Point3 vert=patch->verts[nV].p; Point3 normal (0,0,0); // Count of neigbor for vertex n uint listSize=edgeTab.getNeighborCount (nV); // List of neigbor const uint* pList=edgeTab.getNeighborList (nV); // For each neigbor uint nn; for (nn=0; nn<listSize; nn++) { #if (MAX_RELEASE < 4000) // Compute average plane if (patch->edges[pList[nn]].patch1!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patch1); if (patch->edges[pList[nn]].patch2!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patch2); #else // (MAX_RELEASE <= 4000) // Compute average plane if (patch->edges[pList[nn]].patches[0]!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patches[0]); if (patch->edges[pList[nn]].patches[1]!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patches[1]); #endif // (MAX_RELEASE <= 4000) } // Normalize normal=normal.Normalize(); // Plane float fD=-DotProd(normal, vert); // Reset normales float fNorme=0.f; // For each neigbor for (nn=0; nn<listSize; nn++) { Point3 vect2=patch->verts[(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].v2:patch->edges[pList[nn]].v1].p; vect2-=vert; vect2/=3.f; Point3 tmp1=CrossProd (vect2, normal); tmp1=CrossProd (normal, tmp1); tmp1=Normalize(tmp1); int nTang=(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].vec12:patch->edges[pList[nn]].vec21; patch->vecs[nTang].p=vert+tmp1*DotProd (tmp1,vect2); tmp1=patch->vecs[nTang].p; tmp1-=vert; fNorme+=tmp1.Length(); } // Renorme new normal /*fNorme/=(float)edgeTab[nV].size(); ite=edgeTab[nV].begin(); while (ite!=edgeTab[nV].end()) { int nTang=(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].vec12:patch->edges[pList[nn]].vec21; patch->vecs[nTang].p=fNorme*(Normalize(patch->vecs[nTang].p-vert))+vert; ite++; }*/ } } patch->computeInteriors(); patch->InvalidateGeomCache (); }
bool Exporter::TMNegParity(const Matrix3 &m) { return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?true:false; }
GFA::Vector GFA::Vector::operator*(const Matrix &rhs) const { Vector temp; CrossProd(temp, rhs); return temp; }
int CPointObj::LoadPltFileWithoutHeader(FILE *fp, const int nv, const int, const int nTotalAttrib) { //read vertices; m_nVertexCount = nv; m_nPolygonCount = nv; LoadPltVertices(fp, nv, nTotalAttrib); //assign the radii for particles m_pRadius = new float[m_nPolygonCount]; assert(m_pRadius!=NULL); float *pInputRadius = NULL; int rpos= this->GetFAttributeIndexByName("R"); if (rpos>0) pInputRadius = m_pFAttributes[rpos]; if (pInputRadius){ for (int i=0; i<nv; i++) m_pRadius[i] = pInputRadius[i]; } else{ for (int i=0; i<nv; i++) m_pRadius[i] = 0; } //assign ellipsoid transform matrices m_pMatrix = NULL; const float K = 1.0f; int evXpos= this->GetFAttributeIndexByName("A"); int etXXpos= this->GetFAttributeIndexByName("AX"); int etXYpos= this->GetFAttributeIndexByName("AY"); int etXZpos= this->GetFAttributeIndexByName("AZ"); int evYpos= this->GetFAttributeIndexByName("B"); int etYXpos= this->GetFAttributeIndexByName("BX"); int etYYpos= this->GetFAttributeIndexByName("BY"); int etYZpos= this->GetFAttributeIndexByName("BZ"); int evZpos= this->GetFAttributeIndexByName("C"); int etZXpos= this->GetFAttributeIndexByName("CX"); int etZYpos= this->GetFAttributeIndexByName("CY"); int etZZpos= this->GetFAttributeIndexByName("CZ"); if (evXpos>=0 && etXXpos>=0 && etXYpos>=0 && etXZpos>=0 && evYpos>=0 && etYXpos>=0 && etYYpos>=0 && etYZpos>=0 && evZpos>=0 && etZXpos>=0 && etZYpos>=0 && etZZpos>=0){ float *rx = m_pFAttributes[evXpos]; float *ry = m_pFAttributes[evYpos]; float *rz = m_pFAttributes[evZpos]; float *vxx = m_pFAttributes[etXXpos]; float *vxy = m_pFAttributes[etXYpos]; float *vxz = m_pFAttributes[etXZpos]; float *vyx = m_pFAttributes[etYXpos]; float *vyy = m_pFAttributes[etYYpos]; float *vyz = m_pFAttributes[etYZpos]; float *vzx = m_pFAttributes[etZXpos]; float *vzy = m_pFAttributes[etZYpos]; float *vzz = m_pFAttributes[etZZpos]; m_pMatrix = new float3x3[nv]; for (int i=0; i<nv; i++){ float3x3& m = m_pMatrix[i]; Vector3f* dx = (Vector3f*)(&m.x[0]); Vector3f* dy = (Vector3f*)(&m.x[3]); Vector3f* dz = (Vector3f*)(&m.x[6]); //normalize, right-handrize the 3 axes *dx = Vector3f(vxx[i], vxy[i], vxz[i]); (*dx).normalize(); *dy = Vector3f(vyx[i], vyy[i], vyz[i]); (*dy).normalize(); (*dz) = CrossProd(*dx, *dy); //*dz = Vector3f(vzx[i], vzy[i], vzz[i]); (*dz).normalize(); float3x3 s; s.setIdentityMatrix(); s.x[0]=(fabs(rx[i]))*K; s.x[4]=(fabs(ry[i]))*K; s.x[8]=(fabs(rz[i]))*K; m*=s; } } return 1; }
// Determine is the node has negative scaling. // This is used for mirrored objects for example. They have a negative scale factor // so when calculating the normal we should take the vertices counter clockwise. // If we don't compensate for this the objects will be 'inverted'. BOOL Exporter::TMNegParity(Matrix3 &m) { return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?1:0; }
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; }