# Xi’An Scout Available!

## Greetings Citizens,

We are excited to announce that the first concept model of the Xi’An Khartu-al scout is now available !

As part of the development process, our artists first create a ‘concept model’ to show how a ship will work before beginning work on the finished game-ready mesh. That’s what you’re seeing today: the concept model for the scout that lets us see how its unusual thruster configurations, cockpit, landing gear and more will function!

Here’s the original description of the Scout from the Stretch Goal that added it. The goal is to build alien ships that fly totally differently than those you already know; something that Star Citizen’s robust physics simulation allows us to do!

The Khartu is the light attack craft of the Xi’An military. Contrary to Human ship design, the Khartu doesn’t have a traditional main thruster, instead featuring an array of maneuvering thrusters on articulated rigs. This design allows for incredible agility, making them the bane of UEE pilots, who bestowed the nickname ‘Quark’ because when all of the thrusters are firing, the ship looks like a spark flying through space. The Xi’an Aopoa corporation also manufactures an export model, the Khartu-al, for sale to human civilians as a dedicated scout/explorer. The export model features the same Xi’an maneuvering rig, but control surfaces modified for human use and a more limited armament.

We’ve also found some rough footage from a UEE reconnaissance drone, available below; this is a pre-viz of how the Khartu should function in-game!

### Xi’An Scout concept sale

Want your own scout? The Aopoa Corporation has made another allotment available for the export market! They’re not ready for the Hangar or Arena Commander yet, but we’re adding them to the pledge store for this week (through Friday, June 27.) Get yours now and you can be the first to fly when it’s ready!

**Xi’An Scout** The Khartu is the light attack craft of the Xi’An military.

Press here to purchase.

Latest Google Chrome or FireFox have proper support.’); return false; } $ (e.currentTarget).hide(200); window.Explorer.render(); return false; } function webglSupported() { try { var canvas = document.createElement( ‘canvas’ ); return !! window.WebGLRenderingContext && ( canvas.getContext( ‘webgl’ ) || canvas.getContext( ‘experimental-webgl’ ) ); } catch( e ) { return false; } }; $ (“.js-start-render”).click(onStartClick); });

