Files
filament/docs/posts/cocoapods/index.html
2020-08-07 11:02:38 -07:00

391 lines
41 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
<link href="//gmpg.org/xfn/11" rel="profile">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="generator" content="Hugo 0.72.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CocoaPods Hello Triangle &middot; Filament</title>
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/print.css" media="print">
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/poole.css">
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/syntax.css">
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/hyde.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Tangerine:700|Inconsolata" rel="stylesheet">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
<link rel="shortcut icon" href="/favicon.png">
<link href="" rel="alternate" type="application/rss+xml" title="Filament" />
</head>
<body class="theme-base-0d ">
<aside class="sidebar">
<div class="container sidebar-sticky">
<div class="sidebar-about">
<a href="https://google.github.io/filament/">
<div style="width:100%; height:150px">
<img src="https://google.github.io/filament/images/filament_logo_small.png" class="lightbulb" />
</div>
</a>
<p class="lead">
</p>
</div>
<nav>
<ul class="sidebar-nav">
<li>
<a href="https://github.com/google/filament"> GitHub Project </a>
</li><li>
<a href="/filament/documentation/"> Core Documentation </a>
</li><li>
<a href="/filament/ios/"> iOS Tutorials </a>
</li><li>
<a href="/filament/webgl/"> Web Docs / Demos </a>
</li>
</ul>
</nav>
</div>
</aside>
<main class="content container">
<div class="post">
<h1>CocoaPods Hello Triangle</h1>
<p>As of release 1.8.0, you can install Filament in your iOS application using CocoaPods.</p>
<p>This guide will walk you through creating a basic &ldquo;hello triangle&rdquo; iOS application using Filament and the Metal backend.</p>
<p><img src="rotating-triangle.gif" alt="a rotating triangle"></p>
<p>The full source for this example is <a href="https://github.com/google/filament/tree/main/ios/samples/HelloCocoaPods">here</a>. If you&rsquo;re just looking to get something up and running quickly, download the project, <code>pod install</code>, build, and run.</p>
<p>We&rsquo;ll be walking through 7 steps to get the rotating triangle up and running. All of the code we&rsquo;ll be writing will be in a single ViewController.mm file, and you can follow along <a href="https://github.com/google/filament/blob/main/ios/samples/HelloCocoaPods/HelloCocoaPods/ViewController.mm">here</a>.</p>
<ul>
<li><a href="#creating-a-boilerplate-app-with-filament">1. Creating a Boilerplate App</a></li>
<li><a href="#instantiating-the-filament-engine">2. Instantiating Filament</a></li>
<li><a href="#creating-a-swapchain">3. Creating a SwapChain</a></li>
<li><a href="#clearing-the-screen">4. Clearing the Screen</a></li>
<li><a href="#drawing-a-triangle">5. Drawing a Triangle</a></li>
<li><a href="#compiling-a-custom-material">6. Compiling a Custom Material</a></li>
<li><a href="#animating-the-triangle">7. Animating the Triangle</a></li>
</ul>
<h2 id="creating-a-boilerplate-app-with-filament">Creating a Boilerplate App with Filament</h2>
<p>We&rsquo;ll start fresh by creating a new Single View App in Xcode.</p>
<p><img src="single-view-app.png" alt="create a single view app in Xcodde"></p>
<p>Give your app a name, and use the default options.</p>
<p><img src="default-options.png" alt="use the default options in Xcode"></p>
<p>If you haven&rsquo;t used CocoaPods before, I recommend watching <a href="https://www.youtube.com/watch?v=iEAjvNRdZa0">this Route 85 video</a> to help you get set up.</p>
<p>Create a Podfile in the Xcode project directory with the following:</p>
<pre><code>platform :ios, '11.0'
target 'HelloCocoaPods' do
pod 'Filament'
end
</code></pre><p>Then run:</p>
<pre><code>$ pod install
</code></pre><p>Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file.</p>
<h2 id="instantiating-the-filament-engine">Instantiating the Filament Engine</h2>
<p>Before we do anything with Filament, we first need to include the appropriate headers. Filament exposes a C++ API, so any files that include Filament headers need to be compiled in a variant of C++. We&rsquo;ll be using Objective-C++.</p>
<p>You should be able to simply change the extension of the default ViewController from .m to .mm, though I&rsquo;ve found Xcode to be buggy with this on occasion. To make sure Xcode recognizes it as an Objective-C++ file, check that the type of file is &ldquo;Objective-C++ Source&rdquo;.</p>
<p><img src="obj-cpp.png" alt="change the type of ViewController.m to Objective-C++"></p>
<p>Then, add the following to the top of ViewController.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Engine.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
using namespace filament;
</code></pre></div><p>We&rsquo;ll need to keep track of a few Filament objects, so let&rsquo;s add a section for private instance variables and add a pointer for our <code>Engine</code> instance.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">@implementation</span> <span style="color:#a6e22e">Viewcontroller</span> {
Engine<span style="color:#f92672">*</span> _engine;
}
</code></pre></div><p>The Filament <code>Engine</code> is our main entrypoint into Filament. We start by instantiating it inside <code>viewDidLoad</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">viewDidLoad</span> {
[super viewDidLoad];
_engine <span style="color:#f92672">=</span> Engine<span style="color:#f92672">::</span>create(Engine<span style="color:#f92672">::</span>Backend<span style="color:#f92672">::</span>METAL);
}
</code></pre></div><p>We specify <code>Engine::Backend::METAL</code> to select the Metal backend. Filament also supports OpenGL on iOS, but we strongly recommend sticking to Metal.</p>
<p>Every Filament object we create must also be destroyed. Add the <code>dealloc</code> method and the following:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">dealloc</span> {
_engine<span style="color:#f92672">-&gt;</span>destroy(<span style="color:#f92672">&amp;</span>_engine);
}
</code></pre></div><p>If you compile and run the app now you should see output similar to the following:</p>
<pre><code>FEngine (64 bits) created at 0x10ab94000 (threading is enabled)
FEngine resolved backend: Metal
</code></pre><h2 id="creating-a-swapchain">Creating a SwapChain</h2>
<p>Before we can render anything, we&rsquo;ll first need to create a <code>SwapChain</code>. The <code>SwapChain</code> represents a platform-specific surface that can be rendered into. On iOS with Metal, it&rsquo;s a <a href="https://developer.apple.com/documentation/quartzcore/cametallayer"><code>CAMetalLayer</code></a>.</p>
<p>We could set up our own <code>CAMetalLayer</code> if we wanted to, but Apple provides a <code>MTKView</code> that is already backed by a <code>CAMetalLayer</code>. It also has a delegate protocol with some methods that will make things easier for us.</p>
<p>Inside Main.storyboard, change the type of ViewController&rsquo;s view to a <code>MTKView</code>.</p>
<p><img src="view.png" alt="ViewController view"></p>
<p><img src="mtkview.gif" alt="change type of MTKView"></p>
<p>Include the SwapChain.h and MTKView.h headers and make the <code>ViewController</code> conform to the <code>MTKViewDelegate</code> protocol.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/SwapChain.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
<span style="color:#75715e">#import &lt;MetalKit/MTKView.h&gt;
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">@interface</span> <span style="color:#a6e22e">ViewController</span> () <span style="color:#f92672">&lt;</span>MTKViewDelegate<span style="color:#f92672">&gt;</span>
<span style="color:#66d9ef">@end</span>
</code></pre></div><p>Add a new private var:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">SwapChain<span style="color:#f92672">*</span> _swapChain;
</code></pre></div><p>Inside <code>viewDidLoad</code>, we&rsquo;ll set our <code>ViewController</code> as the <code>MTKView</code> delegate and instantiate our <code>SwapChain</code>. To instantiate the <code>SwapChain</code>, we pass in <code>view.layer</code> which, because we set our <code>View</code> to a <code>MTKView</code>, will be a <code>CAMetalLayer</code>. Filament&rsquo;s API is platform-agnostic, which is why we need to cast the layer to a <code>void*</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">MTKView<span style="color:#f92672">*</span> mtkView <span style="color:#f92672">=</span> (MTKView<span style="color:#f92672">*</span>) self.view;
mtkView.delegate <span style="color:#f92672">=</span> self;
_swapChain <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>createSwapChain((<span style="color:#66d9ef">__bridge</span> <span style="color:#66d9ef">void</span><span style="color:#f92672">*</span>) mtkView.layer);
</code></pre></div><p>The <code>SwapChain</code> needs to be destroyed in our <code>dealloc</code> function. We&rsquo;ll destroy the objects in the reverse order we created them; the <code>Engine</code> object should always be the the last object we destroy.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-&gt;</span>destroy(_swapChain);
_engine<span style="color:#f92672">-&gt;</span>destroy(<span style="color:#f92672">&amp;</span>_engine);
</code></pre></div><p>Finally, add stubs for some <code>MTKViewDelegate</code> methods, which we&rsquo;ll fill in later.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">mtkView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view <span style="color:#a6e22e">drawableSizeWillChange:</span>(CGSize)size {
<span style="color:#75715e">// todo
</span><span style="color:#75715e"></span>}
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
<span style="color:#75715e">// todo
</span><span style="color:#75715e"></span>}
</code></pre></div><h2 id="clearing-the-screen">Clearing The Screen</h2>
<p>We now have a Filament <code>Engine</code> and <code>SwapChain</code> set up. We&rsquo;ll need a few more objects before we can render anything.</p>
<p>A Filament <code>Renderer</code> gives us an API to render frames into the <code>SwapChain</code>. It takes a <code>View</code>, which defines a <code>Viewport</code>, <code>Scene</code> and <code>Camera</code> for rendering. The <code>Camera</code> represents a vantage point into a <code>Scene</code>, which contains references to all the entities we want to render.</p>
<p>Creating these are objects is straightforward. First, include the appropriate headers</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Renderer.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/View.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Camera.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Scene.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Viewport.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
<span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;utils/Entity.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;utils/EntityManager.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">using</span> <span style="color:#66d9ef">namespace</span> utils;
</code></pre></div><p>add the following private vars</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">Renderer<span style="color:#f92672">*</span> _renderer;
View<span style="color:#f92672">*</span> _view;
Scene<span style="color:#f92672">*</span> _scene;
Camera<span style="color:#f92672">*</span> _camera;
Entity _cameraEntity;
</code></pre></div><p>and then instantiate them</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_renderer <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>createRenderer();
_view <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>createView();
_scene <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>createScene();
</code></pre></div><p>The camera is a bit special. Filament uses an entity-component system, so we&rsquo;ll first need to create an <code>Entity</code> which we then attach a <code>Camera</code> component to.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_cameraEntity <span style="color:#f92672">=</span> EntityManager<span style="color:#f92672">::</span>get().create();
_camera <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>createCamera(_cameraEntity);
</code></pre></div><p>Let&rsquo;s also inform our <code>Renderer</code> to clear to a light blue clear color, so we can know everything is working.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_renderer<span style="color:#f92672">-&gt;</span>setClearOptions({
.clearColor <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0.25f</span>, <span style="color:#ae81ff">0.5f</span>, <span style="color:#ae81ff">1.0f</span>, <span style="color:#ae81ff">1.0f</span>},
.clear <span style="color:#f92672">=</span> true
});
</code></pre></div><p>The <code>Camera</code> and <code>Scene</code> need to be wired up to the <code>View</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_view<span style="color:#f92672">-&gt;</span>setScene(_scene);
_view<span style="color:#f92672">-&gt;</span>setCamera(_camera);
</code></pre></div><p>Our newly created objects get cleaned up inside <code>dealloc</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-&gt;</span>destroyCameraComponent(_cameraEntity);
EntityManager<span style="color:#f92672">::</span>get().destroy(_cameraEntity);
_engine<span style="color:#f92672">-&gt;</span>destroy(_scene);
_engine<span style="color:#f92672">-&gt;</span>destroy(_view);
_engine<span style="color:#f92672">-&gt;</span>destroy(_renderer);
</code></pre></div><p>We need to set the <code>Viewport</code> on our <code>View</code>, which we want to do whenever the size of our <code>SwapChain</code> changes. We&rsquo;ll also update the projection matrix on our camera.</p>
<p>Let&rsquo;s create a new method, <code>resize:</code>, which will update the <code>Viewport</code> on our <code>View</code> to a given size. We&rsquo;ll call it in the <code>mtkView:drawableSizeWillChange:</code> delegate method, and at the end of <code>viewDidLoad</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">resize:</span>(CGSize)size {
_view<span style="color:#f92672">-&gt;</span>setViewport({<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, (uint32_t) size.width, (uint32_t) size.height});
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> aspect <span style="color:#f92672">=</span> size.width <span style="color:#f92672">/</span> size.height;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> left <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">2.0</span> <span style="color:#f92672">*</span> aspect;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> right <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.0</span> <span style="color:#f92672">*</span> aspect;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> bottom <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">2.0</span>;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> top <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.0</span>;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> near <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>;
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> far <span style="color:#f92672">=</span> <span style="color:#ae81ff">1.0</span>;
_camera<span style="color:#f92672">-&gt;</span>setProjection(Camera<span style="color:#f92672">::</span>Projection<span style="color:#f92672">::</span>ORTHO, left, right, bottom, top, near, far);
}
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">viewDidLoad</span> {
...
<span style="color:#75715e">// Give our View a starting size based on the drawable size.
</span><span style="color:#75715e"></span> [self resize:mtkView.drawableSize];
}
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">mtkView</span>(nonnull MTKView<span style="color:#f92672">*</span>)view <span style="color:#a6e22e">drawableSizeWillChange:</span>(CGSize)size {
[self resize:size];
}
</code></pre></div><p>Lastly, in order to render, we&rsquo;ll call a few Filament API methods inside the <code>drawInMTKView:</code> method:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
<span style="color:#66d9ef">if</span> (_renderer<span style="color:#f92672">-&gt;</span>beginFrame(_swapChain)) {
_renderer<span style="color:#f92672">-&gt;</span>render(_view);
_renderer<span style="color:#f92672">-&gt;</span>endFrame();
}
}
</code></pre></div><p>The <code>beginFrame</code> method instructs Filament to start rendering to our specific <code>SwapChain</code> instance. It returns <code>true</code> if the engine is ready for another frame. It returns <code>false</code> to signal us to skip this frame, which could happen if we&rsquo;re sending frames down too quickly for the GPU to process.</p>
<p>At this point, you should be able to build and run the app, and you&rsquo;ll see a blue screen.</p>
<p><img src="blue-screen.png" alt="blue screen after clearing"></p>
<h2 id="drawing-a-triangle">Drawing a Triangle</h2>
<p>In order to draw a triangle, we need to create vertex and index buffers to define its geometry. We&rsquo;ll then create a <code>Renderable</code> component.</p>
<p>We&rsquo;ll start by including some additional headers and adding a few new private vars:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/VertexBuffer.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/IndexBuffer.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/RenderableManager.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
...
VertexBuffer<span style="color:#f92672">*</span> _vertexBuffer;
IndexBuffer<span style="color:#f92672">*</span> _indexBuffer;
Entity _triangle;
</code></pre></div><p>First, we&rsquo;ll define the data for a single vertex.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">struct</span> Vertex {
math<span style="color:#f92672">::</span>float2 position;
math<span style="color:#f92672">::</span>float3 color;
};
</code></pre></div><p>Creating a <code>VertexBuffer</code> and <code>IndexBuffer</code> is a matter of giving Filament a pointer to the data, along with information on its layout and size. Filament uses <code>BufferDescriptors</code> to accomplish this.</p>
<p>Inside <code>viewDidLoad</code>, we&rsquo;ll statically define some verticies and indices and create a <code>BufferDescriptor</code> for each.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> Vertex TRIANGLE_VERTICES[<span style="color:#ae81ff">3</span>] <span style="color:#f92672">=</span> {
{ { <span style="color:#ae81ff">0.867</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">0.500</span>}, {<span style="color:#ae81ff">1.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>} },
{ { <span style="color:#ae81ff">0.000</span>, <span style="color:#ae81ff">1.000</span>}, {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>, <span style="color:#ae81ff">0.0</span>} },
{ {<span style="color:#f92672">-</span><span style="color:#ae81ff">0.867</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">0.500</span>}, {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>} },
};
<span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> uint16_t TRIANGLE_INDICES[<span style="color:#ae81ff">3</span>] <span style="color:#f92672">=</span> { <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span> };
VertexBuffer<span style="color:#f92672">::</span>BufferDescriptor vertices(TRIANGLE_VERTICES, <span style="color:#66d9ef">sizeof</span>(Vertex) <span style="color:#f92672">*</span> <span style="color:#ae81ff">3</span>, nullptr);
IndexBuffer<span style="color:#f92672">::</span>BufferDescriptor indices(TRIANGLE_INDICES, <span style="color:#66d9ef">sizeof</span>(uint16_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">3</span>, nullptr);
</code></pre></div><p>The last argument is an optional callback function, which will be called after Filament is done uploading the data to the GPU. Inside the callback, you&rsquo;d typically release the memory of any buffers via a <code>free</code> or <code>delete</code> call. We pass <code>nullptr</code> because we don&rsquo;t need a callback as our vertex and index buffer memory is static.</p>
<p>Now we can instantiate our <code>VertexBuffer</code> and <code>IndexBuffer</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">using Type <span style="color:#f92672">=</span> VertexBuffer<span style="color:#f92672">::</span>AttributeType;
<span style="color:#66d9ef">const</span> uint8_t stride <span style="color:#f92672">=</span> <span style="color:#66d9ef">sizeof</span>(Vertex);
_vertexBuffer <span style="color:#f92672">=</span> VertexBuffer<span style="color:#f92672">::</span>Builder()
.vertexCount(<span style="color:#ae81ff">3</span>)
.bufferCount(<span style="color:#ae81ff">1</span>)
.attribute(VertexAttribute<span style="color:#f92672">::</span>POSITION, <span style="color:#ae81ff">0</span>, Type<span style="color:#f92672">::</span>FLOAT2, offsetof(Vertex, position), stride)
.attribute(VertexAttribute<span style="color:#f92672">::</span>COLOR, <span style="color:#ae81ff">0</span>, Type<span style="color:#f92672">::</span>FLOAT3, offsetof(Vertex, color), stride)
.build(<span style="color:#f92672">*</span>_engine);
_indexBuffer <span style="color:#f92672">=</span> IndexBuffer<span style="color:#f92672">::</span>Builder()
.indexCount(<span style="color:#ae81ff">3</span>)
.bufferType(IndexBuffer<span style="color:#f92672">::</span>IndexType<span style="color:#f92672">::</span>USHORT)
.build(<span style="color:#f92672">*</span>_engine);
_vertexBuffer<span style="color:#f92672">-&gt;</span>setBufferAt(<span style="color:#f92672">*</span>_engine, <span style="color:#ae81ff">0</span>, std<span style="color:#f92672">::</span>move(vertices));
_indexBuffer<span style="color:#f92672">-&gt;</span>setBuffer(<span style="color:#f92672">*</span>_engine, std<span style="color:#f92672">::</span>move(indices));
</code></pre></div><p>We first create an <code>Entity</code> like we did for our camera. This time, we&rsquo;re attaching a <code>Renderable</code> component to the entity. The <code>Renderable</code> component takes geometry defined by our vertex and index buffers, and makes the entity visible in our scene.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_triangle <span style="color:#f92672">=</span> utils<span style="color:#f92672">::</span>EntityManager<span style="color:#f92672">::</span>get().create();
using Primitive <span style="color:#f92672">=</span> RenderableManager<span style="color:#f92672">::</span>PrimitiveType;
RenderableManager<span style="color:#f92672">::</span>Builder(<span style="color:#ae81ff">1</span>)
.geometry(<span style="color:#ae81ff">0</span>, Primitive<span style="color:#f92672">::</span>TRIANGLES, _vertexBuffer, _indexBuffer, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">3</span>)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(<span style="color:#f92672">*</span>_engine, _triangle);
<span style="color:#75715e">// Add the triangle to the scene.
</span><span style="color:#75715e"></span>_scene<span style="color:#f92672">-&gt;</span>addEntity(_triangle);
</code></pre></div><p>Destroy the entity and buffers in <code>dealloc</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-&gt;</span>destroy(_triangle);
EntityManager<span style="color:#f92672">::</span>get().destroy(_triangle);
_engine<span style="color:#f92672">-&gt;</span>destroy(_indexBuffer);
_engine<span style="color:#f92672">-&gt;</span>destroy(_vertexBuffer);
</code></pre></div><p>If you build and run the app now, you should see a plain white triangle. When we created the renderable, we didn&rsquo;t specify any specific <code>Material</code> to use, so Filament used a default, white material. Let&rsquo;s create a custom material to color the triangle.</p>
<p><img src="white-triangle.png" alt="a white triangle"></p>
<h2 id="compiling-a-custom-material">Compiling a Custom Material</h2>
<p>For simplicity, we&rsquo;re going to compile a custom material at runtime. For production, we recommend using our matc tool to compile materials offline. You can download it as part of one of our <a href="https://github.com/google/filament/releases">releases</a>.</p>
<p>First, add a few more headers. We&rsquo;ll be using Filament&rsquo;s filamat library to compile a custom material.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/Material.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/MaterialInstance.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>
<span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filamat/MaterialBuilder.h&gt;</span><span style="color:#75715e">
</span></code></pre></div><p>We&rsquo;ll store our material in a new private var. We&rsquo;ll also need one to store a material <em>instance</em>. You can think of a material as a &ldquo;template&rdquo;, where a material instance is an instantiation of the template (similar to OOP classes and instances). For more information on Filament materials, read the <a href="https://google.github.io/filament/Materials.html">Filament Materials Guide</a>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">Material<span style="color:#f92672">*</span> _material;
MaterialInstance<span style="color:#f92672">*</span> _materialInstance;
</code></pre></div><p>We&rsquo;ll use the filamat library to compile a material into a package, which we can then load into Filament. The material will be simple; it will load the interpolated color attribute and set it as the <code>baseColor</code>.</p>
<p>Make sure to insert this code into <code>viewDidLoad</code> <em>before</em> we create our <code>Renderable</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// init must be called before we can build any materials.
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>init();
<span style="color:#75715e">// Compile a custom material to use on the triangle.
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>Package pkg <span style="color:#f92672">=</span> filamat<span style="color:#f92672">::</span>MaterialBuilder()
<span style="color:#75715e">// The material name, only used for debugging purposes.
</span><span style="color:#75715e"></span> .name(<span style="color:#e6db74">&#34;Triangle material&#34;</span>)
<span style="color:#75715e">// Use the unlit shading mode, because we don&#39;t have any lights in our scene.
</span><span style="color:#75715e"></span> .shading(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>Shading<span style="color:#f92672">::</span>UNLIT)
<span style="color:#75715e">// Expose the COLOR attribute visible to our shader code.
</span><span style="color:#75715e"></span> .require(VertexAttribute<span style="color:#f92672">::</span>COLOR)
<span style="color:#75715e">// Custom GLSL fragment shader
</span><span style="color:#75715e"></span> .material(<span style="color:#e6db74">&#34;void material (inout MaterialInputs material) {&#34;</span>
<span style="color:#e6db74">&#34; prepareMaterial(material);&#34;</span>
<span style="color:#e6db74">&#34; material.baseColor = getColor();&#34;</span>
<span style="color:#e6db74">&#34;}&#34;</span>)
<span style="color:#75715e">// Compile for Metal on mobile platforms.
</span><span style="color:#75715e"></span> .targetApi(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>TargetApi<span style="color:#f92672">::</span>METAL)
.platform(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>Platform<span style="color:#f92672">::</span>MOBILE)
.build();
assert(pkg.isValid());
<span style="color:#75715e">// shutdown should be called after all materials are built.
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>shutdown();
</code></pre></div><p>Now that we have a <code>filamat::Package</code> representing the material, we can use it to instantiate a Filament <code>Material</code>. Note that again, we recommend using the matc command-line tool to compile material packages during your app&rsquo;s compilation phase if possible, instead of at run-time.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Create a Filament material from the Package.
</span><span style="color:#75715e"></span>_material <span style="color:#f92672">=</span> Material<span style="color:#f92672">::</span>Builder()
.package(pkg.getData(), pkg.getSize())
.build(<span style="color:#f92672">*</span>_engine);
_materialInstance <span style="color:#f92672">=</span> _material<span style="color:#f92672">-&gt;</span>getDefaultInstance();
</code></pre></div><p>Now we can use the <code>MaterialInstance</code> when creating our <code>Renderable</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Create a renderable using our geometry and material.
</span><span style="color:#75715e"></span>using Primitive <span style="color:#f92672">=</span> RenderableManager<span style="color:#f92672">::</span>PrimitiveType;
RenderableManager<span style="color:#f92672">::</span>Builder(<span style="color:#ae81ff">1</span>)
.geometry(<span style="color:#ae81ff">0</span>, Primitive<span style="color:#f92672">::</span>TRIANGLES, _vertexBuffer, _indexBuffer, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">3</span>)
<span style="color:#75715e">// Use the MaterialInstance we just created.
</span><span style="color:#75715e"></span> .material(<span style="color:#ae81ff">0</span>, _materialInstance)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(<span style="color:#f92672">*</span>_engine, _triangle);
</code></pre></div><p>Lastly, we make sure to destroy everything inside <code>dealloc</code>.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-&gt;</span>destroy(_materialInstance);
_engine<span style="color:#f92672">-&gt;</span>destroy(_material);
</code></pre></div><p>Build and run. You should see the same triangle, but with colors.</p>
<p><img src="colored-triangle.png" alt="the triangle with our custom material"></p>
<h2 id="animating-the-triangle">Animating the Triangle</h2>
<p>We&rsquo;ll do this by animating a transform on our triangle entity. First, include a new header.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;filament/TransformManager.h&gt;</span><span style="color:#75715e">
</span></code></pre></div><p>When we create our triangle entity, we&rsquo;ll also attach a transform component. We&rsquo;ve already seen two other components: <code>Renderable</code> and <code>Camera</code>. The <code>Transform</code> component allows us to set world-space transformations on entities.</p>
<p>Inside <code>viewDidLoad</code>, after we create the triangle entity&rsquo;s <code>Renderable</code> component, we&rsquo;ll also attach a <code>Transform</code> component.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Add a Transform component to the triangle, so we can animate it.
</span><span style="color:#75715e"></span>_engine<span style="color:#f92672">-&gt;</span>getTransformManager().create(_triangle);
</code></pre></div><p>Create a new function, <code>update</code>, and add call it inside the <code>drawInMTKView:</code> method.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">update</span> {
<span style="color:#66d9ef">auto</span><span style="color:#f92672">&amp;</span> tm <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-&gt;</span>getTransformManager();
<span style="color:#66d9ef">auto</span> i <span style="color:#f92672">=</span> tm.getInstance(_triangle);
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">auto</span> time <span style="color:#f92672">=</span> CACurrentMediaTime();
tm.setTransform(i, math<span style="color:#f92672">::</span>mat4f<span style="color:#f92672">::</span>rotation(time, math<span style="color:#f92672">::</span>float3 {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>}));
}
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
[self update];
<span style="color:#66d9ef">if</span> (_renderer<span style="color:#f92672">-&gt;</span>beginFrame(_swapChain)) {
_renderer<span style="color:#f92672">-&gt;</span>render(_view);
_renderer<span style="color:#f92672">-&gt;</span>endFrame();
}
}
</code></pre></div><p>Now we should see the triangle rotate around its z axis.</p>
<p><img src="rotating-triangle.gif" alt="a rotating triangle"></p>
<h2 id="next-steps">Next Steps</h2>
<p>In this guide we&rsquo;ve covered how to install Filament with CocoaPods and get rendering using the Metal backend. We also compiled a custom material. Again, here&rsquo;s the <a href="https://github.com/google/filament/tree/main/ios/samples/HelloCocoaPods">complete sample code</a> for the app. If you&rsquo;re interesting in learning more, check out Filament&rsquo;s additional <a href="https://github.com/google/filament/tree/main/ios/samples">iOS samples</a>. If you have any problems, feel free to open an <a href="https://github.com/google/filament/issues">issue</a>.</p>
</div>
</main>
</body>
</html>