//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: brush.cpp // Version: v1.00 // Created: 8/7/2002 by Timur. // Compilers: Visual Studio.NET // Description: // ------------------------------------------------------------------------- // History: Based on Andrey's Indoor editor. // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "Brush.h" #include "BrushPlane.h" #include "BrushPoly.h" #include "BrushFace.h" #include "Settings.h" #include "Grid.h" #include "list2.h" #include "EdMesh.h" #include #define MIN_BOUNDS_SIZE 0.01f #define SIDE_FRONT 0 #define SIDE_BACK 1 ////////////////////////////////////////////////////////////////////////// // SBrushSubSelection implementation. ////////////////////////////////////////////////////////////////////////// bool SBrushSubSelection::AddPoint( Vec3 *pnt ) { if (std::find(points.begin(),points.end(),pnt) != points.end()) return false; points.push_back(pnt); return true; }; ////////////////////////////////////////////////////////////////////////// void SBrushSubSelection::Clear() { points.clear(); } SBrush::SBrush() { m_flags = 0; m_prefabGeom = 0; m_indoorGeom = 0; // m_indoorGeom = GetIEditor()->Get3DEngine()->MakeObject(); } ////////////////////////////////////////////////////////////////////////// // // Brush implementations. // ////////////////////////////////////////////////////////////////////////// SBrush::~SBrush() { ClearFaces(); if (m_indoorGeom) { //m_indoorGeom->Release(); // May be will be deleted in Indoors. m_indoorGeom = 0; } //TBrush *b = (TBrush *)this; //b->Unlink(); //UnlinkFromObj(); } SBrush& SBrush::operator = (const SBrush& b) { int i; m_bounds.min = b.m_bounds.min; m_bounds.max = b.m_bounds.max; for (i=0; iMakePlane(); } } ////////////////////////////////////////////////////////////////////////// void SBrush::ClearFaces() { for (int i = 0; i < m_Faces.size(); i++) { delete m_Faces[i]; } m_Faces.clear(); } ////////////////////////////////////////////////////////////////////////// float SBrush::GetVolume() { int i; SBrushPoly *p; Vec3d corner; float d, area, volume; SBrushPlane *plane; p = NULL; for (i=0; im_Poly; if (p) break; } if (!p) return 0; corner = p->m_Pts[0].xyz; volume = 0; for ( ; im_Poly; if (!p) continue; plane = &m_Faces[i]->m_Plane; d = -((corner | plane->normal) - plane->dist); area = p->Area(); volume += d*area; } volume /= 3.0f; return volume; } ////////////////////////////////////////////////////////////////////////// void SBrush::ClipPrefabModels(bool bClip) { //@ASK Andrey. /* SBrushFace *f; int i, j; TPrefabItem *pi; for (i=0; im_Prefabs; pi; pi=pi->Next) { if (!bClip) { if (pi->m_Model) delete pi->m_Model; pi->m_Model = NULL; continue; } if (!pi->m_Geom || !pi->m_Geom->mModel) continue; for (j=0; jClipPrefab(pi); } } } */ } ////////////////////////////////////////////////////////////////////////// SBrushPoly* SBrush::CreateFacePoly( SBrushFace *face ) { SBrushPoly *p; SBrushFace *clip; SBrushPlane plane; bool past; p = face->m_Plane.CreatePoly(); int numFaces = m_Faces.size(); past = false; for (int i = 0; i < numFaces; i++) { clip = m_Faces[i]; if (!p) return p; if (clip == face) { past = true; continue; } if ((face->m_Plane.normal.Dot(clip->m_Plane.normal) > 0.999) && (fabs(face->m_Plane.dist - clip->m_Plane.dist) < DIST_EPSILON)) { if (past) { delete p; return NULL; } continue; } plane.Invert(&clip->m_Plane); p = p->ClipByPlane(&plane, false); } if (!p) return NULL; if (p->m_Pts.size() < 3) { delete p; p = NULL; } //@ASK Andrey. //p->m_Pts.Shrink(); return p; } ////////////////////////////////////////////////////////////////////////// bool SBrush::BuildSolid( bool bRemoveEmptyFaces,bool bBuildRE ) { BBox box; box.Reset(); int i,j; // Clear render element valid flag. m_flags &= ~BRF_RE_VALID; MakeFacePlanes(); for (i = 0; i < m_Faces.size(); i++) { SBrushFace *f = m_Faces[i]; if (f->m_Poly) delete f->m_Poly; f->m_Poly = CreateFacePoly(f); //@ASK Andrey /* f->m_Shader = gcMapEf.mfForName( f->m_TexInfo.name ); */ if (!f->m_Poly) continue; // Build geometry tiles if needed f->BuildPrefabs(); // Add this polygon to bounding box. int numPts = f->m_Poly->m_Pts.size(); for (j=0; j < numPts; j++) { box.Add( f->m_Poly->m_Pts[j].xyz ); } for (j=0; j < numPts; j++) { //@ASK Andrey. //f->CalcTexCoords(f->m_Poly->m_Pts[j], gNE[f->m_Shader->m_Id].m_Tex); } } if (bRemoveEmptyFaces) RemoveEmptyFaces(); // Rebuild render elements. if (bBuildRE) BuildRenderElements(); //@ASK Andrey /* ClipPrefabModels(gcpCryIndEd->m_wndTextureBar.m_bClipPrefab); */ m_bounds.min = box.min; m_bounds.max = box.max; // Check if valid. if (m_bounds.min.x > m_bounds.max.x || m_bounds.min.y > m_bounds.max.y || m_bounds.min.z > m_bounds.max.z) { return false; } if (m_Faces.empty()) return false; return true; } ////////////////////////////////////////////////////////////////////////// bool SBrush::BuildRenderElements() { //@ HACK m_flags |= BRF_RE_VALID; return true; if (m_indoorGeom && m_prefabGeom != NULL) { CLeafBuffer *srcBuf = m_prefabGeom->GetGeometry()->GetLeafBuffer(); CLeafBuffer *trgBuf = m_indoorGeom->GetLeafBuffer(); Vec3 omin = m_prefabGeom->GetGeometry()->GetBoxMin(); Vec3 omax = m_prefabGeom->GetGeometry()->GetBoxMax(); Vec3 bb = (m_bounds.max - m_bounds.min); Vec3 ob = (omax - omin); Vec3 scl; scl.x = bb.x / ob.x; scl.y = bb.y / ob.y; scl.z = bb.z / ob.z; Vec3 objofs = -(omax + omin)/2; objofs = Vec3( objofs.x*scl.x,objofs.y*scl.y,objofs.z*scl.z ); Matrix44 stm; stm.SetIdentity(); //SCALE_CHANGED_BY_IVO //stm.ScaleMatrix(scl.x,scl.y,scl.z); stm=Matrix33::CreateScale( Vec3d(scl.x,scl.y,scl.z) )*stm; stm.SetTranslationOLD( objofs ); Matrix44 tm = stm * m_matrix; // tm.SetTranslation( m_matrix.GetTranslation() ); int numv = srcBuf->m_SecVertCount; int stride1,stride2; unsigned char *src = srcBuf->GetPosPtr( stride1 ); unsigned char *trg = trgBuf->GetPosPtr( stride2 ); for (int i = 0; i < numv; i++) { Vec3 &sv = *(Vec3*)src; Vec3 &tv = *(Vec3*)trg; //Vec3 sv1 = stm.TransformPoint(sv); tv = tm.TransformPointOLD( sv ); src += stride1; trg += stride2; } trgBuf->InvalidateVideoBuffer(); m_flags |= BRF_RE_VALID; return true; } // Clear render element valid flag. m_flags &= ~BRF_RE_VALID; // Make leaf buffer. //CLeafBuffer *buf = GetIEditor()->GetRenderer()->CreateLeafBufferInitialized( //m_indoorGeom->MakeLeafBuffer( m_prefabGeom->GetTriData(),false ); if (m_Faces.empty()) return false; typedef struct_VERTEX_FORMAT_P3F_TEX2F VertexFormat; VertexFormat vtx; list2 verts; list2 indices; int j; int baseIndex = 0; indices.reserve( 12*3 ); verts.reserve( 12 ); for (int i = 0; i < m_Faces.size(); i++) { SBrushFace *f = m_Faces[i]; if (!f->m_Poly) continue; //m_bTesselated = false; int numPts = f->m_Poly->m_Pts.size(); for (j = 0; j < numPts; j++) { SBrushVert *v = &f->m_Poly->m_Pts[numPts-j-1]; Vec3 p = m_matrix.TransformPointOLD( v->xyz ); vtx.xyz = p; vtx.st[0] = v->st[0]; vtx.st[1] = v->st[1]; verts.push_back( vtx ); } for (j = 0; j < numPts-2; j++) { indices.push_back( baseIndex ); indices.push_back( baseIndex+j+1 ); indices.push_back( baseIndex+j+2 ); } baseIndex += numPts; } IRenderer *renderer = GetIEditor()->GetRenderer(); CLeafBuffer *pLeafBuffer = renderer->CreateLeafBufferInitialized( &verts[0],verts.size(),VERTEX_FORMAT_P3F_TEX2F,&indices[0], indices.Count(), R_PRIMV_TRIANGLES,"Brush",eBT_Dynamic ); assert( pLeafBuffer ); CLeafBuffer *prevBuf = m_indoorGeom->GetLeafBuffer(); if (prevBuf) { renderer->DeleteLeafBuffer(prevBuf); } SInputShaderResources rc; CMatInfo mt; mt.m_Id = 0; mt.nFirstIndexId = 0; mt.nNumIndices = indices.size(); mt.nFirstVertId = 0; mt.nNumVerts = verts.size(); mt.shaderItem = renderer->EF_LoadShaderItem( "Default",eSH_Indoor,true,NULL,0,&rc ); mt.pRE = (CREOcLeaf*)renderer->EF_CreateRE(eDATA_OcLeaf); pLeafBuffer->m_pMats->Add(mt); mt.pRE->m_pBuffer = pLeafBuffer; mt.pRE->m_pChunk = &(*pLeafBuffer->m_pMats)[0]; m_indoorGeom->SetLeafBuffer( pLeafBuffer ); m_flags |= BRF_RE_VALID; /* for (int i = 0; i < m_Faces.size(); i++) { SBrushFace *f = m_Faces[i]; if (f->m_Poly) { // Build render element for this face. f->BuildRE( GetMatrix() ); // Set render element valid flag. m_flags |= BRF_RE_VALID; } } */ return m_flags & BRF_RE_VALID; } ////////////////////////////////////////////////////////////////////////// void SBrush::RemoveEmptyFaces() { int i; std::vector fc; // delete faces that doesnt contain polygon. for (i=0; i < m_Faces.size(); i++) { SBrushFace *f = m_Faces[i]; if (f->m_Poly) fc.push_back(f); else delete f; } m_Faces.clear(); // If less then 3 faces left, remove all faces. if (fc.size() < 3) { for (i = 0; i < fc.size(); i++) delete fc[i]; return; } // Copy left brushes to brush face. m_Faces = fc; } ////////////////////////////////////////////////////////////////////////// void SBrush::FitTexture(int nX, int nY) { int i; for (i=0; iFitTexture(nX, nY); } } //////////////////////////////////////////////////////////////////// bool SBrush::IsIntersect(SBrush *Vol) { /* for (int k=0;kIsIntersecting(Vol)) return (true); } //k return (false); */ //return (true); int i; Vec3d Cent = (m_bounds.min + m_bounds.max) * 0.5f; for (i=0; im_Faces.size(); i++) { SBrushFace *f = Vol->m_Faces[i]; if ((f->m_Plane.normal | Cent) > f->m_Plane.dist) break; } if (i == Vol->m_Faces.size()) return true; return false; } ////////////////////////////////////////////////////////////////////////// bool SBrush::IsInside(const Vec3d &vPoint) { for (int i=0;im_Plane.normal.Dot(vPoint)) > f->m_Plane.dist) return(false); } //i return(true); } ////////////////////////////////////////////////////////////////////////// bool SBrush::Intersect(SBrush *Vol, std::vector& List) { SBrush *front, *back; int n = 0; int i; for (i=0 ; i<3 ; i++) { if (m_bounds.min[i] >= Vol->m_bounds.max[i] - DIST_EPSILON || m_bounds.max[i] <= Vol->m_bounds.min[i] + DIST_EPSILON) break; } if (i != 3) return false; SBrush *b = Vol; SBrush *Die = this; float vol; for (int j=0; jm_Faces.size(); j++) { SBrushFace *f = b->m_Faces[j]; SBrushFace *fc = new SBrushFace; *fc = *f; fc->m_PlanePts[0] += f->m_Plane.normal * 16; fc->m_PlanePts[1] += f->m_Plane.normal * 16; fc->m_PlanePts[2] += f->m_Plane.normal * 16; fc->MakePlane(); for (i=0; im_Faces.size(); i++) { for (int k=0; km_Faces[i]->m_Poly->m_Pts.size(); k++) { if ((fc->m_Plane.normal | Die->m_Faces[i]->m_Poly->m_Pts[k].xyz) > fc->m_Plane.dist) break; } if (k!=Die->m_Faces[i]->m_Poly->m_Pts.size()) break; } delete fc; if (i!=Die->m_Faces.size()) break; } if (j == b->m_Faces.size()) { List.push_back( Die ); vol = Die->GetVolume(); return true; } Vec3d Mins = m_bounds.min; Vec3d Maxs = m_bounds.max; for (j=0; jm_Faces.size() && Die; j++) { SBrushFace *f = b->m_Faces[j]; Die->SplitByFace(f, front, back); if (back) back->m_flags |= BRF_TEMP; if (front) delete front; if (Die->m_flags & BRF_TEMP) delete Die; Die = back; } if (Die) { SBrush *b = Die; List.push_back(b); vol = b->GetVolume(); //gCurMap->m_ChoppedBrushes++; n++; } if (n) return true; return false; } ////////////////////////////////////////////////////////////////////////// int SBrush::GetMemorySize() { int Size = sizeof(*this); for (int i=0; im_Poly->m_Pts.size(); } return Size; } ////////////////////////////////////////////////////////////////////////// void SBrush::SnapToGrid() { SBrushFace *f; int i; for (int nf=0; nfm_PlanePts[i] = gSettings.pGrid->Snap( f->m_PlanePts[i] ); } } BuildSolid(); } ////////////////////////////////////////////////////////////////////////// void SBrush::SplitByFace (SBrushFace *f, SBrush* &front, SBrush* &back) { SBrush *b; SBrushFace *nf; Vec3d temp; SBrush *sb; b = new SBrush(); sb = b; *sb = *this; nf = new SBrushFace; *nf = *f; nf->m_TexInfo = b->m_Faces[0]->m_TexInfo; b->m_Faces.push_back(nf); b->BuildSolid(); b->RemoveEmptyFaces(); if ( !b->m_Faces.size() ) { delete b; back = NULL; } else { //@ASK Andrey. /* b->LinkToObj(m_Owner); */ back = b; } b = new SBrush; sb = b; *sb = *this; nf = new SBrushFace; *nf = *f; temp = nf->m_PlanePts[0]; nf->m_PlanePts[0] = nf->m_PlanePts[1]; nf->m_PlanePts[1] = temp; nf->m_TexInfo = b->m_Faces[0]->m_TexInfo; b->m_Faces.push_back(nf); b->BuildSolid(); b->RemoveEmptyFaces (); if ( !b->m_Faces.size() ) { delete b; front = NULL; } else { //@ASK Andrey. /* b->LinkToObj(m_Owner); */ front = b; } } int SBrush::OnSide (SBrushPlane *plane) { int i, j; SBrushPoly *p; float d, max; int side; max = 0; side = SIDE_FRONT; for (i=0;im_Poly; if (!p) continue; for (j=0; jm_Pts.size(); j++) { d = (p->m_Pts[j].xyz | plane->normal) - plane->dist; if (d > max) { max = d; side = SIDE_FRONT; } if (-d > max) { max = -d; side = SIDE_BACK; } } } return side; } SBrush* SBrush::Clone( bool bBuildSoilid ) { SBrush *sb = new SBrush; *sb = *this; if (bBuildSoilid) sb->BuildSolid(); return sb; } void SBrush::SplitByPlane (SBrushPlane *plane, SBrush* &front, SBrush* &back) { SBrushPoly *tp = plane->CreatePoly(); SBrushFace fc; ZeroStruct(fc); fc.m_PlanePts[0] = tp->m_Pts[0].xyz; fc.m_PlanePts[1] = tp->m_Pts[1].xyz; fc.m_PlanePts[2] = tp->m_Pts[2].xyz; SplitByFace(&fc, front, back); delete tp; return; SBrush *b[2]; int i, j; SBrushPoly *p, *cp[2], *midpoly; SBrushPlane plane2; SBrushFace *f; float d, f_front, f_back; front = back = NULL; f_front = f_back = 0; for (i=0; im_Poly; if (!p) continue; for (j=0; jm_Pts.size(); j++) { d = (p->m_Pts[j].xyz | plane->normal) - plane->dist; if (d > 0 && d > f_front) f_front = d; if (d < 0 && d < f_back) f_back = d; } } if (f_front < 0.1) { SBrush *nb = Clone(true); back = nb; return; } if (f_back > -0.1) { SBrush *nb = Clone(true); front = nb; return; } p = plane->CreatePoly(); for (i=0; im_Plane; plane2.Invert(pp); p = p->ClipByPlane(&plane2, true); } if (!p) { int side; side = OnSide (plane); if (side == SIDE_FRONT) { SBrush *nb = Clone(true); front = nb; } if (side == SIDE_BACK) { SBrush *nb = Clone(true); back = nb; } return; } midpoly = p; for (i=0; i<2; i++) { b[i] = Clone(false); for (j=0; jm_Faces.size(); j++) { delete b[i]->m_Faces[j]; } b[i]->m_Faces.clear(); } for (i=0; im_Poly; if (!p) continue; p->ClipByPlane(plane, 0, &cp[0], &cp[1]); if (cp[0] == p) { cp[0] = new SBrushPoly; *(cp[0]) = *p; } if (cp[1] == p) { cp[1] = new SBrushPoly; *(cp[1]) = *p; } for (j=0; j<2; j++) { if (!cp[j]) continue; SBrushFace *ff = new SBrushFace; *ff = *f; ff->m_Poly = cp[j]; b[j]->m_Faces.push_back(ff); } } for (i=0; i<2; i++) { if (b[i]->m_Faces.size() < 3) { delete b[i]; b[i] = NULL; } } if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) CLogFile::WriteLine("Split removed brush\n"); else CLogFile::WriteLine("Split not on both sides\n"); if (b[0]) { delete b[0]; SBrush *nb = Clone(true); front = nb; } if (b[1]) { delete b[1]; SBrush *nb = Clone(true); back = nb; } return; } for (i=0; i<2; i++) { SBrushFace *ff = new SBrushFace; if (!i) ff->m_Plane = *plane; else { plane2.Invert(plane); ff->m_Plane = plane2; } // @FIXME ?? //b[i]->CreateFacePoly( ff ); ff->m_TexInfo = b[i]->m_Faces[0]->m_TexInfo; b[i]->m_Faces.push_back(ff); if (i==0) { SBrushPoly *pl = new SBrushPoly; *pl = *midpoly; ff->m_Poly = pl; } else ff->m_Poly = midpoly; } front = b[0]; back = b[1]; } /* TBrush *SBrush::Parse() { TBrush *b; SBrushFace *f; int i,j; gCurMap->m_ParsedBrushes++; b = new TBrush; do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; if (strcmpi(token, "patchDef2") == 0 || strcmpi(token, "patchDef3") == 0) { delete b; b = SPatch::Parse(strcmpi(token, "patchDef2") == 0); if (b == NULL) { CLogFile::WriteLine ("Warning: parsing patch/brush\n"); return NULL; } else continue; } else { f = new SBrushFace; memset(f, 0, sizeof(SBrushFace)); for (i=0; i<3; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) { CLogFile::WriteLine("Warning: Parsing brush error\n"); delete b; return NULL; } for (j=0 ; j<3 ; j++) { GetToken (false); f->m_PlanePts[i][j] = atof(token); } GetToken (false); if (strcmp (token, ")") ) { CLogFile::WriteLine("Warning: Parsing brush error\n"); delete b; return NULL; } } } { // read the TextureInfo GetToken (false); f->m_TexInfo.SetName(token); GetToken (false); f->m_TexInfo.shift[0] = atof(token); GetToken (false); f->m_TexInfo.shift[1] = atof(token); GetToken (false); f->m_TexInfo.rotate = atof(token); GetToken (false); f->m_TexInfo.scale[0] = atof(token); GetToken (false); f->m_TexInfo.scale[1] = atof(token); // the flags and value field aren't necessarily present f->m_Shader = gcMapEf.mfForName( f->m_TexInfo.name ); if (f->m_Shader->m_SM) { f->m_TexInfo.contents = f->m_Shader->m_SM->m_Contents; f->m_TexInfo.flags = f->m_Shader->m_SM->m_SurfFlags; } else { f->m_TexInfo.contents = 0; f->m_TexInfo.flags = 0; } if (TokenAvailable ()) { GetToken (false); f->m_TexInfo.contents = atoi(token); GetToken (false); f->m_TexInfo.flags = atoi(token); GetToken (false); f->m_TexInfo.value = atoi(token); } if (!f->m_TexInfo.value) f->m_TexInfo.value = gcMapEf.mfGetVal(f->m_Shader, NULL); b->m_Faces.push_back(f); } } while (1); return b; } bool SBrush::ParseFromInd() { gCurMap->m_ParsedBrushes++; GetToken(true); GetToken(true); if (token[0] != '[') { CLogFile::WriteLine("Error: Couldn't parsing IND file\n"); return false; } while (true) { GetToken(true); if (token[0] == ']') break; if (!stricmp(token, "Face")) { SBrushFace *f = new SBrushFace; memset(f, 0, sizeof(SBrushFace)); m_Faces.push_back(f); f->m_Poly = new SBrushPoly; GetToken(true); int nv = atoi(token); for (int i=0; im_Poly->m_Pts.push_back(v); } f->m_PlanePts[0] = f->m_Poly->m_Pts[0].xyz; f->m_PlanePts[1] = f->m_Poly->m_Pts[1].xyz; f->m_PlanePts[2] = f->m_Poly->m_Pts[2].xyz; // Parse TexInfo GetToken(true); GetToken(true); strcpy(f->m_TexInfo.name, token); GetToken(true); f->m_TexInfo.shift[0] = atof(token); GetToken(true); f->m_TexInfo.shift[1] = atof(token); GetToken(true); f->m_TexInfo.rotate = atof(token); GetToken(true); f->m_TexInfo.scale[0] = atof(token); GetToken(true); f->m_TexInfo.scale[1] = atof(token); GetToken(true); f->m_TexInfo.flags = atoi(token); GetToken(true); } else { CLogFile::WriteLine("Error: Couldn't parsing IND file\n"); return false; } } return true; } */ void SBrush::Create( const Vec3& mins, const Vec3& maxs, SMapTexInfo *ti) { int i, j; Vec3d pts[4][2]; SBrushFace *f; ClearFaces(); for (i=0 ; i<3 ; i++) { if (maxs[i] < mins[i]) CLogFile::WriteLine ("Error: SBrush::Create: backwards"); } pts[0][0][0] = mins[0]; pts[0][0][1] = mins[1]; pts[1][0][0] = mins[0]; pts[1][0][1] = maxs[1]; pts[2][0][0] = maxs[0]; pts[2][0][1] = maxs[1]; pts[3][0][0] = maxs[0]; pts[3][0][1] = mins[1]; for (i=0; i<4; i++) { pts[i][0][2] = mins[2]; pts[i][1][0] = pts[i][0][0]; pts[i][1][1] = pts[i][0][1]; pts[i][1][2] = maxs[2]; } for (i=0; i<4; i++) { f = new SBrushFace; ZeroStruct(*f); f->m_TexInfo = *ti; j = (i+1)%4; f->m_PlanePts[0] = pts[j][1]; f->m_PlanePts[1] = pts[i][1]; f->m_PlanePts[2] = pts[i][0]; m_Faces.push_back(f); } f = new SBrushFace; ZeroStruct(*f); f->m_TexInfo = *ti; f->m_PlanePts[0] = pts[0][1]; f->m_PlanePts[1] = pts[1][1]; f->m_PlanePts[2] = pts[2][1]; m_Faces.push_back(f); f = new SBrushFace; ZeroStruct(*f); f->m_TexInfo = *ti; f->m_PlanePts[0] = pts[2][0]; f->m_PlanePts[1] = pts[1][0]; f->m_PlanePts[2] = pts[0][0]; m_Faces.push_back(f); } //=================================================================== void SBrush::MakeSelFace (SBrushFace *f) { //@ASK Andrey. /* SBrushPoly *p; int i; int pnum[128]; p = f->CreatePoly(this); if (!p) return; for (i=0; im_Pts.size(); i++) { pnum[i] = gCurMap->FindPoint(&p->m_Pts[i]); } for (i=0; im_Pts.size(); i++) { gCurMap->FindEdge (pnum[i], pnum[(i+1)%p->m_Pts.size()], f); } delete p; */ } SBrushFace* SBrush::Ray( Vec3d Origin, Vec3d dir, float *dist ) { SBrushFace *f, *FirstFace=0; Vec3d p1, p2; float frac, d1, d2; int i, j; p1 = Origin; for (i=0 ; i<3 ; i++) { p2[i] = p1[i] + dir[i]*32768; } for (j=0; jm_Plane.normal) - f->m_Plane.dist; d2 = (p2 | f->m_Plane.normal) - f->m_Plane.dist; if (d1 >= 0 && d2 >= 0) { *dist = 0; return NULL; } if (d1 <=0 && d2 <= 0) continue; frac = d1 / (d1 - d2); if (d1 > 0) { FirstFace = f; p1 = p1 + frac * (p2 - p1); } else p2 = p1 + frac * (p2 - p1); } p1 -= Origin; d1 = p1 | dir; *dist = d1; return FirstFace; } ////////////////////////////////////////////////////////////////////////// void SBrush::SelectSide( Vec3d Origin,Vec3d Dir,bool shear,SBrushSubSelection &subSelection ) { SBrushFace *f; Vec3d p1, p2; Vec3d Target = Origin + Dir * 10000.0f; for (int j=0; j < m_Faces.size(); j++) { f = m_Faces[j]; p1 = Origin; p2 = Target; // Clip ray by every face of brush, except tested one. for (int i=0; i < m_Faces.size(); i++) { if (m_Faces[i] != f) m_Faces[i]->ClipLine(p1, p2); } //if (i != m_Faces.size()) //continue; if (IsEquivalent(p1,Origin,0)) continue; if (f->ClipLine(p1, p2)) continue; SelectFace( f,shear,subSelection ); } } ////////////////////////////////////////////////////////////////////////// void SBrush::Move(Vec3d& Delta) { int i; SBrushFace *f; for (int j=0; jm_PlanePts[i] += Delta; } } BuildSolid(); } ////////////////////////////////////////////////////////////////////////// void SBrush::Transform( Matrix44 &tm,bool bBuild ) { int i; SBrushFace *f; for (int j=0; jm_PlanePts[i] = tm.TransformPointOLD(f->m_PlanePts[i]); } } if (bBuild) BuildSolid(); } ////////////////////////////////////////////////////////////////////////// void SBrush::SetShader (SMapTexInfo *ti) { for (int i=0; im_TexInfo = *ti; } BuildSolid(); } /* void SBrush::WriteToInd(FILE *f, char *st, int num) { pfprintf (st, f, "Brush '%d'\n", num); pfprintf (st, f, "[\n"); for (int i=0; im_Poly->m_Pts.size()); for (int j=0; jm_Poly->m_Pts.size(); j++) { SBrushVert *v = &fa->m_Poly->m_Pts[j]; fprintf (f, " ("); if (v->xyz[0] - (int)v->xyz[0]) fprintf(f, " %f", v->xyz[0]); else fprintf(f, " %d", (int)v->xyz[0]); if (v->xyz[1] - (int)v->xyz[1]) fprintf(f, " %f", v->xyz[1]); else fprintf(f, " %d", (int)v->xyz[1]); if (v->xyz[2] - (int)v->xyz[2]) fprintf(f, " %f", v->xyz[2]); else fprintf(f, " %d", (int)v->xyz[2]); fprintf(f, " %f", v->st[0]); fprintf(f, " %f", v->st[1]); fprintf (f, " )"); } SMapTexInfo *ti= &fa->m_TexInfo; fprintf(f, " ( %s %f %f %f %f %f %d )\n", ti->name, ti->shift[0], ti->shift[1], ti->rotate, ti->scale[0], ti->scale[1], ti->flags); } pfprintf (st, f, "]\n"); } void SBrush::Select (void) { TBrush *b; CMapObject *o; gCurMap->m_SelectedFace = NULL; if (gCurMap->m_SelectCount < 2) gCurMap->m_SelectOrder[gCurMap->m_SelectCount] = this; gCurMap->m_SelectCount++; o = m_Owner; if (o) { if (o != gCurMap->m_WorldObject) { for (b=gCurMap->m_SelectedBrushes; b; b=b->Next) { if (b->m_Owner == o) goto ss; } for (b=o->m_Brushes.m_NextInObj; b != &o->m_Brushes; b=b->m_NextInObj) { b->Unlink(); b->Link(gCurMap->m_SelectedBrushes); } } else { ss: b = (TBrush *)this; b->Unlink(); b->Link(gCurMap->m_SelectedBrushes); } } } void SBrush::Deselect (void) { TBrush *b; b = (TBrush *)this; b->Unlink(); b->Link(gCurMap->m_ActiveBrushes); } */ void SBrush::SelectFace( SBrushFace *f,bool shear,SBrushSubSelection &subSelection ) { int i; SBrushFace *f2; SBrushPoly *p; float d; int c; c = 0; for (i = 0; i<3; i++) { if (subSelection.AddPoint(&f->m_PlanePts[i])) c++; } if (c == 0) return; /* TBrush *b2; for (b2=gCurMap->m_SelectedBrushes; b2; b2=b2->Next) { if (b2 == this) continue; for (int j=0; jm_Faces.size(); j++) { f2 = b2->m_Faces[j]; for (i=0; i<3; i++) { if (fabs(f2->m_PlanePts[i] | f->m_Plane.normal) - f->m_Plane.dist > DIST_EPSILON) break; } if (i == 3) { b2->SelectFaceForDragging (f2, shear); break; } } } */ if (!shear) return; for (int j=0; jm_Pts.size(); i++) { d = (p->m_Pts[i].xyz | f->m_Plane.normal) - f->m_Plane.dist; if (d > -DIST_EPSILON && d < DIST_EPSILON) { break; } } if (i != p->m_Pts.size()) { if (i == 0) { d = p->m_Pts[p->m_Pts.size()-1].xyz.Dot(f->m_Plane.normal) - f->m_Plane.dist; if (d > -DIST_EPSILON && d < DIST_EPSILON) i = p->m_Pts.size() - 1; } subSelection.AddPoint( &f2->m_PlanePts[0] ); f2->m_PlanePts[0] = p->m_Pts[i].xyz; if (++i == p->m_Pts.size()) i = 0; d = p->m_Pts[i].xyz.Dot(f->m_Plane.normal) - f->m_Plane.dist; if (d > -DIST_EPSILON && d < DIST_EPSILON) { subSelection.AddPoint( &f2->m_PlanePts[1] ); } f2->m_PlanePts[1] = p->m_Pts[i].xyz; if (++i == p->m_Pts.size()) i = 0; f2->m_PlanePts[2] = p->m_Pts[i].xyz; } delete p; } } //void gDrawLightFrustum(CMapObject *o, Vec3d org); bool SBrush::DrawModel(bool b) { //@ASK Andrey. /* CComModel *m = (CComModel *)m_Owner->m_Class->m_Model; if (!m) return false; glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable (GL_TEXTURE_2D); // glMatrixMode(GL_MODELVIEW); glPushMatrix(); Vec3d org = m_Owner->m_Brushes.m_NextInObj->m_bounds.min - m_Owner->m_Class->m_bounds.min; glTranslatef(org[0], org[1], org[2]); float a = m_Owner->FloatForKey("angle"); Vec3d angs; angs[YAW] = a; if (!a) m_Owner->VectorForKey("angles", angs); glRotatef(angs[YAW], 0, 0, 1); glRotatef(angs[PITCH], 0, 1, 0); glRotatef(angs[ROLL], 1, 0, 0); glColor4f(1,1,1,1); bool bRes = m->mfDraw(NULL, true); glPopMatrix(); if (!strnicmp(m_Owner->m_Class->m_Name, "light", 5)) { Vec3d dir; m_Owner->VectorForKey("direction", dir); if (dir != Vec3d(0,0,0)) gDrawLightFrustum(m_Owner, org); } glPopAttrib(); return bRes; */ return false; } void SBrush::Draw() { //@ASK Andrey. /* int i, j; SShader *prev = 0; SBrushPoly *p; if (m_flags & BRF_HIDE) return; if (m_Patch) { m_Patch->Draw(1); return; } EDrawMode dm = gcpMapPersp->m_eDrawMode; if (m_Owner->m_Class->m_flags & OCF_NORESIZE) { bool bp = (m_flags & BRF_FAILMODEL) ? false : DrawModel(true); if (bp) return; } prev = NULL; for (i=0; im_Poly; if (!p) continue; if (f->m_Prefabs) { for (TPrefabItem *pi=f->m_Prefabs; pi; pi=pi->Next) { glPushMatrix(); glMultMatrixf(&pi->m_Matrix.m_values[0][0]); if (pi->m_Model) pi->m_Model->mfDraw(f->m_Shader, NULL); else pi->m_Geom->mfDraw(f->m_Shader, NULL); glPopMatrix(); } continue; } if ((dm == eDM_Textured || dm == eDM_Light) && f->m_Shader != prev) { prev = f->m_Shader; glBindTexture(GL_TEXTURE_2D, gNE[f->m_Shader->m_Id].m_Tex->m_Bind); } if (!m_Patch) glColor4fv(&f->m_Color[0]); else glColor4f (f->m_Color[0], f->m_Color[1], f->m_Color[2], 0.13 ); float a; if ((a=gcMapEf.mfGetAlpha(f->m_Shader)) != 1.0f) glColor4f (f->m_Color[0], f->m_Color[1], f->m_Color[2], a); glBegin(GL_POLYGON); for (j=0; jm_Pts.size(); j++) { if (dm == eDM_Textured || dm == eDM_Light) glTexCoord2fv( &p->m_Pts[j].st[0] ); glVertex3fv(&p->m_Pts[j].xyz[0]); } glEnd(); } if ((m_Owner->m_Class->m_flags & OCF_NORESIZE) && (dm == eDM_Textured || dm == eDM_Light)) glEnable (GL_TEXTURE_2D); glBindTexture( GL_TEXTURE_2D, 0 ); */ } bool SBrush::AddToListModel(bool b) { bool bRes = false; //@ASK Andrey. /* if (!strnicmp(m_Owner->m_Class->m_Name, "light", 5)) return true; Vec3d org = m_Owner->m_Brushes.m_NextInObj->m_bounds.min - m_Owner->m_Class->m_bounds.min; CComModel *m = (CComModel *)m_Owner->m_Class->m_Model; if (!m) return false; glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable (GL_TEXTURE_2D); // glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(org[0], org[1], org[2]); float a = m_Owner->FloatForKey("angle"); Vec3d angs; angs[YAW] = a; if (!a) m_Owner->VectorForKey("angles", angs); glRotatef(angs[YAW], 0, 0, 1); glRotatef(angs[PITCH], 0, 1, 0); glRotatef(angs[ROLL], 1, 0, 0); glColor4f(1,1,1,1); bRes = m->mfDraw(NULL, true); glPopMatrix(); glPopAttrib(); */ return bRes; } void SBrush::AddToList(CCObject *obj) { //@ASK Andrey. /* int i; SShader *prev = 0; SBrushPoly *p; if (m_flags & BRF_HIDE) return; if (m_Patch) { m_Patch->Draw(1); return; } EDrawMode dm = gcpMapPersp->m_eDrawMode; if (m_Owner->m_Class->m_flags & OCF_NORESIZE) { bool bp = (m_flags & BRF_FAILMODEL) ? false : AddToListModel(true); if (bp) return; } prev = NULL; for (i=0; im_Poly; if (!p) continue; ECull cl = f->m_Shader->m_eCull; if (cl != eCULL_None) { float d = f->m_Plane.normal * g_Globals.m_Origin; if (cl == eCULL_Back) { if (d < f->m_Plane.dist-8.0f) continue; } else { if (d > f->m_Plane.dist+8.0f) continue; } } if (f->m_Prefabs) { for (TPrefabItem *pi=f->m_Prefabs; pi; pi=pi->Next) { glPushMatrix(); glMultMatrixf(&pi->m_Matrix.m_values[0][0]); if (pi->m_Model) pi->m_Model->mfDraw(f->m_Shader, NULL); else pi->m_Geom->mfDraw(f->m_Shader, NULL); glPopMatrix(); } continue; } if (g_Globals.m_TemplateId < EFT_USER_FIRST) f->m_Shader->AddTemplate(g_Globals.m_TemplateId); else f->m_Shader->AddTemplate(g_Globals.m_TemplateId, g_Globals.m_CustomTemplate.GetBuffer(0)); SShader *e = f->m_Shader->mfGetTemplate(g_Globals.m_TemplateId); if ((gNE[e->m_Id].m_TessSize && !f->m_bTesselated) || (!gNE[e->m_Id].m_TessSize && f->m_bTesselated)) f->BuildRE(); if (obj) gRenDev->EF_AddEf(0, f->m_RE, f->m_Shader, obj, g_Globals.m_TemplateId, NULL); else gRenDev->EF_AddEf(0, f->m_RE, f->m_Shader, g_Globals.m_TemplateId, NULL); } */ } //============================================================ /* void SBrush::Write(CMemFile* pMemFile) { SBrushFace *fa; const char *pname; int i, j; if (m_Patch) { m_Patch->Write(pMemFile); return; } { gCurMap->FPrintfToMem (pMemFile, "{\n"); for (j=0; jFPrintfToMem(pMemFile, "( "); for (int j = 0; j < 3; j++) { if (fa->m_PlanePts[i][j] == static_cast(fa->m_PlanePts[i][j])) gCurMap->FPrintfToMem(pMemFile, "%i ", static_cast(fa->m_PlanePts[i][j])); else gCurMap->FPrintfToMem(pMemFile, "%f ", fa->m_PlanePts[i][j]); } gCurMap->FPrintfToMem(pMemFile, ") "); } { pname = fa->m_TexInfo.name; if (pname[0] == 0) pname = "unnamed"; gCurMap->FPrintfToMem (pMemFile, "%s %i %i %i ", pname, (int)fa->m_TexInfo.shift[0], (int)fa->m_TexInfo.shift[1], (int)fa->m_TexInfo.rotate); if (fa->m_TexInfo.scale[0] == (int)fa->m_TexInfo.scale[0]) gCurMap->FPrintfToMem (pMemFile, "%i ", (int)fa->m_TexInfo.scale[0]); else gCurMap->FPrintfToMem (pMemFile, "%f ", (float)fa->m_TexInfo.scale[0]); if (fa->m_TexInfo.scale[1] == (int)fa->m_TexInfo.scale[1]) gCurMap->FPrintfToMem (pMemFile, "%i", (int)fa->m_TexInfo.scale[1]); else gCurMap->FPrintfToMem (pMemFile, "%f", (float)fa->m_TexInfo.scale[1]); gCurMap->FPrintfToMem (pMemFile, " %i %i %i", fa->m_TexInfo.contents, fa->m_TexInfo.flags, fa->m_TexInfo.value); } gCurMap->FPrintfToMem (pMemFile, "\n"); } gCurMap->FPrintfToMem (pMemFile, "}\n"); } } */ ////////////////////////////////////////////////////////////////////////// void SBrush::Serialize( XmlNodeRef &xmlNode,bool bLoading ) { if (bLoading) { // Loading. // Load all faces. int numFaces = xmlNode->getChildCount(); m_Faces.resize( numFaces ); for (int i = 0; i < numFaces; i++) { SBrushFace *fc = new SBrushFace; m_Faces[i] = fc; XmlNodeRef faceNode = xmlNode->getChild(i); faceNode->getAttr( "p1",fc->m_PlanePts[0] ); faceNode->getAttr( "p2",fc->m_PlanePts[1] ); faceNode->getAttr( "p3",fc->m_PlanePts[2] ); Vec3 texScale( 1,1,1 ); Vec3 texShift( 0,0,0 ); faceNode->getAttr( "TexScale",texScale ); faceNode->getAttr( "TexShift",texShift ); fc->m_TexInfo.scale[0] = texScale.x; fc->m_TexInfo.scale[1] = texScale.y; fc->m_TexInfo.shift[0] = texShift.x; fc->m_TexInfo.shift[1] = texShift.y; } BuildSolid(false); } else { // Saving. //char str[1024]; // Save all faces. for (int i = 0; i < m_Faces.size(); i++) { SBrushFace *fc = m_Faces[i]; XmlNodeRef faceNode = xmlNode->newChild( "Face" ); faceNode->setAttr( "p1",fc->m_PlanePts[0] ); faceNode->setAttr( "p2",fc->m_PlanePts[1] ); faceNode->setAttr( "p3",fc->m_PlanePts[2] ); Vec3 texScale( fc->m_TexInfo.scale[0],fc->m_TexInfo.scale[1],0 ); Vec3 texShift( fc->m_TexInfo.shift[0],fc->m_TexInfo.shift[1],0 ); faceNode->setAttr( "TexScale",texScale ); faceNode->setAttr( "TexShift",texShift ); //sprintf( } } } ////////////////////////////////////////////////////////////////////////// void SBrush::SetMatrix( const Matrix44 &tm ) { m_matrix = tm; // clear RE valid flag. m_flags &= ~BRF_RE_VALID; } ////////////////////////////////////////////////////////////////////////// void SBrush::Render( IRenderer *renderer,const Vec3 &objectSpaceCamSrc ) { assert( renderer ); if (m_prefabGeom) { Vec3 omin = m_prefabGeom->GetGeometry()->GetBoxMin(); Vec3 omax = m_prefabGeom->GetGeometry()->GetBoxMax(); Matrix44 mov1; Matrix44 mov2; mov1.SetIdentity(); mov2.SetIdentity(); mov1.SetTranslationOLD( -omin ); mov2.SetTranslationOLD( m_bounds.min ); Vec3 bb = (m_bounds.max - m_bounds.min); Vec3 ob = (omax - omin); Vec3 scl; scl.x = bb.x / ob.x; scl.y = bb.y / ob.y; scl.z = bb.z / ob.z; Matrix44 stm; stm.SetIdentity(); stm=Matrix33::CreateScale( Vec3d(scl.x,scl.y,scl.z) )*stm; Matrix44 tm = mov1 * stm * mov2; tm = tm * m_matrix; SRendParams rp; rp.pMatrix = &tm; rp.nShaderTemplate = -1; rp.nDLightMask = 0xFFFFFFFF; rp.dwFObjFlags |= FOB_TRANS_MASK; //m_indoorGeom->Render( rp ); m_prefabGeom->Render( rp ); return; } if (!(m_flags&BRF_RE_VALID)) { if (!BuildRenderElements()) return; } int i; //CCObject *pObj = renderer->EF_GetObject(true); //pObj->m_Matrix = m_matrix; //pObj->m_ObjFlags |= FOB_USEMATRIX; ECull cl = eCULL_Back; for (i = 0; i < m_Faces.size(); i++) { SBrushFace *f = m_Faces[i]; SBrushPoly *p = f->m_Poly; if (!p) continue; //ECull cl = f->m_Shader->m_eCull; //if (cl != eCULL_None) { float d = f->m_Plane.normal * objectSpaceCamSrc; if (cl == eCULL_Back) { if (d < f->m_Plane.dist-0.001f) continue; } else { if (d > f->m_Plane.dist+0.001f) continue; } } /* if (f->m_Prefabs) { for (TPrefabItem *pi=f->m_Prefabs; pi; pi=pi->Next) { glPushMatrix(); glMultMatrixf(&pi->m_Matrix.m_values[0][0]); if (pi->m_Model) pi->m_Model->mfDraw(f->m_Shader, NULL); else pi->m_Geom->mfDraw(f->m_Shader, NULL); glPopMatrix(); } continue; } */ /* if (g_Globals.m_TemplateId < EFT_USER_FIRST) f->m_Shader->AddTemplate(g_Globals.m_TemplateId); else f->m_Shader->AddTemplate(g_Globals.m_TemplateId, g_Globals.m_CustomTemplate.GetBuffer(0)); SShader *e = f->m_Shader->mfGetTemplate(g_Globals.m_TemplateId); if ((gNE[e->m_Id].m_TessSize && !f->m_bTesselated) || (!gNE[e->m_Id].m_TessSize && f->m_bTesselated)) f->BuildRE(); */ if (f->m_RE) { static IShader *pShader = 0; if (!pShader) pShader = renderer->EF_LoadShader( "Textures\\Detail.jpg",eSH_World ); /* static CCObject *pObj = 0; if (!pObj) { pObj = renderer->EF_GetObject(true); pObj->m_Matrix.Identity(); pObj->m_ObjFlags |= FOB_USEMATRIX; } */ renderer->EF_AddEf( 0,f->m_RE,pShader, NULL, NULL, -1 ); } //else //gRenDev->EF_AddEf(0, f->m_RE, f->m_Shader, g_Globals.m_TemplateId, NULL); } } ////////////////////////////////////////////////////////////////////////// void SBrush::SetPrefabGeom( CEdMesh *obj ) { m_prefabGeom = obj; if (m_indoorGeom) { //m_indoorGeom->Release(); //m_indoorGeom = 0; } if (!m_prefabGeom) return; bool bNewCreated = false; if (m_Faces.empty()) { BBox box; m_prefabGeom->GetBounds(box); if (box.max.x - box.min.x == 0) box.max.x = box.min.x + MIN_BOUNDS_SIZE; if (box.max.y - box.min.y == 0) box.max.y = box.min.y + MIN_BOUNDS_SIZE; if (box.max.z - box.min.z == 0) box.max.z = box.min.z + MIN_BOUNDS_SIZE; // Create brush with specified bounds. SMapTexInfo ti; Create( box.min,box.max,&ti ); bNewCreated = true; } /* // Clone prefab geometry to make indoor geometry. m_indoorGeom->MakeLeafBuffer( m_prefabGeom->GetTriData(),false ); m_indoorGeom->SetBBoxMin( box.min ); m_indoorGeom->SetBBoxMax( box.max ); */ if (!bNewCreated) { SMapTexInfo ti; Create( m_bounds.min,m_bounds.max,&ti ); } BuildSolid(false); } ////////////////////////////////////////////////////////////////////////// void SBrush::ResetToPrefabSize() { if (!m_prefabGeom) return; BBox box; m_prefabGeom->GetBounds( box ); if (box.max.x - box.min.x == 0) box.max.x = box.min.x + MIN_BOUNDS_SIZE; if (box.max.y - box.min.y == 0) box.max.y = box.min.y + MIN_BOUNDS_SIZE; if (box.max.z - box.min.z == 0) box.max.z = box.min.z + MIN_BOUNDS_SIZE; // Create brush with specified bounds. SMapTexInfo ti; Create( box.min,box.max,&ti ); BuildSolid(false); } ////////////////////////////////////////////////////////////////////////// IStatObj* SBrush::GetIndoorGeom() { if (!(m_flags&BRF_RE_VALID)) { if (!BuildRenderElements()) return 0; } return m_indoorGeom; }; /* ////////////////////////////////////////////////////////////////////////// CIndexedMesh* SBrush::CreateGeometry() { CIndexedMesh *mesh = 0; if (m_prefabGeom) { CIndexedMesh *src = m_prefabGeom->GetTriData(); Vec3 omin = m_prefabGeom->GetBoxMin(); Vec3 omax = m_prefabGeom->GetBoxMax(); Vec3 bb = (m_bounds.max - m_bounds.min); Vec3 ob = (omax - omin); Vec3 scl; scl.x = bb.x / ob.x; scl.y = bb.y / ob.y; scl.z = bb.z / ob.z; Vec3 objofs = -(omax + omin)/2; objofs = Vec3( objofs.x*scl.x,objofs.y*scl.y,objofs.z*scl.z ); Matrix stm; stm.Identity(); stm.ScaleMatrix(scl.x,scl.y,scl.z); stm.SetTranslation( objofs ); Matrix tm = stm * m_matrix; // Allocated data in indexed mesh. CryFace mesh = new CIndexedMesh //m_pFaces = (CObjFace*)malloc(sizeof(CObjFace)*nFaces); //mesh-> int numv = srcBuf->m_SecVertCount; int stride1,stride2; unsigned char *src = srcBuf->GetPosPtr( stride1 ); unsigned char *trg = trgBuf->GetPosPtr( stride2 ); for (int i = 0; i < numv; i++) { Vec3 &sv = *(Vec3*)src; Vec3 &tv = *(Vec3*)trg; //Vec3 sv1 = stm.TransformPoint(sv); tv = tm.TransformPoint( sv ); src += stride1; trg += stride2; } trgBuf->InvalidateVideoBuffer(); m_flags |= BRF_RE_VALID; return true; } } */