Away3D 4.0 Loading dynamic textures for primitives from arrays - actionscript

I have several arrays storing the attributes needed to build primitives. One array stores the widths, another stores the heights, another the depths, x, y, z etc. I have one more that stores the remote filename for the texture to be applied. After I get my response from the server I attempt to apply the textures to the primitives. When I move the camera to look at the primitive, I am not able to see it. My view seems to freeze up (the view will not update). Once the camera has moved past the primitive, it can see again. Any ideas?
private var loadedBuildingTextures:Object = new Object();
private var map_building_count:Number = 0;
private var loadedBuildings:Number = 0;
public var map_building_widths:Array;
public var map_building_heights:Array;
public var map_building_depths:Array;
public var map_building_xs:Array;
public var map_building_ys:Array;
public var map_building_zs:Array;
public var map_building_textures:Array;
// I POPULATE THE ARRAYS BUT LEFT THAT CODE OUT FOR SIMPLICITY
public function placeBuildings():void {
trace('FUNCTION: placeBuildings() fired');
var buildingsPlaced:Number = 0;
for (var a:Number = 0; a < map_building_count; a++ ) {
loadedBuildingTextures['texture_' + a.toString()] = new BitmapFileMaterial(map_building_textures[a]); // ASSIGNS THE BitmapFileMaterials TO AN OBJECT FOR REFERENCING LATER
loadedBuildingTextures['texture_' + a.toString()].loader.contentLoaderInfo.addEventListener(Event.COMPLETE, postBuildingLoad);
buildingsPlaced++;
}
trace('placed ' + buildingsPlaced.toString() + ' of ' + map_building_count.toString() + ' buildings.'); // OUTPUT = "placed 4 of 4 buildings."
trace('FUNCTION: placeBuildings() completed');
}
public function postBuildingLoad(event : Event):void {
loadedBuildings++;
if (int(loadedBuildings) == int(map_building_count)) {
placeBuildingsStep2();
}
}
public function placeBuildingsStep2():void {
trace('RECEIVED ALL RESPONSES FROM SERVER FOR TEXTURES');
for (var a:Number = 0; a < map_building_count; a++ ) {
cube = new Cube(
loadedBuildingTextures['texture_' + a], // <----- THIS SEEMS TO BE THE PROBLEM
map_building_widths[a], // WIDTH
map_building_heights[a], // HEIGHT
map_building_depths[a], // DEPTH
1, // WIDTH UNITS
1, // HEIGHT UNITS
1, // DEPTH UNITS
true);
cube.x = map_building_xs[a];
cube.y = map_building_ys[a];
cube.z = map_building_zs[a];
view.scene.addChild(cube);
}
}

Although this post is old, it does highlight an important issue. BitmapFileMaterial is more a shortcut to test than a production ready scheme.
I would highly recommend using a loading queue for external assets (like LoaderMax or the new AssetLibrary in 4.0) and then instantiate materials as needed.

Related

In Photon Bolt, how to send List of int data in a token along with event?

My codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Bolt;
using UdpKit;
//using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class ListOfIntToken : IProtocolToken
{
public List<int> intList;
public int byteArraySize;
public void Read(UdpPacket packet)
{
byteArraySize = packet.ReadInt();
var objectBytes = packet.ReadByteArray(byteArraySize);
var mStream = new MemoryStream();
var binFormatter = new BinaryFormatter();
mStream.Write(objectBytes, 0, objectBytes.Length);
mStream.Position = 0;
intList = binFormatter.Deserialize(mStream) as List<int>;
}
public void Write(UdpPacket packet)
{
var binFormatter = new BinaryFormatter();
var mStream = new MemoryStream();
binFormatter.Serialize(mStream, intList);
//byte[] bytes = userId.Select(x => (byte)x).ToArray();
var byteArray = mStream.ToArray();
byteArraySize = byteArray.Length;
packet.WriteInt(byteArraySize);
packet.WriteByteArray(byteArray);
}
}
I have two clients running A and B. A is server. Both are sending the event with this token but with different data for testing. In Write method I print the byteArraySize out, and when the data are received on server I print them out too. The byteArraySize for A's data is 0, and the time it's printed is before the printing line inWrite method, where the size was 221. However for B's data the size was correct. What may causes this problem?
Final solution:
public void Read(UdpPacket packet)
{
intList.Clear();
if (packet.ReadBool()) // check if we have data to read
{
var total = packet.ReadInt();
for (int i = 0; i < total; i++)
{
intList.Add(packet.ReadInt());
}
}
}
public void Write(UdpPacket packet)
{
var total = intList.Count;
if (packet.WriteBool(total > 0)) // Write bool to signal we have some data
{
packet.WriteInt(total);
foreach (var item in intList)
{
packet.WriteInt(item);
}
}
}
The weird bug is caused by assigning to a wrong variable. It's been corrected in the question.