/* Holographic GLSL Fragment Shader Author: b@turbulent.ca © 2012-2014 Cloud Imperium Games Corporation THREE.js VS uniforms & attributes: uniform mat4 modelMatrix; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform mat3 normalMatrix; uniform vec3 cameraPosition; attribute vec3 position; attribute vec3 normal; attribute vec2 uv; attribute vec2 uv2; */ precision highp float; uniform float uCamNear; uniform float uCamFar; uniform vec3 uLightPos; attribute vec3 aBC; //Vertex barycentric coordinates attribute vec3 aHY; //Face hypothenuse vector varying vec3 vW; //Vertex position [world] varying vec3 vP; //Vertex position [clip] varying vec3 vN; //Vertex normal [clip] varying vec2 vUv; //Vertex UV varying vec3 vBC; varying vec3 vHY; varying vec3 vR; varying vec3 vE; varying float D; void main() { //World space vert pos vW = position; // Varying Position in camera/eye space vec4 posCam = modelViewMatrix * vec4(position,1.0); vP = posCam.xyz; //Transform vertex by modelview and projection matrices to get screen position gl_Position = projectionMatrix * vec4(vP, 1.0); // Normal transform (transposed model-view inverse): camera space vN = normalize(normalMatrix * normal); // Vertex uv vUv = uv; //Pass the UV for bary coord to fragment vBC = aBC; //Pass the hypothenuse vector to fragment vHY = aHY; // find world space eye vector. vE = normalize( -vP ); //Reflect vector vR = reflect( vE, vN ); } /* Holographic GLSL Fragment Shader Author: b@turbulent.ca © 2012-2014 Cloud Imperium Games Corporation THREE.js FS uniforms uniform mat4 viewMatrix; uniform vec3 cameraPosition; */ #extension GL_OES_standard_derivatives : enable #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp int; precision highp float; #else precision mediump int; precision mediump float; #endif const float M_PI = 3.1415926535897932384626433832795; const float M_TWOPI = 6.28318531; uniform float uCamNear; uniform float uCamFar; uniform vec3 uLightPos; varying vec3 vW; varying vec3 vP; varying vec3 vN; varying vec2 vUv; varying vec3 vBC; varying vec3 vHY; varying vec3 vR; varying vec3 vE; varying float D; //Material Parameters uniform float uModelSize; uniform float uDiffuseLights; //Wireframe uniform float uWirePower; uniform vec3 uWireLineColor; uniform float uWireAlpha; uniform float uWireQuad; //Diffuse uniform float uDiffusePower; uniform vec3 uDiffuseColor; uniform float uDiffuseAlpha; //Specular uniform float uSpecularPower; uniform float uSpecularHardness; uniform vec3 uSpecularColor; //Reflection uniform float uReflectionPower; uniform float uReflectionOnLines; uniform samplerCube utReflectionMap; //Dissolve uniform sampler2D utDissolveMap; uniform float uDissolveVal; uniform float uDissolveLineWidth; uniform vec3 uDissolveLineColor; uniform vec3 uDissolveSpotColor; uniform float uDissolveNoiseSpeed; //Scanline uniform float uScanValX; uniform float uScanValY; uniform float uScanValZ; uniform float uScanWidth; uniform float uScanSpeed; uniform float uScanPeriod; uniform vec3 uScanColor; //Rim uniform vec3 uRimColor; uniform float uRimPower; uniform float uRimWidth; //Time uniform float uTime; uniform sampler2D utNoiseMap; //Randomizer float rand12(vec2 p) { return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5357); } //2D Noise float noise2d( in vec2 x ) { vec2 p = floor(x); vec2 f = fract(x); f = f*f*(3.0-2.0*f); float a = texture2D(utNoiseMap,(p+vec2(0.5,0.5))/256.0,-32.0).x; float b = texture2D(utNoiseMap,(p+vec2(1.5,0.5))/256.0,-32.0).x; float c = texture2D(utNoiseMap,(p+vec2(0.5,1.5))/256.0,-32.0).x; float d = texture2D(utNoiseMap,(p+vec2(1.5,1.5))/256.0,-32.0).x; return mix(mix( a, b,f.x), mix( c, d,f.x),f.y); } //FBM const mat2 mtx = mat2( 0.80, 0.60, -0.60, 0.80 ); float fbm( vec2 p ) { float f = 0.0; f += 0.5000*(-1.0+2.0*noise2d( p )); p = mtx*p*2.02; f += 0.2500*(-1.0+2.0*noise2d( p )); p = mtx*p*2.03; f += 0.1250*(-1.0+2.0*noise2d( p )); p = mtx*p*2.01; f += 0.0625*(-1.0+2.0*noise2d( p )); return f/0.9375; } float ghostPattern( in vec2 p ) { vec2 q = vec2( fbm( p + vec2(0.0,0.0) ), fbm( p + vec2(5.2,1.3) ) ); vec2 r = vec2( fbm( p + 4.0*q + vec2(1.7,9.2) ), fbm( p + 4.0*q + vec2(8.3,2.8) ) ); return fbm( p + fbm( p + 1.0 / (1.0+uDissolveNoiseSpeed) * r + (uTime) ) ); } float remap(float value, float low1, float high1, float low2, float high2) { return low2 + (value – low1) * (high2 – low2) / (high1 – low1); } float bias(float value, float b) { return (b > 0.0) ? pow(value, log2(b) / log2(0.5)) : 0.0; } vec3 computeScope(vec3 p, vec3 s) { //XXX Invertex axis ZY vec3 scope = vec3(0.0,0.0,0.0); scope.x = 1.0 – clamp((s.z – (((p.y)/uModelSize) + 1.0) / 2.0), 0.0, 1.0); scope.y = 1.0 – clamp((s.y – (((p.z)/uModelSize) + 1.0) / 2.0), 0.0, 1.0); scope.z = 1.0 – clamp((s.x – (((p.x)/uModelSize) + 1.0) / 2.0), 0.0, 1.0); return scope; } void main() { float debug = 0.0; vec4 clear = vec4(0.0,0.0,0.0,0.0); vec3 rates = fwidth(vBC); vec3 a3 = smoothstep(vec3(0.0), rates, vBC); float f = min(min(a3.x, a3.y), a3.z); if(uWireQuad > 0.0) { //1, 0, 1 if(vHY.y > rates.x && vHY.z > rates.x && vBC.x < rates.x && vBC.y > rates.y && vBC.z > rates.z) { f = 1.0; } //1, 0, 1 if(vHY.x > rates.x && vHY.z > rates.x && vBC.y < rates.y && vBC.x > rates.x && vBC.z > rates.z) { f = 1.0; } //1, 1, 0 if(vHY.x > rates.x && vHY.y > rates.x && vBC.z < rates.z && vBC.x > rates.x && vBC.y > rates.y) { f = 1.0; } } vec4 diffuseColor = vec4(uDiffuseColor, uDiffuseAlpha); /* Compute Diffuse */ vec3 L = vec3(viewMatrix * vec4(uLightPos,1.0)) – vP; float Ldist = pow(length(L), 2.0); float I = 1.0; if(uDiffuseLights > 0.0) I = clamp(dot(vN, normalize(L)), 0.0, 1.0); gl_FragColor = I * diffuseColor * uDiffusePower; gl_FragColor.a = uDiffuseAlpha; /* Apply wireframe */ vec4 wireColor = vec4(uWireLineColor, uWireAlpha); vec4 wiredColor = mix( mix(gl_FragColor, wireColor, uWirePower) , gl_FragColor, f); gl_FragColor = wiredColor; /* Compute Specular */ vec3 H = normalize(L – vP); float S = dot(vN, H); I = pow( clamp(S, 0.0, 1.0), uSpecularHardness); vec4 specular = I * vec4(uSpecularColor, 1.0) * uSpecularPower ; gl_FragColor += specular; /* Compute Rim */ float rim = bias(1.0 – max(dot(vN, vE), 0.0), uRimWidth) * uRimPower; gl_FragColor.rgb += uRimColor * rim; //gl_FragColor.a += rim; /* Compute reflection */ float reflectFactor = uReflectionPower; if(uReflectionOnLines > 0.0) reflectFactor = f; vec4 reflectColor = textureCube(utReflectionMap, vR); /* Add in lighting */ gl_FragColor = mix(gl_FragColor, reflectColor + specular, reflectFactor); /* Apply Dissolve */ float d = uDissolveVal; float fshield = ghostPattern(vUv); vec4 dissolveColor = texture2D(utDissolveMap, vUv); float isClear = floor(abs(dissolveColor.r – (d + uDissolveLineWidth) + 0.99)); float isOverLine = floor(abs(dissolveColor.r – (d) + 0.99)); vec3 dissolved = mix(uDissolveSpotColor * d , uDissolveLineColor * fshield, isClear); gl_FragColor.rgb = mix(gl_FragColor.rgb, dissolved, isOverLine); gl_FragColor.a = mix(gl_FragColor.a, 0.75 * d, isClear); /* Compute scan scope */ vec3 scope = computeScope(vW, vec3(uScanValX, uScanValY, uScanValZ)); float sgate, s, sw = 0.0; sgate = (1.0 – step(1.0, scope.z)); s = sgate * (smoothstep(1.0-uScanWidth, 1.0, scope.z)); sw = sgate * (smoothstep(1.0-uScanWidth*5., 1.0, scope.z)); gl_FragColor += s * vec4(uScanColor,1.0) + mix( mix(clear, vec4(uScanColor,1.0), sw) , clear, f); sgate = (1.0 – step(1.0, scope.y)); s = sgate * (smoothstep(1.0-uScanWidth, 1.0, scope.y)); sw = sgate * (smoothstep(1.0-uScanWidth*5., 1.0, scope.y)); gl_FragColor += s * vec4(uScanColor,1.0) + mix( mix(clear, vec4(uScanColor,1.0), sw) , clear, f); sgate = (1.0 – step(1.0, scope.x)); s = sgate * (smoothstep(1.0-uScanWidth, 1.0, scope.x)); sw = sgate * (smoothstep(1.0-uScanWidth*5., 1.0, scope.x)); gl_FragColor += s * vec4(uScanColor,1.0) + mix( mix(clear, vec4(uScanColor,1.0), sw) , clear, f); /* Pulse */ //gl_FragColor.rgb += remap(cos(vW.y / uModelSize + uTime * 3.), -1.0, 1.0, 0.0, 0.05); //gl_FragColor.rgb += remap(sin(vW.x / uModelSize + uTime * 3.), -1.0, 1.0, 0.0, 0.05); //float pulse = fshield * remap(cos(vW.y/uModelSize + uTime /2.), -1.,1.0,0.0, 0.06); //gl_FragColor.rgb -= pulse; } /* Selector Glow GLSL Shader Author: b@turbulent.ca THREE.js VS uniforms & attributes: uniform mat4 modelMatrix; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform mat3 normalMatrix; uniform vec3 cameraPosition; attribute vec3 position; attribute vec3 normal; attribute vec2 uv; attribute vec2 uv2; */ #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp int; precision highp float; #else precision mediump int; precision mediump float; #endif varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } /* Selector Glow GLSL Shader Author: b@turbulent.ca THREE.js FS uniforms uniform mat4 viewMatrix; uniform vec3 cameraPosition; */ #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp int; precision highp float; #else precision mediump int; precision mediump float; #endif uniform sampler2D tDiffuse; uniform sampler2D utGlowMap; uniform float uGlowPower; uniform vec3 uGlowColor; uniform float uOpacity; varying vec2 vUv; void main() { vec4 texel = texture2D(tDiffuse, vUv); vec4 gTexel = texture2D(utGlowMap, vUv); gl_FragColor = (texel + (vec4(uGlowColor,1.0) * gTexel * uGlowPower)) * (uOpacity); }

