void XenoAudio::SetChannelPos(int i) { if (!audioOn) return; if (sounds[i].pos.IsZero()) { Mix_SetPosition(i, 0, 0); } else { Vector3F delta = sounds[i].pos - listenerPos; float len = delta.Length(); float df = len / MAX_DISTANCE; int d = LRintf(df*255.0f); d = Clamp(d, 0, 255); if (delta.LengthSquared() > 0.001f) { delta.Normalize(); } else { delta.Set(0, 0, -1); } static const Vector3F UP = { 0, 1, 0 }; Vector3F listenerRight = CrossProduct(listenerDir, UP); float dotFront = DotProduct(delta, listenerDir); float dotRight = DotProduct(delta, listenerRight); // 0 is north, 90 is east, etc. WTF. float rad = atan2(dotRight, dotFront); float deg = rad * 180.0f / PI; int degi = int(deg); if (degi < 0) degi += 360; int result = Mix_SetPosition(i, degi, d); GLASSERT(result != 0); (void)result; } }
void FireWidget::Place( BattleScene* battle, const Unit* shooterUnit, const Unit* targetUnit, const grinliz::Vector2I& targetPos ) { GLASSERT( targetUnit || targetPos.x >= 0 ); const Model* targetModel = 0; const Model* targetWeapon = 0; Vector3F target; BulletTarget bulletTarget; if ( targetPos.x >= 0 ) { target.Set( (float)targetPos.x+0.5f, 1.0f, (float)targetPos.y+0.5f ); } else { targetModel = battle->GetModel( targetUnit ); targetWeapon = battle->GetWeaponModel( targetUnit ); GLASSERT( targetModel ); targetModel->CalcTarget( &target ); targetModel->CalcTargetSize( &bulletTarget.width, &bulletTarget.height ); } Vector3F distVector = target - shooterUnit->Pos(); bulletTarget.distance = distVector.Length(); const Inventory* inventory = shooterUnit->GetInventory(); const WeaponItemDef* wid = shooterUnit->GetWeaponDef(); float snapTU=0, autoTU=0, altTU=0; if ( wid ) { shooterUnit->AllFireTimeUnits( &snapTU, &autoTU, &altTU ); } int numButtons = 0; if ( wid ) { for( int i=0; i<WeaponItemDef::MAX_MODE && wid->HasWeapon(i); ++i ) { float fraction, anyFraction, dptu, tu; shooterUnit->FireStatistics( i, bulletTarget, &fraction, &anyFraction, &tu, &dptu ); int nRounds = inventory->CalcClipRoundsTotal( wid->GetClipItemDef( i) ); anyFraction = Clamp( anyFraction, 0.0f, 0.95f ); char buffer0[32]; char buffer1[32]; SNPrintf( buffer0, 32, "%s %d%%", wid->weapon[i]->desc, (int)LRintf( anyFraction*100.0f ) ); SNPrintf( buffer1, 32, "%d/%d", wid->RoundsNeeded( i ), nRounds ); fireButton[i].SetEnabled( true ); fireButton[i].SetVisible( true ); fireButton[i].SetText( buffer0 ); fireButton[i].SetText2( buffer1 ); if ( shooterUnit->CanFire( i )) { // Reflect the TU left. float tuAfter = shooterUnit->TU() - tu; int tuIndicator = ICON_ORANGE_WALK_MARK; if ( tuAfter >= autoTU ) { tuIndicator = ICON_GREEN_WALK_MARK; } else if ( tuAfter >= snapTU ) { tuIndicator = ICON_YELLOW_WALK_MARK; } else if ( tuAfter < snapTU ) { tuIndicator = ICON_ORANGE_WALK_MARK; } fireButton[i].SetDeco( Game::CalcIconAtom( tuIndicator, true ), Game::CalcIconAtom( tuIndicator, false ) ); } else { fireButton[i].SetEnabled( false ); RenderAtom nullAtom; fireButton[i].SetDeco( nullAtom, nullAtom ); } numButtons = i+1; } } else { fireButton[0].SetVisible( true ); fireButton[0].SetEnabled( false ); fireButton[0].SetText( "[none]" ); fireButton[0].SetText2( "" ); RenderAtom nullAtom; fireButton[0].SetDeco( nullAtom, nullAtom ); numButtons = 1; } for( int i=numButtons; i<WeaponItemDef::MAX_MODE; ++i ) { fireButton[i].SetVisible( false ); } Vector2F view, ui; const Screenport& port = battle->GetEngine()->GetScreenport(); port.WorldToView( target, &view ); port.ViewToUI( view, &ui ); const int DX = 10; // Make sure it fits on the screen. UIRenderer::LayoutListOnScreen( fireButton, numButtons, sizeof(fireButton[0]), ui.x+DX, ui.y, FIRE_BUTTON_SPACING, port ); ParticleSystem* ps = ParticleSystem::Instance(); Game* game = battle->GetGame(); Color4F color0 = Convert_4U8_4F( game->MainPaletteColor( 0, 5 )); Color4F color1 = Convert_4U8_4F( game->MainPaletteColor( 0, 4 )); const Model* model = battle->GetModel( shooterUnit ); if ( model ) { Vector3F trigger; Vector2I t2 = { (int)target.x, (int)target.z }; float fireRotation = shooterUnit->AngleBetween( t2, false ); model->CalcTrigger( &trigger, &fireRotation ); Ray ray = { trigger, target-trigger }; const Model* ignore[] = { model, battle->GetWeaponModel( shooterUnit ), 0 }; Vector3F intersection; Model* m = battle->GetEngine()->IntersectModel( ray, TEST_TRI, 0, 0, ignore, &intersection ); if ( !m || m == targetModel || m == targetWeapon ) { ps->EmitBeam( trigger, target, color0 ); } else { ps->EmitBeam( trigger, intersection, color0 ); ps->EmitBeam( intersection, target, color1 ); } } }
int grinliz::IntersectionRayAABB( const Ray& ray, const Rectangle3F& aabb, Rectangle3F* result ) { bool hasStart = false; bool hasEnd = false; Vector3F start, end; GLASSERT( Equal( ray.direction.Length(), 1.0f, 0.001f ) ); if ( aabb.Contains( ray.origin ) ) { hasStart = true; start = ray.origin; } // Check for an intersection with each face. If t>0 and it is a // positive dot then it is an 'end'. If t>0 and it is a negative // dot, then it is a 'start'. Vector3F at; float t; for( int k=0; k<=1; ++k ) { Vector3F originToPlane; if ( k == 0 ) { // min originToPlane.Set( aabb.min.x - ray.origin.x, aabb.min.y - ray.origin.y, aabb.min.z - ray.origin.z ); } else { // max originToPlane.Set( aabb.max.x - ray.origin.x, aabb.max.y - ray.origin.y, aabb.max.z - ray.origin.z ); } for ( int i=0; i<3 && !(hasEnd && hasStart); ++i ) { int i1 = (i + 1)%3; int i2 = (i + 2)%3; Vector3F planeNormal = { 0.0f, 0.0f, 0.0f }; planeNormal.X(i) = ( k == 0 ) ? -1.0f : 1.0f; float dot = DotProduct( originToPlane, planeNormal ); int hit = IntersectRayAAPlane( ray.origin, ray.direction, i, (k==0) ? aabb.min.X(i) : aabb.max.X(i), &at, &t ); if ( hit == INTERSECT && t > 0.0f && InRange( at.X(i1), aabb.min.X(i1), aabb.max.X(i1) ) && InRange( at.X(i2), aabb.min.X(i2), aabb.max.X(i2) ) ) { // We have hit a face of the AABB if ( dot > 0.0f ) { GLASSERT( !hasEnd ); hasEnd = true; if ( t > ray.length ) end = ray.origin + ray.direction*t; else end = at; } else if ( dot < 0.0f ) { GLASSERT( !hasStart ); hasStart = true; if ( t > ray.length ) { return REJECT; // the start never gets to the AABB } start = at; } } } } if( !hasStart || !hasEnd ) { return REJECT; } for( int i=0; i<3; ++i ) { result->min.X(i) = Min( start.X(i), end.X(i) ); result->max.X(i) = Max( start.X(i), end.X(i) ); } return INTERSECT; }
void ProcessAC3D( ACObject* ob, ModelBuilder* builder, const Matrix4& parent, const char* groupFilter ) { Matrix4 m, matrix; m.SetTranslation( ob->loc.x, ob->loc.y, ob->loc.z ); MultMatrix4( parent, m, &matrix ); builder->SetMatrix( matrix ); const char* texname = ob->textureName; if ( texname ) { // Textures just use names, not paths. if ( strrchr( texname, '/' )) { texname = strrchr( texname, '/' )+1; } } builder->SetTexture( texname ? texname : "" ); builder->PushObjectName( ob->name ); Vertex vertex[16]; memset(vertex, 0, sizeof(Vertex)* 16); for( int i = 0; i < ob->num_surf; ++i ) { ACSurface *surf = &ob->surfaces[i]; Vector3F normal = { surf->normal.x, surf->normal.y, surf->normal.z }; if ( normal.Length() == 0.f ) { GLASSERT( 0 ); normal.Set( 0.0f, 1.0f, 0.0f ); } int st = surf->flags & 0xf; if ( st == SURFACE_TYPE_POLYGON && surf->num_vertref<16 ) // hopefully convex and simple { for( int j=0; j<surf->num_vertref; ++j ) { ACVertex *v = &ob->vertices[surf->vertref[j]]; float tx = 0, ty = 0; if (ob->texture ) { float tu = surf->uvs[j].u; float tv = surf->uvs[j].v; tx = ob->texture_offset_x + tu * ob->texture_repeat_x; ty = ob->texture_offset_y + tv * ob->texture_repeat_y; } GLASSERT( j<16 ); vertex[j].pos.Set( v->x, v->y, v->z ); vertex[j].normal = normal; vertex[j].tex.Set( tx, ty ); } int numTri = surf->num_vertref-2; for( int j=0; j<numTri; ++j ) { builder->AddTri( vertex[0], vertex[j+1], vertex[j+2] ); } } else { GLOUTPUT(( "Input polygon or type not supported.\n" )); GLASSERT( 0 ); } } for ( int n = 0; n < ob->num_kids; n++) { if ( groupFilter ) { // Sleazy trick to only look at the top node. Set to node // after we find the top. if ( StrEqual( ob->kids[n]->name, groupFilter ) ) ProcessAC3D(ob->kids[n], builder, matrix, 0 ); } else { ProcessAC3D(ob->kids[n], builder, matrix, 0 ); } } builder->PopObjectName(); }