/** * Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.ctdp.rfdynhud.widgets.etv2010.telemetry; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.geom.Rectangle2D; import java.io.IOException; import net.ctdp.rfdynhud.gamedata.LiveGameData; import net.ctdp.rfdynhud.gamedata.ProfileInfo.SpeedUnits; import net.ctdp.rfdynhud.gamedata.TelemetryData; import net.ctdp.rfdynhud.gamedata.VehicleScoringInfo; import net.ctdp.rfdynhud.properties.BackgroundProperty; import net.ctdp.rfdynhud.properties.BooleanProperty; import net.ctdp.rfdynhud.properties.ColorProperty; import net.ctdp.rfdynhud.properties.FontProperty; import net.ctdp.rfdynhud.properties.ImageProperty; import net.ctdp.rfdynhud.properties.IntProperty; import net.ctdp.rfdynhud.properties.PropertiesContainer; import net.ctdp.rfdynhud.properties.PropertyLoader; import net.ctdp.rfdynhud.render.ImageTemplate; import net.ctdp.rfdynhud.render.Texture2DCanvas; import net.ctdp.rfdynhud.render.TextureImage2D; import net.ctdp.rfdynhud.render.TransformableTexture; import net.ctdp.rfdynhud.util.PropertyWriter; import net.ctdp.rfdynhud.util.SubTextureCollector; import net.ctdp.rfdynhud.valuemanagers.Clock; import net.ctdp.rfdynhud.widgets.base.revneedlemeter.AbstractRevNeedleMeterWidget; import net.ctdp.rfdynhud.widgets.base.widget.Widget; import net.ctdp.rfdynhud.widgets.etv2010._util.ETVWidgetSet; /** * This {@link Widget} attempts to imitate the 2010er TV overlay for F1 telemetry * (revs, velocity and throttle/brake). * * @author Marvin Froehlich (CTDP) */ public class ETVTelemetryWidget extends AbstractRevNeedleMeterWidget { public static final int MAX_VELOCITY_LOCAL_Z_INDEX = NEEDLE_LOCAL_Z_INDEX / 2; public static final int CONTROLS_LOCAL_Z_INDEX = MAX_VELOCITY_LOCAL_Z_INDEX + 1; private final BooleanProperty displayVelocityNumbers = new BooleanProperty( "displayVelocityNumbers", "displayNumbers", true ); private final FontProperty velocityNumberFont = new FontProperty( "velocityNumberFont", "font", ETVWidgetSet.ETV_VELOCITY_FONT.getKey(), false ); private final ColorProperty velocityNumberFontColor = new ColorProperty( "velocityNumberFontColor", "color", ETVWidgetSet.ETV_CAPTION_FONT_COLOR.getKey(), false ); private final IntProperty velocityNumber1PosX = new IntProperty( "velocityNumber1PosX", "pos1X", 270 ); private final IntProperty velocityNumber1PosY = new IntProperty( "velocityNumber1PosY", "pos1Y", 620 ); private final IntProperty velocityNumber2PosX = new IntProperty( "velocityNumber2PosX", "pos2X", 100 ); private final IntProperty velocityNumber3PosX = new IntProperty( "velocityNumber3PosX", "pos3X", 100 ); private final IntProperty velocityNumber4PosX = new IntProperty( "velocityNumber4PosX", "pos4X", 270 ); private final IntProperty velocityNumber4PosY = new IntProperty( "velocityNumber4PosY", "pos4Y", 50 ); private final IntProperty velocityUnitsPosX = new IntProperty( "velocityUnitsPosX", "unitsPosX", 300 ); private final IntProperty velocityUnitsPosY = new IntProperty( "velocityUnitsPosY", "unitsPosY", 660 ); private final IntProperty maxVelocity = new IntProperty( "maxVelocity", 340, 1, 1000 ); private final IntProperty velocity2 = new IntProperty( "velocity2", 110, 1, 1000 ); private final IntProperty velocity3 = new IntProperty( "velocity3", 220, 1, 1000 ); private final ImageProperty maxVelocityOverlay = new ImageProperty( "maxVelocityOverlay", "image", "etv2010/telemetry/max_velocity.png" ); private TransformableTexture maxVelocityTexture = null; private final IntProperty maxVelocityLeftOffset = new IntProperty( "maxVelocityLeftOffset", "leftOffset", 15 ); private final IntProperty maxVelocityTopOffset = new IntProperty( "maxVelocityTopOffset", "topOffset", 80 ); private boolean throttleDirty = true; private final ImageProperty throttleImage = new ImageProperty( "throttleImage", null, "etv2010/telemetry/throttle.png", false, false ) { @Override protected void onValueChanged( String oldValue, String newValue ) { throttleDirty = true; } }; private TransformableTexture texThrottle1 = null; private TransformableTexture texThrottle2 = null; private boolean brakeDirty = true; private final ImageProperty brakeImage = new ImageProperty( "brakeImage", null, "etv2010/telemetry/brake.png", false, false ) { @Override protected void onValueChanged( String oldValue, String newValue ) { brakeDirty = true; } }; private TransformableTexture texBrake1 = null; private TransformableTexture texBrake2 = null; private final IntProperty controlsPosX = new IntProperty( "controlsPosX", 600 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { throttleDirty = true; brakeDirty = true; } }; private final IntProperty controlsPosY = new IntProperty( "controlsPosY", 400 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { throttleDirty = true; brakeDirty = true; } }; private final IntProperty controlsWidth = new IntProperty( "controlsWidth", 350 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { throttleDirty = true; brakeDirty = true; } }; private final IntProperty controlsHeight = new IntProperty( "controlsHeight", 80 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { throttleDirty = true; brakeDirty = true; } }; private final IntProperty controlsGap = new IntProperty( "controlsGap", "gap", 2 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { throttleDirty = true; brakeDirty = true; } }; private final FontProperty controlsLabelFont = new FontProperty( "controlsLabelFont", "labelFont", ETVWidgetSet.ETV_CONTROLS_LABEL_FONT.getKey() ) { @Override protected void onValueChanged( String oldValue, String newValue ) { throttleDirty = true; brakeDirty = true; } }; private final ColorProperty controlsLabelFontColor = new ColorProperty( "controlsLabelFontColor", "labelFontColor", "#FFFFFF" ) { @Override protected void onValueChanged( String oldValue, String newValue ) { throttleDirty = true; brakeDirty = true; } }; private final IntProperty controlsLabelOffset = new IntProperty( "controlsLabelOffset", "labelOffset", 15 ) { @Override protected void onValueChanged( Integer oldValue, int newValue ) { brakeDirty = true; throttleDirty = true; } }; public ETVTelemetryWidget() { super( ETVWidgetSet.INSTANCE, ETVWidgetSet.WIDGET_PACKAGE, 19.6915f, 21.75f ); minValue.setFloatValue( 4000 ); displayMarkers.setBooleanValue( false ); markersInnerRadius.setIntValue( 170 ); markersLength.setIntValue( 50 ); markersOnCircle.setBooleanValue( true ); firstMarkerNumberOffset.setFloatValue( +5 ); lastMarkerNumberOffset.setFloatValue( -5 ); markersBigStep.setIntValue( 2000 ); markersSmallStep.setIntValue( 1000 ); markersFont.setFont( ETVWidgetSet.ETV_REV_MARKERS_FONT.getKey() ); markersFontColor.setColor( "#FFFFFF" ); markersFontDropShadowColor.setColor( "#000000" ); markerNumbersCentered.setBooleanValue( true ); needlePivotBottomOffset.setIntValue( -171 ); peakNeedlePivotBottomOffset.setIntValue( -226 ); needleMountX.setIntValue( 506 ); needleMountY.setIntValue( 350 ); needleRotationForMinValue.setFloatValue( -178 ); needleRotationForMaxValue.setFloatValue( +72.5f ); displayValue.setBooleanValue( false ); displayGear.setBooleanValue( true ); gearPosX.setIntValue( 510 ); gearPosY.setIntValue( 345 ); gearFont.setFont( ETVWidgetSet.ETV_GEAR_FONT.getKey() ); gearFontColor.setColor( "#D9E0EB" ); } /** * {@inheritDoc} */ @Override public void prepareForMenuItem() { super.prepareForMenuItem(); controlsLabelFont.setFont( "Dialog", Font.PLAIN, 4, false, true ); velocityNumberFont.setFont( "Dialog", Font.PLAIN, 4, false, true ); } @Override protected void saveDigiValueProperties( PropertyWriter writer ) throws IOException { // We don't need these here! } @Override protected void getDigiValueProperties( PropertiesContainer propsCont, boolean forceAll ) { // We don't need these here! } /** * {@inheritDoc} */ @Override public void saveProperties( PropertyWriter writer ) throws IOException { super.saveProperties( writer ); writer.writeProperty( displayVelocityNumbers, "Display nicely positioned velocity numbers?" ); writer.writeProperty( velocityNumberFont, "The font for the velocity numbers." ); writer.writeProperty( velocityNumberFontColor, "The font color for the velocity numbers." ); writer.writeProperty( velocityNumber1PosX, "The x-position in background texture space for the first velocity number." ); writer.writeProperty( velocityNumber1PosY, "The y-position in background texture space for the first velocity number." ); writer.writeProperty( velocity2, "The second velocity." ); writer.writeProperty( velocityNumber2PosX, "The x-position in background texture space for the second velocity number." ); writer.writeProperty( velocity3, "The third velocity." ); writer.writeProperty( velocityNumber3PosX, "The x-position in background texture space for the third velocity number." ); writer.writeProperty( velocityNumber4PosX, "The x-position in background texture space for the fourth velocity number." ); writer.writeProperty( velocityNumber4PosY, "The y-position in background texture space for the fourth velocity number." ); writer.writeProperty( velocityUnitsPosX, "The x-position in background texture space for te units display." ); writer.writeProperty( velocityUnitsPosY, "The y-position in background texture space for te units display." ); writer.writeProperty( maxVelocity, "The maximum velocity in km/h." ); writer.writeProperty( maxVelocityOverlay, "The image name for the max velocity overlay." ); writer.writeProperty( maxVelocityLeftOffset, "The x-offset in background image space for the max velocity overlay." ); writer.writeProperty( maxVelocityTopOffset, "The y-offset in background image space for the max velocity overlay." ); writer.writeProperty( throttleImage, "The image for the throttle gauge." ); writer.writeProperty( brakeImage, "The image for the brake gauge." ); writer.writeProperty( controlsPosX, "The x-offset in background image space for the controls display." ); writer.writeProperty( controlsPosY, "The y-offset in background image space for the controls display." ); writer.writeProperty( controlsWidth, "The width in background image space for the controls display." ); writer.writeProperty( controlsHeight, "The height in background image space for the controls display." ); writer.writeProperty( controlsGap, "The gap in pixels between the throttle and brake bars." ); writer.writeProperty( controlsLabelFont, "The font for the controls labels." ); writer.writeProperty( controlsLabelFontColor, "The font color for the controls labels." ); writer.writeProperty( controlsLabelOffset, "The offset for bar text from the left boundary of the bar." ); } /** * {@inheritDoc} */ @Override public void loadProperty( PropertyLoader loader ) { super.loadProperty( loader ); if ( loader.loadProperty( displayVelocityNumbers ) ); else if ( loader.loadProperty( velocityNumberFont ) ); else if ( loader.loadProperty( velocityNumberFont ) ); else if ( loader.loadProperty( velocityNumber1PosX ) ); else if ( loader.loadProperty( velocityNumber1PosY ) ); else if ( loader.loadProperty( velocity2 ) ); else if ( loader.loadProperty( velocityNumber2PosX ) ); else if ( loader.loadProperty( velocity3 ) ); else if ( loader.loadProperty( velocityNumber3PosX ) ); else if ( loader.loadProperty( velocityNumber4PosX ) ); else if ( loader.loadProperty( velocityNumber4PosY ) ); else if ( loader.loadProperty( velocityUnitsPosX ) ); else if ( loader.loadProperty( velocityUnitsPosY ) ); else if ( loader.loadProperty( maxVelocity ) ); else if ( loader.loadProperty( maxVelocityOverlay ) ); else if ( loader.loadProperty( maxVelocityLeftOffset ) ); else if ( loader.loadProperty( maxVelocityTopOffset ) ); else if ( loader.loadProperty( throttleImage ) ); else if ( loader.loadProperty( brakeImage ) ); else if ( loader.loadProperty( controlsPosX ) ); else if ( loader.loadProperty( controlsPosY ) ); else if ( loader.loadProperty( controlsWidth ) ); else if ( loader.loadProperty( controlsHeight ) ); else if ( loader.loadProperty( controlsGap ) ); else if ( loader.loadProperty( controlsLabelFont ) ); else if ( loader.loadProperty( controlsLabelFontColor ) ); else if ( loader.loadProperty( controlsLabelOffset ) ); } /** * {@inheritDoc} */ @Override public void getProperties( PropertiesContainer propsCont, boolean forceAll ) { super.getProperties( propsCont, forceAll ); propsCont.addGroup( "Velocity Numbers" ); propsCont.addProperty( displayVelocityNumbers ); if ( displayVelocityNumbers.getBooleanValue() || forceAll ) { propsCont.addProperty( velocityNumberFont ); propsCont.addProperty( velocityNumberFontColor ); propsCont.addProperty( velocityNumber1PosX ); propsCont.addProperty( velocityNumber1PosY ); propsCont.addProperty( velocity2 ); propsCont.addProperty( velocityNumber2PosX ); propsCont.addProperty( velocity3 ); propsCont.addProperty( velocityNumber3PosX ); propsCont.addProperty( velocityNumber4PosX ); propsCont.addProperty( velocityNumber4PosY ); propsCont.addProperty( velocityUnitsPosX ); propsCont.addProperty( velocityUnitsPosY ); } propsCont.addGroup( "Max Velocity Overlay" ); propsCont.addProperty( maxVelocity ); propsCont.addProperty( maxVelocityOverlay ); propsCont.addProperty( maxVelocityLeftOffset ); propsCont.addProperty( maxVelocityTopOffset ); propsCont.addGroup( "Controls" ); propsCont.addProperty( throttleImage ); propsCont.addProperty( brakeImage ); propsCont.addProperty( controlsPosX ); propsCont.addProperty( controlsPosY ); propsCont.addProperty( controlsWidth ); propsCont.addProperty( controlsHeight ); propsCont.addProperty( controlsGap ); propsCont.addProperty( controlsLabelFont ); propsCont.addProperty( controlsLabelFontColor ); propsCont.addProperty( controlsLabelOffset ); } @Override protected String getInitialBackground() { return ( BackgroundProperty.IMAGE_INDICATOR + "etv2010/telemetry/background.png" ); } @Override protected void onBackgroundChanged( boolean imageChanged, float deltaScaleX, float deltaScaleY ) { super.onBackgroundChanged( imageChanged, deltaScaleX, deltaScaleY ); maxVelocityLeftOffset.setIntValue( Math.round( maxVelocityLeftOffset.getIntValue() * deltaScaleX ) ); maxVelocityTopOffset.setIntValue( Math.round( maxVelocityTopOffset.getIntValue() * deltaScaleY ) ); } @Override protected String getInitialNeedleImage() { return ( "etv2010/telemetry/needle.png" ); } @Override protected String getInitialPeakNeedleImage() { return ( "etv2010/telemetry/peak_needle.png" ); } /** * {@inheritDoc} */ @Override protected Boolean onVehicleControlChanged( VehicleScoringInfo viewedVSI, LiveGameData gameData, boolean isEditorMode ) { Boolean result = super.onVehicleControlChanged( viewedVSI, gameData, isEditorMode ); if ( texThrottle1 != null ) texThrottle1.setVisible( viewedVSI.isPlayer() ); if ( texThrottle2 != null ) texThrottle2.setVisible( viewedVSI.isPlayer() ); if ( texBrake1 != null ) texBrake1.setVisible( viewedVSI.isPlayer() ); if ( texBrake2 != null ) texBrake2.setVisible( viewedVSI.isPlayer() ); return ( result ); } private void drawBarLabel( String label, TextureImage2D texture, int offsetX, int offsetY, int width, int height ) { Rectangle2D bounds = TextureImage2D.getStringBounds( label, controlsLabelFont ); //int lblOff = Math.round( controlsLabelOffset.getIntValue() * getBackground().getScaleX() ); int lblOff = controlsLabelOffset.getIntValue(); if ( ( lblOff > -bounds.getWidth() ) && ( lblOff < width ) ) texture.drawString( label, offsetX + lblOff, offsetY + ( height - (int)bounds.getHeight() ) / 2 - (int)bounds.getY(), bounds, controlsLabelFont.getFont(), controlsLabelFont.isAntiAliased(), controlsLabelFontColor.getColor(), true, null ); } private void loadThrottleTexture( boolean isEditorMode, int w, int h ) { if ( ( texThrottle1 == null ) || ( texThrottle1.getWidth() != w ) || ( texThrottle1.getHeight() != h ) || throttleDirty ) { texThrottle1 = TransformableTexture.getOrCreate( w, h, TransformableTexture.DEFAULT_PIXEL_PERFECT_POSITIONING, texThrottle1, isEditorMode ); texThrottle2 = TransformableTexture.getOrCreate( w, h, TransformableTexture.DEFAULT_PIXEL_PERFECT_POSITIONING, texThrottle2, isEditorMode ); ImageTemplate it = throttleImage.getImage(); it.drawScaled( 0, 0, it.getBaseWidth(), it.getBaseHeight() / 2, 0, 0, w, h, texThrottle1.getTexture(), true ); it.drawScaled( 0, it.getBaseHeight() / 2, it.getBaseWidth(), it.getBaseHeight() / 2, 0, 0, w, h, texThrottle2.getTexture(), true ); drawBarLabel( Loc.throttle_label, texThrottle1.getTexture(), 0, 0, texThrottle1.getWidth(), texThrottle1.getHeight() ); drawBarLabel( Loc.throttle_label, texThrottle2.getTexture(), 0, 0, texThrottle2.getWidth(), texThrottle2.getHeight() ); float x = controlsPosX.getFloatValue() * getBackground().getScaleX(); float y = controlsPosY.getFloatValue() * getBackground().getScaleY(); texThrottle1.setTranslation( x, y ); texThrottle2.setTranslation( x, y ); texThrottle1.setLocalZIndex( CONTROLS_LOCAL_Z_INDEX ); texThrottle2.setLocalZIndex( CONTROLS_LOCAL_Z_INDEX + 1 ); throttleDirty = false; } } private void loadBrakeTexture( boolean isEditorMode, int w, int h ) { if ( ( texBrake1 == null ) || ( texBrake1.getWidth() != w ) || ( texBrake1.getHeight() != h ) || brakeDirty ) { texBrake1 = TransformableTexture.getOrCreate( w, h, TransformableTexture.DEFAULT_PIXEL_PERFECT_POSITIONING, texBrake1, isEditorMode ); texBrake2 = TransformableTexture.getOrCreate( w, h, TransformableTexture.DEFAULT_PIXEL_PERFECT_POSITIONING, texBrake2, isEditorMode ); ImageTemplate it = brakeImage.getImage(); it.drawScaled( 0, 0, it.getBaseWidth(), it.getBaseHeight() / 2, 0, 0, w, h, texBrake1.getTexture(), true ); it.drawScaled( 0, it.getBaseHeight() / 2, it.getBaseWidth(), it.getBaseHeight() / 2, 0, 0, w, h, texBrake2.getTexture(), true ); drawBarLabel( Loc.brake_label, texBrake1.getTexture(), 0, 0, texBrake1.getWidth(), texBrake1.getHeight() ); drawBarLabel( Loc.brake_label, texBrake2.getTexture(), 0, 0, texBrake2.getWidth(), texBrake2.getHeight() ); float x = controlsPosX.getFloatValue() * getBackground().getScaleX(); float y = controlsPosY.getFloatValue() * getBackground().getScaleY() + h + controlsGap.getIntValue(); texBrake1.setTranslation( x, y ); texBrake2.setTranslation( x, y ); texBrake1.setLocalZIndex( CONTROLS_LOCAL_Z_INDEX ); texBrake2.setLocalZIndex( CONTROLS_LOCAL_Z_INDEX + 1 ); brakeDirty = false; } } private void drawVelocityNumbers( SpeedUnits units, TextureImage2D texture, int offsetX, int offsetY ) { if ( !displayVelocityNumbers.getBooleanValue() || ( maxVelocityTexture == null ) ) return; float scaleX = getBackground().getScaleX(); float scaleY = getBackground().getScaleY(); Texture2DCanvas texCanvas = texture.getTextureCanvas(); texCanvas.setFont( velocityNumberFont.getFont() ); texCanvas.setAntialiazingEnabled( velocityNumberFont.isAntiAliased() ); FontMetrics metrics = texCanvas.getFontMetrics(); Color dropShadowColor = markersFontDropShadowColor.getColor(); float dropShadowOffset = 2.2f; //numberFont.getFont().getSize() * 0.2f; boolean drawDropShadow = ( dropShadowColor.getAlpha() > 0 ); int velo = 0; String s = String.valueOf( velo ); Rectangle2D bounds = metrics.getStringBounds( s, texCanvas ); int x = offsetX + Math.round( velocityNumber1PosX.getFloatValue() * scaleX - (float)( bounds.getWidth() / 2 ) ); int y = offsetY + Math.round( velocityNumber1PosY.getFloatValue() * scaleY -(float)bounds.getY() - (float)( bounds.getHeight() / 2 ) ); if ( drawDropShadow ) { texCanvas.setColor( dropShadowColor ); texCanvas.drawString( s, x + dropShadowOffset, y + dropShadowOffset ); } texCanvas.setColor( velocityNumberFontColor.getColor() ); texCanvas.drawString( s, x, y, bounds, true, null ); velo = velocity2.getIntValue(); s = String.valueOf( velo ); bounds = metrics.getStringBounds( s, texCanvas ); x = offsetX + Math.round( velocityNumber2PosX.getFloatValue() * scaleX - (float)( bounds.getWidth() / 2 ) ); y = offsetY + Math.round( Math.round( maxVelocityTopOffset.getFloatValue() * scaleY + maxVelocityTexture.getHeight() - velocity2.getFloatValue() * maxVelocityTexture.getHeight() / maxVelocity.getFloatValue() ) -(float)bounds.getY() - (float)( bounds.getHeight() / 2 ) ); if ( drawDropShadow ) { texCanvas.setColor( dropShadowColor ); texCanvas.drawString( s, x + dropShadowOffset, y + dropShadowOffset ); } texCanvas.setColor( velocityNumberFontColor.getColor() ); texCanvas.drawString( s, x, y, bounds, true, null ); velo = velocity3.getIntValue(); s = String.valueOf( velo ); bounds = metrics.getStringBounds( s, texCanvas ); x = offsetX + Math.round( velocityNumber3PosX.getFloatValue() * scaleX - (float)( bounds.getWidth() / 2 ) ); y = offsetY + Math.round( Math.round( maxVelocityTopOffset.getFloatValue() * scaleY + maxVelocityTexture.getHeight() - velocity3.getFloatValue() * maxVelocityTexture.getHeight() / maxVelocity.getFloatValue() ) -(float)bounds.getY() - (float)( bounds.getHeight() / 2 ) ); if ( drawDropShadow ) { texCanvas.setColor( dropShadowColor ); texCanvas.drawString( s, x + dropShadowOffset, y + dropShadowOffset ); } texCanvas.setColor( velocityNumberFontColor.getColor() ); texCanvas.drawString( s, x, y, bounds, true, null ); velo = maxVelocity.getIntValue(); s = String.valueOf( velo ); bounds = metrics.getStringBounds( s, texCanvas ); x = offsetX + Math.round( velocityNumber4PosX.getFloatValue() * scaleX - (float)( bounds.getWidth() / 2 ) ); y = offsetY + Math.round( velocityNumber4PosY.getFloatValue() * scaleY -(float)bounds.getY() - (float)( bounds.getHeight() / 2 ) ); if ( drawDropShadow ) { texCanvas.setColor( dropShadowColor ); texCanvas.drawString( s, x + dropShadowOffset, y + dropShadowOffset ); } texCanvas.setColor( velocityNumberFontColor.getColor() ); texCanvas.drawString( s, x, y, bounds, true, null ); s = String.valueOf( units == SpeedUnits.MIH ? Loc.velocity_units_IMPERIAL : Loc.velocity_units_METRIC ); bounds = metrics.getStringBounds( s, texCanvas ); x = offsetX + Math.round( velocityUnitsPosX.getFloatValue() * scaleX - (float)( bounds.getWidth() / 2 ) ); y = offsetY + Math.round( velocityUnitsPosY.getFloatValue() * scaleY -(float)bounds.getY() - (float)( bounds.getHeight() / 2 ) ); if ( drawDropShadow ) { texCanvas.setColor( dropShadowColor ); texCanvas.drawString( s, x + dropShadowOffset, y + dropShadowOffset ); } texCanvas.setColor( velocityNumberFontColor.getColor() ); texCanvas.drawString( s, x, y, bounds, true, null ); } private boolean loadMaxVelocityTexture( SpeedUnits speedUnits, boolean isEditorMode ) { if ( maxVelocityOverlay.isNoImage() ) { maxVelocityTexture = null; return ( false ); } try { ImageTemplate it = maxVelocityOverlay.getImage(); if ( it == null ) { maxVelocityTexture = null; return ( false ); } int w = Math.round( it.getBaseWidth() * getBackground().getScaleX() ); int h = Math.round( it.getBaseHeight() * getBackground().getScaleY() ); boolean[] changeInfo = new boolean[ 2 ]; maxVelocityTexture = it.getScaledTransformableTexture( w, h, maxVelocityTexture, isEditorMode, changeInfo ); //maxVelocityTexture.setDynamic( true ); if ( changeInfo[1] ) drawVelocityNumbers( speedUnits, maxVelocityTexture.getTexture(), -Math.round( maxVelocityLeftOffset.getFloatValue() * getBackground().getScaleX() ), -Math.round( maxVelocityTopOffset.getFloatValue() * getBackground().getScaleY() ) ); maxVelocityTexture.setTranslation( maxVelocityLeftOffset.getFloatValue() * getBackground().getScaleX(), maxVelocityTopOffset.getFloatValue() * getBackground().getScaleY() ); maxVelocityTexture.setLocalZIndex( MAX_VELOCITY_LOCAL_Z_INDEX ); } catch ( Throwable t ) { log( t ); return ( false ); } return ( true ); } @Override protected void initSubTextures( LiveGameData gameData, boolean isEditorMode, int widgetInnerWidth, int widgetInnerHeight, SubTextureCollector collector ) { super.initSubTextures( gameData, isEditorMode, widgetInnerWidth, widgetInnerHeight, collector ); if ( loadMaxVelocityTexture( gameData.getProfileInfo().getSpeedUnits(), isEditorMode ) ) collector.add( maxVelocityTexture ); int cw = Math.round( controlsWidth.getFloatValue() * getBackground().getScaleX() ); int ch = Math.round( controlsHeight.getFloatValue() * getBackground().getScaleY() ); loadThrottleTexture( isEditorMode, cw, ch ); collector.add( texThrottle1 ); collector.add( texThrottle2 ); loadBrakeTexture( isEditorMode, cw, ch ); collector.add( texBrake1 ); collector.add( texBrake2 ); } @Override protected void drawBackground( LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height, boolean isRoot ) { super.drawBackground( gameData, isEditorMode, texture, offsetX, offsetY, width, height, isRoot ); drawVelocityNumbers( gameData.getProfileInfo().getSpeedUnits(), texture, offsetX, offsetY ); } @Override protected void drawWidget( Clock clock, boolean needsCompleteRedraw, LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height ) { super.drawWidget( clock, needsCompleteRedraw, gameData, isEditorMode, texture, offsetX, offsetY, width, height ); TelemetryData telemData = gameData.getTelemetryData(); if ( maxVelocityTexture != null ) { float normVelo = Math.min( telemData.getScalarVelocityKmh(), maxVelocity.getFloatValue() ) / maxVelocity.getFloatValue(); int h = Math.round( maxVelocityTexture.getHeight() * normVelo ); maxVelocityTexture.setClipRect( 0, maxVelocityTexture.getHeight() - h, maxVelocityTexture.getWidth(), h, true ); } float uBrake = isEditorMode ? 0.2f : telemData.getUnfilteredBrake(); float uThrottle = isEditorMode ? 0.4f : telemData.getUnfilteredThrottle(); final int w = texThrottle2.getWidth(); int brake = (int)( w * uBrake ); int throttle = (int)( w * uThrottle ); texThrottle2.setClipRect( 0, 0, throttle, texThrottle2.getHeight(), true ); texBrake2.setClipRect( 0, 0, brake, texBrake2.getHeight(), true ); } }