# Holographic Web Viewer

The web team at Turbulent is working to build an all-new Star Citizen pledge store complete with interactive ship models… and we’re demoing a prototype of this holographic viewer for you with this announcement! This prototype is loaded up with the actual concept model of the Xi’An Karthu Scout.

### Tech

The viewer is using WebGL to display the ship meshes in your browser. This technology allows us to use the GPU to display a high number of polygons on screen and also provides us with a rendering pipeline to create view options and effects with the shader API. The viewer at the moment is powered by the outstanding open source three.js library , we thank them for providing such convenience!

Star Citizen ship meshes are very large and thus required some work to get them loaded in the browser in an acceptable time frame. Most graphics card can render very high poly counts even in the browser and so transferring the model data from the web servers to your browser becomes the bottleneck. We start from a 3DS / CryEngine output of the model in pieces (which can weight over 100 megs and up!) and construct and a highly optimized and compressed binary triangle stream using the OpenCTM format. For example, the full-resolution Hornet weights about 85 megabytes in OBJ format and compresses down to 1.5 megs with this method. We can then easily unwrap and load in GL buffers without too much additional processing on the browser side. Using this method, in our test lab, we were able to load and render a 6 million polygon ship! *(I will let you guess which one)* Obviously, we still plan on using LOD versions for the higher end ships in order to support a broader range of hardware profiles.

As WebGL sees more adoption and support, we think harnessing it’s power to bring some of the game elements on the web though utility viewers and browsers is a great way to enhance the immersion in the game universe.

### Controls

- Orbit Cam: Click and drag around the viewer with your mouse to view the model in all angles.
- Orbit Cam: Right click to pan.
- Mouse wheel to zoom in and out
- Use the buttons at the bottom to jump to preset locations around the ship or activate view options.
- Free Cam: Use your keyboard keys (WASD, QE) in conjunction with mouse drag to move the camera anywhere on the ship.

Keep in mind this is a ** prototype** of the viewer. It has been mostly developed using Google Chrome and so should be best viewed with that browser (though it should work properly on Firefox as well, fingers crossed). As the team iterates on it many more features will be added like additional view modes, ship components and compatibility , loadout options. The goal is to bring every ship and variants available in that viewer for you to browse!

*Note:**If you are unable to access the viewer with Chrome, make sure that WebGL is active by typing “about:flags” in the URL bar and making sure the option “Disable Webgl” is not set.*

The Citizen Inquisitor Ship Specs 101, Ep. 2: Hardpoints Explains internal and external ship hardpoints in Star Citizen. There are some great videos out ther…

**Video Rating: 5 / 5**