I have 2 objects, one is with texture, the other one is without texture. I use 2 shaders and I use 2 render pipelines to draw the 2 objects. the 2 object are drawn fine...but when the second obejct is drawn ...it overlaps the first object, so only the second object is on screen... don't know where goes wrong...Maybe because the pipeline is not used right, in the last part of the draw() function. the model draws properly first, but when the sky is drew, the model disappeared...Please help me,,,Thanks
Here is my code:
class PanoViewController: GameViewController {
//sky
var depthStencilState: MTLDepthStencilState! = nil
var vertexBufferSky: MTLBuffer! = nil
var uniformBufferSky: MTLBuffer! = nil
var depthTextureSky: MTLTexture! = nil
var diffuseTextureSky: MTLTexture! = nil
var samplerStateSky: MTLSamplerState! = nil
//model
var depthStencilStateModel: MTLDepthStencilState! = nil
var vertexBufferModel: MTLBuffer! = nil
var normalBufferModel: MTLBuffer! = nil
var colorBufferModel: MTLBuffer! = nil
var uniformBufferModel: MTLBuffer! = nil
override func buildPipeline() {
//Model
let library = device!.newDefaultLibrary()!
//pipeline descriptor
// buildPipelinForSky(library)
// buildPipelineForModel(library)
//pipeline
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(buildPipelinForSky(library))
pipelineModel = try device!.newRenderPipelineStateWithDescriptor(buildPipelineForModel(library))
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
}
func buildPipelineForModel(library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let pipeLineDesc = MTLRenderPipelineDescriptor()
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
pipeLineDesc.vertexFunction = vertexFunctionModel
pipeLineDesc.vertexDescriptor = vertexDescriptorModel
pipeLineDesc.fragmentFunction = fragmentFunctionModel
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
return pipeLineDesc
}
func buildPipelinForSky(library: MTLLibrary ) -> MTLRenderPipelineDescriptor{
let pipeLineDesc = MTLRenderPipelineDescriptor()
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
pipeLineDesc.vertexFunction = vertexFunctionSky
pipeLineDesc.vertexDescriptor = vertexDescriptorSky
pipeLineDesc.fragmentFunction = fragmentFunctionSky
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
pipeLineDesc.depthAttachmentPixelFormat = .Depth32Float
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
return pipeLineDesc
}
override func buildResources() {
//Model
(vertexBufferModel,normalBufferModel,colorBufferModel) = PointCloud.model(device!)
uniformBufferModel = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
//Sky
vertexBufferSky = SkySphere.sphere(device!)
uniformBufferSky = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
diffuseTextureSky = self.textureForImage(UIImage(named: "bluemarble")!, device: device!)
}
override func resize() {
//Model
super.resize()
//Sky
let layerSizeSky = metalLayer.drawableSize
let depthTextureDescriptorSky = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.Depth32Float,
width: Int(layerSizeSky.width),
height: Int(layerSizeSky.height),
mipmapped: false)
depthTextureSky = device!.newTextureWithDescriptor(depthTextureDescriptorSky)
}
override func draw() {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
//command buffer
let commandBuffer = commandQueue.commandBuffer()
commandBuffer.addCompletedHandler{ [weak self] commandBuffer in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//model
var passDescriptor = MTLRenderPassDescriptor()
passDescriptor = passDescrForModel(drawable,passDescriptor: passDescriptor)
var commandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoder.pushDebugGroup("model pass")
commandEncoder.label = "model buffer"
pointCloudDraw(commandEncoder)
commandEncoder.endEncoding()
commandEncoder.popDebugGroup()
passDescriptor = passDescrForSky(drawable,passDescriptor: passDescriptor)
commandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoder.pushDebugGroup("sky pass")
commandEncoder.label = "sky buffer"
skyDraw(commandEncoder)
commandEncoder.popDebugGroup()
commandEncoder.endEncoding()
commandBuffer.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBuffer.commit()
}
}
func passDescrForModel(drawable: CAMetalDrawable, passDescriptor:MTLRenderPassDescriptor) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
// passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
return passDescriptor
}
func passDescrForSky(drawable: CAMetalDrawable, passDescriptor:MTLRenderPassDescriptor) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
// passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.depthAttachment.texture = depthTextureSky
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .DontCare
return passDescriptor
}
func pointCloudDraw(commandencodeModel: MTLRenderCommandEncoder){
commandencodeModel.setRenderPipelineState(pipelineModel)
commandencodeModel.setDepthStencilState(depthStencilState)
commandencodeModel.setFrontFacingWinding(.CounterClockwise)
commandencodeModel.setCullMode(.Back)
commandencodeModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandencodeModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandencodeModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandencodeModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandencodeModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandencodeModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
}
func skyDraw(commandencodeSky: MTLRenderCommandEncoder) {
commandencodeSky.setRenderPipelineState(pipelineSky)
commandencodeSky.setDepthStencilState(depthStencilState)
commandencodeSky.setFrontFacingWinding(.CounterClockwise)
commandencodeSky.setCullMode(.Back)
commandencodeSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandencodeSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandencodeSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandencodeSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandencodeSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
}
}
Change the passDescrForSky function passDescriptor.colorAttachments[0].loadAction = .Load
Then the 2 objects are all shows up...Something happen to the position...the first one needs to be adjusted...
Related
I need to convert YUV Frames to CVPixelBuffer that I get from OTVideoFrame Class
This class provides an array of planes in the video frame which contains three elements for y,u,v frame each at index 0,1,2.
#property (nonatomic, retain) NSPointerArray *planes
and format of the video frame
#property (nonatomic, retain) OTVideoFormat *format
That contains Properties like width, height, bytesPerRow etc. of the frame
I need to add filter to the image I receive in the form of OTVideoFrame, I have already tried these answers :
How to convert from YUV to CIImage for iOS
Create CVPixelBuffer from YUV with IOSurface backed
These two links have the solutions in Objective-C but I want to do it in swift. One of the answers in second link was in swift but it lacks some information about the YUVFrame struct that the answer has reference to.
The Format that I receive is NV12
Here is what I have been trying to do till now but I don't know how to proceed next :-
/**
* Calcualte the size of each plane from OTVideoFrame.
*
* #param frame The frame to render.
* #return tuple containing three elements for size of each plane
*/
fileprivate func calculatePlaneSize(forFrame frame: OTVideoFrame)
-> (ySize: Int, uSize: Int, vSize: Int){
guard let frameFormat = frame.format
else {
return (0, 0 ,0)
}
let baseSize = Int(frameFormat.imageWidth * frameFormat.imageHeight) * MemoryLayout<GLubyte>.size
return (baseSize, baseSize / 4, baseSize / 4)
}
/**
* Renders a frame to the video renderer.
*
* #param frame The frame to render.
*/
func renderVideoFrame(_ frame: OTVideoFrame) {
let planeSize = calculatePlaneSize(forFrame: frame)
let yPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.ySize)
let uPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.uSize)
let vPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.vSize)
memcpy(yPlane, frame.planes?.pointer(at: 0), planeSize.ySize)
memcpy(uPlane, frame.planes?.pointer(at: 1), planeSize.uSize)
memcpy(vPlane, frame.planes?.pointer(at: 2), planeSize.vSize)
let yStride = frame.format!.bytesPerRow.object(at: 0) as! Int
// multiply chroma strides by 2 as bytesPerRow represents 2x2 subsample
let uStride = frame.format!.bytesPerRow.object(at: 1) as! Int
let vStride = frame.format!.bytesPerRow.object(at: 2) as! Int
let width = frame.format!.imageWidth
let height = frame.format!.imageHeight
var pixelBuffer: CVPixelBuffer? = nil
var err: CVReturn;
err = CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height), kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, nil, &pixelBuffer)
if (err != 0) {
NSLog("Error at CVPixelBufferCreate %d", err)
fatalError()
}
}
Taking Guidance from those two links I tried to create Pixel buffer but I got stuck every time at this point because the conversion of the Objective-C code after this is not similar to what we have in Swift 3.
For those who are looking for a fast solution, I did with swift Accelerate
using vImageConvert_AnyToAny(_:_:_:_:_:) function.
import Foundation
import Accelerate
import UIKit
import OpenTok
class Accelerater{
var infoYpCbCrToARGB = vImage_YpCbCrToARGB()
init() {
_ = configureYpCbCrToARGBInfo()
}
func configureYpCbCrToARGBInfo() -> vImage_Error {
print("Configuring")
var pixelRange = vImage_YpCbCrPixelRange(Yp_bias: 0,
CbCr_bias: 128,
YpRangeMax: 255,
CbCrRangeMax: 255,
YpMax: 255,
YpMin: 1,
CbCrMax: 255,
CbCrMin: 0)
let error = vImageConvert_YpCbCrToARGB_GenerateConversion(
kvImage_YpCbCrToARGBMatrix_ITU_R_601_4!,
&pixelRange,
&infoYpCbCrToARGB,
kvImage420Yp8_Cb8_Cr8,
kvImageARGB8888,
vImage_Flags(kvImagePrintDiagnosticsToConsole))
print("Configration done \(error)")
return error
}
public func convertFrameVImageYUV(toUIImage frame: OTVideoFrame, flag: Bool) -> UIImage {
var result: UIImage? = nil
let width = frame.format?.imageWidth ?? 0
let height = frame.format?.imageHeight ?? 0
var pixelBuffer: CVPixelBuffer? = nil
_ = CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height), kCVPixelFormatType_32BGRA, nil, &pixelBuffer)
_ = convertFrameVImageYUV(frame, to: pixelBuffer)
var ciImage: CIImage? = nil
if let pixelBuffer = pixelBuffer {
ciImage = CIImage(cvPixelBuffer: pixelBuffer)
}
let temporaryContext = CIContext(options: nil)
var uiImage: CGImage? = nil
if let ciImage = ciImage {
uiImage = temporaryContext.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer!), height: CVPixelBufferGetHeight(pixelBuffer!)))
}
if let uiImage = uiImage {
result = UIImage(cgImage: uiImage)
}
CVPixelBufferUnlockBaseAddress(pixelBuffer!, [])
return result!
}
func convertFrameVImageYUV(_ frame: OTVideoFrame, to pixelBufferRef: CVPixelBuffer?) -> vImage_Error{
let start = CFAbsoluteTimeGetCurrent()
if pixelBufferRef == nil {
print("No PixelBuffer refrance found")
return vImage_Error(kvImageInvalidParameter)
}
let width = frame.format?.imageWidth ?? 0
let height = frame.format?.imageHeight ?? 0
let subsampledWidth = frame.format!.imageWidth/2
let subsampledHeight = frame.format!.imageHeight/2
print("subsample height \(subsampledHeight) \(subsampledWidth)")
let planeSize = calculatePlaneSize(forFrame: frame)
print("ysize : \(planeSize.ySize) \(planeSize.uSize) \(planeSize.vSize)")
let yPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.ySize)
let uPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.uSize)
let vPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.vSize)
memcpy(yPlane, frame.planes?.pointer(at: 0), planeSize.ySize)
memcpy(uPlane, frame.planes?.pointer(at: 1), planeSize.uSize)
memcpy(vPlane, frame.planes?.pointer(at: 2), planeSize.vSize)
let yStride = frame.format!.bytesPerRow.object(at: 0) as! Int
// multiply chroma strides by 2 as bytesPerRow represents 2x2 subsample
let uStride = frame.format!.bytesPerRow.object(at: 1) as! Int
let vStride = frame.format!.bytesPerRow.object(at: 2) as! Int
var yPlaneBuffer = vImage_Buffer(data: yPlane, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: yStride)
var uPlaneBuffer = vImage_Buffer(data: uPlane, height: vImagePixelCount(subsampledHeight), width: vImagePixelCount(subsampledWidth), rowBytes: uStride)
var vPlaneBuffer = vImage_Buffer(data: vPlane, height: vImagePixelCount(subsampledHeight), width: vImagePixelCount(subsampledWidth), rowBytes: vStride)
CVPixelBufferLockBaseAddress(pixelBufferRef!, .readOnly)
let pixelBufferData = CVPixelBufferGetBaseAddress(pixelBufferRef!)
let rowBytes = CVPixelBufferGetBytesPerRow(pixelBufferRef!)
var destinationImageBuffer = vImage_Buffer()
destinationImageBuffer.data = pixelBufferData
destinationImageBuffer.height = vImagePixelCount(height)
destinationImageBuffer.width = vImagePixelCount(width)
destinationImageBuffer.rowBytes = rowBytes
var permuteMap: [UInt8] = [3, 2, 1, 0] // BGRA
let convertError = vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(&yPlaneBuffer, &uPlaneBuffer, &vPlaneBuffer, &destinationImageBuffer, &infoYpCbCrToARGB, &permuteMap, 255, vImage_Flags(kvImagePrintDiagnosticsToConsole))
CVPixelBufferUnlockBaseAddress(pixelBufferRef!, [])
yPlane.deallocate()
uPlane.deallocate()
vPlane.deallocate()
let end = CFAbsoluteTimeGetCurrent()
print("Decoding time \((end-start)*1000)")
return convertError
}
fileprivate func calculatePlaneSize(forFrame frame: OTVideoFrame)
-> (ySize: Int, uSize: Int, vSize: Int)
{
guard let frameFormat = frame.format
else {
return (0, 0 ,0)
}
let baseSize = Int(frameFormat.imageWidth * frameFormat.imageHeight) * MemoryLayout<GLubyte>.size
return (baseSize, baseSize / 4, baseSize / 4)
}
}
Performance tested on iPhone7, one frame conversion is less than a millisecond.
Here's what worked for me (I've taken your function and changed it a bit):
func createPixelBufferWithVideoFrame(_ frame: OTVideoFrame) -> CVPixelBuffer? {
if let fLock = frameLock {
fLock.lock()
let planeSize = calculatePlaneSize(forFrame: frame)
let yPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.ySize)
let uPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.uSize)
let vPlane = UnsafeMutablePointer<GLubyte>.allocate(capacity: planeSize.vSize)
memcpy(yPlane, frame.planes?.pointer(at: 0), planeSize.ySize)
memcpy(uPlane, frame.planes?.pointer(at: 1), planeSize.uSize)
memcpy(vPlane, frame.planes?.pointer(at: 2), planeSize.vSize)
let width = frame.format!.imageWidth
let height = frame.format!.imageHeight
var pixelBuffer: CVPixelBuffer? = nil
var err: CVReturn;
err = CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height), kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, nil, &pixelBuffer)
if (err != 0) {
NSLog("Error at CVPixelBufferCreate %d", err)
return nil
}
if let pixelBuffer = pixelBuffer {
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
let yPlaneTo = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
memcpy(yPlaneTo, yPlane, planeSize.ySize)
let uvRow: Int = planeSize.uSize*2/Int(width)
let halfWidth: Int = Int(width)/2
if let uPlaneTo = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1) {
let uvPlaneTo = uPlaneTo.bindMemory(to: GLubyte.self, capacity: Int(uvRow*halfWidth*2))
for i in 0..<uvRow {
for j in 0..<halfWidth {
let dataIndex: Int = Int(i) * Int(halfWidth) + Int(j)
let uIndex: Int = (i * Int(width)) + Int(j) * 2
let vIndex: Int = uIndex + 1
uvPlaneTo[uIndex] = uPlane[dataIndex]
uvPlaneTo[vIndex] = vPlane[dataIndex]
}
}
}
}
fLock.unlock()
return pixelBuffer
}
return nil
}
Guys I am new in swift and programming, I want to generate a Image to be shown for a ColorPicker I'm using SwiftColorPicker for that and have the following code for image creating:
private func createImageFromData(_ width:Int, height:Int) {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
provider = CGDataProvider(data: mutableData)
imageSource = CGImageSourceCreateWithDataProvider(provider, nil)
let cgImage = CGImage.init(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * 4, space: colorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
if let cgimg = cgImage {
image = UIImage(cgImage: cgimg)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: self.image!)
}, completionHandler: { success, error in
if success {
print("succes")
}
else if let error = error {
print("error")
debugPrint(error as Any)
}
else {
print("woooot?")
}
})
} else { print("Where the hell is the image?") }
}
func changeSize(_ width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
CFDataSetLength(mutableData, size)
createImageFromData(width, height: height)
}
init(width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
mutableData = CFDataCreateMutable(kCFAllocatorDefault, size)
createImageFromData(width, height: height)
}
public func writeColorData(_ h:CGFloat, a:CGFloat) {
let d = CFDataGetMutableBytePtr(self.mutableData)
if width == 0 || height == 0 {
return
}
var i:Int = 0
let h360:CGFloat = ((h == 1 ? 0 : h) * 360) / 60.0
let sector:Int = Int(floor(h360))
let f:CGFloat = h360 - CGFloat(sector)
let f1:CGFloat = 1.0 - f
var p:CGFloat = 0.0
var q:CGFloat = 0.0
var t:CGFloat = 0.0
let sd:CGFloat = 1.0 / CGFloat(width)
let vd:CGFloat = 1 / CGFloat(height)
var double_s:CGFloat = 0
var pf:CGFloat = 0
let v_range = 0..<height
let s_range = 0..<width
for v in v_range {
pf = 255 * CGFloat(v) * vd
for s in s_range {
i = (v * width + s) * 4
d?[i] = UInt8(255)
if s == 0 {
q = pf
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(q)
continue
}
double_s = CGFloat(s) * sd
p = pf * (1.0 - double_s)
q = pf * (1.0 - double_s * f)
t = pf * ( 1.0 - double_s * f1)
switch(sector) {
case 0:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(t)
d?[i+3] = UInt8(p)
case 1:
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(p)
case 2:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(t)
case 3:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(pf)
case 4:
d?[i+1] = UInt8(t)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(pf)
default:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(q)
}
}
}
}
and:
// Write colors to data array
if self.data1Shown { self.pickerImage2!.writeColorData(self.h, a:self.a) }
else { self.pickerImage1!.writeColorData(self.h, a:self.a)}
with:
public var a:CGFloat = 1 {
didSet {
if a < 0 || a > 1 {
a = max(0, min(1, a))
}
}
}
public var h:CGFloat = 0 { // // [0,1]
didSet {
if h > 1 || h < 0 {
h = max(0, min(1, h))
}
renderBitmap()
setNeedsDisplay()
}
}
It fails when i try to save it to photo albums (i've done that cause I was not able to set the image so I tried to save it to understand if it's ok or it's a error), and voila! An graceful error appears:
Error Domain=NSCocoaErrorDomain Code=-1 "(null)"
So I wanna know maybe someone knows what to do, how I can fix this, here is the link on gitHub for the Source (maybe it will help):
https://github.com/MrMatthias/SwiftColorPicker
Thanks!
I've made a helpful swift extension to create a UIImage of a solid color.
Feel free to use it:
extension UIImage {
static func image(of color: UIColor, size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
color.set()
UIRectFill(CGRect(origin: CGPoint.zero, size: size))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
Then you can use it by:
let redImage = UIImage.image(of: .red, size: CGSize(width: 200, height: 200))
Hope that helps!
I am trying to make a synchronized animation (a large video decomposed by frames on separated and smaller puzzle jigsaw parts). This game is a video puzzle. Here is the code I use in three parts by way of example:
func Anim_Puzzle13 (Node13 : SKPuzzle) {
let puzzle13 = SKAction.animateWithTextures(sheet_puzzle13.Puzzle13_(), timePerFrame: 0.066)
NPuzzle13 = Node13
NPuzzle13.runAction(SKAction.repeatActionForever(puzzle13))
NPuzzle13.position = CGPoint(x: 500, y: 400)
NPuzzle13.zPosition = 1
}
func Anim_Puzzle19 (Node19 : SKPuzzle) {
let puzzle19 = SKAction.animateWithTextures(sheet_puzzle19.Puzzle19_(), timePerFrame: 0.066)
NPuzzle19 = Node19
NPuzzle19.runAction(SKAction.repeatActionForever(puzzle19))
NPuzzle19.position = CGPoint(x: 600, y: 500)
NPuzzle19.zPosition = 1
}
func Anim_Puzzle30 (Node30 : SKPuzzle) {
let puzzle30 = SKAction.animateWithTextures(sheet_puzzle30.Puzzle30_(), timePerFrame: 0.066)
NPuzzle30 = Node30
NPuzzle30.runAction(SKAction.repeatActionForever(puzzle30))
NPuzzle30.position = CGPoint(x: 700, y: 600)
NPuzzle30.zPosition = 1
}
It works well but it does not synchronize between the animations and the video has no integrity. I searched for a long time for a solution to make the animations synchronize; I see two possibilities: first is to create a unique SKNode() with all the jigsaw parts inside, but I want to be able to move each jigsaw part independently and have had no success getting a synchronized animation with this method.
The other way seem to be to create a group with all the animations together but this doesn't work, and causes the application to stop.
Here is all the code I use:
import SpriteKit
import UIKit
import AVFoundation
import AVKit
import CoreFoundation
private let kpuzzleNodeName = "puzzle"
private let kdancing = "dancing"
class SKPuzzle: SKSpriteNode {
var name2:String = "";
}
class GameScene: SKScene {
var background = SKVideoNode(videoFileNamed: "Video_Socle.mov")
var selectedNode = SKPuzzle()
var player:AVPlayer?
var videoNode:SKVideoNode?
var NPuzzle13 = SKPuzzle()
var NPuzzle19 = SKPuzzle()
var NPuzzle30 = SKPuzzle()
var NPuzzle11 = SKPuzzle()
var NPuzzle29 = SKPuzzle()
var NPuzzle35 = SKPuzzle()
var puzzle13 = SKAction()
var puzzle19 = SKAction()
var puzzle30 = SKAction()
var puzzle11 = SKAction()
var puzzle29 = SKAction()
var puzzle35 = SKAction()
let sheet_puzzle13 = Puzzle13()
let sheet_puzzle19 = Puzzle19()
let sheet_puzzle30 = Puzzle30()
let sheet_puzzle11 = Puzzle11()
let sheet_puzzle29 = Puzzle29()
let sheet_puzzle35 = Puzzle35()
override init(size: CGSize) {
super.init(size: size)
// 1
self.background.name = kdancing
self.background.anchorPoint = CGPointZero
background.zPosition = 0
self.addChild(background)
// 2
let sheet = Statiques()
let sprite_dancing1 = SKSpriteNode(texture: sheet.Dancing1())
let sprite_dancing2 = SKSpriteNode(texture: sheet.Dancing2())
sprite_dancing1.name = kdancing
sprite_dancing2.name = kdancing
sprite_dancing1.position = CGPoint(x: 837, y: 752)
sprite_dancing1.zPosition = 2
sprite_dancing2.position = CGPoint(x: 1241, y: 752)
sprite_dancing2.zPosition = 2
background.addChild(sprite_dancing1)
background.addChild(sprite_dancing2)
let imageNames = [sheet.Puzzle13() , sheet.Puzzle19(), sheet.Puzzle30(), sheet.Puzzle11(), sheet.Puzzle29(), sheet.Puzzle35() ]
for i in 0..<imageNames.count {
let imageName = imageNames[i]
let sprite = SKPuzzle(texture: imageName)
sprite.name = kpuzzleNodeName
sprite.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageNames.count) + 1.0)
sprite.position = CGPoint(x: size.width * offsetFraction, y: size.height / 2)
sprite.zPosition = 3
background.addChild(sprite)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let positionInScene = touch.locationInNode(self)
selectNodeForTouch(positionInScene)
}
}
override func didMoveToView(view: SKView) {
let urlStr = NSBundle.mainBundle().pathForResource("Video_Socle", ofType: "mov")
let url = NSURL(fileURLWithPath: urlStr!)
player = AVPlayer(URL: url)
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
videoNode = SKVideoNode(AVPlayer: player!)
videoNode!.position = CGPointMake(frame.size.width/2, frame.size.height/2)
videoNode!.size = CGSize(width: 2048, height: 1536)
videoNode!.zPosition = 0
background.addChild(videoNode!)
videoNode!.play()
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GameScene.handlePanFrom(_:)))
self.view!.addGestureRecognizer(gestureRecognizer)
}
func handlePanFrom(recognizer : UIPanGestureRecognizer) {
if recognizer.state == .Began {
var touchLocation = recognizer.locationInView(recognizer.view)
touchLocation = self.convertPointFromView(touchLocation)
self.selectNodeForTouch(touchLocation)
} else if recognizer.state == .Changed {
var translation = recognizer.translationInView(recognizer.view!)
translation = CGPoint(x: translation.x, y: -translation.y)
self.panForTranslation(translation)
recognizer.setTranslation(CGPointZero, inView: recognizer.view)
} else if recognizer.state == .Ended {
}
}
func degToRad(degree: Double) -> CGFloat {
return CGFloat(degree / 180.0 * M_PI)
}
func selectNodeForTouch(touchLocation : CGPoint) {
// 1
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode is SKPuzzle {
// 2
if !selectedNode.isEqual(touchedNode) {
selectedNode.runAction(SKAction.rotateToAngle(0.0, duration: 0.1))
selectedNode = touchedNode as! SKPuzzle
// 3
if touchedNode.name! == kpuzzleNodeName {
let sequence = SKAction.sequence([SKAction.rotateByAngle(degToRad(-4.0), duration: 0.1),
SKAction.rotateByAngle(0.0, duration: 0.1),
SKAction.rotateByAngle(degToRad(4.0), duration: 0.1)])
selectedNode.runAction(SKAction.repeatActionForever(sequence))
}
}
}
}
func panForTranslation(translation : CGPoint) {
let position = selectedNode.position
if selectedNode.name! == kpuzzleNodeName {
selectedNode.position = CGPoint(x: position.x + translation.x * 2, y: position.y + translation.y * 2)
print (selectedNode.name)
print (selectedNode.name2)
if selectedNode.name2 == "0" {
Anim_Puzzle13(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "1" {
Anim_Puzzle19(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "2" {
Anim_Puzzle30(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "3" {
Anim_Puzzle11(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "4" {
Anim_Puzzle29(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "5" {
Anim_Puzzle35(selectedNode)
}
}
}
func Anim_Puzzle13 (Node13 : SKPuzzle) {
let puzzle13 = SKAction.animateWithTextures(sheet_puzzle13.Puzzle13_(), timePerFrame: 0.066)
NPuzzle13 = Node13
NPuzzle13.runAction(SKAction.repeatActionForever(puzzle13))
NPuzzle13.position = CGPoint(x: 500, y: 400)
NPuzzle13.zPosition = 1
}
func Anim_Puzzle19 (Node19 : SKPuzzle) {
let puzzle19 = SKAction.animateWithTextures(sheet_puzzle19.Puzzle19_(), timePerFrame: 0.066)
NPuzzle19 = Node19
NPuzzle19.runAction(SKAction.repeatActionForever(puzzle19))
NPuzzle19.position = CGPoint(x: 600, y: 500)
NPuzzle19.zPosition = 1
}
func Anim_Puzzle30 (Node30 : SKPuzzle) {
let puzzle30 = SKAction.animateWithTextures(sheet_puzzle30.Puzzle30_(), timePerFrame: 0.066)
NPuzzle30 = Node30
NPuzzle30.runAction(SKAction.repeatActionForever(puzzle30))
NPuzzle30.position = CGPoint(x: 700, y: 600)
NPuzzle30.zPosition = 1
}
func Anim_Puzzle11 (Node11 : SKPuzzle) {
let puzzle11 = SKAction.animateWithTextures(sheet_puzzle11.Puzzle11_(), timePerFrame: 0.066)
NPuzzle11 = Node11
NPuzzle11.runAction(SKAction.repeatActionForever(puzzle11))
NPuzzle11.position = CGPoint(x: 800, y: 700)
NPuzzle11.zPosition = 1
}
func Anim_Puzzle29 (Node29 : SKPuzzle) {
let puzzle29 = SKAction.animateWithTextures(sheet_puzzle29.Puzzle29_(), timePerFrame: 0.066)
NPuzzle29 = Node29
NPuzzle29.runAction(SKAction.repeatActionForever(puzzle29))
NPuzzle29.position = CGPoint(x: 900, y: 800)
NPuzzle29.zPosition = 1
}
func Anim_Puzzle35 (Node35 : SKPuzzle) {
let puzzle35 = SKAction.animateWithTextures(sheet_puzzle35.Puzzle35_(), timePerFrame: 0.066)
NPuzzle35 = Node35
NPuzzle35.runAction(SKAction.repeatActionForever(puzzle35))
NPuzzle35.position = CGPoint(x: 1000, y: 900)
NPuzzle35.zPosition = 1
}
}
I'm not sure if it's possible to synchronize animations like this: with SKAction() in several separated parts, because it's necessary to be able to select them individually.
UPDATE: I've tried to follow the action group way but I have the same animation playing on each sprite instead of a different animation synchronized for each sprite (6 different animations synchronized: 6 different sprites):
let sheet13 = Puzzle13()
let sheet19 = Puzzle19()
let sheet30 = Puzzle30()
let sheet11 = Puzzle11()
let sheet29 = Puzzle29()
let sheet35 = Puzzle35()
let imageAnims = [sheet13.Puzzle13_0000() , sheet19.Puzzle19_0000(), sheet30.Puzzle30_0000(), sheet11.Puzzle11_0000(), sheet29.Puzzle29_0000(), sheet35.Puzzle35_0000() ]
let puzzle13 = SKAction.animateWithTextures(sheet13.Puzzle13_(), timePerFrame: 0.066)
let puzzle19 = SKAction.animateWithTextures(sheet19.Puzzle19_(), timePerFrame: 0.066)
let puzzle30 = SKAction.animateWithTextures(sheet30.Puzzle30_(), timePerFrame: 0.066)
let puzzle11 = SKAction.animateWithTextures(sheet11.Puzzle11_(), timePerFrame: 0.066)
let puzzle29 = SKAction.animateWithTextures(sheet29.Puzzle29_(), timePerFrame: 0.066)
let puzzle35 = SKAction.animateWithTextures(sheet35.Puzzle35_(), timePerFrame: 0.066)
let group = SKAction.group([puzzle13,puzzle19,puzzle30,puzzle11,puzzle29,puzzle35])
for i in 0..<imageAnims.count {
let imageAnim = imageAnims[i]
let spriteAnim = SKPuzzle(texture: imageAnim)
spriteAnim.name = kanimNodeName
spriteAnim.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageAnims.count) + 1.0)
spriteAnim.position = CGPoint(x: ((size.width)*2) * offsetFraction, y: size.height * 1.5)
spriteAnim.zPosition = 3
spriteAnim.runAction(SKAction.repeatActionForever(group))
background.addChild(spriteAnim)
}
First of all I want to list two differents method to create your SKAction:
Starting with parallel actions by using SKAction.group:
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let scale = SKAction.scaleTo(0.1, duration: 0.5)
let fade = SKAction.fadeOutWithDuration(0.5)
let group = SKAction.group([scale, fade])
sprite.runAction(group)
Another useful method can be the completion , so you can know when an SKAction was finished:
extension SKNode
{
func runAction( action: SKAction!, withKey: String!, optionalCompletion: dispatch_block_t? )
{
if let completion = optionalCompletion
{
let completionAction = SKAction.runBlock( completion )
let compositeAction = SKAction.sequence([ action, completionAction ])
runAction( compositeAction, withKey: withKey )
}
else
{
runAction( action, withKey: withKey )
}
}
}
Usage:
node.runAction(move,withKey:"swipeMove",optionalCompletion: {
// here the action is finished, do whatever you want
})
After that, about your project, I've seen many node.runAction.., you can also adopt this strategy to sinchronize your actions:
var myAction30 :SKAction!
var myAction31 :SKAction!
self.myAction30 = SKAction.repeatActionForever(puzzle30)
self.myAction31 = SKAction.repeatActionForever(puzzle31)
let group = SKAction.group([self.myAction30, self.myAction31])
self.runAction(group)
UPDATE: I've seen your update part, when you speak about "synchronize" probably you don't means the "running in parallel" actions.
So, if you want to run an action after another there is also:
self.myAction30 = SKAction.repeatActionForever(puzzle30)
self.myAction31 = SKAction.repeatActionForever(puzzle31)
let sequence = SKAction.sequence([self.myAction30, self.myAction31])
self.runAction(sequence)
I want to render 2 different objects with Metal...one is with texture, the other one is without texture. I have 2 different shaders, 2 different vertex descriptors, is that means i should use 2 different render pipeline? .. There is only one object drawing (the model with out texture)on the screen correct, the other one is wrong, I don't know where I went wrong.... Here is the code:
override func buildPipeline() {
//Model
let library = device!.newDefaultLibrary()!
let pipelineDescriptor = MTLRenderPipelineDescriptor()
buildPipelinForSky(pipelineDescriptor, library: library)
buildPipelineForModel(pipelineDescriptor, library: library)
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(pipelineDescriptor)
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
}
func buildPipelineForModel(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
pipeLineDesc.vertexFunction = vertexFunctionModel
pipeLineDesc.vertexDescriptor = vertexDescriptorModel
pipeLineDesc.fragmentFunction = fragmentFunctionModel
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
return pipeLineDesc
}
func buildPipelinForSky(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary ) -> MTLRenderPipelineDescriptor{
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
pipeLineDesc.vertexFunction = vertexFunctionSky
pipeLineDesc.vertexDescriptor = vertexDescriptorSky
pipeLineDesc.fragmentFunction = fragmentFunctionSky
pipeLineDesc.depthAttachmentPixelFormat = .Depth32Float
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
return pipeLineDesc
}
override func buildResources() {
// (vertexBuffer, indexBuffer) = SphereGenerator.sphereWithRadius(1, stacks: 30, slices: 30, device: device!)
//Model
(vertexBufferModel,normalBufferModel,colorBufferModel) = PointCloud.model(device!)
uniformBufferModel = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
//Sky
vertexBufferSky = SkySphere.sphere(device!)
uniformBufferSky = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
diffuseTextureSky = self.textureForImage(UIImage(named: "bluemarble")!, device: device!)
}
override func resize() {
//Model
super.resize()
//Sky
let layerSizeSky = metalLayer.drawableSize
let depthTextureDescriptorSky = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.Depth32Float,
width: Int(layerSizeSky.width),
height: Int(layerSizeSky.height),
mipmapped: false)
depthTextureSky = device!.newTextureWithDescriptor(depthTextureDescriptorSky)
}
override func draw() {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
//Sky
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
let commandBufferSky = commandQueue.commandBuffer()
commandBufferSky.addCompletedHandler{ [weak self] commandBufferSky in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
//Sky
let passDescriptor = MTLRenderPassDescriptor()
passDescrForSky(passDescriptor, drawable: drawable)
passDescrForModel(passDescriptor, drawable: drawable)
//Sky
let commandEncoderSky = commandBufferSky.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoderSky.setRenderPipelineState(pipelineSky)
commandEncoderSky.setDepthStencilState(depthStencilState)
commandEncoderSky.setFrontFacingWinding(.CounterClockwise)
commandEncoderSky.setCullMode(.Back)
pointCloudDraw(commandEncoderSky)
skyDraw(commandEncoderSky)
commandEncoderSky.endEncoding()
commandBufferSky.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBufferSky.commit()
}
}
func passDescrForModel(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
return passDescriptor
}
func passDescrForSky(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.depthAttachment.texture = depthTextureSky
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .DontCare
return passDescriptor
}
func pointCloudDraw(commandencodeModel: MTLRenderCommandEncoder) {
commandencodeModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandencodeModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandencodeModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandencodeModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandencodeModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandencodeModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
}
func skyDraw(commandencodeSky: MTLRenderCommandEncoder) {
commandencodeSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandencodeSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandencodeSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandencodeSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandencodeSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
}
here is the vertex buffer for the sky:
struct Vector4
{
var x: Float32
var y: Float32
var z: Float32
var w: Float32
}
struct TexCoords
{
var u: Float32
var v: Float32
}
struct Vertex
{
var position: Vector4
var normal: Vector4
var texCoords: TexCoords
}
var vertexCountSky: Int = 0
struct SkySphere
{
static func sphere(device: MTLDevice) -> (MTLBuffer!)
{
let ply = plyVntReader.init(objFileName: "test")
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(Vertex) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.vertices)
vertexCountSky = ply!.vertexCount
return (vertexBuffer)
}
}
And here is vertex buffer for the model:
var vertextCountModel: Int = 0
struct PointCloud
{
static func model(device: MTLDevice) -> (MTLBuffer!, MTLBuffer!, MTLBuffer!)
{
let ply = plyVncReader.init(objFileName: "controller_ascii")
vertextCountModel = ply!.vertexCount
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let normalBuffer = device.newBufferWithBytes(ply!.normals, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let colorBuffer = device.newBufferWithBytes(ply!.colors, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.colors)
return (vertexBuffer, normalBuffer, colorBuffer)
}
}
shaders for the sky
using namespace metal;
//Sky
struct TexturedInVertex
{
packed_float4 position [[attribute(0)]];
packed_float4 normal [[attribute(1)]];
packed_float2 texCoords [[attribute(2)]];
};
struct TexturedColoredOutVertex
{
float4 position [[position]];
float3 normal;
float2 texCoords;
float pointsize[[point_size]];
};
struct UniformsSky
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex TexturedColoredOutVertex vertex_sky(device TexturedInVertex *vert [[buffer(0)]],
constant UniformsSky &uniforms [[buffer(1)]],
uint vid [[vertex_id]])
{
float4x4 MV = uniforms.modelViewMatrix;
float3x3 normalMatrix(MV[0].xyz, MV[1].xyz, MV[2].xyz);
float4 modelNormal = vert[vid].normal;
TexturedColoredOutVertex outVertex;
outVertex.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(vert[vid].position);
outVertex.normal = normalMatrix * modelNormal.xyz;
outVertex.texCoords = vert[vid].texCoords;
outVertex.pointsize = 10.0;
return outVertex;
};
fragment half4 fragment_sky(TexturedColoredOutVertex vert [[stage_in]],
texture2d<float, access::sample> diffuseTexture [[texture(0)]],
sampler samplr [[sampler(0)]])
{
float4 diffuseColor = diffuseTexture.sample(samplr, vert.texCoords);
return half4(diffuseColor.r, diffuseColor.g, diffuseColor.b, 1);
};
here is shader for the model
//model
struct ColoredVertex
{
float4 position [[position]];
float4 normal;
float4 color;
float pointsize[[point_size]];
};
struct UniformsPoint
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex ColoredVertex vertex_ply(constant float4 *position [[buffer(0)]],
constant float4 *normal [[buffer(1)]],
constant float4 *color [[buffer(2)]],
constant UniformsPoint &uniforms [[buffer(3)]],
uint vid [[vertex_id]])
{
ColoredVertex vert;
vert.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position[vid];
vert.normal = normal[vid];
vert.color = color[vid];
vert.pointsize = 5.0;
return vert;
}
fragment float4 fragment_ply(ColoredVertex vert [[stage_in]])
{
return vert.color;
}
How do I render primitives into off screen texture, not directly into the screen?
I have a set of triangles and corresponding color, I just want to draw them the same way I do to screen, but into off screen texture, that I can save into a file.
Can anybody show me a code sample of that?
Ok, I realized it myself. This code does the job, with only exception that it draw too huge triangles, but that is a different topic for Vertex function.
Here is my code:
let fragmentProgram = defaultLibrary.newFunctionWithName("image_fragmentT")
let vertexProgram = defaultLibrary.newFunctionWithName("image_vertexT")
struct VertexT {
var x, y, z, w : Float
var r, g, b, a : Float
}
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].format = .Float4
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[1].offset = 0
vertexDescriptor.attributes[1].format = .Float4
vertexDescriptor.attributes[1].bufferIndex = 0
vertexDescriptor.layouts[0].stepFunction = .PerVertex
vertexDescriptor.layouts[0].stride = sizeof(VertexT)
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexDescriptor = vertexDescriptor
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .RGBA8Unorm;
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = true
pipelineStateDescriptor.sampleCount = 4
pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
let sampleDesc = MTLTextureDescriptor()
sampleDesc.textureType = MTLTextureType.Type2DMultisample
sampleDesc.width = inTexture.width
sampleDesc.height = inTexture.height
sampleDesc.sampleCount = 4
sampleDesc.pixelFormat = .RGBA8Unorm
sampleDesc.storageMode = .Private
sampleDesc.usage = .RenderTarget
let sampletex = device.device.newTextureWithDescriptor(sampleDesc)
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = sampletex
renderPassDescriptor.colorAttachments[0].resolveTexture = outTexture
renderPassDescriptor.colorAttachments[0].loadAction = .Clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
renderPassDescriptor.colorAttachments[0].storeAction = .MultisampleResolve
let renderCB = commandQueue.commandBuffer()
let renderCommandEncoder = renderCB.renderCommandEncoderWithDescriptor(renderPassDescriptor)
let pipelineState = try! device.device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor)
renderCommandEncoder.setRenderPipelineState(pipelineState)
let vertexBuf = device.device.newBufferWithLength(triangles.count * 3 * sizeof(VertexT), options: .CPUCacheModeDefaultCache)
var vBufPointer = [VertexT]()
for i in 0..<triangles.count {
// create buffer here
}
memcpy(vertexBuf.contents(), &vBufPointer, triangles.count * 3 * sizeof(VertexT))
renderCommandEncoder.setVertexBuffer(vertexBuf, offset: 0, atIndex: 0)
renderCommandEncoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: triangles.count * 3)
renderCommandEncoder.endEncoding()
renderCB.commit()
renderCB.waitUntilCompleted()
You image now is in outTexture variable.