/* |
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
|
Abstract: |
|
This is the main renderer for this sample. Acts as the update and render delegate for the view controller and performs rendering. renders in 2 passes, 1) shadow pass, 2) Gbuffer pass which retains the drawable and presents it to the screen while discard the remaining attachments |
|
*/ |
|
#import "AAPLRenderer.h" |
#import "AAPLViewController.h" |
#import "AAPLView.h" |
#import "AAPLTransforms.h" |
#import "AAPLUtilities.h" |
|
#import "common.h" |
|
#import <simd/simd.h> |
|
using namespace AAPL; |
|
// number of fairy lights spinning around temple |
static const uint32_t kNumFairies = 64; |
|
// light radius |
static const float kLightRadius = 0.5f; |
|
// total number of frames to preapare in advanced |
static const int kMaxFrameLag = 3; |
|
@implementation AAPLRenderer |
{ |
id <MTLCommandQueue> _commandQueue; |
id <MTLLibrary> _defaultLibrary; |
CFTimeInterval _frameTime; |
dispatch_semaphore_t _inflight_semaphore; |
|
float4 _fairyColors[kNumFairies]; |
float _fairyAngles[kNumFairies]; |
float _fairyPhases[kNumFairies]; |
float _fairySpeeds[kNumFairies]; |
|
LightFragmentInputs _lightData[kNumFairies+1]; |
|
float3 _sunColor; |
ClearColorBuffers _clear_color_buffers; |
float _structureCameraRotationRate; |
float _skyboxCameraRotationRate; |
float _skyboxScale; |
float _structureScale; |
float4x4 _projectionMatrix; |
|
id<MTLDepthStencilState> _noDepthStencilState; |
id<MTLDepthStencilState> _lightMaskStencilState; |
id<MTLDepthStencilState> _lightColorStencilState; |
id<MTLDepthStencilState> _lightColorStencilStateNoDepth; |
id<MTLDepthStencilState> _gBufferDepthStencilState; |
id<MTLDepthStencilState> _shadowDepthStencilState; |
id<MTLDepthStencilState> _compositionDepthState; |
|
MTLRenderPassDescriptor* _shadowRenderPassDescriptor; |
id<MTLTexture> _shadow_texture; |
|
id<MTLRenderPipelineState> _shadow_render_pipeline; |
id<MTLRenderPipelineState> _skybox_render_pipeline; |
id<MTLRenderPipelineState> _gbuffer_render_pipeline; |
id<MTLRenderPipelineState> _light_mask_pipeline; |
id<MTLRenderPipelineState> _light_color_pipeline; |
id<MTLRenderPipelineState> _composition_pipeline; |
id<MTLRenderPipelineState> _fairy_pipeline; |
id<MTLRenderPipelineState> _texture_copy_pipeline; |
|
id<MTLTexture> _skyboxTexture; |
id<MTLTexture> _fairyTexture; |
NSMutableArray* _structureModelGroupDiffuseTextures; |
NSMutableArray* _structureModelGroupSpecularTextures; |
NSMutableArray* _structureModelGroupBumpTextures; |
|
id<MTLBuffer> _skyboxVertexBuffer; |
id<MTLBuffer> _quadPositionBuffer; |
id<MTLBuffer> _quadTexcoordBuffer; |
id<MTLBuffer> _structureVertexBuffer; |
id<MTLBuffer> _structureIndexBuffer; |
id<MTLBuffer> _lightModelVertexBuffer; |
id<MTLBuffer> _lightModelIndexBuffer; |
id<MTLBuffer> _spriteBuffer; |
|
NSMutableArray* _lightModelMatrixBuffers; |
NSMutableArray* _lightDataBuffers; |
NSMutableArray* _sunDataBuffers; |
NSMutableArray* _skyboxMatrixBuffers; |
NSMutableArray* _modelMatricesBuffers; |
NSMutableArray* _zOnlyProjectionBuffers; |
NSMutableArray* _fairySpriteBuffers; |
|
id<MTLBuffer> _clearColorBuffer1; |
id<MTLBuffer> _clearColorBuffer2; |
|
AAPLOBJModel* _structureModel; |
AAPLOBJModelGroup* _structureModelGroup; |
|
MTLIndexType _structureModelGroupIndexDataType; |
int _fairyCount; |
|
int _numFrames; |
int _currFrameIndex; |
} |
|
- (instancetype)init |
{ |
self = [super init]; |
if (self) { |
|
// find a usable Device |
_device = MTLCreateSystemDefaultDevice(); |
|
// create a new command queue |
_commandQueue = [_device newCommandQueue]; |
|
_defaultLibrary = [_device newDefaultLibrary]; |
if(!_defaultLibrary) { |
NSLog(@">> ERROR: Couldnt create a default shader library"); |
|
// assert here becuase if the shader libary isnt loading, shaders arent compiling |
assert(0); |
} |
|
//Setup Globals constants |
_sunColor = {1.0f, 0.875f, 0.75f}; |
|
_clear_color_buffers.clear_color = {0.0, 0.0, 0.0, 1.0}; |
_clear_color_buffers.albedo_clear_color = {_sunColor.x * 0.75f + 0.075f, _sunColor.y * 0.75f + 0.075f, _sunColor.z * 0.75f + 0.075f, 1.0}; |
_clear_color_buffers.light_buffer_clear_color = {0.1f, 0.1f, 0.125f, 0.0f}; |
|
// Clear linear depth buffer to far plane in eye-space (25) |
_clear_color_buffers.linear_depth_clear_color = {25.0f, 25.0f, 25.0f, 25.0f}; |
|
_structureCameraRotationRate = 7.5f; |
_structureScale = 0.01f; |
_skyboxCameraRotationRate = 2.0f; |
_skyboxScale = 10.0f; |
|
_structureModelGroupBumpTextures = [[NSMutableArray alloc] initWithCapacity: 10]; |
_structureModelGroupDiffuseTextures = [[NSMutableArray alloc] initWithCapacity: 10]; |
_structureModelGroupSpecularTextures = [[NSMutableArray alloc] initWithCapacity: 10]; |
|
_lightDataBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_lightModelMatrixBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_sunDataBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_skyboxMatrixBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_modelMatricesBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_zOnlyProjectionBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
_fairySpriteBuffers = [[NSMutableArray alloc] initWithCapacity: kMaxFrameLag]; |
|
_fairyCount = kNumFairies; |
|
for (NSInteger i = 0; i < kNumFairies; i++) |
{ |
_fairyColors[i] = Utilities::randomColor(); |
_fairyAngles[i] = Utilities::randomFloat(0.0f, Utilities::PI * 2.0f); |
_fairyPhases[i] = Utilities::randomFloat(0.0f, 1.0f); |
_fairySpeeds[i] = Utilities::randomFloat(5.0f, 15.0f); |
} |
|
_inflight_semaphore = dispatch_semaphore_create(kMaxFrameLag); |
_numFrames = kMaxFrameLag; |
_currFrameIndex = 0; |
} |
return self; |
} |
|
#pragma mark LOAD |
|
- (void)configure:(AAPLView *)view |
{ |
// set up g-buffer framebuffer via view for final pass (which is also g-buffer pass). |
// Final results are composited to color_attachment0 in the view, which is the main drawable texture |
// so the other attachemnts do not need to be stored unless we want to view the intermediate results |
view->colorAttachmentFormat[0] = MTLPixelFormatBGRA8Unorm; |
view->colorAttachmentClearValue[0] = MTLClearColorMake(_clear_color_buffers.albedo_clear_color[0], _clear_color_buffers.albedo_clear_color[1], _clear_color_buffers.albedo_clear_color[2], _clear_color_buffers.albedo_clear_color[3]); |
|
view->colorAttachmentFormat[1] = MTLPixelFormatBGRA8Unorm; |
view->colorAttachmentClearValue[1] = MTLClearColorMake(_clear_color_buffers.clear_color[0], _clear_color_buffers.clear_color[1], _clear_color_buffers.clear_color[2], _clear_color_buffers.clear_color[3]); |
|
view->colorAttachmentFormat[2] = MTLPixelFormatR32Float; |
view->colorAttachmentClearValue[2] = MTLClearColorMake(_clear_color_buffers.linear_depth_clear_color[0], _clear_color_buffers.linear_depth_clear_color[1], _clear_color_buffers.linear_depth_clear_color[2], _clear_color_buffers.linear_depth_clear_color[3]); |
|
view->colorAttachmentFormat[3] = MTLPixelFormatBGRA8Unorm; |
view->colorAttachmentClearValue[3] = MTLClearColorMake(_clear_color_buffers.light_buffer_clear_color[0], _clear_color_buffers.light_buffer_clear_color[1], _clear_color_buffers.light_buffer_clear_color[2], _clear_color_buffers.light_buffer_clear_color[3]); |
|
view->depthPixelFormat = MTLPixelFormatDepth32Float; |
view->depthAttachmentClearValue = 1.0; |
|
view->stencilPixelFormat = MTLPixelFormatStencil8; |
view->stencilAttachmentClearValue = 0; |
|
// setup shadow buffer for first pass |
|
MTLTextureDescriptor *shadowTextureDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatDepth32Float width: 1024 height: 1024 mipmapped: NO]; |
_shadow_texture = [_device newTextureWithDescriptor: shadowTextureDesc]; |
|
_shadowRenderPassDescriptor = [MTLRenderPassDescriptor new]; |
MTLRenderPassDepthAttachmentDescriptor *shadow_attachment = _shadowRenderPassDescriptor.depthAttachment; |
shadow_attachment.texture = _shadow_texture; |
shadow_attachment.loadAction = MTLLoadActionClear; |
shadow_attachment.storeAction = MTLStoreActionStore; |
shadow_attachment.clearDepth = 1.0; |
|
// |
// Shader loading |
//******************************************************************* |
|
id<MTLFunction> fairyVert = _newFunctionFromLibrary(_defaultLibrary, @"fairyVertex"); |
id<MTLFunction> fairyFrag = _newFunctionFromLibrary(_defaultLibrary, @"fairyFragment"); |
|
id<MTLFunction> gBufferVert = _newFunctionFromLibrary(_defaultLibrary, @"gBufferVert"); |
id<MTLFunction> gBufferFrag = _newFunctionFromLibrary(_defaultLibrary, @"gBufferFrag"); |
|
id<MTLFunction> lightVert = _newFunctionFromLibrary(_defaultLibrary, @"lightVert"); |
id<MTLFunction> lightFrag = _newFunctionFromLibrary(_defaultLibrary, @"lightFrag"); |
|
id<MTLFunction> compositionVert = _newFunctionFromLibrary(_defaultLibrary, @"compositionVertex"); |
id<MTLFunction> compositionFrag = _newFunctionFromLibrary(_defaultLibrary, @"compositionFrag"); |
|
id<MTLFunction> skyboxVert = _newFunctionFromLibrary(_defaultLibrary, @"skyboxVert"); |
id<MTLFunction> skyboxFrag = _newFunctionFromLibrary(_defaultLibrary, @"skyboxFrag"); |
|
id<MTLFunction> zOnlyVert = _newFunctionFromLibrary(_defaultLibrary, @"zOnly"); |
|
// Pipeline setup |
//********************************************************************* |
{ |
MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new]; |
NSError *err = nil; |
|
desc.label = @"Shadow Render"; |
desc.vertexFunction = zOnlyVert; |
desc.fragmentFunction = nil; |
desc.depthAttachmentPixelFormat = _shadow_texture.pixelFormat; |
_shadow_render_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_shadow_render_pipeline, err); |
|
desc.label = @"Skybox Render"; |
desc.vertexFunction = skyboxVert; |
desc.fragmentFunction = skyboxFrag; |
for (int i = 0; i <= 3; i++) |
desc.colorAttachments[i].pixelFormat = view->colorAttachmentFormat[i]; |
desc.depthAttachmentPixelFormat = view->depthPixelFormat; |
desc.stencilAttachmentPixelFormat = view->stencilPixelFormat; |
_skybox_render_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_skybox_render_pipeline, err); |
|
//[desc reset]; |
desc.label = @"GBuffer Render"; |
desc.vertexFunction = gBufferVert; |
desc.fragmentFunction = gBufferFrag; |
_gbuffer_render_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_gbuffer_render_pipeline, err); |
|
desc.label = @"Light Mask Render"; |
desc.vertexFunction = lightVert; |
desc.fragmentFunction = nil; |
//Have active rendertargets but don't want to write to color |
//setup a blendsetate with no color writes for light mask pipeline |
for (int i = 0; i <= 3; i++) |
desc.colorAttachments[i].writeMask = MTLColorWriteMaskNone; |
_light_mask_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_light_mask_pipeline, err); |
|
desc.label = @"Light Color Render"; |
desc.vertexFunction = lightVert; |
desc.fragmentFunction = lightFrag; |
//reset default |
for (int i = 0; i <= 3; i++) |
desc.colorAttachments[i].writeMask = MTLColorWriteMaskAll; |
_light_color_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_light_color_pipeline, err); |
|
desc.label = @"Composition Render"; |
desc.vertexFunction = compositionVert; |
desc.fragmentFunction = compositionFrag; |
_composition_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_composition_pipeline, err); |
|
desc.label = @"Fairy Sprites"; |
desc.vertexFunction = fairyVert; |
desc.fragmentFunction = fairyFrag; |
_fairy_pipeline = [_device newRenderPipelineStateWithDescriptor: desc error: &err]; |
CheckPipelineError(_fairy_pipeline, err); |
} |
|
//Setup depth and stencil state objects |
//********************************************************************* |
{ |
MTLDepthStencilDescriptor *desc = [[MTLDepthStencilDescriptor alloc] init]; |
MTLStencilDescriptor *stencilState = [[MTLStencilDescriptor alloc] init]; |
|
desc.depthWriteEnabled = NO; |
desc.depthCompareFunction = MTLCompareFunctionAlways; |
_noDepthStencilState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
desc.depthWriteEnabled = YES; |
desc.depthCompareFunction = MTLCompareFunctionLessEqual; |
_shadowDepthStencilState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
desc.depthWriteEnabled = YES; |
stencilState.stencilCompareFunction = MTLCompareFunctionAlways; |
stencilState.stencilFailureOperation = MTLStencilOperationKeep; |
stencilState.depthFailureOperation = MTLStencilOperationKeep; |
stencilState.depthStencilPassOperation = MTLStencilOperationReplace; |
stencilState.readMask = 0xFF; |
stencilState.writeMask = 0xFF; |
desc.depthCompareFunction = MTLCompareFunctionLessEqual; |
desc.frontFaceStencil = stencilState; |
desc.backFaceStencil = stencilState; |
_gBufferDepthStencilState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
desc.depthWriteEnabled = NO; |
stencilState.stencilCompareFunction = MTLCompareFunctionEqual; |
stencilState.stencilFailureOperation = MTLStencilOperationKeep; |
stencilState.depthFailureOperation = MTLStencilOperationIncrementClamp; |
stencilState.depthStencilPassOperation = MTLStencilOperationKeep; |
stencilState.readMask = 0xFF; |
stencilState.writeMask = 0xFF; |
desc.depthCompareFunction = MTLCompareFunctionLessEqual; |
desc.frontFaceStencil = stencilState; |
desc.backFaceStencil = stencilState; |
_lightMaskStencilState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
desc.depthWriteEnabled = NO; |
stencilState.stencilCompareFunction = MTLCompareFunctionLess; |
stencilState.stencilFailureOperation = MTLStencilOperationKeep; |
stencilState.depthFailureOperation = MTLStencilOperationDecrementClamp; |
stencilState.depthStencilPassOperation = MTLStencilOperationDecrementClamp; |
stencilState.readMask = 0xFF; |
stencilState.writeMask = 0xFF; |
desc.depthCompareFunction = MTLCompareFunctionLessEqual; |
desc.frontFaceStencil = stencilState; |
desc.backFaceStencil = stencilState; |
_lightColorStencilState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
//Sharing the same state but just always passing depth |
desc.depthCompareFunction = MTLCompareFunctionAlways; |
_lightColorStencilStateNoDepth = [_device newDepthStencilStateWithDescriptor: desc]; |
|
desc.depthWriteEnabled = NO; |
stencilState.stencilCompareFunction = MTLCompareFunctionEqual; |
stencilState.stencilFailureOperation = MTLStencilOperationKeep; |
stencilState.depthFailureOperation = MTLStencilOperationKeep; |
stencilState.depthStencilPassOperation = MTLStencilOperationKeep; |
stencilState.readMask = 0xFF; |
stencilState.writeMask = 0; |
desc.depthCompareFunction = MTLCompareFunctionAlways; |
desc.frontFaceStencil = stencilState; |
desc.backFaceStencil = stencilState; |
_compositionDepthState = [_device newDepthStencilStateWithDescriptor: desc]; |
|
stencilState = nil; |
} |
|
//Load models and create buffers |
//********************************************************************* |
SpriteData sprite; |
sprite.con_scale_intensity[0] = 2.0f; |
sprite.con_scale_intensity[1] = 4.0f; |
sprite.con_scale_intensity[2] = 2.0f; |
|
_spriteBuffer = [_device newBufferWithBytes:&sprite length:sizeof(SpriteData) options:0]; |
|
float texcoords[] = |
{ |
0.0f, 0.0f, |
1.0f, 1.0f, |
0.0f, 1.0f, |
|
0.0f, 0.0f, |
1.0f, 0.0f, |
1.0f, 1.0f |
}; |
|
//All the combinations of quads needed. |
float quadVerts[] = |
{ |
-1.0f, 1.0f, |
1.0f, -1.0f, |
-1.0f, -1.0f, |
-1.0f, 1.0f, |
1.0f, 1.0f, |
1.0f, -1.0f, |
|
-1.0f, 1.0f, |
0.0f, 0.0f, |
-1.0f, 0.0f, |
-1.0f, 1.0f, |
0.0f, 1.0f, |
0.0f, 0.0f, |
|
0.0f, 1.0f, |
1.0f, 0.0f, |
0.0f, 0.0f, |
0.0f, 1.0f, |
1.0f, 1.0f, |
1.0f, 0.0f, |
|
-1.0f, 0.0f, |
0.0f, -1.0f, |
-1.0f, -1.0f, |
-1.0f, 0.0f, |
0.0f, 0.0f, |
0.0f, -1.0f, |
|
0.0f, 0.0f, |
1.0f, -1.0f, |
0.0f, -1.0f, |
0.0f, 0.0f, |
1.0f, 0.0f, |
1.0f, -1.0f, |
|
}; |
|
_quadPositionBuffer = [_device newBufferWithBytes:quadVerts length:sizeof(quadVerts) options:0]; |
_quadTexcoordBuffer = [_device newBufferWithBytes:texcoords length:sizeof(texcoords) options:0]; |
|
NSBundle *bundle = [NSBundle mainBundle]; |
NSString *bundlePath = [bundle pathForResource: @"Temple" ofType: @"obj"]; |
_structureModel = [[AAPLOBJModel alloc] initWithContentsOfFile:bundlePath computeTangentSpace: YES normalizeNormals: NO]; |
_structureModelGroup = [[[_structureModel objects] objectForKey: AAPLOBJModelObjectDefaultKey] objectForKey: @"cage_stairs_01"]; |
|
assert(_structureModel); |
assert(_structureModelGroup); |
|
_structureVertexBuffer = [_device newBufferWithBytes:[[_structureModel vertexData] bytes] length:[[_structureModel vertexData] length] options:0]; |
_structureIndexBuffer = [_device newBufferWithBytes:[[_structureModelGroup indexData] bytes] length:[[_structureModelGroup indexData] length] options:0]; |
|
_structureModelGroupIndexDataType = MTLIndexTypeUInt16; |
if([_structureModelGroup bytesPerIndex] == 4) |
{ |
_structureModelGroupIndexDataType = MTLIndexTypeUInt32; |
} |
|
static float vdata[24][4] = |
{ |
// posx |
{ -1.0f, 1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, -1.0f, 1.0f } |
, |
|
// negz |
{ -1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, -1.0f, 1.0f } |
, |
{ 1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ 1.0f, -1.0f, -1.0f, 1.0f } |
, |
|
// negx |
{ 1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ 1.0f, -1.0f, -1.0f, 1.0f } |
, |
{ 1.0f, 1.0f, 1.0f, 1.0f } |
, |
{ 1.0f, -1.0f, 1.0f, 1.0f } |
, |
|
// posz |
{ 1.0f, 1.0f, 1.0f, 1.0f } |
, |
{ 1.0f, -1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, 1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, 1.0f, 1.0f } |
, |
|
// posy |
{ 1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ 1.0f, 1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, 1.0f, -1.0f, 1.0f } |
, |
{ -1.0f, 1.0f, 1.0f, 1.0f } |
, |
|
// negy |
{ 1.0f, -1.0f, 1.0f, 1.0f } |
, |
{ 1.0f, -1.0f, -1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, 1.0f, 1.0f } |
, |
{ -1.0f, -1.0f, -1.0f, 1.0f } |
, |
}; |
|
_skyboxVertexBuffer = [_device newBufferWithBytes:vdata length:sizeof(vdata) options:0]; |
|
// set up icosahedron for point lights |
float X = 0.5 / Utilities::inscribe; |
float Z = X * (1.0 + sqrtf(5.0)) / 2.0; |
float lightVdata[12][4] = |
{ |
{ -X, 0.0, Z, 1.0f } |
, |
{ X, 0.0, Z, 1.0f } |
, |
{ -X, 0.0, -Z, 1.0f } |
, |
{ X, 0.0, -Z, 1.0f } |
, |
{ 0.0, Z, X, 1.0f } |
, |
{ 0.0, Z, -X, 1.0f } |
, |
{ 0.0, -Z, X, 1.0f } |
, |
{ 0.0, -Z, -X, 1.0f } |
, |
{ Z, X, 0.0, 1.0f } |
, |
{ -Z, X, 0.0, 1.0f } |
, |
{ Z, -X, 0.0, 1.0f } |
, |
{ -Z, -X, 0.0, 1.0f } |
}; |
unsigned short tindices[20][3] = |
{ |
{ 0, 1, 4 } |
, |
{ 0, 4, 9 } |
, |
{ 9, 4, 5 } |
, |
{ 4, 8, 5 } |
, |
{ 4, 1, 8 } |
, |
{ 8, 1, 10 } |
, |
{ 8, 10, 3 } |
, |
{ 5, 8, 3 } |
, |
{ 5, 3, 2 } |
, |
{ 2, 3, 7 } |
, |
{ 7, 3, 10 } |
, |
{ 7, 10, 6 } |
, |
{ 7, 6, 11 } |
, |
{ 11, 6, 0 } |
, |
{ 0, 6, 1 } |
, |
{ 6, 10, 1 } |
, |
{ 9, 11, 0 } |
, |
{ 9, 2, 11 } |
, |
{ 9, 5, 2 } |
, |
{ 7, 11, 2 } |
}; |
|
_lightModelVertexBuffer = [_device newBufferWithBytes:lightVdata length:sizeof(lightVdata) options:0]; |
_lightModelIndexBuffer = [_device newBufferWithBytes:tindices length:sizeof(tindices) options:0]; |
|
//Constant Buffers |
_clearColorBuffer1 = [_device newBufferWithBytes:&((float&)_clear_color_buffers) length:sizeof(_clear_color_buffers) options:0]; |
_clearColorBuffer2 = [_device newBufferWithBytes:&((float&)_clear_color_buffers.light_buffer_clear_color.x) length:sizeof(_clear_color_buffers.light_buffer_clear_color) options:0]; |
|
//Setup dynamic constant buffers |
for(int i = 0; i < _numFrames; i++) |
{ |
[_zOnlyProjectionBuffers addObject: [_device newBufferWithLength: sizeof(float4x4) options:0]]; |
[_skyboxMatrixBuffers addObject: [_device newBufferWithLength: sizeof(float4x4) options:0]]; |
[_modelMatricesBuffers addObject: [_device newBufferWithLength: sizeof(ModelMatrices) options:0]]; |
[_lightModelMatrixBuffers addObject: [_device newBufferWithLength: sizeof(LightModelMatrices)* _fairyCount options:0]]; |
[_lightDataBuffers addObject: [_device newBufferWithLength: sizeof(LightFragmentInputs)*(_fairyCount + 1) options:0]]; |
[_sunDataBuffers addObject: [_device newBufferWithLength: sizeof(MaterialSunData) options:0]]; |
[_fairySpriteBuffers addObject: [_device newBufferWithLength: sizeof(float4x4) options:0]]; |
} |
|
//Load other model data and textures |
bundlePath = [bundle pathForResource:@"skybox" ofType:@"png"]; |
_skyboxTexture = [self loadCubeTextureWithName:[bundlePath UTF8String]]; |
|
bundlePath = [bundle pathForResource:@"fairy" ofType:@"png"]; |
_fairyTexture = [self load2DTextureWithName:[bundlePath UTF8String] pixelFormat:MTLPixelFormatR8Unorm]; |
|
[self loadModel]; |
} |
|
- (id<MTLTexture>)load2DTextureWithName:(const char *)name pixelFormat:(MTLPixelFormat) format |
{ |
|
ImageInfo tex_info; |
CreateImageInfo(name, tex_info); |
|
if (tex_info.bitmapData == NULL) return nil; |
|
if (tex_info.hasAlpha == false && tex_info.bitsPerPixel >= 24) |
{ |
RGB8ImageToRGBA8(&tex_info); |
} |
|
id<MTLTexture> texture = [_device newTextureWithDescriptor: [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format width: tex_info.width height: tex_info.height mipmapped: NO]]; |
|
[texture replaceRegion:MTLRegionMake2D(0, 0, tex_info.width, tex_info.height) |
mipmapLevel:0 |
withBytes:tex_info.bitmapData |
bytesPerRow:tex_info.width * tex_info.bitsPerPixel / 8]; |
|
free(tex_info.bitmapData); |
|
return texture; |
} |
|
- (id<MTLTexture>)loadCubeTextureWithName:(const char *)name |
{ |
ImageInfo tex_info; |
CreateImageInfo(name, tex_info); |
|
if (tex_info.bitmapData == NULL) return nil; |
|
if (tex_info.hasAlpha == 0) |
{ |
RGB8ImageToRGBA8(&tex_info); |
} |
|
unsigned Npixels = tex_info.width * tex_info.width; |
id<MTLTexture> texture = [_device newTextureWithDescriptor: [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat: MTLPixelFormatRGBA8Unorm size: tex_info.width mipmapped: NO]]; |
|
for (int i = 0; i < 6; i++) |
{ |
[texture replaceRegion:MTLRegionMake2D(0, 0, tex_info.width, tex_info.width) |
mipmapLevel:0 |
slice:i |
withBytes:(uint8_t *)(tex_info.bitmapData) + (i * Npixels * 4) |
bytesPerRow:4 * tex_info.width |
bytesPerImage:Npixels * 4]; |
} |
|
free(tex_info.bitmapData); |
|
return texture; |
} |
|
- (void)loadModel |
{ |
if (_structureModelGroup) |
{ |
NSLog(@"IndexData length (bytes): %lu", (unsigned long)[[_structureModelGroup indexData] length]); |
NSLog(@"BytesPerIndex: %lu", [_structureModelGroup bytesPerIndex]); |
NSLog(@"IndexData count: %lu", [_structureModelGroup indexCount]); |
|
|
int i = 0; |
for (AAPLObjMaterialUsage *materialUsage in [_structureModelGroup materialUsages]) |
{ |
NSLog(@"MaterialUsage #%d: location: %lu, length: %lu", |
i, |
(unsigned long)[materialUsage indexRange].location, |
(unsigned long)[materialUsage indexRange].length); |
|
if ([[materialUsage material] diffuseMapName]) |
{ |
NSString *diffuseMapResourceNameAndType = [[materialUsage material] diffuseMapName]; |
NSArray *compAry = [diffuseMapResourceNameAndType componentsSeparatedByString: @"."]; |
NSBundle *bundle = [NSBundle mainBundle]; |
NSString *bundlePath = [bundle pathForResource: [compAry objectAtIndex: 0] ofType: [compAry objectAtIndex: 1]]; |
[_structureModelGroupDiffuseTextures addObject: [self load2DTextureWithName:[bundlePath UTF8String] pixelFormat:MTLPixelFormatRGBA8Unorm]]; |
} |
|
if ([[materialUsage material] specularMapName]) |
{ |
NSString *specularMapResourceNameAndType = [[materialUsage material] specularMapName]; |
NSArray *compAry = [specularMapResourceNameAndType componentsSeparatedByString: @"."]; |
NSBundle *bundle = [NSBundle mainBundle]; |
NSString *bundlePath = [bundle pathForResource: [compAry objectAtIndex: 0] ofType: [compAry objectAtIndex: 1]]; |
[_structureModelGroupSpecularTextures addObject: [self load2DTextureWithName:[bundlePath UTF8String] pixelFormat:MTLPixelFormatRGBA8Unorm]]; |
} |
|
if ([[materialUsage material] bumpMapName]) |
{ |
NSString *bumpMapResourceNameAndType = [[materialUsage material] bumpMapName]; |
NSArray *compAry = [bumpMapResourceNameAndType componentsSeparatedByString: @"."]; |
NSBundle *bundle = [NSBundle mainBundle]; |
NSString *bundlePath = [bundle pathForResource: [compAry objectAtIndex: 0] ofType: [compAry objectAtIndex: 1]]; |
|
[_structureModelGroupBumpTextures addObject: [self load2DTextureWithName:[bundlePath UTF8String] pixelFormat:MTLPixelFormatRGBA8Unorm]]; |
|
} |
i++; |
} |
|
} else |
{ |
NSLog(@"Failed to find group"); |
} |
} |
|
#pragma mark RENDER |
|
- (void)render:(AAPLView *)view |
{ |
dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER); |
|
// create a new command buffer for each renderpass to the current drawable |
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; |
|
// 1st pass (shadow depth map pass) |
[self renderShadowBufferForTime:_frameTime commandBuffer:commandBuffer]; |
|
// ------ Begin 2nd pass (gbuffer pass) ------- // |
|
//Create main encoder used for gbuffer and light buffer |
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:view.renderPassDescriptor]; |
[encoder pushDebugGroup:@"g-buffer pass"]; |
encoder.label = @"g-buffer"; |
|
[encoder setDepthStencilState: _shadowDepthStencilState]; |
|
//GBuffer and lighting render |
[self renderGBufferForTime:_frameTime renderEncoder:encoder]; |
[self renderLightBufferForTime:_frameTime renderEncoder:encoder]; |
|
//Combine the gBuffers for final lit scene (with sun) |
|
[encoder pushDebugGroup:@"sun"]; |
[encoder setRenderPipelineState: _composition_pipeline]; |
[encoder setCullMode: MTLCullModeNone]; |
[encoder setFragmentBuffer: _sunDataBuffers[_currFrameIndex] offset: 0 atIndex: 0]; |
|
[encoder setDepthStencilState: _compositionDepthState]; |
[encoder setStencilReferenceValue: 128]; |
[self drawQuadWithRect:encoder offset:0 useTexture:false]; |
|
// end sun |
[encoder popDebugGroup]; |
|
//Draw Sprites on top of lit scene |
|
[encoder pushDebugGroup:@"fairy sprites"]; |
[encoder setRenderPipelineState: _fairy_pipeline]; |
[encoder setDepthStencilState: _noDepthStencilState]; |
[encoder setCullMode: MTLCullModeNone]; |
|
[encoder setVertexBuffer: _lightDataBuffers[_currFrameIndex] offset: 0 atIndex: 0]; |
[encoder setVertexBuffer: _fairySpriteBuffers[_currFrameIndex] offset: 0 atIndex: 1]; |
|
[encoder setFragmentTexture: _fairyTexture atIndex: 0]; |
[encoder setFragmentBuffer: _spriteBuffer offset: 0 atIndex: 0]; |
|
[encoder drawPrimitives: MTLPrimitiveTypePoint vertexStart: 0 vertexCount: _fairyCount]; |
|
// end fairy sprites |
[encoder popDebugGroup]; |
|
// End 2nd pass (Gbuffer Pass) |
[encoder popDebugGroup]; |
[encoder endEncoding]; |
|
__block dispatch_semaphore_t block_sema = _inflight_semaphore; |
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) { |
dispatch_semaphore_signal(block_sema); |
}]; |
|
_currFrameIndex = (_currFrameIndex + 1) % _numFrames; |
|
// schedule a present once the framebuffer is complete |
[commandBuffer presentDrawable:view.currentDrawable]; |
[commandBuffer commit]; |
} |
|
- (void)drawQuadWithRect:(id<MTLRenderCommandEncoder>) encoder offset:(int)quadOffset useTexture:(bool)textured |
{ |
|
int offset = quadOffset * sizeof(float) * 12; |
|
[encoder setVertexBuffer: _quadPositionBuffer offset: offset atIndex: 0]; |
|
if (textured) |
{ |
[encoder setVertexBuffer: _quadTexcoordBuffer offset: 0 atIndex: 1]; |
} |
|
[encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: 6]; |
} |
|
- (void)renderShadowBufferForTime:(CFTimeInterval) time commandBuffer:(id<MTLCommandBuffer>)commandBuffer |
{ |
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:_shadowRenderPassDescriptor]; |
[encoder pushDebugGroup:@"shadow buffer pass"]; |
encoder.label = @"shadow buffer"; |
|
// setup encoder state |
[encoder setRenderPipelineState: _shadow_render_pipeline]; |
[encoder setDepthStencilState: _shadowDepthStencilState]; |
[encoder setCullMode: MTLCullModeFront]; |
[encoder setDepthBias: 0.01 slopeScale: 1.0f clamp: 0.01]; |
|
[encoder setVertexBuffer: _zOnlyProjectionBuffers[_currFrameIndex] offset: 0 atIndex: 1]; |
[encoder setVertexBuffer: _structureVertexBuffer offset: 0 atIndex: 0]; |
|
for (AAPLObjMaterialUsage *materialUsage in [_structureModelGroup materialUsages]) |
{ |
|
[encoder drawIndexedPrimitives: MTLPrimitiveTypeTriangle indexCount: [materialUsage indexRange].length indexType: _structureModelGroupIndexDataType indexBuffer: _structureIndexBuffer indexBufferOffset: [materialUsage indexRange].location * [_structureModelGroup bytesPerIndex]]; |
} |
|
[encoder popDebugGroup]; |
[encoder endEncoding]; |
} |
|
- (void)renderGBufferForTime:(CFTimeInterval)time renderEncoder:(id<MTLRenderCommandEncoder>) encoder |
{ |
[encoder pushDebugGroup:@"skybox"]; |
|
// Render the skybox and mark in stencil to not process these pixels in subsequent passes |
[encoder setRenderPipelineState: _skybox_render_pipeline]; |
|
[encoder setVertexBuffer: _skyboxVertexBuffer offset: 0 atIndex: 0]; |
[encoder setVertexBuffer: _skyboxMatrixBuffers[_currFrameIndex] offset: 0 atIndex: 1]; |
|
[encoder setFragmentTexture: _skyboxTexture atIndex: 0]; |
|
//Bind pixel constants |
[encoder setFragmentBuffer: _clearColorBuffer1 offset: 0 atIndex: 0]; |
|
for (int i = 0; i < 6; ++i) |
[encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: i * 4 vertexCount: 4]; |
|
// end skybox |
[encoder popDebugGroup]; |
|
//GBuffer Render |
[encoder pushDebugGroup:@"structure"]; |
|
[encoder setRenderPipelineState: _gbuffer_render_pipeline]; |
[encoder setCullMode: MTLCullModeBack]; |
[encoder setDepthStencilState: _gBufferDepthStencilState]; |
[encoder setStencilReferenceValue: 128]; |
|
[encoder setVertexBuffer: _structureVertexBuffer offset: 0 atIndex: 0]; |
[encoder setVertexBuffer: _modelMatricesBuffers[_currFrameIndex] offset: 0 atIndex: 1]; |
|
[encoder setFragmentBuffer: _clearColorBuffer2 offset: 0 atIndex: 0]; |
|
NSInteger i = 0; |
for (AAPLObjMaterialUsage *materialUsage in [_structureModelGroup materialUsages]) |
{ |
|
[encoder setFragmentTexture: _structureModelGroupBumpTextures[i] atIndex: 0]; |
[encoder setFragmentTexture: _structureModelGroupDiffuseTextures[i] atIndex: 1]; |
[encoder setFragmentTexture: _structureModelGroupSpecularTextures[i] atIndex: 2]; |
[encoder setFragmentTexture: _shadow_texture atIndex: 3]; |
i++; |
|
[encoder drawIndexedPrimitives: MTLPrimitiveTypeTriangle indexCount: [materialUsage indexRange].length indexType: _structureModelGroupIndexDataType indexBuffer: _structureIndexBuffer indexBufferOffset: [materialUsage indexRange].location * [_structureModelGroup bytesPerIndex]]; |
} |
|
// end structure |
[encoder popDebugGroup]; |
} |
|
- (void)renderLightBufferForTime:(CFTimeInterval)time renderEncoder:(id<MTLRenderCommandEncoder>) encoder |
{ |
[encoder pushDebugGroup:@"light accumulation"]; |
|
float near = 0.1f; |
|
id<MTLBuffer> lightModelMatricesBuffer = _lightModelMatrixBuffers[_currFrameIndex]; |
id<MTLBuffer> lightDataBuffer = _lightDataBuffers[_currFrameIndex]; |
|
LightFragmentInputs *gpuLights = (LightFragmentInputs *)[lightDataBuffer contents]; |
LightModelMatrices *matrixData = (LightModelMatrices *)[lightModelMatricesBuffer contents]; |
|
LightModelMatrices fairyMatrices[kNumFairies]; |
float4x4 structureCameraMatrix = [self cameraMatrixForTime:_frameTime rateOfRotation:_structureCameraRotationRate]; |
|
// update the lights |
[self updateLightsForTime:_frameTime]; |
|
for (NSInteger i = 0; i < _fairyCount; i++) |
{ |
// update each light |
LightModelMatrices &matrixState = fairyMatrices[i]; |
|
matrixState.mvMatrix = structureCameraMatrix; |
float4x4 tempMatrix = translate(_lightData[i].light_position[0], _lightData[i].light_position[1], _lightData[i].light_position[2]); |
matrixState.mvMatrix = matrixState.mvMatrix * tempMatrix; |
|
tempMatrix = scale(_lightData[i].light_color_radius[3], _lightData[i].light_color_radius[3], _lightData[i].light_color_radius[3]); |
matrixState.mvMatrix = matrixState.mvMatrix * tempMatrix; |
matrixState.mvpMatrix = _projectionMatrix; |
matrixState.mvpMatrix = matrixState.mvpMatrix * matrixState.mvMatrix; |
|
memcpy(matrixData + i, &matrixState, sizeof(LightModelMatrices)); |
|
_lightData[i].view_light_position = structureCameraMatrix * _lightData[i].light_position; |
memcpy(gpuLights + i, &_lightData[i], sizeof(LightFragmentInputs)); |
|
[encoder pushDebugGroup:@"stencil"]; |
[encoder setRenderPipelineState: _light_mask_pipeline]; |
|
// increment stencil on fragments in front of the backside of the volume |
[encoder setDepthStencilState: _lightMaskStencilState]; |
[encoder setStencilReferenceValue: 128]; |
[encoder setCullMode: MTLCullModeFront]; |
|
[encoder setVertexBuffer: _lightModelVertexBuffer offset: 0 atIndex: 0]; |
[encoder setVertexBuffer: lightModelMatricesBuffer offset: i * sizeof(LightModelMatrices) atIndex: 1]; |
[encoder drawIndexedPrimitives: MTLPrimitiveTypeTriangle indexCount: 60 indexType: MTLIndexTypeUInt16 indexBuffer: _lightModelIndexBuffer indexBufferOffset: 0]; |
|
// end stencil |
[encoder popDebugGroup]; |
|
[encoder pushDebugGroup:@"volume"]; |
// shade the front face if it won't clip through the front plane, otherwise use the back plane |
[encoder setRenderPipelineState: _light_color_pipeline]; |
|
bool clip = (_lightData[i].light_position[2] + (_lightData[i].light_color_radius[3] * Utilities::circumscribe / Utilities::inscribe)) < near; |
if (clip) |
{ |
[encoder setDepthStencilState: _lightColorStencilStateNoDepth]; |
[encoder setCullMode: MTLCullModeFront]; |
} else |
{ |
[encoder setDepthStencilState: _lightColorStencilState]; |
[encoder setCullMode: MTLCullModeBack]; |
} |
|
[encoder setStencilReferenceValue: 128]; |
|
[encoder setVertexBuffer: _lightModelVertexBuffer offset: 0 atIndex: 0]; |
[encoder setVertexBuffer: lightModelMatricesBuffer offset: i * sizeof(LightModelMatrices) atIndex: 1]; |
|
[encoder setFragmentBuffer: lightDataBuffer offset: i * sizeof(LightFragmentInputs) atIndex: 0]; |
|
[encoder drawIndexedPrimitives: MTLPrimitiveTypeTriangle indexCount: 60 indexType: MTLIndexTypeUInt16 indexBuffer: _lightModelIndexBuffer indexBufferOffset: 0]; |
|
// end light volume |
[encoder popDebugGroup]; |
} |
|
// light accumulation |
[encoder popDebugGroup]; |
} |
|
- (void)reshape:(AAPLView *)view |
{ |
// called by the view when orientation changes or layer is updated |
|
float aspect = fabsf(view.bounds.size.width / view.bounds.size.height); |
_projectionMatrix = perspective_fov(75.0f, aspect, 0.1f, 25.0f); |
} |
|
#pragma mark UPDATE |
|
- (void)update:(AAPLViewController *)controller |
{ |
_frameTime += controller.timeSinceLastDraw; |
|
// update shadow matrix for shadow pass |
float4x4 shadowMatrix = [self shadowMatrixForTime:_frameTime]; |
float4x4 scaleMatrix = scale(_structureScale, _structureScale, _structureScale); |
shadowMatrix = shadowMatrix * scaleMatrix; |
memcpy([_zOnlyProjectionBuffers[_currFrameIndex] contents], &shadowMatrix, sizeof(float4x4)); |
|
// -------- skybox updates -------- // |
float4x4 skyboxCameraMatrix = [self cameraMatrixForTime:_frameTime rateOfRotation:_skyboxCameraRotationRate]; |
scaleMatrix = scale(_skyboxScale, _skyboxScale, _skyboxScale); |
skyboxCameraMatrix = skyboxCameraMatrix * scaleMatrix; |
|
// shadow matrix buffer |
float4x4 skyboxModelViewProjectionMatrix = _projectionMatrix; |
skyboxModelViewProjectionMatrix = skyboxModelViewProjectionMatrix * skyboxCameraMatrix; |
memcpy([_skyboxMatrixBuffers[_currFrameIndex] contents], &skyboxModelViewProjectionMatrix, sizeof(float4x4)); |
|
// calculate camera matrix |
float4x4 cameraMatrix = [self cameraMatrixForTime:_frameTime rateOfRotation:_structureCameraRotationRate]; |
float3x4 normalMatrix(cameraMatrix.columns[0], cameraMatrix.columns[1], cameraMatrix.columns[2]); |
|
// ------- gBuffer structure updates ------- // |
ModelMatrices* gBuffermatrixState = (ModelMatrices*)[_modelMatricesBuffers[_currFrameIndex] contents]; |
gBuffermatrixState->mvMatrix = cameraMatrix; |
scaleMatrix = scale(_structureScale, _structureScale, _structureScale); |
gBuffermatrixState->mvMatrix = gBuffermatrixState->mvMatrix * scaleMatrix; |
|
//Inverse and Transpose the model matrix for the normal matrix....but if it's just a rotation and uniform scale |
//Inverse is the transpose so just copy it over. |
gBuffermatrixState->normalMatrix = gBuffermatrixState->mvMatrix; |
gBuffermatrixState->mvpMatrix = _projectionMatrix; |
gBuffermatrixState->mvpMatrix = gBuffermatrixState->mvpMatrix * gBuffermatrixState->mvMatrix; |
|
gBuffermatrixState->shadowMatrix = translate(0.5f, 0.5f, 0.0f); |
gBuffermatrixState->shadowMatrix = gBuffermatrixState->shadowMatrix * scale(0.5f, -0.5f, 1.0f); |
gBuffermatrixState->shadowMatrix = gBuffermatrixState->shadowMatrix * [self shadowMatrixForTime:_frameTime]; |
gBuffermatrixState->shadowMatrix = gBuffermatrixState->shadowMatrix * scaleMatrix; |
|
// ------- sun updates ------- // |
MaterialSunData* sunData = (MaterialSunData*)[_sunDataBuffers[_currFrameIndex] contents]; |
float3 direction = [self sunDirectionForTime:_frameTime]; |
sunData->sunDirection = {direction.x, direction.y, direction.z, 0.0f}; |
sunData->sunColor = {1.0f, 0.875f, 0.75f, 1.0f}; |
sunData->sunColor = sunData->sunColor * sunData->sunDirection.y; |
sunData->sunDirection = normalMatrix * sunData->sunDirection.xyz; |
|
// ------- fairy sprites ------- // |
float4x4 mvpMatrix = _projectionMatrix; |
mvpMatrix = mvpMatrix * cameraMatrix; |
memcpy([_fairySpriteBuffers[_currFrameIndex] contents], &mvpMatrix, sizeof(float4x4)); |
} |
|
- (void)updateLightsForTime:(CFTimeInterval) time |
{ |
NSInteger i; |
|
for (i = 0; i < kNumFairies / 2; i++) |
{ |
double fairyTime = time / _fairySpeeds[i] + _fairyPhases[i]; |
fairyTime -= floor(fairyTime); |
|
float fairyAlpha = MIN((0.5 - fabsf(0.5f - fairyTime)) * 8.0f, 1.0f); |
float r = 0.5 + 2.0 * powf(fairyTime, 5.0f); |
|
_lightData[i].light_position = {cosf(_fairyAngles[i]) * r, (float)(fairyTime * 6.0f), sinf(_fairyAngles[i]) * r, 1.0f}; |
_lightData[i].light_color_radius = _fairyColors[i] * fairyAlpha; |
_lightData[i].light_color_radius[3] = kLightRadius; |
} |
|
for (; i < kNumFairies; i++) |
{ |
float r = 2.0 + 0.85 * cos(time / (4.0 * _fairySpeeds[i])); |
float t = time * copysignf(fabsf(_fairyPhases[i]) + 0.25f, _fairyPhases[i] - 0.5) / 4.0f; |
|
_lightData[i].light_position = {cosf(t) * r, 1.5f, sinf(t) * r, 1.0f}; |
_lightData[i].light_color_radius = _fairyColors[i]; |
_lightData[i].light_color_radius[3] = kLightRadius; |
} |
|
_lightData[i].light_position = {0.0f, 2.0f, 2.0f, 1.0f}; |
_lightData[i].light_color_radius = {1.0f, 0.875f, 0.75f, 1.0f}; |
_lightData[i].light_color_radius[3] = 5.0f; |
} |
|
- (float3)sunDirectionForTime:(CFTimeInterval)time |
{ |
double sunTime = (time / 22.5f); |
sunTime -= floor(sunTime); |
sunTime = MIN(sunTime * 1.5, 1.0); |
|
float offAngle = Utilities::DegreesToRadians(-105.0f); |
float pathAngle = Utilities::DegreesToRadians(-sunTime * 180.0f); |
float pathScale = sinf(offAngle); |
|
return {pathScale * cosf(pathAngle), pathScale * sinf(pathAngle), cosf(offAngle)}; |
} |
|
- (float4x4)cameraMatrixForTime:(CFTimeInterval)time rateOfRotation:(float) rotationRate |
{ |
|
float4x4 cameraMatrix = translate(0.0f, -2.0f, 6.0f); |
float4x4 rotationMatrix = rotate(-15.0f, 1.0f, 0.0f, 0.0f); |
|
cameraMatrix = cameraMatrix * rotationMatrix; |
rotationMatrix = rotate(-time * rotationRate, 0.0f, 1.0f, 0.0f); |
cameraMatrix = cameraMatrix * rotationMatrix; |
|
return cameraMatrix; |
|
} |
|
- (float4x4)shadowMatrixForTime:(CFTimeInterval) time |
{ |
float3 sunLocation = [self sunDirectionForTime:time] * 12.0f; |
|
float4x4 cameraMatrix = lookAt(sunLocation, (float3){0.0f, 0.0f, 0.0f}, (float3){0.0f, 1.0f, 0.0f}); |
float4x4 orthoMatrix = ortho2d_oc(-6.5f, 6.5f, -6.5f, 6.5f, 5.5f, 18.5f); |
|
return orthoMatrix * cameraMatrix; |
} |
|
- (void)controller:(AAPLViewController *)controller willPause:(BOOL)pause |
{ |
// timer is suspended/resumed |
// Can do any non-rendering related background work here when suspended |
} |
|
|
@end |