Typescript, How to avoid code duplication in constructor?

Consider this class that is used as a data model in a Model-View-Controller scenario (I'm using TypeScript 3.5):
export class ViewSource {
private viewName : string;
private viewStruct : IViewStruct;
private rows : any[];
private rowIndex : number|null;
constructor(viewName : string) {
// Same as this.setViewName(viewName);
this.viewName = viewName;
this.viewStruct = api.meta.get_view_struct(viewName);
if (!this.viewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
this.rows = [];
this.rowIndex = null;
}
public setViewName = (viewName: string) => {
this.viewName = viewName;
this.viewStruct = api.meta.get_view_struct(viewName);
if (!this.viewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
this.rows = [];
this.rowIndex = null;
}
public getViewStruct = ():IViewStruct => { return this.viewStruct; }
public getCellValue = (rowIndex: number, columnName: string) : any => {
const row = this.rows[rowIndex] as any;
return row[columnName];
}
}
This is not a complete class, I only included a few methods to demonstrate the problem. ViewSource is a mutable object. It can be referenced from multiple parts of the application. (Please note that being a mutable object is a fact. This question is not about choosing a different data model that uses immutable objects.)
Whenever I want to change the state of a ViewSource object, I call its setViewName method. It does work, but it is also very clumsy. Every line of code in the constructor is repeated in the setViewName method.
Of course, it is not possible to use this constructor:
constructor(viewName : string) {
this.setViewName(viewName);
}
because that results in TS2564 error:
Property 'viewStruct' has no initializer and is not definitely assigned in the constructor.ts(2564)
I do not want to ignore TS2564 errors in general. But I also do not want to repeat all attribute initializations. I have some other classes with even more properties (>10), and the corresponding code duplication looks ugly, and it is error prone. (I might forget that some things have to bee modified in two methods...)
So how can I avoid duplicating many lines of code?
I think the best method to avoid code duplication in this case would be to create a function that contains the initialization code, but instead of setting the value, it retunrs the value that need to be set.
Something like the following:
export class ViewSource {
private viewName : string;
private viewStruct : IViewStruct;
private rows : any[];
private rowIndex : number|null;
constructor(viewName : string) {
const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
this.viewName = newViewName;
this.newViewStruct = newViewStruct;
// Rest of initialization goes here
}
public setViewName = (viewName: string) => {
const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
// Rest of initialization goes here
}
privat getNewValues = (viewName) => {
const newViewName = viewName;
const newViewStruct = api.meta.get_view_struct(viewName);
if (!newViewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
const newRows = [];
const newRowIndex = null;
return {newViewName, newViewStruct, newRows, newRowIndex};
}
}
This way the only thing you duplicate is setting the values, not calculating them, and if the values calculations will get more complicated you can simply expand the returned value.
A less complex approach than the accepted answer is to use the //#ts-ignore[1] comment above each member that is initialized elsewhere.
Consider this contrived example
class Foo {
// #ts-ignore TS2564 - initialized in the init method
a: number;
// #ts-ignore TS2564 - initialized in the init method
b: string;
// #ts-ignore TS2564 - initialized in the init method
c: number;
constructor(a: number, b: string) {
if(a === 0) {
this.init(a,b,100);
} else {
this.init(a,b,4912);
}
}
private init(a: number, b: string, c: number): void {
this.a = a;
this.b = b;
this.c = c;
}
}
Since TypeScript 3.9 there exists the //#ts-expect-error[2] comment, but I think #ts-ignore is suitable.
[1] Suppress errors in .ts files
[2] TS expect errors comment
Since TypeScript 2.7 you can use the definite assignment assertion modifier which means adding an exclamation mark between the variable name and the colon:
private viewName!: string
This has the same effect as adding a // #ts-ignore TS2564 comment above it as #RamblinRose suggested.

InputFilter not behaving correctly

I have the following input filter on my Xamarin.Android app. When created, it sets if the input is caps only, alpha only, numeric only, alpha with separators etc - it's fairly flexible. The code is a direct port of some Java code found on here.
public ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
{
if (source is SpannableStringBuilder)
{
var sourceAsSpannableBuilder = (SpannableStringBuilder)source;
for (var i = end - 1; i >= start; i--)
{
if (!isCharacterOk(source.CharAt(i)))
{
sourceAsSpannableBuilder.Delete(i, i + 1);
}
}
return source;
}
else
{
var filteredStringBuilder = new SpannableStringBuilder();
for (int i = start; i < end; i++)
{
var currentChar = source.CharAt(i);
if (isCharacterOk(currentChar))
{
filteredStringBuilder.Append(currentChar);
}
}
return filteredStringBuilder;
}
}
The isCharacterOK method checks if the character is correct or not (for example the filter for caps only checks if the character is Upper and if Alpha is set). It works happily.
The filter works fine for code going forward (for example, if I type ASDFGjhkl, only ASDFG show in the edittext).
The problem is when I press delete, the dest seems to still include hjkl which means I need to hit delete 5 times before the letter G is removed.
Have I stumbled on a Xamarin bug, an android oddity or is this correct behavior? It seems very strange that dest is somehow picking up characters not in the EditText widget.
you could change like this:
if (source is SpannableStringBuilder)
{
var sourceAsSpannableBuilder = (SpannableStringBuilder)source;
for (var i = end - 1; i >= start; i--)
{
if (!isCharacterOk(source.CharAt(i)))
{
sourceAsSpannableBuilder.Delete(i, i + 1);
//return the new SpannableStringBuilder.
sourceAsSpannableBuilder = new SpannableStringBuilder(sourceAsSpannableBuilder);
}
}
return sourceAsSpannableBuilder;
}
the effect like below:

jqgrid + EF + MVC: How to export in excel? Which method you suggest?

I am using jqgrid (standard) with EF 4 + MVC3. I'd like to implement excel export. Which method you would suggest me?
To generate excel, I'd like to use this library by Dr Stephen Walther, which has three types of output and allows to define headers too. Please tell me if you find it valid for my purpose.
I ask this question because I am still approaching to implement excel export and I found several techniques. Some suggest making a csv export, others indicate that it should return a JSON output and it is not clear to me whether this capability is present in the free version of jqgrid. In any case, I would like to pass the data to Walther's object.
About the jqgrid code, I found this interesting answer by Oleg, but I do not understand if could be applied to my needs.
Unfortunately, by now I only found parts of solutions for excel export with EF MVC, but no solution or complete examples...
About the MVC logic, I am going to implement and develop this code as kindly suggested by #Tommy.
Please sorry if the question could be silly, I am just a (enthusiast) beginner.
Thanks for your precious help!
Best Regards
As I wrote before (see here and here for example) the best way to export grid data to XML is the usage of Open XML SDK 2.0.
The post of Dr Stephen Walther shows how to create HTML file which can be read by Excel. It's not Excel file and have to be still converted to Excel format. The usage of CSV has even more problems. Depend on the content in the source table the automatic conversion to Excel data types can be absolutely wrong. In one project which I developed for a customer the grid contained information about software products: product name, version, and so on. The software version looks sometime as the date (1.3.1963 for example) and such cells will be wrong converted (in German one use '.' as the separator in the date). As the result one had really hard problems. The usage of CSV with texts having commas inside will be also frequently wrong imported. Even when one quotes the cells having commas (,) and escaped the texts having quotas the import still be wrong especially in the first column. I don't want to explain here the whole history of all attempts and errors, but after all I decide to give up with the usage of CSV and HTML and started to use Open XML SDK 2.0 which allows to create real Excel files with extension XLSX. The way seems me perfect because one don't need any Office
components installed on the server, no additional licenses.
The only restriction is that one should be able to use DocumentFormat.OpenXml.dll, so your server program should run on any Windows operation system. As it's well known, XLSX file is ZIP file which contains some XML files inside. If you still don't know that I recommend you to rename the XLSX file to ZIP file and extract it. The Open XML SDK 2.0 is the library which works with XLSX file like with XML files. So no additional Office components are required.
One can find a lot of information how to use Open XML SDK 2.0 (see here, here and here). Many helpful code examples one cam find directly on the MSDN (see here). Nevertheless the practical usage of Open XML SDK 2.0 is not so easy at least at the first time. So I created a demo from the parts of the code which I used myself.
You can download the demo project from here. The demo is an extension of the demos from the answer and this one.
To export data I use the DataForExcel helper class. It has constructor in the form
DataForExcel(string[] headers, DataType[] colunmTypes, List<string[]> data,
string sheetName)
or in a little simplified form
DataForExcel(string[] headers, List<string[]> data, string sheetName)
and the only public method
CreateXlsxAndFillData(Stream stream)
The usage of the class to create Excel file can be like the following
var excelData = new DataForExcel (
// column Header
new[]{"Col1", "Col2", "Col3"},
new[]{DataForExcel.DataType.String, DataForExcel.DataType.Integer,
DataForExcel.DataType.String},
new List<string[]> {
new[] {"a", "1", "c1"},
new[] {"a", "2", "c2"}
},
"Test Grid");
Stream stream = new FileStream ("Test.xlsx", FileMode.Create);
excelData.CreateXlsxAndFillData (stream);
stream.Close();
The usage in the demo from ASP.NET MVC is the following
static readonly string[] HeadersQuestions = {
"Id", "Votes", "Title"
};
static readonly DataForExcel.DataType[] ColunmTypesQuestions = {
DataForExcel.DataType.Integer,
DataForExcel.DataType.Integer,
DataForExcel.DataType.String
};
public ActionResult ExportAllQuestionsToExcel () {
var context = new HaackOverflowEntities ();
var questions = context.Questions;
questions.MergeOption = MergeOption.NoTracking; // we don't want to update the data
// to be able to use ToString() below which is NOT exist in the LINQ to Entity
// we should include in query only the properies which we will use below
var query = questions.ToList ();
if (query.Count == 0)
return new EmptyResult ();
var data = new List<string[]> (query.Count);
data.AddRange (query.Select (item => new[] {
item.Id.ToString(CultureInfo.InvariantCulture),
item.Votes.ToString(CultureInfo.InvariantCulture),
item.Title
}));
return new ExcelResult (HeadersQuestions, ColunmTypesQuestions, data,
"Questions.xlsx", "Questions");
}
where ExcelResult are defined as
public class ExcelResult : ActionResult {
private readonly DataForExcel _data;
private readonly string _fileName;
public ExcelResult (string[] headers, List<string[]> data, string fileName, string sheetName) {
_data = new DataForExcel (headers, data, sheetName);
_fileName = fileName;
}
public ExcelResult (string[] headers, DataForExcel.DataType[] colunmTypes, List<string[]> data, string fileName, string sheetName) {
_data = new DataForExcel (headers, colunmTypes, data, sheetName);
_fileName = fileName;
}
public override void ExecuteResult (ControllerContext context) {
var response = context.HttpContext.Response;
response.ClearContent();
response.ClearHeaders();
response.Cache.SetMaxAge (new TimeSpan (0));
using (var stream = new MemoryStream()) {
_data.CreateXlsxAndFillData (stream);
//Return it to the client - strFile has been updated, so return it.
response.AddHeader ("content-disposition", "attachment; filename=" + _fileName);
// see http://filext.com/faq/office_mime_types.php
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.ContentEncoding = Encoding.UTF8;
stream.WriteTo (response.OutputStream);
}
response.Flush();
response.Close();
}
}
To make the code full I have to include the code of the class DataForExcel:
public class DataForExcel {
public enum DataType {
String,
Integer
}
private readonly string[] _headers;
private readonly DataType[] _colunmTypes;
private readonly List<string[]> _data;
private readonly string _sheetName = "Grid1";
private readonly SortedSet<string> _os = new SortedSet<string> ();
private string[] _sharedStrings;
private static string ConvertIntToColumnHeader(int index) {
var sb = new StringBuilder ();
while (index > 0) {
if (index <= 'Z' - 'A') // index=0 -> 'A', 25 -> 'Z'
break;
sb.Append (ConvertIntToColumnHeader (index / ('Z' - 'A' + 1) - 1));
index = index % ('Z' - 'A' + 1);
}
sb.Append ((char)('A' + index));
return sb.ToString ();
}
private static Row CreateRow(UInt32 index, IList<string> data) {
var r = new Row { RowIndex = index };
for (var i = 0; i < data.Count; i++)
r.Append (new OpenXmlElement[] { CreateTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
return r;
}
private Row CreateRowWithSharedStrings(UInt32 index, IList<string> data) {
var r = new Row { RowIndex = index };
for (var i = 0; i < data.Count; i++)
r.Append (new OpenXmlElement[] { CreateSharedTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
return r;
}
private Row CreateRowWithSharedStrings(UInt32 index, IList<string> data, IList<DataType> colunmTypes) {
var r = new Row { RowIndex = index };
for (var i = 0; i < data.Count; i++)
if (colunmTypes != null && i < colunmTypes.Count && colunmTypes[i] == DataType.Integer)
r.Append (new OpenXmlElement[] { CreateNumberCell (ConvertIntToColumnHeader (i), index, data[i]) });
else
r.Append (new OpenXmlElement[] { CreateSharedTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
return r;
}
private static Cell CreateTextCell(string header, UInt32 index, string text) {
// create Cell with InlineString as a child, which has Text as a child
return new Cell (new InlineString (new Text { Text = text })) {
// Cell properties
DataType = CellValues.InlineString,
CellReference = header + index
};
}
private Cell CreateSharedTextCell(string header, UInt32 index, string text) {
for (var i=0; i<_sharedStrings.Length; i++) {
if (String.Compare (_sharedStrings[i], text, StringComparison.Ordinal) == 0) {
return new Cell (new CellValue { Text = i.ToString (CultureInfo.InvariantCulture) }) {
// Cell properties
DataType = CellValues.SharedString,
CellReference = header + index
};
}
}
// create Cell with InlineString as a child, which has Text as a child
throw new InstanceNotFoundException();
}
private static Cell CreateNumberCell(string header, UInt32 index, string numberAsString) {
// create Cell with CellValue as a child, which has Text as a child
return new Cell (new CellValue { Text = numberAsString }) {
// Cell properties
CellReference = header + index
};
}
private void FillSharedStringTable(IEnumerable<string> data) {
foreach (var item in data)
_os.Add (item);
}
private void FillSharedStringTable(IList<string> data, IList<DataType> colunmTypes) {
for (var i = 0; i < data.Count; i++)
if (colunmTypes == null || i >= colunmTypes.Count || colunmTypes[i] == DataType.String)
_os.Add (data[i]);
}
public DataForExcel(string[] headers, List<string[]> data, string sheetName) {
_headers = headers;
_data = data;
_sheetName = sheetName;
}
public DataForExcel(string[] headers, DataType[] colunmTypes, List<string[]> data, string sheetName) {
_headers = headers;
_colunmTypes = colunmTypes;
_data = data;
_sheetName = sheetName;
}
private void FillSpreadsheetDocument(SpreadsheetDocument spreadsheetDocument) {
// create and fill SheetData
var sheetData = new SheetData ();
// first row is the header
sheetData.AppendChild (CreateRow (1, _headers));
//const UInt32 iAutoFilter = 2;
// skip next row (number 2) for the AutoFilter
//var i = iAutoFilter + 1;
UInt32 i = 2;
// first of all collect all different strings in OrderedSet<string> _os
foreach (var dataRow in _data)
if (_colunmTypes != null)
FillSharedStringTable (dataRow, _colunmTypes);
else
FillSharedStringTable (dataRow);
_sharedStrings = _os.ToArray ();
foreach (var dataRow in _data)
sheetData.AppendChild (_colunmTypes != null
? CreateRowWithSharedStrings (i++, dataRow, _colunmTypes)
: CreateRowWithSharedStrings (i++, dataRow));
var sst = new SharedStringTable ();
foreach (var text in _os)
sst.AppendChild (new SharedStringItem (new Text (text)));
// add empty workbook and worksheet to the SpreadsheetDocument
var workbookPart = spreadsheetDocument.AddWorkbookPart ();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart> ();
var shareStringPart = workbookPart.AddNewPart<SharedStringTablePart> ();
shareStringPart.SharedStringTable = sst;
shareStringPart.SharedStringTable.Save ();
// add sheet data to Worksheet
worksheetPart.Worksheet = new Worksheet (sheetData);
worksheetPart.Worksheet.Save ();
// fill workbook with the Worksheet
spreadsheetDocument.WorkbookPart.Workbook = new Workbook (
new FileVersion { ApplicationName = "Microsoft Office Excel" },
new Sheets (
new Sheet {
Name = _sheetName,
SheetId = (UInt32Value)1U,
// generate the id for sheet
Id = workbookPart.GetIdOfPart (worksheetPart)
}
)
);
spreadsheetDocument.WorkbookPart.Workbook.Save ();
spreadsheetDocument.Close ();
}
public void CreateXlsxAndFillData(Stream stream) {
// Create workbook document
using (var spreadsheetDocument = SpreadsheetDocument.Create (stream, SpreadsheetDocumentType.Workbook)) {
FillSpreadsheetDocument (spreadsheetDocument);
}
}
}
The above code create new XLSX file directly. You can extend the code to support more data types as String and Integer which I used in the code.
In more professional version of your application you can create some XLSX templates for exporting different tables. In the code you can place the data in the cells instead, so modify the spreadsheet instead of creating. In the way you can create perfect formatted XLSX files. The examples from the MSDN (see here) will help you to implement the way when it will be required.
UPDATED: The answer contains updated code which allows generate Excel documented with more cell formatting.
I looked at Stephen's post and it's old as hell, which btw doesn't make it wrong.
If you don't need custom formatting, headers and styles, then I think use CSV as it's very simple.
More importantly, don't think that excel export from MVC site that internally uses EF for data access is harder than, say, Ruby on Rails site that uses ActiveRecord. For me it's independent concerns, export shouldn't new anything about underlying technologies (at least not directly), just the structure of your data, that's all.
Search for codeplex libraries that allows to do Excel reading/writing and export, there are plenty of them these days, many really good solutions that's regularly maintained and tested by thousand of developers all over the globe. If I were you I won't use Stephen solution because it looks like he occasionally typed it in a notepad and then pasted to the post - no unit tests, no extensibility points + it's in VB so it even harder to understand, but may be that's just me.
Hope this help and good luck

HLSL: Enforce Constant Register Limit at Compile Time

In HLSL, is there any way to limit the number of constant registers that the compiler uses?
Specifically, if I have something like:
float4 foobar[300];
In a vs_2_0 vertex shader, the compiler will merrily generate the effect with more than 256 constant registers. But a 2.0 vertex shader is only guaranteed to have access to 256 constant registers, so when I try to use the effect, it fails in an obscure and GPU-dependent way at runtime. I would much rather have it fail at compile time.
This problem is especially annoying as the compiler itself allocates constant registers behind the scenes, on top of the ones I am asking for. I have to check the assembly to see if I'm over the limit.
Ideally I'd like to do this in HLSL (I'm using the XNA content pipeline), but if there's a flag that can be passed to the compiler that would also be interesting.
Based on Stringer Bell's pointing out of the Disassemble method, I have whipped up a small post-build utility to parse and check the effect. Be warned that this is not very pretty. It is designed for XNA 3.1 and requires the ServiceContainer and GraphicsDeviceService classes from the XNA WinForms sample. Pass a content directory path on the command line with no trailing slash.
class Program
{
const int maxRegisters = 256; // Sutiable for VS 2.0, not much else
static int retval = 0;
static string root;
static ContentManager content;
static void CheckFile(string path)
{
string name = path.Substring(root.Length+1, path.Length - (root.Length+1) - #".xnb".Length);
Effect effect;
try { effect = content.Load<Effect>(name); }
catch { return; } // probably not an Effect
string effectSource = effect.Disassemble(false);
int highest = -1; // highest register allocated
var matches = Regex.Matches(effectSource, #" c([0-9]+)"); // quick and dirty
foreach(Match match in matches)
{
int register = Int32.Parse(match.Groups[1].ToString());
if(register > highest)
highest = register;
}
var parameters = Regex.Matches(effectSource, #"^ *// *[a-zA-Z_0-9]+ +c([0-9]+) +([0-9]+)", RegexOptions.Multiline);
foreach(Match match in parameters)
{
int register = Int32.Parse(match.Groups[1].ToString()) + Int32.Parse(match.Groups[2].ToString()) - 1;
if(register > highest)
highest = register;
}
if(highest+1 > maxRegisters)
{
Console.WriteLine("Error: Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers, which is TOO MANY!");
retval = 1;
}
else
{
Console.WriteLine("Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers (OK)");
}
}
static void CheckDirectory(string path)
{
try
{
foreach(string file in Directory.GetFiles(path, #"*.xnb"))
CheckFile(file);
foreach(string dir in Directory.GetDirectories(path))
CheckDirectory(dir);
}
catch { return; } // Don't care
}
static int Main(string[] args)
{
root = args[0];
Form form = new Form(); // Dummy form for creating a graphics device
GraphicsDeviceService gds = GraphicsDeviceService.AddRef(form.Handle,
form.ClientSize.Width, form.ClientSize.Height);
ServiceContainer services = new ServiceContainer();
services.AddService<IGraphicsDeviceService>(gds);
content = new ContentManager(services, root);
CheckDirectory(root);
return retval;
}
}

Resources