From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/raytrace/raytrace.cpp | 1802 +++++++++++++++++++++--------------------- 1 file changed, 901 insertions(+), 901 deletions(-) (limited to 'mp/src/raytrace/raytrace.cpp') diff --git a/mp/src/raytrace/raytrace.cpp b/mp/src/raytrace/raytrace.cpp index 142220e2..7b951dfb 100644 --- a/mp/src/raytrace/raytrace.cpp +++ b/mp/src/raytrace/raytrace.cpp @@ -1,901 +1,901 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// $Id$ - -#include "raytrace.h" -#include -#include -#include - -static bool SameSign(float a, float b) -{ - int32 aa=*((int *) &a); - int32 bb=*((int *) &b); - return ((aa^bb)&0x80000000)==0; -} - -int FourRays::CalculateDirectionSignMask(void) const -{ - // this code treats the floats as integers since all it cares about is the sign bit and - // floating point compares suck. - - int ret; - int ormask; - int andmask; - int32 const *treat_as_int=((int32 const *) (&direction)); - - ormask=andmask=*(treat_as_int++); - ormask|=*treat_as_int; - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - if (ormask>=0) - ret=0; - else - { - if (andmask<0) - ret=1; - else return -1; - } - ormask=andmask=*(treat_as_int++); - ormask|=*treat_as_int; - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - if (ormask<0) - { - if (andmask<0) - ret|=2; - else return -1; - } - ormask=andmask=*(treat_as_int++); - ormask|=*treat_as_int; - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - ormask|=*(treat_as_int); - andmask&=*(treat_as_int++); - if (ormask<0) - { - if (andmask<0) - ret|=4; - else return -1; - } - return ret; -} - - - - -void RayTracingEnvironment::MakeRoomForTriangles( int ntris ) -{ - //OptimizedTriangleList.EnsureCapacity( ntris ); - if (! (Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS)) - TriangleColors.EnsureCapacity( ntris ); -} - - -void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1, - const Vector &v2, const Vector &v3, - const Vector &color) -{ - AddTriangle( id, v1, v2, v3, color, 0, 0 ); -} - -void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1, - const Vector &v2, const Vector &v3, - const Vector &color, uint16 flags, int32 materialIndex) -{ - CacheOptimizedTriangle tmptri; - tmptri.m_Data.m_GeometryData.m_nTriangleID = id; - tmptri.Vertex( 0 ) = v1; - tmptri.Vertex( 1 ) = v2; - tmptri.Vertex( 2 ) = v3; - tmptri.m_Data.m_GeometryData.m_nFlags = flags; - OptimizedTriangleList.AddToTail(tmptri); - if (! ( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS) ) - TriangleColors.AddToTail(color); - if ( !( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_MATERIALS) ) - TriangleMaterials.AddToTail(materialIndex); -// printf("add triange from (%f %f %f),(%f %f %f),(%f %f %f) id %d\n", -// XYZ(v1),XYZ(v2),XYZ(v3),id); -} - -void RayTracingEnvironment::AddQuad( - int32 id, const Vector &v1, const Vector &v2, const Vector &v3, - const Vector &v4, // specify vertices in cw or ccw order - const Vector &color) -{ - AddTriangle(id,v1,v2,v3,color); - AddTriangle(id+1,v1,v3,v4,color); -} - - -void RayTracingEnvironment::AddAxisAlignedRectangularSolid(int id,Vector minc, Vector maxc, - const Vector &color) -{ - - // "far" face - AddQuad(id, - Vector(minc.x,maxc.y,maxc.z), - Vector(maxc.x,maxc.y,maxc.z),Vector(maxc.x,minc.y,maxc.z), - Vector(minc.x,minc.y,maxc.z),color); - // "near" face - AddQuad(id, - Vector(minc.x,maxc.y,minc.z), - Vector(maxc.x,maxc.y,minc.z),Vector(maxc.x,minc.y,minc.z), - Vector(minc.x,minc.y,minc.z),color); - - // "left" face - AddQuad(id, - Vector(minc.x,maxc.y,maxc.z), - Vector(minc.x,maxc.y,minc.z), - Vector(minc.x,minc.y,minc.z), - Vector(minc.x,minc.y,maxc.z),color); - // "right" face - AddQuad(id, - Vector(maxc.x,maxc.y,maxc.z), - Vector(maxc.x,maxc.y,minc.z), - Vector(maxc.x,minc.y,minc.z), - Vector(maxc.x,minc.y,maxc.z),color); - - // "top" face - AddQuad(id, - Vector(minc.x,maxc.y,maxc.z), - Vector(maxc.x,maxc.y,maxc.z), - Vector(maxc.x,maxc.y,minc.z), - Vector(minc.x,maxc.y,minc.z),color); - // "bot" face - AddQuad(id, - Vector(minc.x,minc.y,maxc.z), - Vector(maxc.x,minc.y,maxc.z), - Vector(maxc.x,minc.y,minc.z), - Vector(minc.x,minc.y,minc.z),color); -} - - - -static Vector GetEdgeEquation(Vector p1, Vector p2, int c1, int c2, Vector InsidePoint) -{ - float nx=p1[c2]-p2[c2]; - float ny=p2[c1]-p1[c1]; - float d=-(nx*p1[c1]+ny*p1[c2]); -// assert(fabs(nx*p1[c1]+ny*p1[c2]+d)<0.01); -// assert(fabs(nx*p2[c1]+ny*p2[c2]+d)<0.01); - - // use the convention that negative is "outside" - float trial_dist=InsidePoint[c1]*nx+InsidePoint[c2]*ny+d; - if (trial_dist<0) - { - nx = -nx; - ny = -ny; - d = -d; - trial_dist = -trial_dist; - } - nx /= trial_dist; // scale so that it will be =1.0 at the oppositve vertex - ny /= trial_dist; - d /= trial_dist; - - return Vector(nx,ny,d); -} - -void CacheOptimizedTriangle::ChangeIntoIntersectionFormat(void) -{ - // lose the vertices and use edge equations instead - - // grab the whole original triangle to we don't overwrite it - TriGeometryData_t srcTri = m_Data.m_GeometryData; - - m_Data.m_IntersectData.m_nFlags = srcTri.m_nFlags; - m_Data.m_IntersectData.m_nTriangleID = srcTri.m_nTriangleID; - - Vector p1 = srcTri.Vertex( 0 ); - Vector p2 = srcTri.Vertex( 1 ); - Vector p3 = srcTri.Vertex( 2 ); - - Vector e1 = p2 - p1; - Vector e2 = p3 - p1; - - Vector N = e1.Cross( e2 ); - N.NormalizeInPlace(); - // now, determine which axis to drop - int drop_axis = 0; - for(int c=1 ; c<3 ; c++) - if ( fabs(N[c]) > fabs( N[drop_axis] ) ) - drop_axis = c; - - m_Data.m_IntersectData.m_flD = N.Dot( p1 ); - m_Data.m_IntersectData.m_flNx = N.x; - m_Data.m_IntersectData.m_flNy = N.y; - m_Data.m_IntersectData.m_flNz = N.z; - - // decide which axes to keep - int nCoordSelect0 = ( drop_axis + 1 ) % 3; - int nCoordSelect1 = ( drop_axis + 2 ) % 3; - - m_Data.m_IntersectData.m_nCoordSelect0 = nCoordSelect0; - m_Data.m_IntersectData.m_nCoordSelect1 = nCoordSelect1; - - - Vector edge1 = GetEdgeEquation( p1, p2, nCoordSelect0, nCoordSelect1, p3 ); - m_Data.m_IntersectData.m_ProjectedEdgeEquations[0] = edge1.x; - m_Data.m_IntersectData.m_ProjectedEdgeEquations[1] = edge1.y; - m_Data.m_IntersectData.m_ProjectedEdgeEquations[2] = edge1.z; - - Vector edge2 = GetEdgeEquation( p2, p3, nCoordSelect0, nCoordSelect1, p1 ); - m_Data.m_IntersectData.m_ProjectedEdgeEquations[3] = edge2.x; - m_Data.m_IntersectData.m_ProjectedEdgeEquations[4] = edge2.y; - m_Data.m_IntersectData.m_ProjectedEdgeEquations[5] = edge2.z; - - -} - -int n_intersection_calculations=0; - -int CacheOptimizedTriangle::ClassifyAgainstAxisSplit(int split_plane, float split_value) -{ - // classify a triangle against an axis-aligned plane - float minc=Vertex(0)[split_plane]; - float maxc=minc; - for(int v=1;v<3;v++) - { - minc=min(minc,Vertex(v)[split_plane]); - maxc=max(maxc,Vertex(v)[split_plane]); - } - - if (minc>=split_value) - return PLANECHECK_POSITIVE; - if (maxc<=split_value) - return PLANECHECK_NEGATIVE; - if (minc==maxc) - return PLANECHECK_POSITIVE; - return PLANECHECK_STRADDLING; -} - -#define MAILBOX_HASH_SIZE 256 -#define MAX_TREE_DEPTH 21 -#define MAX_NODE_STACK_LEN (40*MAX_TREE_DEPTH) - -struct NodeToVisit { - CacheOptimizedKDNode const *node; - fltx4 TMin; - fltx4 TMax; -}; - - -static fltx4 FourEpsilons={1.0e-10,1.0e-10,1.0e-10,1.0e-10}; -static fltx4 FourZeros={1.0e-10,1.0e-10,1.0e-10,1.0e-10}; -static fltx4 FourNegativeEpsilons={-1.0e-10,-1.0e-10,-1.0e-10,-1.0e-10}; - -static float BoxSurfaceArea(Vector const &boxmin, Vector const &boxmax) -{ - Vector boxdim=boxmax-boxmin; - return 2.0*((boxdim[0]*boxdim[2])+(boxdim[0]*boxdim[1])+(boxdim[1]*boxdim[2])); -} - -void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, - RayTracingResult *rslt_out, - int32 skip_id, ITransparentTriangleCallback *pCallback) -{ - int msk=rays.CalculateDirectionSignMask(); - if (msk!=-1) - Trace4Rays(rays,TMin,TMax,msk,rslt_out,skip_id, pCallback); - else - { - // sucky case - can't trace 4 rays at once. in the worst case, need to trace all 4 - // separately, but usually we will still get 2x, Since our tracer only does 4 at a - // time, we will have to cover up the undesired rays with the desired ray - - //!! speed!! there is room for some sse-ization here - FourRays tmprays; - tmprays.origin=rays.origin; - - uint8 need_trace[4]={1,1,1,1}; - for(int try_trace=0;try_trace<4;try_trace++) - { - if (need_trace[try_trace]) - { - need_trace[try_trace]=2; // going to trace it - // replicate the ray being traced into all 4 rays - tmprays.direction.x=ReplicateX4(rays.direction.X(try_trace)); - tmprays.direction.y=ReplicateX4(rays.direction.Y(try_trace)); - tmprays.direction.z=ReplicateX4(rays.direction.Z(try_trace)); - // now, see if any of the other remaining rays can be handled at the same time. - for(int try2=try_trace+1;try2<4;try2++) - if (need_trace[try2]) - { - if ( - SameSign(rays.direction.X(try2), - rays.direction.X(try_trace)) && - SameSign(rays.direction.Y(try2), - rays.direction.Y(try_trace)) && - SameSign(rays.direction.Z(try2), - rays.direction.Z(try_trace))) - { - need_trace[try2]=2; - tmprays.direction.X(try2) = rays.direction.X(try2); - tmprays.direction.Y(try2) = rays.direction.Y(try2); - tmprays.direction.Z(try2) = rays.direction.Z(try2); - } - } - // ok, now trace between 1 and 3 rays, and output the results - RayTracingResult tmpresults; - msk=tmprays.CalculateDirectionSignMask(); - assert(msk!=-1); - Trace4Rays(tmprays,TMin,TMax,msk,&tmpresults,skip_id, pCallback); - // now, move results to proper place - for(int i=0;i<4;i++) - if (need_trace[i]==2) - { - need_trace[i]=0; - rslt_out->HitIds[i]=tmpresults.HitIds[i]; - SubFloat(rslt_out->HitDistance, i) = SubFloat(tmpresults.HitDistance, i); - rslt_out->surface_normal.X(i) = tmpresults.surface_normal.X(i); - rslt_out->surface_normal.Y(i) = tmpresults.surface_normal.Y(i); - rslt_out->surface_normal.Z(i) = tmpresults.surface_normal.Z(i); - } - - } - } - } -} - - -void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, - int DirectionSignMask, RayTracingResult *rslt_out, - int32 skip_id, ITransparentTriangleCallback *pCallback) -{ - rays.Check(); - - memset(rslt_out->HitIds,0xff,sizeof(rslt_out->HitIds)); - - rslt_out->HitDistance=ReplicateX4(1.0e23); - - rslt_out->surface_normal.DuplicateVector(Vector(0.,0.,0.)); - FourVectors OneOverRayDir=rays.direction; - OneOverRayDir.MakeReciprocalSaturate(); - - // now, clip rays against bounding box - for(int c=0;c<3;c++) - { - fltx4 isect_min_t= - MulSIMD(SubSIMD(ReplicateX4(m_MinBound[c]),rays.origin[c]),OneOverRayDir[c]); - fltx4 isect_max_t= - MulSIMD(SubSIMD(ReplicateX4(m_MaxBound[c]),rays.origin[c]),OneOverRayDir[c]); - TMin=MaxSIMD(TMin,MinSIMD(isect_min_t,isect_max_t)); - TMax=MinSIMD(TMax,MaxSIMD(isect_min_t,isect_max_t)); - } - fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active - if (! IsAnyNegative(active) ) - return; // missed bounding box - - int32 mailboxids[MAILBOX_HASH_SIZE]; // used to avoid redundant triangle tests - memset(mailboxids,0xff,sizeof(mailboxids)); // !!speed!! keep around? - - int front_idx[3],back_idx[3]; // based on ray direction, whether to - // visit left or right node first - - if (DirectionSignMask & 1) - { - back_idx[0]=0; - front_idx[0]=1; - } - else - { - back_idx[0]=1; - front_idx[0]=0; - } - if (DirectionSignMask & 2) - { - back_idx[1]=0; - front_idx[1]=1; - } - else - { - back_idx[1]=1; - front_idx[1]=0; - } - if (DirectionSignMask & 4) - { - back_idx[2]=0; - front_idx[2]=1; - } - else - { - back_idx[2]=1; - front_idx[2]=0; - } - - NodeToVisit NodeQueue[MAX_NODE_STACK_LEN]; - CacheOptimizedKDNode const *CurNode=&(OptimizedKDTree[0]); - NodeToVisit *stack_ptr=&NodeQueue[MAX_NODE_STACK_LEN]; - while(1) - { - while (CurNode->NodeType() != KDNODE_STATE_LEAF) // traverse until next leaf - { - int split_plane_number=CurNode->NodeType(); - CacheOptimizedKDNode const *FrontChild=&(OptimizedKDTree[CurNode->LeftChild()]); - - fltx4 dist_to_sep_plane= // dist=(split-org)/dir - MulSIMD( - SubSIMD(ReplicateX4(CurNode->SplittingPlaneValue), - rays.origin[split_plane_number]),OneOverRayDir[split_plane_number]); - fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active - - // now, decide how to traverse children. can either do front,back, or do front and push - // back. - fltx4 hits_front=AndSIMD(active,CmpGeSIMD(dist_to_sep_plane,TMin)); - if (! IsAnyNegative(hits_front)) - { - // missed the front. only traverse back - //printf("only visit back %d\n",CurNode->LeftChild()+back_idx[split_plane_number]); - CurNode=FrontChild+back_idx[split_plane_number]; - TMin=MaxSIMD(TMin, dist_to_sep_plane); - - } - else - { - fltx4 hits_back=AndSIMD(active,CmpLeSIMD(dist_to_sep_plane,TMax)); - if (! IsAnyNegative(hits_back) ) - { - // missed the back - only need to traverse front node - //printf("only visit front %d\n",CurNode->LeftChild()+front_idx[split_plane_number]); - CurNode=FrontChild+front_idx[split_plane_number]; - TMax=MinSIMD(TMax, dist_to_sep_plane); - } - else - { - // at least some rays hit both nodes. - // must push far, traverse near - //printf("visit %d,%d\n",CurNode->LeftChild()+front_idx[split_plane_number], - // CurNode->LeftChild()+back_idx[split_plane_number]); - assert(stack_ptr>NodeQueue); - --stack_ptr; - stack_ptr->node=FrontChild+back_idx[split_plane_number]; - stack_ptr->TMin=MaxSIMD(TMin,dist_to_sep_plane); - stack_ptr->TMax=TMax; - CurNode=FrontChild+front_idx[split_plane_number]; - TMax=MinSIMD(TMax,dist_to_sep_plane); - } - } - } - // hit a leaf! must do intersection check - int ntris=CurNode->NumberOfTrianglesInLeaf(); - if (ntris) - { - int32 const *tlist=&(TriangleIndexList[CurNode->TriangleIndexStart()]); - do - { - int tnum=*(tlist++); - //printf("try tri %d\n",tnum); - // check mailbox - int mbox_slot=tnum & (MAILBOX_HASH_SIZE-1); - TriIntersectData_t const *tri = &( OptimizedTriangleList[tnum].m_Data.m_IntersectData ); - if ( ( mailboxids[mbox_slot] != tnum ) && ( tri->m_nTriangleID != skip_id ) ) - { - n_intersection_calculations++; - mailboxids[mbox_slot] = tnum; - // compute plane intersection - - - FourVectors N; - N.x = ReplicateX4( tri->m_flNx ); - N.y = ReplicateX4( tri->m_flNy ); - N.z = ReplicateX4( tri->m_flNz ); - - fltx4 DDotN = rays.direction * N; - // mask off zero or near zero (ray parallel to surface) - fltx4 did_hit = OrSIMD( CmpGtSIMD( DDotN,FourEpsilons ), - CmpLtSIMD( DDotN, FourNegativeEpsilons ) ); - - fltx4 numerator=SubSIMD( ReplicateX4( tri->m_flD ), rays.origin * N ); - - fltx4 isect_t=DivSIMD( numerator,DDotN ); - // now, we have the distance to the plane. lets update our mask - did_hit = AndSIMD( did_hit, CmpGtSIMD( isect_t, FourZeros ) ); - //did_hit=AndSIMD(did_hit,CmpLtSIMD(isect_t,TMax)); - did_hit = AndSIMD( did_hit, CmpLtSIMD( isect_t, rslt_out->HitDistance ) ); - - if ( ! IsAnyNegative( did_hit ) ) - continue; - - // now, check 3 edges - fltx4 hitc1 = AddSIMD( rays.origin[tri->m_nCoordSelect0], - MulSIMD( isect_t, rays.direction[ tri->m_nCoordSelect0] ) ); - fltx4 hitc2 = AddSIMD( rays.origin[tri->m_nCoordSelect1], - MulSIMD( isect_t, rays.direction[tri->m_nCoordSelect1] ) ); - - // do barycentric coordinate check - fltx4 B0 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[0] ), hitc1 ); - - B0 = AddSIMD( - B0, - MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[1] ), hitc2 ) ); - B0 = AddSIMD( - B0, ReplicateX4( tri->m_ProjectedEdgeEquations[2] ) ); - - did_hit = AndSIMD( did_hit, CmpGeSIMD( B0, FourZeros ) ); - - fltx4 B1 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[3] ), hitc1 ); - B1 = AddSIMD( - B1, - MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[4]), hitc2 ) ); - - B1 = AddSIMD( - B1, ReplicateX4( tri->m_ProjectedEdgeEquations[5] ) ); - - did_hit = AndSIMD( did_hit, CmpGeSIMD( B1, FourZeros ) ); - - fltx4 B2 = AddSIMD( B1, B0 ); - did_hit = AndSIMD( did_hit, CmpLeSIMD( B2, Four_Ones ) ); - - if ( ! IsAnyNegative( did_hit ) ) - continue; - - // if the triangle is transparent - if ( tri->m_nFlags & FCACHETRI_TRANSPARENT ) - { - if ( pCallback ) - { - // assuming a triangle indexed as v0, v1, v2 - // the projected edge equations are set up such that the vert opposite the first - // equation is v2, and the vert opposite the second equation is v0 - // Therefore we pass them back in 1, 2, 0 order - // Also B2 is currently B1 + B0 and needs to be 1 - (B1+B0) in order to be a real - // barycentric coordinate. Compute that now and pass it to the callback - fltx4 b2 = SubSIMD( Four_Ones, B2 ); - if ( pCallback->VisitTriangle_ShouldContinue( *tri, rays, &did_hit, &B1, &b2, &B0, tnum ) ) - { - did_hit = Four_Zeros; - } - } - } - // now, set the hit_id and closest_hit fields for any enabled rays - fltx4 replicated_n = ReplicateIX4(tnum); - StoreAlignedSIMD((float *) rslt_out->HitIds, - OrSIMD(AndSIMD(replicated_n,did_hit), - AndNotSIMD(did_hit,LoadAlignedSIMD( - (float *) rslt_out->HitIds)))); - rslt_out->HitDistance=OrSIMD(AndSIMD(isect_t,did_hit), - AndNotSIMD(did_hit,rslt_out->HitDistance)); - - rslt_out->surface_normal.x=OrSIMD( - AndSIMD(N.x,did_hit), - AndNotSIMD(did_hit,rslt_out->surface_normal.x)); - rslt_out->surface_normal.y=OrSIMD( - AndSIMD(N.y,did_hit), - AndNotSIMD(did_hit,rslt_out->surface_normal.y)); - rslt_out->surface_normal.z=OrSIMD( - AndSIMD(N.z,did_hit), - AndNotSIMD(did_hit,rslt_out->surface_normal.z)); - - } - } while (--ntris); - // now, check if all rays have terminated - fltx4 raydone=CmpLeSIMD(TMax,rslt_out->HitDistance); - if (! IsAnyNegative(raydone)) - { - return; - } - } - - if (stack_ptr==&NodeQueue[MAX_NODE_STACK_LEN]) - { - return; - } - // pop stack! - CurNode=stack_ptr->node; - TMin=stack_ptr->TMin; - TMax=stack_ptr->TMax; - stack_ptr++; - } -} - - -int RayTracingEnvironment::MakeLeafNode(int first_tri, int last_tri) -{ - CacheOptimizedKDNode ret; - ret.Children=KDNODE_STATE_LEAF+(TriangleIndexList.Count()<<2); - ret.SetNumberOfTrianglesInLeafNode(1+(last_tri-first_tri)); - for(int tnum=first_tri;tnum<=last_tri;tnum++) - TriangleIndexList.AddToTail(tnum); - OptimizedKDTree.AddToTail(ret); - return OptimizedKDTree.Count()-1; -} - - -void RayTracingEnvironment::CalculateTriangleListBounds(int32 const *tris,int ntris, - Vector &minout, Vector &maxout) -{ - minout = Vector( 1.0e23, 1.0e23, 1.0e23); - maxout = Vector( -1.0e23, -1.0e23, -1.0e23); - for(int i=0; iMaxBound[axis]) || (trial_splitvalueMAX_TREE_DEPTH)) - { - // no benefit to splitting. just make this a leaf node - OptimizedKDTree[node_number].Children=KDNODE_STATE_LEAF+(TriangleIndexList.Count()<<2); - OptimizedKDTree[node_number].SetNumberOfTrianglesInLeafNode(ntris); -#ifdef DEBUG_RAYTRACE - OptimizedKDTree[node_number].vecMins = MinBound; - OptimizedKDTree[node_number].vecMaxs = MaxBound; -#endif - for(int t=0;t +#include +#include + +static bool SameSign(float a, float b) +{ + int32 aa=*((int *) &a); + int32 bb=*((int *) &b); + return ((aa^bb)&0x80000000)==0; +} + +int FourRays::CalculateDirectionSignMask(void) const +{ + // this code treats the floats as integers since all it cares about is the sign bit and + // floating point compares suck. + + int ret; + int ormask; + int andmask; + int32 const *treat_as_int=((int32 const *) (&direction)); + + ormask=andmask=*(treat_as_int++); + ormask|=*treat_as_int; + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + if (ormask>=0) + ret=0; + else + { + if (andmask<0) + ret=1; + else return -1; + } + ormask=andmask=*(treat_as_int++); + ormask|=*treat_as_int; + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + if (ormask<0) + { + if (andmask<0) + ret|=2; + else return -1; + } + ormask=andmask=*(treat_as_int++); + ormask|=*treat_as_int; + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + ormask|=*(treat_as_int); + andmask&=*(treat_as_int++); + if (ormask<0) + { + if (andmask<0) + ret|=4; + else return -1; + } + return ret; +} + + + + +void RayTracingEnvironment::MakeRoomForTriangles( int ntris ) +{ + //OptimizedTriangleList.EnsureCapacity( ntris ); + if (! (Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS)) + TriangleColors.EnsureCapacity( ntris ); +} + + +void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1, + const Vector &v2, const Vector &v3, + const Vector &color) +{ + AddTriangle( id, v1, v2, v3, color, 0, 0 ); +} + +void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1, + const Vector &v2, const Vector &v3, + const Vector &color, uint16 flags, int32 materialIndex) +{ + CacheOptimizedTriangle tmptri; + tmptri.m_Data.m_GeometryData.m_nTriangleID = id; + tmptri.Vertex( 0 ) = v1; + tmptri.Vertex( 1 ) = v2; + tmptri.Vertex( 2 ) = v3; + tmptri.m_Data.m_GeometryData.m_nFlags = flags; + OptimizedTriangleList.AddToTail(tmptri); + if (! ( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS) ) + TriangleColors.AddToTail(color); + if ( !( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_MATERIALS) ) + TriangleMaterials.AddToTail(materialIndex); +// printf("add triange from (%f %f %f),(%f %f %f),(%f %f %f) id %d\n", +// XYZ(v1),XYZ(v2),XYZ(v3),id); +} + +void RayTracingEnvironment::AddQuad( + int32 id, const Vector &v1, const Vector &v2, const Vector &v3, + const Vector &v4, // specify vertices in cw or ccw order + const Vector &color) +{ + AddTriangle(id,v1,v2,v3,color); + AddTriangle(id+1,v1,v3,v4,color); +} + + +void RayTracingEnvironment::AddAxisAlignedRectangularSolid(int id,Vector minc, Vector maxc, + const Vector &color) +{ + + // "far" face + AddQuad(id, + Vector(minc.x,maxc.y,maxc.z), + Vector(maxc.x,maxc.y,maxc.z),Vector(maxc.x,minc.y,maxc.z), + Vector(minc.x,minc.y,maxc.z),color); + // "near" face + AddQuad(id, + Vector(minc.x,maxc.y,minc.z), + Vector(maxc.x,maxc.y,minc.z),Vector(maxc.x,minc.y,minc.z), + Vector(minc.x,minc.y,minc.z),color); + + // "left" face + AddQuad(id, + Vector(minc.x,maxc.y,maxc.z), + Vector(minc.x,maxc.y,minc.z), + Vector(minc.x,minc.y,minc.z), + Vector(minc.x,minc.y,maxc.z),color); + // "right" face + AddQuad(id, + Vector(maxc.x,maxc.y,maxc.z), + Vector(maxc.x,maxc.y,minc.z), + Vector(maxc.x,minc.y,minc.z), + Vector(maxc.x,minc.y,maxc.z),color); + + // "top" face + AddQuad(id, + Vector(minc.x,maxc.y,maxc.z), + Vector(maxc.x,maxc.y,maxc.z), + Vector(maxc.x,maxc.y,minc.z), + Vector(minc.x,maxc.y,minc.z),color); + // "bot" face + AddQuad(id, + Vector(minc.x,minc.y,maxc.z), + Vector(maxc.x,minc.y,maxc.z), + Vector(maxc.x,minc.y,minc.z), + Vector(minc.x,minc.y,minc.z),color); +} + + + +static Vector GetEdgeEquation(Vector p1, Vector p2, int c1, int c2, Vector InsidePoint) +{ + float nx=p1[c2]-p2[c2]; + float ny=p2[c1]-p1[c1]; + float d=-(nx*p1[c1]+ny*p1[c2]); +// assert(fabs(nx*p1[c1]+ny*p1[c2]+d)<0.01); +// assert(fabs(nx*p2[c1]+ny*p2[c2]+d)<0.01); + + // use the convention that negative is "outside" + float trial_dist=InsidePoint[c1]*nx+InsidePoint[c2]*ny+d; + if (trial_dist<0) + { + nx = -nx; + ny = -ny; + d = -d; + trial_dist = -trial_dist; + } + nx /= trial_dist; // scale so that it will be =1.0 at the oppositve vertex + ny /= trial_dist; + d /= trial_dist; + + return Vector(nx,ny,d); +} + +void CacheOptimizedTriangle::ChangeIntoIntersectionFormat(void) +{ + // lose the vertices and use edge equations instead + + // grab the whole original triangle to we don't overwrite it + TriGeometryData_t srcTri = m_Data.m_GeometryData; + + m_Data.m_IntersectData.m_nFlags = srcTri.m_nFlags; + m_Data.m_IntersectData.m_nTriangleID = srcTri.m_nTriangleID; + + Vector p1 = srcTri.Vertex( 0 ); + Vector p2 = srcTri.Vertex( 1 ); + Vector p3 = srcTri.Vertex( 2 ); + + Vector e1 = p2 - p1; + Vector e2 = p3 - p1; + + Vector N = e1.Cross( e2 ); + N.NormalizeInPlace(); + // now, determine which axis to drop + int drop_axis = 0; + for(int c=1 ; c<3 ; c++) + if ( fabs(N[c]) > fabs( N[drop_axis] ) ) + drop_axis = c; + + m_Data.m_IntersectData.m_flD = N.Dot( p1 ); + m_Data.m_IntersectData.m_flNx = N.x; + m_Data.m_IntersectData.m_flNy = N.y; + m_Data.m_IntersectData.m_flNz = N.z; + + // decide which axes to keep + int nCoordSelect0 = ( drop_axis + 1 ) % 3; + int nCoordSelect1 = ( drop_axis + 2 ) % 3; + + m_Data.m_IntersectData.m_nCoordSelect0 = nCoordSelect0; + m_Data.m_IntersectData.m_nCoordSelect1 = nCoordSelect1; + + + Vector edge1 = GetEdgeEquation( p1, p2, nCoordSelect0, nCoordSelect1, p3 ); + m_Data.m_IntersectData.m_ProjectedEdgeEquations[0] = edge1.x; + m_Data.m_IntersectData.m_ProjectedEdgeEquations[1] = edge1.y; + m_Data.m_IntersectData.m_ProjectedEdgeEquations[2] = edge1.z; + + Vector edge2 = GetEdgeEquation( p2, p3, nCoordSelect0, nCoordSelect1, p1 ); + m_Data.m_IntersectData.m_ProjectedEdgeEquations[3] = edge2.x; + m_Data.m_IntersectData.m_ProjectedEdgeEquations[4] = edge2.y; + m_Data.m_IntersectData.m_ProjectedEdgeEquations[5] = edge2.z; + + +} + +int n_intersection_calculations=0; + +int CacheOptimizedTriangle::ClassifyAgainstAxisSplit(int split_plane, float split_value) +{ + // classify a triangle against an axis-aligned plane + float minc=Vertex(0)[split_plane]; + float maxc=minc; + for(int v=1;v<3;v++) + { + minc=min(minc,Vertex(v)[split_plane]); + maxc=max(maxc,Vertex(v)[split_plane]); + } + + if (minc>=split_value) + return PLANECHECK_POSITIVE; + if (maxc<=split_value) + return PLANECHECK_NEGATIVE; + if (minc==maxc) + return PLANECHECK_POSITIVE; + return PLANECHECK_STRADDLING; +} + +#define MAILBOX_HASH_SIZE 256 +#define MAX_TREE_DEPTH 21 +#define MAX_NODE_STACK_LEN (40*MAX_TREE_DEPTH) + +struct NodeToVisit { + CacheOptimizedKDNode const *node; + fltx4 TMin; + fltx4 TMax; +}; + + +static fltx4 FourEpsilons={1.0e-10,1.0e-10,1.0e-10,1.0e-10}; +static fltx4 FourZeros={1.0e-10,1.0e-10,1.0e-10,1.0e-10}; +static fltx4 FourNegativeEpsilons={-1.0e-10,-1.0e-10,-1.0e-10,-1.0e-10}; + +static float BoxSurfaceArea(Vector const &boxmin, Vector const &boxmax) +{ + Vector boxdim=boxmax-boxmin; + return 2.0*((boxdim[0]*boxdim[2])+(boxdim[0]*boxdim[1])+(boxdim[1]*boxdim[2])); +} + +void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, + RayTracingResult *rslt_out, + int32 skip_id, ITransparentTriangleCallback *pCallback) +{ + int msk=rays.CalculateDirectionSignMask(); + if (msk!=-1) + Trace4Rays(rays,TMin,TMax,msk,rslt_out,skip_id, pCallback); + else + { + // sucky case - can't trace 4 rays at once. in the worst case, need to trace all 4 + // separately, but usually we will still get 2x, Since our tracer only does 4 at a + // time, we will have to cover up the undesired rays with the desired ray + + //!! speed!! there is room for some sse-ization here + FourRays tmprays; + tmprays.origin=rays.origin; + + uint8 need_trace[4]={1,1,1,1}; + for(int try_trace=0;try_trace<4;try_trace++) + { + if (need_trace[try_trace]) + { + need_trace[try_trace]=2; // going to trace it + // replicate the ray being traced into all 4 rays + tmprays.direction.x=ReplicateX4(rays.direction.X(try_trace)); + tmprays.direction.y=ReplicateX4(rays.direction.Y(try_trace)); + tmprays.direction.z=ReplicateX4(rays.direction.Z(try_trace)); + // now, see if any of the other remaining rays can be handled at the same time. + for(int try2=try_trace+1;try2<4;try2++) + if (need_trace[try2]) + { + if ( + SameSign(rays.direction.X(try2), + rays.direction.X(try_trace)) && + SameSign(rays.direction.Y(try2), + rays.direction.Y(try_trace)) && + SameSign(rays.direction.Z(try2), + rays.direction.Z(try_trace))) + { + need_trace[try2]=2; + tmprays.direction.X(try2) = rays.direction.X(try2); + tmprays.direction.Y(try2) = rays.direction.Y(try2); + tmprays.direction.Z(try2) = rays.direction.Z(try2); + } + } + // ok, now trace between 1 and 3 rays, and output the results + RayTracingResult tmpresults; + msk=tmprays.CalculateDirectionSignMask(); + assert(msk!=-1); + Trace4Rays(tmprays,TMin,TMax,msk,&tmpresults,skip_id, pCallback); + // now, move results to proper place + for(int i=0;i<4;i++) + if (need_trace[i]==2) + { + need_trace[i]=0; + rslt_out->HitIds[i]=tmpresults.HitIds[i]; + SubFloat(rslt_out->HitDistance, i) = SubFloat(tmpresults.HitDistance, i); + rslt_out->surface_normal.X(i) = tmpresults.surface_normal.X(i); + rslt_out->surface_normal.Y(i) = tmpresults.surface_normal.Y(i); + rslt_out->surface_normal.Z(i) = tmpresults.surface_normal.Z(i); + } + + } + } + } +} + + +void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, + int DirectionSignMask, RayTracingResult *rslt_out, + int32 skip_id, ITransparentTriangleCallback *pCallback) +{ + rays.Check(); + + memset(rslt_out->HitIds,0xff,sizeof(rslt_out->HitIds)); + + rslt_out->HitDistance=ReplicateX4(1.0e23); + + rslt_out->surface_normal.DuplicateVector(Vector(0.,0.,0.)); + FourVectors OneOverRayDir=rays.direction; + OneOverRayDir.MakeReciprocalSaturate(); + + // now, clip rays against bounding box + for(int c=0;c<3;c++) + { + fltx4 isect_min_t= + MulSIMD(SubSIMD(ReplicateX4(m_MinBound[c]),rays.origin[c]),OneOverRayDir[c]); + fltx4 isect_max_t= + MulSIMD(SubSIMD(ReplicateX4(m_MaxBound[c]),rays.origin[c]),OneOverRayDir[c]); + TMin=MaxSIMD(TMin,MinSIMD(isect_min_t,isect_max_t)); + TMax=MinSIMD(TMax,MaxSIMD(isect_min_t,isect_max_t)); + } + fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active + if (! IsAnyNegative(active) ) + return; // missed bounding box + + int32 mailboxids[MAILBOX_HASH_SIZE]; // used to avoid redundant triangle tests + memset(mailboxids,0xff,sizeof(mailboxids)); // !!speed!! keep around? + + int front_idx[3],back_idx[3]; // based on ray direction, whether to + // visit left or right node first + + if (DirectionSignMask & 1) + { + back_idx[0]=0; + front_idx[0]=1; + } + else + { + back_idx[0]=1; + front_idx[0]=0; + } + if (DirectionSignMask & 2) + { + back_idx[1]=0; + front_idx[1]=1; + } + else + { + back_idx[1]=1; + front_idx[1]=0; + } + if (DirectionSignMask & 4) + { + back_idx[2]=0; + front_idx[2]=1; + } + else + { + back_idx[2]=1; + front_idx[2]=0; + } + + NodeToVisit NodeQueue[MAX_NODE_STACK_LEN]; + CacheOptimizedKDNode const *CurNode=&(OptimizedKDTree[0]); + NodeToVisit *stack_ptr=&NodeQueue[MAX_NODE_STACK_LEN]; + while(1) + { + while (CurNode->NodeType() != KDNODE_STATE_LEAF) // traverse until next leaf + { + int split_plane_number=CurNode->NodeType(); + CacheOptimizedKDNode const *FrontChild=&(OptimizedKDTree[CurNode->LeftChild()]); + + fltx4 dist_to_sep_plane= // dist=(split-org)/dir + MulSIMD( + SubSIMD(ReplicateX4(CurNode->SplittingPlaneValue), + rays.origin[split_plane_number]),OneOverRayDir[split_plane_number]); + fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active + + // now, decide how to traverse children. can either do front,back, or do front and push + // back. + fltx4 hits_front=AndSIMD(active,CmpGeSIMD(dist_to_sep_plane,TMin)); + if (! IsAnyNegative(hits_front)) + { + // missed the front. only traverse back + //printf("only visit back %d\n",CurNode->LeftChild()+back_idx[split_plane_number]); + CurNode=FrontChild+back_idx[split_plane_number]; + TMin=MaxSIMD(TMin, dist_to_sep_plane); + + } + else + { + fltx4 hits_back=AndSIMD(active,CmpLeSIMD(dist_to_sep_plane,TMax)); + if (! IsAnyNegative(hits_back) ) + { + // missed the back - only need to traverse front node + //printf("only visit front %d\n",CurNode->LeftChild()+front_idx[split_plane_number]); + CurNode=FrontChild+front_idx[split_plane_number]; + TMax=MinSIMD(TMax, dist_to_sep_plane); + } + else + { + // at least some rays hit both nodes. + // must push far, traverse near + //printf("visit %d,%d\n",CurNode->LeftChild()+front_idx[split_plane_number], + // CurNode->LeftChild()+back_idx[split_plane_number]); + assert(stack_ptr>NodeQueue); + --stack_ptr; + stack_ptr->node=FrontChild+back_idx[split_plane_number]; + stack_ptr->TMin=MaxSIMD(TMin,dist_to_sep_plane); + stack_ptr->TMax=TMax; + CurNode=FrontChild+front_idx[split_plane_number]; + TMax=MinSIMD(TMax,dist_to_sep_plane); + } + } + } + // hit a leaf! must do intersection check + int ntris=CurNode->NumberOfTrianglesInLeaf(); + if (ntris) + { + int32 const *tlist=&(TriangleIndexList[CurNode->TriangleIndexStart()]); + do + { + int tnum=*(tlist++); + //printf("try tri %d\n",tnum); + // check mailbox + int mbox_slot=tnum & (MAILBOX_HASH_SIZE-1); + TriIntersectData_t const *tri = &( OptimizedTriangleList[tnum].m_Data.m_IntersectData ); + if ( ( mailboxids[mbox_slot] != tnum ) && ( tri->m_nTriangleID != skip_id ) ) + { + n_intersection_calculations++; + mailboxids[mbox_slot] = tnum; + // compute plane intersection + + + FourVectors N; + N.x = ReplicateX4( tri->m_flNx ); + N.y = ReplicateX4( tri->m_flNy ); + N.z = ReplicateX4( tri->m_flNz ); + + fltx4 DDotN = rays.direction * N; + // mask off zero or near zero (ray parallel to surface) + fltx4 did_hit = OrSIMD( CmpGtSIMD( DDotN,FourEpsilons ), + CmpLtSIMD( DDotN, FourNegativeEpsilons ) ); + + fltx4 numerator=SubSIMD( ReplicateX4( tri->m_flD ), rays.origin * N ); + + fltx4 isect_t=DivSIMD( numerator,DDotN ); + // now, we have the distance to the plane. lets update our mask + did_hit = AndSIMD( did_hit, CmpGtSIMD( isect_t, FourZeros ) ); + //did_hit=AndSIMD(did_hit,CmpLtSIMD(isect_t,TMax)); + did_hit = AndSIMD( did_hit, CmpLtSIMD( isect_t, rslt_out->HitDistance ) ); + + if ( ! IsAnyNegative( did_hit ) ) + continue; + + // now, check 3 edges + fltx4 hitc1 = AddSIMD( rays.origin[tri->m_nCoordSelect0], + MulSIMD( isect_t, rays.direction[ tri->m_nCoordSelect0] ) ); + fltx4 hitc2 = AddSIMD( rays.origin[tri->m_nCoordSelect1], + MulSIMD( isect_t, rays.direction[tri->m_nCoordSelect1] ) ); + + // do barycentric coordinate check + fltx4 B0 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[0] ), hitc1 ); + + B0 = AddSIMD( + B0, + MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[1] ), hitc2 ) ); + B0 = AddSIMD( + B0, ReplicateX4( tri->m_ProjectedEdgeEquations[2] ) ); + + did_hit = AndSIMD( did_hit, CmpGeSIMD( B0, FourZeros ) ); + + fltx4 B1 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[3] ), hitc1 ); + B1 = AddSIMD( + B1, + MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[4]), hitc2 ) ); + + B1 = AddSIMD( + B1, ReplicateX4( tri->m_ProjectedEdgeEquations[5] ) ); + + did_hit = AndSIMD( did_hit, CmpGeSIMD( B1, FourZeros ) ); + + fltx4 B2 = AddSIMD( B1, B0 ); + did_hit = AndSIMD( did_hit, CmpLeSIMD( B2, Four_Ones ) ); + + if ( ! IsAnyNegative( did_hit ) ) + continue; + + // if the triangle is transparent + if ( tri->m_nFlags & FCACHETRI_TRANSPARENT ) + { + if ( pCallback ) + { + // assuming a triangle indexed as v0, v1, v2 + // the projected edge equations are set up such that the vert opposite the first + // equation is v2, and the vert opposite the second equation is v0 + // Therefore we pass them back in 1, 2, 0 order + // Also B2 is currently B1 + B0 and needs to be 1 - (B1+B0) in order to be a real + // barycentric coordinate. Compute that now and pass it to the callback + fltx4 b2 = SubSIMD( Four_Ones, B2 ); + if ( pCallback->VisitTriangle_ShouldContinue( *tri, rays, &did_hit, &B1, &b2, &B0, tnum ) ) + { + did_hit = Four_Zeros; + } + } + } + // now, set the hit_id and closest_hit fields for any enabled rays + fltx4 replicated_n = ReplicateIX4(tnum); + StoreAlignedSIMD((float *) rslt_out->HitIds, + OrSIMD(AndSIMD(replicated_n,did_hit), + AndNotSIMD(did_hit,LoadAlignedSIMD( + (float *) rslt_out->HitIds)))); + rslt_out->HitDistance=OrSIMD(AndSIMD(isect_t,did_hit), + AndNotSIMD(did_hit,rslt_out->HitDistance)); + + rslt_out->surface_normal.x=OrSIMD( + AndSIMD(N.x,did_hit), + AndNotSIMD(did_hit,rslt_out->surface_normal.x)); + rslt_out->surface_normal.y=OrSIMD( + AndSIMD(N.y,did_hit), + AndNotSIMD(did_hit,rslt_out->surface_normal.y)); + rslt_out->surface_normal.z=OrSIMD( + AndSIMD(N.z,did_hit), + AndNotSIMD(did_hit,rslt_out->surface_normal.z)); + + } + } while (--ntris); + // now, check if all rays have terminated + fltx4 raydone=CmpLeSIMD(TMax,rslt_out->HitDistance); + if (! IsAnyNegative(raydone)) + { + return; + } + } + + if (stack_ptr==&NodeQueue[MAX_NODE_STACK_LEN]) + { + return; + } + // pop stack! + CurNode=stack_ptr->node; + TMin=stack_ptr->TMin; + TMax=stack_ptr->TMax; + stack_ptr++; + } +} + + +int RayTracingEnvironment::MakeLeafNode(int first_tri, int last_tri) +{ + CacheOptimizedKDNode ret; + ret.Children=KDNODE_STATE_LEAF+(TriangleIndexList.Count()<<2); + ret.SetNumberOfTrianglesInLeafNode(1+(last_tri-first_tri)); + for(int tnum=first_tri;tnum<=last_tri;tnum++) + TriangleIndexList.AddToTail(tnum); + OptimizedKDTree.AddToTail(ret); + return OptimizedKDTree.Count()-1; +} + + +void RayTracingEnvironment::CalculateTriangleListBounds(int32 const *tris,int ntris, + Vector &minout, Vector &maxout) +{ + minout = Vector( 1.0e23, 1.0e23, 1.0e23); + maxout = Vector( -1.0e23, -1.0e23, -1.0e23); + for(int i=0; iMaxBound[axis]) || (trial_splitvalueMAX_TREE_DEPTH)) + { + // no benefit to splitting. just make this a leaf node + OptimizedKDTree[node_number].Children=KDNODE_STATE_LEAF+(TriangleIndexList.Count()<<2); + OptimizedKDTree[node_number].SetNumberOfTrianglesInLeafNode(ntris); +#ifdef DEBUG_RAYTRACE + OptimizedKDTree[node_number].vecMins = MinBound; + OptimizedKDTree[node_number].vecMaxs = MaxBound; +#endif + for(int t=0;t