Unity2021.3.10f1, URP, SRP Batcher on
I created a 10x10x10 cube matrix in the sence.
The cube is a prefab. It 1:1:1 default size, with a SRP Batcher compatible shader.
Now 5000+ Batches in runtime. How can I reduce the batches?
source: https://gist.github.com/andrew-raphael-lukasik/df4a36ff2ad89078258fd653c422a021
// scr* https://gist.github.com/andrew-raphael-lukasik/df4a36ff2ad89078258fd653c422a021
using System.Collections.Generic;
using UnityEngine;
public class GpuInstancingForGameObjects : MonoBehaviour
{
[SerializeField] Camera _camera = null;
[SerializeField] MeshRenderer[] _meshRenderers = new MeshRenderer[0];
/// <summary>
/// Prefer "true" ☑ as "false" ☐ require updates every frame.
/// It is a good idea to keep lists of still and moving mesh renderers in a separate components.
/// </summary>
public bool meshesAreStill = true;
Dictionary<(Mesh mesh,Material material),(List<Transform> transforms,Bounds aabb)> _sources = new Dictionary<(Mesh,Material),(List<Transform>,Bounds)>();
Dictionary<(Mesh mesh,Material material),(Matrix4x4[] matrices,Bounds aabb)> _batches = new Dictionary<(Mesh,Material),(Matrix4x4[],Bounds)>();
Dictionary<int,Stack<Matrix4x4[]>> _freeMatrices = new Dictionary<int,Stack<Matrix4x4[]>>();
Plane[] _frustum = new Plane[6];
void Start ()
{
Initialize();
UpdateMatrices();
if( _camera==null ) _camera = Camera.main;
if( _camera==null )
{
Debug.LogError( "no camera, can't continue" , this );
enabled = false;
}
}
void Update ()
{
if( !meshesAreStill ) UpdateMatrices();
GeometryUtility.CalculateFrustumPlanes( _camera , _frustum );
foreach( var batch in _batches )
{
var meshMaterialPair = batch.Key;
var matricesAabbPair = batch.Value;
var aabb = matricesAabbPair.aabb;
if( GeometryUtility.TestPlanesAABB(_frustum,aabb) )
{
Graphics.DrawMeshInstanced(
mesh: meshMaterialPair.mesh ,
submeshIndex: 0 ,
material: meshMaterialPair.material ,
matrices: matricesAabbPair.matrices
);
}
}
}
#if UNITY_EDITOR
// void OnDrawGizmosSelected ()
void OnDrawGizmos ()
{
Initialize();
Gizmos.color = Color.yellow;
foreach( var source in _sources )
{
var transformsAabbPair = source.Value;
var aabb = transformsAabbPair.aabb;
Gizmos.DrawWireCube( aabb.center , aabb.size );
if( Application.isPlaying && !GeometryUtility.TestPlanesAABB(_frustum,aabb) )
UnityEditor.Handles.Label( aabb.center , "(out of camera view)" );
}
}
#endif
void Initialize ()
{
_sources.Clear();
foreach( var meshRenderer in _meshRenderers )
{
if( meshRenderer==null ) continue;
var meshFilter = meshRenderer.GetComponent<MeshFilter>();
if( meshFilter==null ) continue;
var mesh = meshFilter.sharedMesh;
if( mesh==null ) continue;
foreach( var material in meshRenderer.sharedMaterials )
{
if( !material.enableInstancing && Application.isPlaying )
{
Debug.LogWarning($"\"{material.name}\" material won't be rendered as it's <b>GPU Instancing</b> is not enabled",meshRenderer);
continue;
}
if( material==null ) continue;
var aabb = meshRenderer.bounds;
var meshMaterialPair = ( mesh , material );
if( _sources.ContainsKey( meshMaterialPair ) )
{
var transforms = _sources[meshMaterialPair].transforms;
transforms.Add( meshRenderer.transform );
var newAabb = _sources[meshMaterialPair].aabb;
newAabb.Encapsulate( aabb );
_sources[meshMaterialPair] = ( transforms , newAabb );
}
else
{
_sources.Add( meshMaterialPair , ( new List<Transform>(){ meshRenderer.transform } , aabb ) );
}
}
if( Application.isPlaying )
meshRenderer.enabled = false;
}
}
void UpdateMatrices ()
{
foreach( var batch in _batches )
{
var matricesAabbPair = batch.Value;
var matrices = matricesAabbPair.matrices;
if( _freeMatrices.ContainsKey( matrices.Length ) )
{
_freeMatrices[matrices.Length].Push( matrices );
}
else
{
var stack = new Stack<Matrix4x4[]>();
stack.Push( matrices );
_freeMatrices.Add( matrices.Length , stack );
}
}
_batches.Clear();
foreach( var source in _sources )
{
var meshMaterialPair = source.Key;
var transformsAabbPair = source.Value;
var transforms = transformsAabbPair.transforms;
int numTransforms = transforms.Count;
Matrix4x4[] matrices = null;
if( _freeMatrices.ContainsKey(numTransforms) && _freeMatrices[numTransforms].Count!=0 )
{
matrices = _freeMatrices[numTransforms].Pop();
}
else matrices = new Matrix4x4[ numTransforms ];
for( int i=0 ; i<numTransforms ; i++ )
matrices[i] = transforms[i].localToWorldMatrix;
_batches.Add( meshMaterialPair , ( matrices , transformsAabbPair.aabb ) );
}
}
}
This solution provides AABB frustum culling - it is not enough to optimize your voxels-like case but a starting point. If you consider these boxes to represent voxels then your next step would be to implement a voxel occlusion culling here (to hide boxes that are inside this shape).
Related
I'm trying to do the following:
private void AssignPlayer()
{
EntityQuery playerQuery = GetEntityQuery(ComponentType.ReadOnly<PlayerTag>());
Entity playerEntity = playerQuery.ToEntityArray(Allocator.Temp);
Entities.
WithAll<ChaserTag>().
ForEach((ref TargetData targetData) =>
{
if (playerEntity != Entity.Null)
{
targetData.targetEntity = playerEntity;
}
}).Schedule();
}
But the line that assigns targetData.targetEntity = playerEntity; throws the error:
Cannot implicitly convert type 'Unity.Collections.NativeArray<Unity.Entities.Entity>' to 'Unity.Entities.Entity'
if( players.Length!=0 )
{
NativeArray<Entity> players = playerQuery.ToEntityArray( Allocator.TempJob );
Entities.WithName("targetData_update_job")
.WithReadOnly( players ).WithDeallocateOnJobCompletion( players )
.WithAll<ChaserTag>()
.ForEach( ( ref TargetData targetData ) =>
{
targetData.targetEntity = players[0];
}
).ScheduleParallel();
}
else Debug.LogWarning($"{nameof(AssignPlayer)}(): no players tho");
When i use this code in CKE Editor:
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
can i change this:
Image 1
to this:
Image 2
Problem: The PN Messenger in our CMS reply the last message with blockquote in blockquote. And with several replies this is very confusing.
Your Code
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
Updated
<blockquote>test</blockquote>
<blockquote>test</blockquote>
You want to display every blockquote in a new line. Try this
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
<head>
<title>Page Title</title>
</head>
<body>
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
</body>
I insert your code here (line 24) Is that correct? But it do not change.
1/**
* #license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
( function() {
function noBlockLeft( bqBlock ) {
for ( var i = 0, length = bqBlock.getChildCount(), child; i < length && ( child = bqBlock.getChild( i ) ); i++ ) {
if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
return false;
}
return true;
}
var commandObject = {
exec: function( editor ) {
var state = editor.getCommand( 'blockquote' ).state,
selection = editor.getSelection(),
range = selection && selection.getRanges()[ 0 ];
if ( !range )
return;
// Rakibul Islam
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
var bookmarks = selection.createBookmarks();
// Kludge for https://dev.ckeditor.com/ticket/1592: if the bookmark nodes are in the beginning of
// blockquote, then move them to the nearest block element in the
// blockquote.
if ( CKEDITOR.env.ie ) {
var bookmarkStart = bookmarks[ 0 ].startNode,
bookmarkEnd = bookmarks[ 0 ].endNode,
cursor;
if ( bookmarkStart && bookmarkStart.getParent().getName() == 'blockquote' ) {
cursor = bookmarkStart;
while ( ( cursor = cursor.getNext() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkStart.move( cursor, true );
break;
}
}
}
if ( bookmarkEnd && bookmarkEnd.getParent().getName() == 'blockquote' ) {
cursor = bookmarkEnd;
while ( ( cursor = cursor.getPrevious() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkEnd.move( cursor );
break;
}
}
}
}
var iterator = range.createIterator(),
block;
iterator.enlargeBr = editor.config.enterMode != CKEDITOR.ENTER_BR;
if ( state == CKEDITOR.TRISTATE_OFF ) {
var paragraphs = [];
while ( ( block = iterator.getNextParagraph() ) )
paragraphs.push( block );
// If no paragraphs, create one from the current selection position.
if ( paragraphs.length < 1 ) {
var para = editor.document.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ),
firstBookmark = bookmarks.shift();
range.insertNode( para );
para.append( new CKEDITOR.dom.text( '\ufeff', editor.document ) );
range.moveToBookmark( firstBookmark );
range.selectNodeContents( para );
range.collapse( true );
firstBookmark = range.createBookmark();
paragraphs.push( para );
bookmarks.unshift( firstBookmark );
}
// Make sure all paragraphs have the same parent.
var commonParent = paragraphs[ 0 ].getParent(),
tmp = [];
for ( var i = 0; i < paragraphs.length; i++ ) {
block = paragraphs[ i ];
commonParent = commonParent.getCommonAncestor( block.getParent() );
}
// The common parent must not be the following tags: table, tbody, tr, ol, ul.
var denyTags = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 };
while ( denyTags[ commonParent.getName() ] )
commonParent = commonParent.getParent();
// Reconstruct the block list to be processed such that all resulting blocks
// satisfy parentNode.equals( commonParent ).
var lastBlock = null;
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
while ( !block.getParent().equals( commonParent ) )
block = block.getParent();
if ( !block.equals( lastBlock ) )
tmp.push( block );
lastBlock = block;
}
// If any of the selected blocks is a blockquote, remove it to prevent
// nested blockquotes.
while ( tmp.length > 0 ) {
block = tmp.shift();
if ( block.getName() == 'blockquote' ) {
var docFrag = new CKEDITOR.dom.documentFragment( editor.document );
while ( block.getFirst() ) {
docFrag.append( block.getFirst().remove() );
paragraphs.push( docFrag.getLast() );
}
docFrag.replace( block );
} else {
paragraphs.push( block );
}
}
// Now we have all the blocks to be included in a new blockquote node.
var bqBlock = editor.document.createElement( 'blockquote' );
bqBlock.insertBefore( paragraphs[ 0 ] );
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
bqBlock.append( block );
}
} else if ( state == CKEDITOR.TRISTATE_ON ) {
var moveOutNodes = [],
database = {};
while ( ( block = iterator.getNextParagraph() ) ) {
var bqParent = null,
bqChild = null;
while ( block.getParent() ) {
if ( block.getParent().getName() == 'blockquote' ) {
bqParent = block.getParent();
bqChild = block;
break;
}
block = block.getParent();
}
// Remember the blocks that were recorded down in the moveOutNodes array
// to prevent duplicates.
if ( bqParent && bqChild && !bqChild.getCustomData( 'blockquote_moveout' ) ) {
moveOutNodes.push( bqChild );
CKEDITOR.dom.element.setMarker( database, bqChild, 'blockquote_moveout', true );
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
var movedNodes = [],
processedBlockquoteBlocks = [];
database = {};
while ( moveOutNodes.length > 0 ) {
var node = moveOutNodes.shift();
bqBlock = node.getParent();
// If the node is located at the beginning or the end, just take it out
// without splitting. Otherwise, split the blockquote node and move the
// paragraph in between the two blockquote nodes.
if ( !node.getPrevious() )
node.remove().insertBefore( bqBlock );
else if ( !node.getNext() )
node.remove().insertAfter( bqBlock );
else {
node.breakParent( node.getParent() );
processedBlockquoteBlocks.push( node.getNext() );
}
// Remember the blockquote node so we can clear it later (if it becomes empty).
if ( !bqBlock.getCustomData( 'blockquote_processed' ) ) {
processedBlockquoteBlocks.push( bqBlock );
CKEDITOR.dom.element.setMarker( database, bqBlock, 'blockquote_processed', true );
}
movedNodes.push( node );
}
CKEDITOR.dom.element.clearAllMarkers( database );
// Clear blockquote nodes that have become empty.
for ( i = processedBlockquoteBlocks.length - 1; i >= 0; i-- ) {
bqBlock = processedBlockquoteBlocks[ i ];
if ( noBlockLeft( bqBlock ) )
bqBlock.remove();
}
if ( editor.config.enterMode == CKEDITOR.ENTER_BR ) {
var firstTime = true;
while ( movedNodes.length ) {
node = movedNodes.shift();
if ( node.getName() == 'div' ) {
docFrag = new CKEDITOR.dom.documentFragment( editor.document );
var needBeginBr = firstTime && node.getPrevious() && !( node.getPrevious().type == CKEDITOR.NODE_ELEMENT && node.getPrevious().isBlockBoundary() );
if ( needBeginBr )
docFrag.append( editor.document.createElement( 'br' ) );
var needEndBr = node.getNext() && !( node.getNext().type == CKEDITOR.NODE_ELEMENT && node.getNext().isBlockBoundary() );
while ( node.getFirst() )
node.getFirst().remove().appendTo( docFrag );
if ( needEndBr )
docFrag.append( editor.document.createElement( 'br' ) );
docFrag.replace( node );
firstTime = false;
}
}
}
}
selection.selectBookmarks( bookmarks );
editor.focus();
},
refresh: function( editor, path ) {
// Check if inside of blockquote.
var firstBlock = path.block || path.blockLimit;
this.setState( editor.elementPath( firstBlock ).contains( 'blockquote', 1 ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
},
context: 'blockquote',
allowedContent: 'blockquote',
requiredContent: 'blockquote'
};
CKEDITOR.plugins.add( 'blockquote', {
// jscs:disable maximumLineLength
lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
// jscs:enable maximumLineLength
icons: 'blockquote', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
init: function( editor ) {
if ( editor.blockless )
return;
editor.addCommand( 'blockquote', commandObject );
editor.ui.addButton && editor.ui.addButton( 'Blockquote', {
label: editor.lang.blockquote.toolbar,
command: 'blockquote',
toolbar: 'blocks,10'
} );
}
} );
} )();
I'm trying to make my flextables columns resizeable.
So the user can decide how width a column should be.
One solution is to use css resize property but this doesn't work in IE.
Does someone has an idea how to make this working in IE?
My idea was to catch the onBrowserEvent and register some mousehandlers but I don't get the right element to resize.
I hove you can help me.
Thanks in advance!
Hi I found a solution for my own. Maybe this can help someone.
public class ResizableFlexTable
extends FlexTable
{
private int mouseDownX;
private boolean mouseDown;
private int columnWidth;
private TableCellElement headerElem;
private int column;
private ResizeMode resizeMode;
/**
*
*/
public ResizableFlexTable()
{
super();
sinkEvents( Event.ONMOUSEMOVE );
sinkEvents( Event.ONMOUSEDOWN );
sinkEvents( Event.ONMOUSEUP );
sinkEvents( Event.ONCLICK );
Event.setEventListener( getElement(), this );
}
#Override
public void onBrowserEvent( Event event )
{
if ( event.getType().equals( "change" ) )
{
super.onBrowserEvent( event );
return;
}
boolean eventMouseUp = event.getType().equals( "mouseup" );
boolean eventClick = event.getType().equals( "click" );
boolean eventMouseDown = event.getType().equals( "mousedown" );
boolean eventMouseMove = event.getType().equals( "mousemove" );
if ( eventMouseUp )
{
mouseDownX = -1;
mouseDown = false;
}
if ( resizeMode == ResizeMode.RESIZING && eventClick )
{
resizeMode = ResizeMode.ENDING;
}
TableCellElement tableCellElement = findNearestParentCell( (Element) event.getEventTarget()
.cast() );
if ( tableCellElement == null )
{
super.onBrowserEvent( event );
return;
}
Element trElem = tableCellElement.getParentElement();
if ( trElem == null )
{
super.onBrowserEvent( event );
return;
}
TableRowElement tr = TableRowElement.as( trElem );
// Element sectionElem = tr.getParentElement();
if ( tr == null )
{
super.onBrowserEvent( event );
return;
}
int col = tableCellElement.getCellIndex();
if ( tr == getTableHeadElement() )
{
int mouseX = event.getClientX();
int right = tableCellElement.getAbsoluteLeft() + tableCellElement.getOffsetWidth();
if ( !mouseDown && eventMouseDown )
{
if ( mouseX <= right && mouseX >= right - 10 )
{
headerElem = tableCellElement;
column = col;
mouseDownX = event.getClientX();
mouseDown = true;
columnWidth = headerElem.getOffsetWidth();
}
}
else if ( eventMouseMove )
{
if ( mouseDown || mouseX <= right && mouseX >= right - 10 )
{
tableCellElement.getStyle().setCursor( Cursor.COL_RESIZE );
}
else
{
tableCellElement.getStyle().setCursor( Cursor.AUTO );
}
}
}
else
{
super.onBrowserEvent( event );
}
if ( headerElem != null && eventMouseMove && mouseDown )
{
int move = event.getClientX() - mouseDownX;
#SuppressWarnings ( "hiding")
int columnWidth = this.columnWidth + move;
if ( columnWidth > 24 )
{
getColumnFormatter().getElement( column ).setAttribute( "width", columnWidth + "px" );
resizeMode = ResizeMode.RESIZING;
}
}
if ( resizeMode != ResizeMode.RESIZING && resizeMode != ResizeMode.ENDING && eventClick )
{
super.onBrowserEvent( event );
}
else if ( resizeMode == ResizeMode.ENDING )
{
resizeMode = ResizeMode.NO;
}
}
/**
* #return
*/
private TableRowElement getTableHeadElement()
{
TableRowElement row = getRowFormatter().getElement( 0 ).cast();
return row;
}
/**
* Diese Methode ist 1 zu 1 aus der Oberklasse übernommen. Nicht ändern!!!
*/
private TableCellElement findNearestParentCell( Element elem )
{
while ( (elem != null) && (elem != getElement()) )
{
String tagName = elem.getTagName();
if ( "td".equalsIgnoreCase( tagName ) || "th".equalsIgnoreCase( tagName ) )
{
return elem.cast();
}
elem = elem.getParentElement();
}
return null;
}
}
This is not very nice but it works for me.
I'm building an App with actionscript 3.0 in my Flash builder. This is a followup question to this question, It works but when I take the picture, the image comes out rotated to the left. how can I check which way the user is holding the phone? and then what code do I use to rotate the image to it's corresponding place?
Thanks in advanced!
EDIT:
I'm using this code to rotate the image, but it seems to only rotate the image being displayed not the image file, any ideas?
var mat:Matrix = new Matrix();
mat.translate(-W/2, -H/2);
mat.rotate(Math.PI/2);
mat.translate(+W/2, +H/2);
mat.concat(myObj.transform.matrix);
myObj.transform.matrix = mat;
~Myy
You can't get stage.orientation data when you have the cameraUI view active, so here's how i solved the same issue, please note that this controller has many reusable functions, i first get the raw image data from media promise (this raw data has EXIF data that i will read to get image orientation from the camera) i then convert this byteArray to BitmapData and then resize and scale as i need. One thing to note is that if you convert the raw image data to Bitmap data you will lose EXIF data so read it before you modify the image, hope this helps and i posted it here late i know but there's no other solution in the web and maybe someone will need this sometime. Cheers
SnapshotController.as
package controllers
{
import flash.display.BitmapData;
import flash.display.JPEGEncoderOptions;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.MediaEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.media.CameraUI;
import flash.media.MediaPromise;
import flash.media.MediaType;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
import mx.utils.Base64Encoder;
import classes.APIupload;
public class SnapshotController
{
private var cameraUI:CameraUI = new CameraUI();
private var dataSource:IDataInput;
private var tempDir:File = new File();
private var imageOrientation:int = 0 ;
public function SnapshotController()
{
}
public function LaunchCameraUI():void
{
if( CameraUI.isSupported ) {
trace( "Initializing camera..." );
cameraUI.addEventListener( MediaEvent.COMPLETE, imageSelected );
cameraUI.launch( MediaType.IMAGE );
} else {
trace( "CameraUI is not supported.");
}
}
private function imageSelected( event:MediaEvent ):void {
trace( "Media selected..." );
var imagePromise:MediaPromise = event.data;
dataSource = imagePromise.open();
if( imagePromise.isAsync ) {
trace( "Asynchronous media promise." );
var eventSource:IEventDispatcher = dataSource as IEventDispatcher;
eventSource.addEventListener( Event.COMPLETE, onMediaLoaded );
} else {
trace( "Synchronous media promise." );
readMediaData();
}
}
private function onMediaLoaded( event:Event ):void {
trace("Media load complete");
readMediaData();
}
private function readMediaData():void {
var imageBytes:ByteArray = new ByteArray();
dataSource.readBytes( imageBytes );
imageOrientation = getOrientation(imageBytes);
//saveImageFile(imageBytes);
//Saving this byteArray will save a big file with the EXIF in it.
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, onMediaPromiseLoaded);
loader.loadBytes(imageBytes);
}
private function onMediaPromiseLoaded(e:Event):void
{
trace("Media file loaded");
var base64Enc:Base64Encoder = new Base64Encoder();
var mpLoaderInfo:LoaderInfo = e.target as LoaderInfo;
mpLoaderInfo.removeEventListener(Event.COMPLETE, onMediaPromiseLoaded);
var scale:Number = 0.25;
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
var jpgQuality:int = 80;
var bmd:BitmapData = new BitmapData(mpLoaderInfo.width*scale, mpLoaderInfo.height*scale);
bmd.draw(mpLoaderInfo.content,matrix,null,null,null,true);
var rotatedBMD:BitmapData = rotateBitmapData(bmd, imageOrientation);
var bytes:ByteArray = rotatedBMD.encode(new Rectangle(0,0, rotatedBMD.width , rotatedBMD.height), new JPEGEncoderOptions(jpgQuality), bytes);
saveImageFile(bytes); //this is the smaller file saved. it does not have EXIF data but you can write your own using AS3 eXIF class.
}
private function rotateBitmapData( bitmapData:BitmapData, degree:int = 0 ) :BitmapData
{
var newBitmap:BitmapData;
var matrix:Matrix = new Matrix();
matrix.rotate( degree * (Math.PI / 180) );
if ( degree == 90 ) {
newBitmap = new BitmapData( bitmapData.height, bitmapData.width, true );
matrix.translate( bitmapData.height, 0 );
} else if ( degree == -90 || degree == 270) {
newBitmap = new BitmapData( bitmapData.height, bitmapData.width, true );
matrix.translate( 0, bitmapData.width );
} else if ( degree == 180 ) {
newBitmap = new BitmapData( bitmapData.width, bitmapData.height, true );
matrix.translate( bitmapData.width, bitmapData.height );
}else if(degree == 0){
newBitmap = new BitmapData( bitmapData.width, bitmapData.height, true );
//matrix.translate( bitmapData.width, bitmapData.height );
}
newBitmap.draw( bitmapData, matrix, null, null, null, true )
return newBitmap;
}
private function saveImageFile(ba:ByteArray):void{
var now:Date = new Date();
var filename:String = "IMG" + now.fullYear + now.month + now.day + now.hours + now.minutes + now.seconds + ".jpg";
var temp:File = File.documentsDirectory.resolvePath( filename );
var stream:FileStream = new FileStream();
stream.open( temp, FileMode.WRITE );
stream.writeBytes( ba );
stream.close();
}
private function getOrientation(jpeg:ByteArray):int{
if (jpeg == null) {
return 0;
}
var offset:int = 0;
var length:int = 0;
while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
var marker:int = jpeg[offset] & 0xFF;
// Check if the marker is a padding.
if (marker == 0xFF) {
continue;
}
offset++;
// Check if the marker is SOI or TEM.
if (marker == 0xD8 || marker == 0x01) {
continue;
}
// Check if the marker is EOI or SOS.
if (marker == 0xD9 || marker == 0xDA) {
break;
}
// Get the length and check if it is reasonable.
length = pack(jpeg, offset, 2, false);
if (length < 2 || offset + length > jpeg.length) {
trace("Invalid length");
return 0;
}
// Break if the marker is EXIF in APP1.
if (marker == 0xE1 && length >= 8 &&
pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
pack(jpeg, offset + 6, 2, false) == 0) {
offset += 8;
length -= 8;
break;
}
// Skip other markers.
offset += length;
length = 0;
}
if (length > 8) {
// Identify the byte order.
var tag:int = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
trace("Invalid byte order");
return 0;
}
var littleEndian:Boolean = (tag == 0x49492A00);
// Get the offset and check if it is reasonable.
var count:int = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
trace( "Invalid offset");
return 0;
}
offset += count;
length -= count;
// Get the count and go through all the elements.
count = pack(jpeg, offset - 2, 2, littleEndian);
while (count-- > 0 && length >= 12) {
// Get the tag and check if it is orientation.
tag = pack(jpeg, offset, 2, littleEndian);
if (tag == 0x0112) {
// We do not really care about type and count, do we?
var orientation:int = pack(jpeg, offset + 8, 2, littleEndian);
switch (orientation) {
case 1:
return 0;
case 3:
return 180;
case 6:
return 90;
case 8:
return 270;
}
trace( "Unsupported orientation");
return 0;
}
offset += 12;
length -= 12;
}
}
trace( "Orientation not found");
return 0;
}
private function pack(bytes:ByteArray,offset:int,length:int,
littleEndian:Boolean):int {
var step:int = 1;
if (littleEndian) {
offset += length - 1;
step = -1;
}
var value:int = 0;
while (length-- > 0) {
value = (value << 8) | (bytes[offset] & 0xFF);
offset += step;
}
return value;
}
}
}
You can use Stage.deviceOrientation or Stage.orientation* to determine which way round the phone is.
*not sure if this one works on iOS
Is it the BitmapData result itself that you want to rotate (ie create a new BitmapData with rotated image) or just rotate a Bitmap on the display list?
Edit:
Ok, heres some code to rotate a BitmapData object:
function rotateBitmapData(angle:int, source:BitmapData):BitmapData
{
var newWidth:int = source.rect.width;
var newHeight:int = source.rect.height;
if (angle==90 || angle==270)
{
newWidth = source.rect.height;
newHeight = source.rect.width;
}
var newBmd:BitmapData = new BitmapData(newWidth, newHeight, source.transparent);
var tx:Number = 0;
var ty:Number = 0;
if (angle==90 || angle==180)
{
tx = newWidth;
}
if (angle==180 || angle==270)
{
ty = newHeight;
}
var matrix:Matrix = new Matrix();
matrix.createBox(1, 1, Math.PI*angle/180, tx, ty);
newBmd.draw(source, matrix);
return newBmd;
}
angle should be 0,90,180 or 270. It will return a new BitmapData object rotated by specified angle.
You can use StageOrientationEvent.ORIENTATION_CHANGING Event :
stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, OrientationChangeHandler);
private function OrientationChangeHandler(e:StageOrientationEvent):void
{
switch (e.afterOrientation)
{
case StageOrientation.DEFAULT :
break;
case StageOrientation.ROTATED_RIGHT :
break;
case StageOrientation.ROTATED_LEFT :
break;
case StageOrientation.UPSIDE_DOWN :
break;
}
}
this can help you.
I need help with this code about Three.js. I do not know why it does not work, since everything is correct and copied from other codes that do work. The problem is that no Mouse Hovering effect works.
<html>
<head>
<title>NUEVO</title>
<style>canvas { width: 100%; height: 100% }</style>
</head>
<body>
<script src="js/libs/Tween.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="https://raw.github.com/mrdoob/three.js/master/build/three.js"></script>
<script>
var scene,camera,rendered,projector;
var mouseX, mouseY, stats, container;
var objects=[];
var INTERSECTED;
var theta = 0;
init();
animate();
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 20000);
camera.position.set( 0, 150, 400 );
camera.lookAt(scene.position);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
projector = new THREE.Projector();
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
for (var i=0; i<3; i++)
{
var geometry = new THREE.CubeGeometry(40,40,40);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
cube.position.x = (i*200) -200
objects.push(cube);
scene.add(cube);
}
window.addEventListener( 'resize', onWindowResize, false );
}
function onDocumentMouseDown( event )
{
event.preventDefault();
mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
mouseY = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate()
{
requestAnimationFrame(animate);
render();
update();
}
function update()
{
var vector = new THREE.Vector3( mouseX, mouseY, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
// store reference to closest object as current intersection object
INTERSECTED = intersects[ 0 ].object;
// store color of closest object (for later restoration)
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// set a new color for closest object
INTERSECTED.material.color.setHex( 0xffff00 );
} else {// there are no intersections
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
}
/* for(var i=0; i<3; i++)
{
objects[i].rotation.y += 0.05;
}*/
}
}
function render()
{
//theta += 0.4;
camera.position.x = 300 * Math.sin( theta * Math.PI / 360 );
camera.position.y = 300 * Math.sin( theta * Math.PI / 360 );
camera.position.z = 300 * Math.cos( theta * Math.PI / 360 );
camera.lookAt( scene.position );
renderer.render(scene, camera);
}
</script>
</body>
</html>
This is what You want:
http://jsfiddle.net/Lx6nE/4/
What was wrong:
The link to Three.js should be towards the minified source.
The braces should be indented right becuase of flickering
The mousedown event should be captured.
The code:
var scene,camera,rendered,projector;
var mouseX, mouseY, stats, container;
var objects=[];
var INTERSECTED;
var theta = 0;
init();
animate();
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 20000);
camera.position.set( 0, 150, 400 );
camera.lookAt(scene.position);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
projector = new THREE.Projector();
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
for (var i=0; i<3; i++)
{
var geometry = new THREE.CubeGeometry(40,40,40);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
cube.position.x = (i*50) -60
objects.push(cube);
scene.add(cube);
}
window.addEventListener( 'resize', onWindowResize, false );
window.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( event )
{
event.preventDefault();
mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
mouseY = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate()
{
requestAnimationFrame(animate);
render();
update();
}
function update()
{
var vector = new THREE.Vector3( mouseX, mouseY, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
// store reference to closest object as current intersection object
INTERSECTED = intersects[ 0 ].object;
// store color of closest object (for later restoration)
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// set a new color for closest object
INTERSECTED.material.color.setHex( 0xffff00 );
}
} else {// there are no intersections
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
/* for(var i=0; i<3; i++)
{
objects[i].rotation.y += 0.05;
}*/
}
}
function render()
{
//theta += 0.4;
camera.position.x = 300 * Math.sin( theta * Math.PI / 360 );
camera.position.y = 300 * Math.sin( theta * Math.PI / 360 );
camera.position.z = 300 * Math.cos( theta * Math.PI / 360 );
camera.lookAt( scene.position );
renderer.render(scene, camera);
}